nodebpy 520.17.0__tar.gz → 520.19.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.
Files changed (87) hide show
  1. {nodebpy-520.17.0 → nodebpy-520.19.0}/PKG-INFO +3 -1
  2. {nodebpy-520.17.0 → nodebpy-520.19.0}/pyproject.toml +6 -1
  3. nodebpy-520.19.0/src/nodebpy/__init__.py +26 -0
  4. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/assets/__init__.py +5 -2
  5. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/assets/__main__.py +9 -5
  6. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/assets/_codegen.py +5 -5
  7. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/assets/_library.py +343 -61
  8. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/builder/__init__.py +14 -0
  9. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/builder/_utils.py +36 -0
  10. nodebpy-520.17.0/src/nodebpy/builder/arrange.py → nodebpy-520.19.0/src/nodebpy/builder/layout.py +267 -33
  11. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/builder/node.py +1 -1
  12. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/builder/tree.py +325 -42
  13. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/export/__init__.py +2 -0
  14. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/export/codegen.py +143 -49
  15. nodebpy-520.19.0/src/nodebpy/export/plot.py +225 -0
  16. nodebpy-520.19.0/src/nodebpy/lib/nodearrange/VENDORED.md +79 -0
  17. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/lib/nodearrange/arrange/graph.py +38 -18
  18. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/lib/nodearrange/arrange/ordering.py +10 -8
  19. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/lib/nodearrange/arrange/realize.py +35 -27
  20. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/lib/nodearrange/arrange/stacking.py +11 -11
  21. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/lib/nodearrange/arrange/structs.py +45 -16
  22. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/lib/nodearrange/arrange/sugiyama.py +113 -33
  23. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/lib/nodearrange/arrange/x_coords.py +17 -10
  24. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/lib/nodearrange/arrange/y_coords.py +30 -25
  25. nodebpy-520.19.0/src/nodebpy/lib/nodearrange/config.py +57 -0
  26. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/lib/nodearrange/utils.py +22 -20
  27. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/nodes/compositor/manual.py +2 -1
  28. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/nodes/geometry/manual.py +2 -1
  29. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/nodes/shader/manual.py +3 -2
  30. nodebpy-520.17.0/src/nodebpy/__init__.py +0 -14
  31. nodebpy-520.17.0/src/nodebpy/lib/nodearrange/config.py +0 -45
  32. {nodebpy-520.17.0 → nodebpy-520.19.0}/README.md +0 -0
  33. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/builder/_registry.py +0 -0
  34. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/builder/accessor.py +0 -0
  35. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/builder/asset.py +0 -0
  36. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/builder/items.py +0 -0
  37. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/builder/mixins.py +0 -0
  38. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/builder/socket.py +0 -0
  39. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/export/diagram.py +0 -0
  40. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/export/parity.py +0 -0
  41. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/export/web_render.py +0 -0
  42. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/lib/nodearrange/__init__.py +0 -0
  43. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/lib/nodearrange/arrange/__init__.py +0 -0
  44. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/lib/nodearrange/arrange/ranking.py +0 -0
  45. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/nodes/__init__.py +0 -0
  46. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/nodes/_mixins.py +0 -0
  47. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/nodes/compositor/__init__.py +0 -0
  48. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/nodes/compositor/assets.py +0 -0
  49. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/nodes/compositor/color.py +0 -0
  50. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/nodes/compositor/converter.py +0 -0
  51. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/nodes/compositor/distort.py +0 -0
  52. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/nodes/compositor/filter.py +0 -0
  53. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/nodes/compositor/group.py +0 -0
  54. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/nodes/compositor/input.py +0 -0
  55. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/nodes/compositor/interface.py +0 -0
  56. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/nodes/compositor/matte.py +0 -0
  57. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/nodes/compositor/output.py +0 -0
  58. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/nodes/compositor/vector.py +0 -0
  59. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/nodes/geometry/__init__.py +0 -0
  60. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/nodes/geometry/assets.py +0 -0
  61. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/nodes/geometry/attribute.py +0 -0
  62. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/nodes/geometry/color.py +0 -0
  63. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/nodes/geometry/converter.py +0 -0
  64. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/nodes/geometry/geometry.py +0 -0
  65. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/nodes/geometry/grid.py +0 -0
  66. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/nodes/geometry/group.py +0 -0
  67. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/nodes/geometry/groups.py +0 -0
  68. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/nodes/geometry/input.py +0 -0
  69. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/nodes/geometry/interface.py +0 -0
  70. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/nodes/geometry/output.py +0 -0
  71. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/nodes/geometry/texture.py +0 -0
  72. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/nodes/geometry/utilities.py +0 -0
  73. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/nodes/geometry/vector.py +0 -0
  74. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/nodes/geometry/zone.py +0 -0
  75. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/nodes/shader/__init__.py +0 -0
  76. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/nodes/shader/assets.py +0 -0
  77. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/nodes/shader/color.py +0 -0
  78. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/nodes/shader/converter.py +0 -0
  79. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/nodes/shader/grid.py +0 -0
  80. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/nodes/shader/group.py +0 -0
  81. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/nodes/shader/input.py +0 -0
  82. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/nodes/shader/output.py +0 -0
  83. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/nodes/shader/script.py +0 -0
  84. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/nodes/shader/shader.py +0 -0
  85. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/nodes/shader/texture.py +0 -0
  86. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/nodes/shader/vector.py +0 -0
  87. {nodebpy-520.17.0 → nodebpy-520.19.0}/src/nodebpy/types.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: nodebpy
3
- Version: 520.17.0
3
+ Version: 520.19.0
4
4
  Summary: Build nodes trees in Blender more elegantly with code
5
5
  Author: Brady Johnston
6
6
  Author-email: Brady Johnston <brady.johnston@me.com>
@@ -8,10 +8,12 @@ License: GPL-3.0-or-later
8
8
  Requires-Dist: bpy==5.2.* ; extra == 'bpy'
9
9
  Requires-Dist: ruff>=0.14.11 ; extra == 'format'
10
10
  Requires-Dist: networkx>=3.6.1 ; extra == 'networkx'
11
+ Requires-Dist: matplotlib>=3.8 ; extra == 'plot'
11
12
  Requires-Python: >=3.13
12
13
  Provides-Extra: bpy
13
14
  Provides-Extra: format
14
15
  Provides-Extra: networkx
16
+ Provides-Extra: plot
15
17
  Description-Content-Type: text/markdown
16
18
 
17
19
  # nodebpy
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "nodebpy"
3
- version = "520.17.0"
3
+ version = "520.19.0"
4
4
  description = "Build nodes trees in Blender more elegantly with code"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -16,6 +16,9 @@ nodebpy = "nodebpy:main"
16
16
  networkx = [
17
17
  "networkx>=3.6.1",
18
18
  ]
19
+ plot = [
20
+ "matplotlib>=3.8",
21
+ ]
19
22
  bpy = [
20
23
  "bpy==5.2.*",
21
24
  ]
@@ -46,6 +49,7 @@ dev = [
46
49
  "griffe<2.0.0",
47
50
  "ty==0.0.74",
48
51
  "types-networkx>=3.6.1.20260728",
52
+ "matplotlib>=3.11.2",
49
53
  ]
50
54
 
51
55
  # The bpy stubs mark most collections/pointers as `| None` and cannot type
@@ -67,3 +71,4 @@ ignore = ["SIM117", "RUF059"]
67
71
  [tool.ruff.lint.per-file-ignores]
68
72
  "tests/test_codegen.py" = ["S102"]
69
73
  "tests/test_assets.py" = ["S102"]
74
+ "tests/test_layout_snapshot.py" = ["S102"]
@@ -0,0 +1,26 @@
1
+ from . import builder, export, nodes, types
2
+ from .builder import (
3
+ ArrangeMethod,
4
+ SimpleOptions,
5
+ SugiyamaOptions,
6
+ TreeBuilder,
7
+ arrange,
8
+ default_sugiyama_options,
9
+ )
10
+ from .nodes import compositor, geometry, shader
11
+
12
+ __all__ = [
13
+ "ArrangeMethod",
14
+ "SimpleOptions",
15
+ "SugiyamaOptions",
16
+ "TreeBuilder",
17
+ "arrange",
18
+ "builder",
19
+ "compositor",
20
+ "default_sugiyama_options",
21
+ "export",
22
+ "geometry",
23
+ "nodes",
24
+ "shader",
25
+ "types",
26
+ ]
@@ -10,12 +10,14 @@ compositor}.assets`` and are re-exported alongside the built-in nodes.
10
10
  ``dump_library`` and ``build_library`` round-trip a ``.blend`` asset library
11
11
  through Python source: every asset is dumped to its own ``.py`` file (the
12
12
  version-controlled source of truth) and the ``.blend`` is rebuilt from them
13
- (``python -m nodebpy.assets dump/build``).
13
+ (``python -m nodebpy.assets dump/build``). ``plot_library`` renders node
14
+ groups from a ``.blend`` to PNG images by name or wildcard
15
+ (``python -m nodebpy.assets plot``), for review outside Blender.
14
16
  """
15
17
 
16
18
  from ..builder import AssetLibrary, BundledLibrary, PackageLibrary
17
19
  from ._codegen import generate_asset_api, generate_asset_modules
18
- from ._library import build_library, dump_library
20
+ from ._library import build_library, dump_library, plot_library
19
21
 
20
22
  __all__ = [
21
23
  "AssetLibrary",
@@ -25,4 +27,5 @@ __all__ = [
25
27
  "dump_library",
26
28
  "generate_asset_api",
27
29
  "generate_asset_modules",
30
+ "plot_library",
28
31
  ]
@@ -77,9 +77,12 @@ def parse_args() -> argparse.Namespace: # pragma: no cover - CLI wrapper
77
77
  ".blend to per-asset .py source files\n"
78
78
  " build <source-dir> <blend> rebuild the .blend asset library "
79
79
  "from dumped .py source files\n"
80
+ " plot <blend> <output-dir> [names ...]\n"
81
+ " render node groups (wildcards "
82
+ "supported) to PNG images\n"
80
83
  "\n"
81
- "See 'python -m nodebpy.assets dump --help' and "
82
- "'python -m nodebpy.assets build --help' for their options."
84
+ "See 'python -m nodebpy.assets dump --help', '… build --help' "
85
+ "and ' plot --help' for their options."
83
86
  ),
84
87
  formatter_class=argparse.RawDescriptionHelpFormatter,
85
88
  )
@@ -124,9 +127,10 @@ def parse_args() -> argparse.Namespace: # pragma: no cover - CLI wrapper
124
127
 
125
128
 
126
129
  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
+ # The dump/build/plot subcommands (blend ↔ .py round-trip, PNG renders)
131
+ # have their own parser; everything else keeps the original flag-based
132
+ # interface.
133
+ if len(sys.argv) > 1 and sys.argv[1] in ("dump", "build", "plot"):
130
134
  from ._library import main as library_main
131
135
 
132
136
  library_main(sys.argv[1:])
@@ -290,7 +290,7 @@ def _introspect(library: AssetLibrary, names: set[str] | None) -> list[_AssetCla
290
290
  def _library_source(library: AssetLibrary) -> str:
291
291
  """Source expression that reconstructs ``library`` in the generated module."""
292
292
  if isinstance(library, BundledLibrary):
293
- return f"BundledLibrary({library.filename!r})"
293
+ return f"BundledLibrary({_fmt(library.filename)})"
294
294
  from ..builder import PackageLibrary
295
295
 
296
296
  if isinstance(library, PackageLibrary):
@@ -298,7 +298,7 @@ def _library_source(library: AssetLibrary) -> str:
298
298
  # so the generated module imports cleanly and stays cross-platform even
299
299
  # when ``relative`` was passed as a ``Path``.
300
300
  relative = Path(library.relative).as_posix()
301
- return f"PackageLibrary(__file__, {relative!r})"
301
+ return f"PackageLibrary(__file__, {_fmt(relative)})"
302
302
  raise TypeError(f"Cannot serialise asset library: {library!r}")
303
303
 
304
304
 
@@ -355,8 +355,8 @@ def _render_class(cls: _AssetClass, docstrings: bool = False) -> str:
355
355
  return f"""class {cls.class_name}({base}):
356
356
  {docstring}
357
357
 
358
- _name = {cls.asset_name!r}
359
- _asset_name = {cls.asset_name!r}
358
+ _name = {_fmt(cls.asset_name)}
359
+ _asset_name = {_fmt(cls.asset_name)}
360
360
  _library = {cls.library_source}
361
361
 
362
362
  {inputs_cls}
@@ -451,7 +451,7 @@ def interface_parts(
451
451
  if library_source:
452
452
  base = asset_group_base(cls.tree_idname).__name__
453
453
  attr_lines = [
454
- f"_asset_name = {cls.asset_name!r}",
454
+ f"_asset_name = {_fmt(cls.asset_name)}",
455
455
  f"_library = {library_source}",
456
456
  ]
457
457