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()
treeAsset 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: 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:
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:
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:
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:
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:
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_apiin 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
Nonein the generated signature; the appended group keeps its own socket defaults. nodebpy’s own bundled-essentials modules are regenerated bypython -m nodebpy.assets(wired intomake generate).