---
title: Asset Node Groups
---

Blender ships node-group *assets* (the bundled "essentials" libraries), and many
add-ons distribute their own. `nodebpy` can generate typed Python classes for
these assets so they read, link and type-check **exactly like any built-in
node** — the only difference is that instantiating one *appends* the asset's
node group from its `.blend` at runtime (and points a Group node at it) instead
of building the tree from scratch.

This is the asset counterpart to [Custom Node Groups](custom-node-groups.qmd):
a custom group *builds* its tree in `_build_group`; an asset group *appends* a
pre-authored one.

## Using the bundled essentials

The classes for Blender's bundled essentials libraries are generated into
`nodebpy.nodes.{geometry,shader,compositor}` and exported alongside the built-in
nodes, so you reach them straight off the editor module:

```{python}
from nodebpy import geometry as g

with g.tree("Assets") as tree:
    mesh = g.SmoothByAngle(mesh=g.Cube(), angle=0.6).o.mesh
    _ = g.Array(geometry=mesh, count=4) >> tree.outputs.geometry()

tree
```

Each asset is a normal node: typed inputs/outputs (`g.SmoothByAngle().o.mesh` is
a `GeometrySocket`), `>>` chaining, IDE autocomplete, and the usual operators.
The underlying node group is appended once and reused on subsequent uses.

## Generating an API for your own assets

`generate_asset_api` introspects a `.blend` asset library and writes a module of
typed classes. Point it at your library and a destination `.py` file:

```{python}
# | eval: false
from nodebpy.assets import generate_asset_api, PackageLibrary

generate_asset_api(
    PackageLibrary(__file__, "data/my_assets.blend"),
    "my_addon/nodes/assets.py",
)
```

`PackageLibrary(anchor, relative)` locates a `.blend` shipped inside your own
package (resolved relative to `anchor`, usually `__file__`); use
`BundledLibrary("…")` for a library that ships with Blender. You can pass a list
of libraries to merge several into one module, and `names={...}` to restrict
generation to specific node groups.

If your libraries mix tree types, use `generate_asset_modules` instead — it
takes a directory and writes one module per tree type (`geometry.py`,
`shader.py`, `compositor.py`), skipping tree types with no assets:

```{python}
# | eval: false
from nodebpy.assets import generate_asset_modules, PackageLibrary

generate_asset_modules(
    PackageLibrary(__file__, "data/my_assets.blend"),
    "my_addon/nodes/",
)
```

Because asset names repeat across editors, splitting also keeps the generated
class names collision-free where a single mixed module would silently shadow
one tree type's class with another's.
=======
### Docstrings and menu types

By default the generated classes carry numpy-style docstrings built from the
asset's own interface — the group description, then `Parameters`, `Inputs` and
`Outputs` sections using each socket's tooltip — so editors show documentation
next to the type hints. Menu sockets are narrowed to the items they actually
offer:

```{python}
# | eval: false
def __init__(
    self,
    geometry: InputGeometry = None,
    shape: InputMenu | Literal["Line", "Circle", "Curve", "Transform"] = "Line",
    ...
```

Pass `docstrings=False` (or `--no-docstrings` on the command line) for a terser
module without the class docstrings.

The generated module imports from `nodebpy` and looks like any other node
module, so import and use it directly:

```{python}
# | eval: false
from my_addon.nodes import assets as a

with a.tree("Demo") as tree:
    _ = a.MyAsset(value=2.0) >> tree.outputs.geometry()
```

Re-run `generate_asset_api` whenever the `.blend` changes — the classes are
regenerated from the assets' current interfaces.

## How resolution works

Generated classes carry the library reference, not a hard-coded path, and the
group is located on demand:

| Library | Resolves to |
|:--------|:------------|
| `BundledLibrary("geometry_nodes_essentials.blend")` | Blender's system datafiles (`…/datafiles/assets/nodes/…`) |
| `PackageLibrary(__file__, "data/my_assets.blend")` | a path relative to the generated module's file |

Because asset names can collide across editors (a geometry **and** a compositor
"Combine Spherical" both exist), an appended group is reused only when its tree
type matches; otherwise the correct one is appended fresh.

## Notes

- The generator is a normal runtime tool — call `generate_asset_api` in a build
  step, a test, or once by hand; commit the generated module like the rest of
  your source.
- Inputs with non-scalar defaults (vectors, colours) take `None` in the
  generated signature; the appended group keeps its own socket defaults.
- `nodebpy`'s own bundled-essentials modules are regenerated by
  `python -m nodebpy.assets` (wired into `make generate`).
