nodebpy 520.16.0__tar.gz → 520.18.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {nodebpy-520.16.0 → nodebpy-520.18.0}/PKG-INFO +1 -1
- {nodebpy-520.16.0 → nodebpy-520.18.0}/pyproject.toml +1 -1
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/assets/__init__.py +8 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/assets/__main__.py +34 -2
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/assets/_codegen.py +119 -6
- nodebpy-520.18.0/src/nodebpy/assets/_library.py +1307 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/builder/__init__.py +2 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/builder/_utils.py +42 -2
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/builder/asset.py +42 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/builder/node.py +51 -1
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/builder/socket.py +109 -21
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/builder/tree.py +428 -12
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/export/__init__.py +3 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/export/codegen.py +1133 -142
- nodebpy-520.18.0/src/nodebpy/export/parity.py +545 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/nodes/_mixins.py +52 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/nodes/geometry/attribute.py +1 -1
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/nodes/geometry/converter.py +219 -178
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/nodes/geometry/geometry.py +7 -6
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/nodes/geometry/grid.py +287 -259
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/nodes/geometry/input.py +2 -2
- {nodebpy-520.16.0 → nodebpy-520.18.0}/README.md +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/__init__.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/builder/_registry.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/builder/accessor.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/builder/arrange.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/builder/items.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/builder/mixins.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/export/diagram.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/export/web_render.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/lib/nodearrange/__init__.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/lib/nodearrange/arrange/__init__.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/lib/nodearrange/arrange/graph.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/lib/nodearrange/arrange/ordering.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/lib/nodearrange/arrange/ranking.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/lib/nodearrange/arrange/realize.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/lib/nodearrange/arrange/stacking.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/lib/nodearrange/arrange/structs.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/lib/nodearrange/arrange/sugiyama.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/lib/nodearrange/arrange/x_coords.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/lib/nodearrange/arrange/y_coords.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/lib/nodearrange/config.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/lib/nodearrange/utils.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/nodes/__init__.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/nodes/compositor/__init__.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/nodes/compositor/assets.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/nodes/compositor/color.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/nodes/compositor/converter.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/nodes/compositor/distort.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/nodes/compositor/filter.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/nodes/compositor/group.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/nodes/compositor/input.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/nodes/compositor/interface.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/nodes/compositor/manual.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/nodes/compositor/matte.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/nodes/compositor/output.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/nodes/compositor/vector.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/nodes/geometry/__init__.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/nodes/geometry/assets.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/nodes/geometry/color.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/nodes/geometry/group.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/nodes/geometry/groups.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/nodes/geometry/interface.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/nodes/geometry/manual.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/nodes/geometry/output.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/nodes/geometry/texture.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/nodes/geometry/utilities.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/nodes/geometry/vector.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/nodes/geometry/zone.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/nodes/shader/__init__.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/nodes/shader/assets.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/nodes/shader/color.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/nodes/shader/converter.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/nodes/shader/grid.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/nodes/shader/group.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/nodes/shader/input.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/nodes/shader/manual.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/nodes/shader/output.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/nodes/shader/script.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/nodes/shader/shader.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/nodes/shader/texture.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/nodes/shader/vector.py +0 -0
- {nodebpy-520.16.0 → nodebpy-520.18.0}/src/nodebpy/types.py +0 -0
|
@@ -6,15 +6,23 @@ classes for the node groups in a ``.blend`` asset library into a single module;
|
|
|
6
6
|
(``geometry.py`` / ``shader.py`` / ``compositor.py``). The bundled-essentials
|
|
7
7
|
APIs generated for nodebpy itself live in ``nodebpy.nodes.{geometry,shader,
|
|
8
8
|
compositor}.assets`` and are re-exported alongside the built-in nodes.
|
|
9
|
+
|
|
10
|
+
``dump_library`` and ``build_library`` round-trip a ``.blend`` asset library
|
|
11
|
+
through Python source: every asset is dumped to its own ``.py`` file (the
|
|
12
|
+
version-controlled source of truth) and the ``.blend`` is rebuilt from them
|
|
13
|
+
(``python -m nodebpy.assets dump/build``).
|
|
9
14
|
"""
|
|
10
15
|
|
|
11
16
|
from ..builder import AssetLibrary, BundledLibrary, PackageLibrary
|
|
12
17
|
from ._codegen import generate_asset_api, generate_asset_modules
|
|
18
|
+
from ._library import build_library, dump_library
|
|
13
19
|
|
|
14
20
|
__all__ = [
|
|
15
21
|
"AssetLibrary",
|
|
16
22
|
"BundledLibrary",
|
|
17
23
|
"PackageLibrary",
|
|
24
|
+
"build_library",
|
|
25
|
+
"dump_library",
|
|
18
26
|
"generate_asset_api",
|
|
19
27
|
"generate_asset_modules",
|
|
20
28
|
]
|
|
@@ -5,12 +5,17 @@ libraries and writes typed classes to ``nodebpy/nodes/{geometry,shader,
|
|
|
5
5
|
compositor}/assets.py``, where ``python -m gen`` re-exports them so they are
|
|
6
6
|
available alongside the built-in nodes (e.g. ``g.SmoothByAngle()``). Run *before*
|
|
7
7
|
``python -m gen`` and through the ruff/ty post-processing (see the Makefile).
|
|
8
|
+
|
|
9
|
+
``python -m nodebpy.assets dump <blend> <dir>`` and ``… build <dir> <blend>``
|
|
10
|
+
instead round-trip a ``.blend`` asset library through per-asset Python sources
|
|
11
|
+
(see :mod:`nodebpy.assets._library`).
|
|
8
12
|
"""
|
|
9
13
|
|
|
10
14
|
from __future__ import annotations
|
|
11
15
|
|
|
12
16
|
import argparse
|
|
13
17
|
import os
|
|
18
|
+
import sys
|
|
14
19
|
from pathlib import Path
|
|
15
20
|
|
|
16
21
|
from ..builder import BundledLibrary, PackageLibrary
|
|
@@ -57,8 +62,27 @@ def generate_essentials(
|
|
|
57
62
|
return written
|
|
58
63
|
|
|
59
64
|
|
|
60
|
-
def parse_args() -> argparse.Namespace:
|
|
61
|
-
parser = argparse.ArgumentParser(
|
|
65
|
+
def parse_args() -> argparse.Namespace: # pragma: no cover - CLI wrapper
|
|
66
|
+
parser = argparse.ArgumentParser(
|
|
67
|
+
prog="python -m nodebpy.assets",
|
|
68
|
+
description=(
|
|
69
|
+
"Generate typed nodebpy API classes for node-group assets. "
|
|
70
|
+
"Without a subcommand this regenerates the bundled-essentials "
|
|
71
|
+
"asset APIs (or, with --blend-file, generates an API module for "
|
|
72
|
+
"a custom asset library)."
|
|
73
|
+
),
|
|
74
|
+
epilog=(
|
|
75
|
+
"subcommands:\n"
|
|
76
|
+
" dump <blend> <output-dir> dump every node-group asset in a "
|
|
77
|
+
".blend to per-asset .py source files\n"
|
|
78
|
+
" build <source-dir> <blend> rebuild the .blend asset library "
|
|
79
|
+
"from dumped .py source files\n"
|
|
80
|
+
"\n"
|
|
81
|
+
"See 'python -m nodebpy.assets dump --help' and "
|
|
82
|
+
"'python -m nodebpy.assets build --help' for their options."
|
|
83
|
+
),
|
|
84
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
85
|
+
)
|
|
62
86
|
parser.add_argument(
|
|
63
87
|
"--blend-file",
|
|
64
88
|
"-b",
|
|
@@ -100,6 +124,14 @@ def parse_args() -> argparse.Namespace:
|
|
|
100
124
|
|
|
101
125
|
|
|
102
126
|
def main() -> None: # pragma: no cover - CLI wrapper
|
|
127
|
+
# The dump/build subcommands (blend ↔ .py round-trip) have their own
|
|
128
|
+
# parser; everything else keeps the original flag-based interface.
|
|
129
|
+
if len(sys.argv) > 1 and sys.argv[1] in ("dump", "build"):
|
|
130
|
+
from ._library import main as library_main
|
|
131
|
+
|
|
132
|
+
library_main(sys.argv[1:])
|
|
133
|
+
return
|
|
134
|
+
|
|
103
135
|
args = parse_args()
|
|
104
136
|
output = (
|
|
105
137
|
args.output
|
|
@@ -22,7 +22,8 @@ from typing import cast
|
|
|
22
22
|
import bpy
|
|
23
23
|
|
|
24
24
|
from ..builder import AssetLibrary, BundledLibrary, asset_group_base
|
|
25
|
-
from ..builder._utils import normalize_name
|
|
25
|
+
from ..builder._utils import normalize_name, typed_param_names
|
|
26
|
+
from ..export.codegen import GroupInterface, _fmt
|
|
26
27
|
|
|
27
28
|
# bl_socket_type substring → (Socket accessor class, Input* parameter type).
|
|
28
29
|
# Order matters: more specific keys (IntVector before Int) come first.
|
|
@@ -90,7 +91,10 @@ def _format_default(socket: bpy.types.NodeSocket) -> str:
|
|
|
90
91
|
if isinstance(value, int):
|
|
91
92
|
return str(value)
|
|
92
93
|
if isinstance(value, float):
|
|
93
|
-
|
|
94
|
+
# The exact float32 round-trip formatter (which also folds pi/tau/e
|
|
95
|
+
# constants): a lossy default here would *change* the socket value on
|
|
96
|
+
# every instantiation, since the typed __init__ always passes it.
|
|
97
|
+
return _fmt(value)
|
|
94
98
|
if isinstance(value, str):
|
|
95
99
|
return repr(value)
|
|
96
100
|
return "None"
|
|
@@ -286,7 +290,7 @@ def _introspect(library: AssetLibrary, names: set[str] | None) -> list[_AssetCla
|
|
|
286
290
|
def _library_source(library: AssetLibrary) -> str:
|
|
287
291
|
"""Source expression that reconstructs ``library`` in the generated module."""
|
|
288
292
|
if isinstance(library, BundledLibrary):
|
|
289
|
-
return f"BundledLibrary({library.filename
|
|
293
|
+
return f"BundledLibrary({_fmt(library.filename)})"
|
|
290
294
|
from ..builder import PackageLibrary
|
|
291
295
|
|
|
292
296
|
if isinstance(library, PackageLibrary):
|
|
@@ -294,7 +298,7 @@ def _library_source(library: AssetLibrary) -> str:
|
|
|
294
298
|
# so the generated module imports cleanly and stays cross-platform even
|
|
295
299
|
# when ``relative`` was passed as a ``Path``.
|
|
296
300
|
relative = Path(library.relative).as_posix()
|
|
297
|
-
return f"PackageLibrary(__file__, {relative
|
|
301
|
+
return f"PackageLibrary(__file__, {_fmt(relative)})"
|
|
298
302
|
raise TypeError(f"Cannot serialise asset library: {library!r}")
|
|
299
303
|
|
|
300
304
|
|
|
@@ -351,8 +355,8 @@ def _render_class(cls: _AssetClass, docstrings: bool = False) -> str:
|
|
|
351
355
|
return f"""class {cls.class_name}({base}):
|
|
352
356
|
{docstring}
|
|
353
357
|
|
|
354
|
-
_name = {cls.asset_name
|
|
355
|
-
_asset_name = {cls.asset_name
|
|
358
|
+
_name = {_fmt(cls.asset_name)}
|
|
359
|
+
_asset_name = {_fmt(cls.asset_name)}
|
|
356
360
|
_library = {cls.library_source}
|
|
357
361
|
|
|
358
362
|
{inputs_cls}
|
|
@@ -370,6 +374,112 @@ def _render_class(cls: _AssetClass, docstrings: bool = False) -> str:
|
|
|
370
374
|
"""
|
|
371
375
|
|
|
372
376
|
|
|
377
|
+
def interface_parts(
|
|
378
|
+
group,
|
|
379
|
+
*,
|
|
380
|
+
library_source: str | None,
|
|
381
|
+
nodebpy_pkg: str = "nodebpy",
|
|
382
|
+
) -> tuple[GroupInterface, list[str]]:
|
|
383
|
+
"""The typed-interface parts of a merged dump class for ``group`` (a live
|
|
384
|
+
``bpy.types.NodeTree``), plus the import lines they require.
|
|
385
|
+
|
|
386
|
+
The parts — numpydoc docstring, ``_asset_name``/``_library`` attributes,
|
|
387
|
+
``_Inputs``/``_Outputs`` accessors and the typed ``__init__`` — are spliced
|
|
388
|
+
by :func:`nodebpy.export.to_python` into the class it emits around
|
|
389
|
+
``_build_group``, so one class both documents the group and carries the
|
|
390
|
+
recipe that regenerates it.
|
|
391
|
+
|
|
392
|
+
With ``library_source`` (a ``PackageLibrary(...)`` expression), the class
|
|
393
|
+
becomes an appending ``Asset*Group``; without it (shared helper groups,
|
|
394
|
+
which are not assets), only the typed API is added and the ``Custom*Group``
|
|
395
|
+
base stays.
|
|
396
|
+
|
|
397
|
+
The ``__init__`` keys its super call by socket *name* (with a
|
|
398
|
+
``_named_links`` fallback for duplicate names) rather than by identifier:
|
|
399
|
+
identifiers are authoring-history artifacts that a tree rebuilt from
|
|
400
|
+
``_build_group`` reassigns, while names round-trip. Parameter names come
|
|
401
|
+
from :func:`~nodebpy.builder._utils.typed_param_names` — the same mapping
|
|
402
|
+
codegen uses for call sites, so generated calls match the signature.
|
|
403
|
+
"""
|
|
404
|
+
cls = _introspect_group(group, group.name, library_source or "")
|
|
405
|
+
# Align accessor/parameter names with the shared call-site mapping.
|
|
406
|
+
for side in (cls.inputs, cls.outputs):
|
|
407
|
+
params = typed_param_names(side)
|
|
408
|
+
for s in side:
|
|
409
|
+
s.attr = params[s.identifier]
|
|
410
|
+
|
|
411
|
+
docstring = f" {_class_docstring(cls)}"
|
|
412
|
+
|
|
413
|
+
params = [f"{s.attr}: {s.param_type} = {s.default}" for s in cls.inputs]
|
|
414
|
+
signature = (
|
|
415
|
+
"(\n self,\n " + ",\n ".join(params) + ",\n )"
|
|
416
|
+
if params
|
|
417
|
+
else "(self)"
|
|
418
|
+
)
|
|
419
|
+
name_counts = Counter(s.name for s in cls.inputs)
|
|
420
|
+
keyed = [
|
|
421
|
+
f"{_quote(s.name)}: {s.attr}" for s in cls.inputs if name_counts[s.name] == 1
|
|
422
|
+
]
|
|
423
|
+
pairs = [
|
|
424
|
+
f"({_quote(s.name)}, {s.attr})" for s in cls.inputs if name_counts[s.name] > 1
|
|
425
|
+
]
|
|
426
|
+
args = []
|
|
427
|
+
if keyed:
|
|
428
|
+
args.append("**{" + ", ".join(keyed) + "}")
|
|
429
|
+
if pairs:
|
|
430
|
+
args.append("_named_links=[" + ", ".join(pairs) + "]")
|
|
431
|
+
init = f" def __init__{signature}:\n super().__init__({', '.join(args)})"
|
|
432
|
+
|
|
433
|
+
type_checking = (
|
|
434
|
+
" if TYPE_CHECKING:\n"
|
|
435
|
+
" @property\n"
|
|
436
|
+
" def i(self) -> _Inputs: ...\n"
|
|
437
|
+
" @property\n"
|
|
438
|
+
" def o(self) -> _Outputs: ..."
|
|
439
|
+
)
|
|
440
|
+
body = "\n\n".join(
|
|
441
|
+
[
|
|
442
|
+
_accessor(cls.inputs, "_Inputs", docstrings=True),
|
|
443
|
+
_accessor(cls.outputs, "_Outputs", docstrings=True),
|
|
444
|
+
type_checking,
|
|
445
|
+
init,
|
|
446
|
+
]
|
|
447
|
+
)
|
|
448
|
+
|
|
449
|
+
base: str | None = None
|
|
450
|
+
attr_lines: list[str] = []
|
|
451
|
+
if library_source:
|
|
452
|
+
base = asset_group_base(cls.tree_idname).__name__
|
|
453
|
+
attr_lines = [
|
|
454
|
+
f"_asset_name = {_fmt(cls.asset_name)}",
|
|
455
|
+
f"_library = {library_source}",
|
|
456
|
+
]
|
|
457
|
+
|
|
458
|
+
socket_classes = sorted({s.socket_class for s in cls.inputs + cls.outputs})
|
|
459
|
+
input_types = sorted({s.input_type for s in cls.inputs})
|
|
460
|
+
typing_names = ["TYPE_CHECKING"]
|
|
461
|
+
if any(s.menu_items for s in cls.inputs):
|
|
462
|
+
typing_names.append("Literal")
|
|
463
|
+
builder_names = {"SocketAccessor", *socket_classes}
|
|
464
|
+
if library_source:
|
|
465
|
+
builder_names.add(library_source.partition("(")[0])
|
|
466
|
+
import_lines = [
|
|
467
|
+
f"from typing import {', '.join(typing_names)}",
|
|
468
|
+
f"from {nodebpy_pkg}.builder import {', '.join(sorted(builder_names))}",
|
|
469
|
+
]
|
|
470
|
+
if any("math." in s.default for s in cls.inputs):
|
|
471
|
+
import_lines.insert(0, "import math")
|
|
472
|
+
if input_types:
|
|
473
|
+
import_lines.append(f"from {nodebpy_pkg}.types import {', '.join(input_types)}")
|
|
474
|
+
|
|
475
|
+
return (
|
|
476
|
+
GroupInterface(
|
|
477
|
+
docstring=docstring, body=body, base=base, attr_lines=attr_lines
|
|
478
|
+
),
|
|
479
|
+
import_lines,
|
|
480
|
+
)
|
|
481
|
+
|
|
482
|
+
|
|
373
483
|
def _render_module(
|
|
374
484
|
classes: list[_AssetClass],
|
|
375
485
|
nodebpy_pkg: str = "nodebpy",
|
|
@@ -399,6 +509,9 @@ def _render_module(
|
|
|
399
509
|
|
|
400
510
|
lines = [
|
|
401
511
|
"# Auto-generated by nodebpy.assets.generate_asset_api — do not edit manually.",
|
|
512
|
+
"import math"
|
|
513
|
+
if any("math." in s.default for c in classes for s in c.inputs)
|
|
514
|
+
else "",
|
|
402
515
|
f"from typing import {', '.join(typing_imports)}",
|
|
403
516
|
"",
|
|
404
517
|
f"from {nodebpy_pkg}.builder import (\n {',\n '.join(builder_imports)},\n)",
|