# Node variants
URL: https://lsd.tools/docs/tools-variants
Updated: 2026-07-17

> Pass overrides="pkg.node" to a step to make it a variant of another package's node identity. override_mode="replace" makes your step win that identity by default; "coexist" keeps both providers available so the user (or Engine.set_variant) picks. Identity is "toolname.funcname", so a variant targets an existing node without editing it.

A **variant** is a step that stands in for another node's *identity* without editing the original
package. You point at the target with `overrides="pkg.node"` and choose how they relate with
`override_mode`. Recall that a node's identity is `f"{tool_name}.{fn_name}"` — that string is what a
variant targets.

## Usage

```python
from lsdtools import Tool, Table

tool = Tool("fastmath")

@tool.shape(overrides="stats.normalize", override_mode="replace")
def normalize(t: Table, column: str) -> Table:
    """A faster drop-in for stats.normalize — same identity, my code."""
    ...
# → wherever "stats.normalize" is used, this provider now runs
```

## `replace` vs `coexist`

- **`replace`** (the default) — your step **wins the identity by default**. Anywhere the target node
 is used, your implementation runs. Use it for a straight upgrade or a bug-fixed drop-in.
- **`coexist`** — the original and your step **both stay available** for the identity. Neither wins
 automatically; the user picks the active provider (in the Package Manager, or via the engine).
 Use it when both implementations are legitimately useful for different projects.

## A real case: openpit's `pit_shell`

The `mim-openpit` package offers a pit-specific solid that stands in for the base `mim.solid` node,
but the generic solid stays useful — so it **coexists** rather than replaces:

```python
from lsdtools import Tool, Table

tool = Tool("openpit", label="Open Pit")

@tool.shape(overrides="mim.solid", override_mode="coexist")
def pit_shell(t: Table, ramp_width: float = 30.0) -> Table:
    """An open-pit shell that coexists with mim.solid.
    The user chooses which provider builds the node."""
    ...
```

Both `mim.solid` and this `openpit.pit_shell` are then offered for the `mim.solid` identity — a
project that never mines open pits simply leaves the default provider selected.

## Choosing the active provider

For a **coexist** identity, the desktop [Package Manager](/docs/pkg-manager) shows a *variants*
section where you pick the active provider per project. From Python, use `Engine.set_variant`:

```python
from lsdtools import Engine

eng = Engine("mine.lsd")
eng.set_variant("mim.solid", mode="coexist", provider="openpit.pit_shell")
```

The choice is project-scoped, so different projects on the same install can resolve the same
identity to different providers.

## Related

**Learn** — [The Tool object](/docs/tools-tool-object) · [Entity types](/docs/tools-entities) · [The Package Manager](/docs/pkg-manager)

**API** — [Tool](/docs/api-tool) · [Engine](/docs/api-engine) · [Table](/docs/api-table)

**Examples** — [A node variant](/docs/ex-variant) · [A complete package](/docs/ex-full-package)

## FAQ
### What is the difference between replace and coexist?

replace makes your step the default provider of the target identity — it silently swaps in wherever that node is used. coexist keeps the original and yours both available for the identity, and the user chooses the active provider in the Package Manager.

### Does a variant edit the original package?

No. A variant targets another node's identity string from your own package — the original is untouched. Uninstall your package and the original provider takes over again.
