nodebpy 520.26.0__tar.gz → 520.27.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 (88) hide show
  1. {nodebpy-520.26.0 → nodebpy-520.27.0}/PKG-INFO +1 -1
  2. {nodebpy-520.26.0 → nodebpy-520.27.0}/pyproject.toml +1 -1
  3. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/assets/__main__.py +4 -1
  4. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/assets/_library.py +83 -32
  5. {nodebpy-520.26.0 → nodebpy-520.27.0}/README.md +0 -0
  6. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/__init__.py +0 -0
  7. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/assets/__init__.py +0 -0
  8. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/assets/_codegen.py +0 -0
  9. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/assets/_pipeline.py +0 -0
  10. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/builder/__init__.py +0 -0
  11. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/builder/_registry.py +0 -0
  12. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/builder/_socket_order.py +0 -0
  13. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/builder/_utils.py +0 -0
  14. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/builder/accessor.py +0 -0
  15. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/builder/asset.py +0 -0
  16. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/builder/items.py +0 -0
  17. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/builder/layout.py +0 -0
  18. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/builder/mixins.py +0 -0
  19. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/builder/node.py +0 -0
  20. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/builder/socket.py +0 -0
  21. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/builder/tree.py +0 -0
  22. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/export/__init__.py +0 -0
  23. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/export/codegen.py +0 -0
  24. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/export/diagram.py +0 -0
  25. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/export/parity.py +0 -0
  26. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/export/plot.py +0 -0
  27. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/export/web_render.py +0 -0
  28. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/lib/nodearrange/VENDORED.md +0 -0
  29. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/lib/nodearrange/__init__.py +0 -0
  30. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/lib/nodearrange/arrange/__init__.py +0 -0
  31. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/lib/nodearrange/arrange/balancing.py +0 -0
  32. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/lib/nodearrange/arrange/graph.py +0 -0
  33. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/lib/nodearrange/arrange/ordering.py +0 -0
  34. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/lib/nodearrange/arrange/ranking.py +0 -0
  35. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/lib/nodearrange/arrange/realize.py +0 -0
  36. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/lib/nodearrange/arrange/stacking.py +0 -0
  37. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/lib/nodearrange/arrange/structs.py +0 -0
  38. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/lib/nodearrange/arrange/sugiyama.py +0 -0
  39. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/lib/nodearrange/arrange/x_coords.py +0 -0
  40. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/lib/nodearrange/arrange/y_coords.py +0 -0
  41. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/lib/nodearrange/config.py +0 -0
  42. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/lib/nodearrange/utils.py +0 -0
  43. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/nodes/__init__.py +0 -0
  44. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/nodes/_mixins.py +0 -0
  45. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/nodes/compositor/__init__.py +0 -0
  46. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/nodes/compositor/assets.py +0 -0
  47. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/nodes/compositor/color.py +0 -0
  48. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/nodes/compositor/converter.py +0 -0
  49. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/nodes/compositor/distort.py +0 -0
  50. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/nodes/compositor/filter.py +0 -0
  51. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/nodes/compositor/group.py +0 -0
  52. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/nodes/compositor/input.py +0 -0
  53. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/nodes/compositor/interface.py +0 -0
  54. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/nodes/compositor/manual.py +0 -0
  55. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/nodes/compositor/matte.py +0 -0
  56. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/nodes/compositor/output.py +0 -0
  57. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/nodes/compositor/vector.py +0 -0
  58. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/nodes/geometry/__init__.py +0 -0
  59. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/nodes/geometry/assets.py +0 -0
  60. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/nodes/geometry/attribute.py +0 -0
  61. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/nodes/geometry/color.py +0 -0
  62. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/nodes/geometry/converter.py +0 -0
  63. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/nodes/geometry/geometry.py +0 -0
  64. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/nodes/geometry/grid.py +0 -0
  65. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/nodes/geometry/group.py +0 -0
  66. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/nodes/geometry/groups.py +0 -0
  67. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/nodes/geometry/input.py +0 -0
  68. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/nodes/geometry/interface.py +0 -0
  69. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/nodes/geometry/manual.py +0 -0
  70. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/nodes/geometry/output.py +0 -0
  71. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/nodes/geometry/texture.py +0 -0
  72. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/nodes/geometry/utilities.py +0 -0
  73. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/nodes/geometry/vector.py +0 -0
  74. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/nodes/geometry/zone.py +0 -0
  75. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/nodes/shader/__init__.py +0 -0
  76. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/nodes/shader/assets.py +0 -0
  77. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/nodes/shader/color.py +0 -0
  78. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/nodes/shader/converter.py +0 -0
  79. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/nodes/shader/grid.py +0 -0
  80. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/nodes/shader/group.py +0 -0
  81. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/nodes/shader/input.py +0 -0
  82. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/nodes/shader/manual.py +0 -0
  83. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/nodes/shader/output.py +0 -0
  84. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/nodes/shader/script.py +0 -0
  85. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/nodes/shader/shader.py +0 -0
  86. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/nodes/shader/texture.py +0 -0
  87. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/nodes/shader/vector.py +0 -0
  88. {nodebpy-520.26.0 → nodebpy-520.27.0}/src/nodebpy/types.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: nodebpy
3
- Version: 520.26.0
3
+ Version: 520.27.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>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "nodebpy"
3
- version = "520.26.0"
3
+ version = "520.27.0"
4
4
  description = "Build nodes trees in Blender more elegantly with code"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -90,8 +90,11 @@ def parse_args(
90
90
  ),
91
91
  epilog=(
92
92
  "subcommands:\n"
93
- " dump <blend> <output-dir> dump every node-group asset in a "
93
+ " dump <blend> <output-dir> [--names ...]\n"
94
+ " dump every node-group asset in a "
94
95
  ".blend to per-asset .py source files\n"
96
+ " (or only those matching --names, "
97
+ "wildcards supported)\n"
95
98
  " build <source-dir> <blend> rebuild the .blend asset library "
96
99
  "from dumped .py source files\n"
97
100
  " ensure <source-dir> <blend> rebuild only when the .blend or "
@@ -467,7 +467,7 @@ def dump_library(
467
467
  blend_path: str | Path,
468
468
  output_dir: str | Path,
469
469
  *,
470
- names: set[str] | None = None,
470
+ names: Iterable[str] | None = None,
471
471
  nodebpy_pkg: str = "nodebpy",
472
472
  snapshot_positions: bool = False,
473
473
  keep_reroutes: bool = False,
@@ -511,11 +511,20 @@ def dump_library(
511
511
  output_dir:
512
512
  Directory to write the per-asset modules into (created if needed).
513
513
  names:
514
- Restrict the dump to these asset (node-group or material) names;
515
- defaults to all. A full dump first clears the managed subdirectories
516
- (``geometry``/``shader``/``compositor``/``materials``) so files from
517
- renamed or deleted assets don't linger; a filtered dump leaves the
518
- other assets' files in place.
514
+ Restrict the dump to the assets (node groups or materials) matching
515
+ these exact names or :mod:`fnmatch` wildcard patterns (``"Style *"``);
516
+ defaults to all. A pattern matching nothing raises. Meant for quick
517
+ iteration on a few assets: only the selected assets' modules are
518
+ regenerated, plus the ``_shared`` helper and ``materials/`` modules
519
+ they depend on — byte-identical to what a full dump writes for them,
520
+ because the whole library is still appended to decide what is shared
521
+ (a helper also used by an unselected asset stays in ``_shared``;
522
+ an unselected nested asset stays imported from its own module).
523
+ Edits to unselected assets are not picked up; a full dump (or the
524
+ ``check`` subcommand) catches those. A full dump first clears the
525
+ managed subdirectories (``geometry``/``shader``/``compositor``/
526
+ ``materials``) so files from renamed or deleted assets don't linger;
527
+ a filtered dump leaves the other assets' files in place.
519
528
  nodebpy_pkg:
520
529
  Import anchor for nodebpy in the generated sources, as for
521
530
  :func:`nodebpy.export.to_python`.
@@ -572,13 +581,18 @@ def dump_library(
572
581
  else (output_dir, blend_path)
573
582
  )
574
583
 
575
- # Append every wanted asset in one load, so groups shared between assets
576
- # (including assets nested in other assets) arrive as single trees and the
577
- # sharing structure can be read off the session directly. Dependencies of
578
- # other kinds (materials, images, …) come along too; everything appended
579
- # is cleaned back out afterwards. The pre-append checks run inside the
580
- # load context (the load itself happens on exit, with nothing selected
581
- # when a check raises), so the .blend is opened only once.
584
+ # Append every asset in one load — even for a filtered dump — so groups
585
+ # shared between assets (including assets nested in other assets) arrive
586
+ # as single trees and the sharing structure can be read off the session
587
+ # directly: whether a helper is embedded or goes to _shared/ depends on
588
+ # how many assets reach it, and an asset outside the selection counts
589
+ # just the same. Appending is cheap next to code generation, which only
590
+ # runs for the selected roots. Dependencies of other kinds (materials,
591
+ # images, …) come along too; everything appended is cleaned back out
592
+ # afterwards. The pre-append checks run inside the load context (the
593
+ # load itself happens on exit, with nothing selected when a check
594
+ # raises), so the .blend is opened only once.
595
+ patterns = list(names) if names is not None else None
582
596
  before = {
583
597
  coll: set(getattr(bpy.data, coll).keys()) for coll in _CLEANUP_COLLECTIONS
584
598
  }
@@ -589,25 +603,28 @@ def dump_library(
589
603
  # assets_only exposes exactly the asset-marked materials: they are
590
604
  # dump roots alongside the node-group assets.
591
605
  available_materials = list(src.materials)
592
- wanted = [n for n in available if names is None or n in names]
593
- wanted_materials = [
594
- n for n in available_materials if names is None or n in names
595
- ]
596
- if names is not None and (
597
- missing := names - set(wanted) - set(wanted_materials)
598
- ):
599
- raise KeyError(f"Assets not found in {blend_path}: {sorted(missing)}")
600
- clashes = sorted(n for n in wanted if n in bpy.data.node_groups)
606
+ selected: set[str] | None = None
607
+ if patterns is not None:
608
+ roots = available + available_materials
609
+ selected = {
610
+ n for n in roots if any(fnmatch.fnmatchcase(n, p) for p in patterns)
611
+ }
612
+ unmatched = [
613
+ p for p in patterns if not any(fnmatch.fnmatchcase(n, p) for n in roots)
614
+ ]
615
+ if unmatched:
616
+ raise KeyError(f"No assets in {blend_path} match: {unmatched}")
617
+ clashes = sorted(n for n in available if n in bpy.data.node_groups)
601
618
  if clashes:
602
619
  raise RuntimeError(
603
620
  f"Node groups already exist in this session: {clashes}. "
604
621
  "Appending would rename them and corrupt the dumped sources — "
605
622
  "dump from a fresh session (e.g. python -m nodebpy.assets dump)."
606
623
  )
607
- dst.node_groups = list(wanted)
624
+ dst.node_groups = list(available)
608
625
  # A same-named material already in the session renames the appended
609
626
  # one — caught by the post-append renamed-datablock guard below.
610
- dst.materials = list(wanted_materials)
627
+ dst.materials = list(available_materials)
611
628
  added = {
612
629
  coll: [db for db in getattr(bpy.data, coll) if db.name not in before[coll]]
613
630
  for coll in _CLEANUP_COLLECTIONS
@@ -641,11 +658,12 @@ def dump_library(
641
658
  format=format,
642
659
  library_blend=anchor_blend.resolve() if typed_api else None,
643
660
  anchor_dir=anchor_dir.resolve(),
661
+ roots=selected,
644
662
  # A full dump owns the managed subdirectories: clear stale modules
645
663
  # from assets since renamed or deleted, so the next build_library
646
664
  # doesn't silently resurrect them. A filtered dump (names=...)
647
665
  # leaves the other assets' files alone.
648
- clean_stale=names is None,
666
+ clean_stale=selected is None,
649
667
  )
650
668
  finally:
651
669
  for coll in _CLEANUP_COLLECTIONS:
@@ -699,6 +717,7 @@ def _dump_appended(
699
717
  format: bool,
700
718
  library_blend: Path | None = None,
701
719
  anchor_dir: Path | None = None,
720
+ roots: set[str] | None = None,
702
721
  clean_stale: bool = False,
703
722
  ) -> dict[str, Path]:
704
723
  """Partition the appended groups (and material roots and referenced
@@ -713,7 +732,11 @@ def _dump_appended(
713
732
  typed API — see ``dump_library(typed_api=...)`` — and ``anchor_dir``
714
733
  (default: ``output_dir``) is the directory the ``PackageLibrary``
715
734
  relative paths are computed against — see
716
- ``dump_library(library_anchor=...)``. ``clean_stale`` removes
735
+ ``dump_library(library_anchor=...)``. ``roots`` restricts the modules
736
+ written to those assets (node-group or material names) plus the shared
737
+ helpers and materials they depend on — the partition itself is always
738
+ computed over every appended asset, so a filtered dump writes exactly
739
+ what a full one would for those modules. ``clean_stale`` removes
717
740
  modules under the managed subdirectories that this dump did not write
718
741
  (hand-written ``__init__.py`` files are kept, as ever).
719
742
  """
@@ -820,6 +843,24 @@ def _dump_appended(
820
843
  # and group calls to any of them use the typed parameter names.
821
844
  typed_groups = external if library_blend is not None else set()
822
845
 
846
+ # The modules this dump writes. A filtered dump writes the selected roots
847
+ # plus the modules they depend on that no other asset owns: the _shared
848
+ # helpers in their closures, the materials their footers record and those
849
+ # materials' shared helpers. Unselected assets — nested in a selected one
850
+ # or not — keep their own modules untouched and are imported as usual.
851
+ if roots is None:
852
+ write_assets = list(asset_trees)
853
+ write_materials = set(material_trees)
854
+ else:
855
+ write_assets = [t for t in asset_trees if t.name in roots]
856
+ write_materials = {k for k, m in material_trees.items() if m in roots}
857
+ for tree in write_assets:
858
+ for mat_name in module_dependencies(tree.name).get("materials", ()):
859
+ if (key := f"material:{mat_name}") in material_trees:
860
+ write_materials.add(key)
861
+ write_roots = {t.name for t in write_assets} | write_materials
862
+ write_shared = shared & set().union(*(closures[r] for r in write_roots))
863
+
823
864
  def write_module(key: str, module: str, *, kind: str) -> Path:
824
865
  group = trees[key]
825
866
  # Emitted here: the tree itself plus, for roots, its private helpers.
@@ -876,13 +917,13 @@ def _dump_appended(
876
917
  return path
877
918
 
878
919
  written: dict[str, Path] = {}
879
- for tree in asset_trees:
920
+ for tree in write_assets:
880
921
  written[tree.name] = write_module(tree.name, modules[tree.name], kind="asset")
881
922
  all_written = set(written.values())
882
- for name in sorted(shared):
923
+ for name in sorted(write_shared):
883
924
  all_written.add(write_module(name, modules[name], kind="shared"))
884
925
  asset_material_names = set(asset_materials)
885
- for key in sorted(material_trees):
926
+ for key in sorted(write_materials):
886
927
  path = write_module(key, modules[key], kind="material")
887
928
  all_written.add(path)
888
929
  # Material roots are assets the caller asked for, so they belong in
@@ -895,12 +936,14 @@ def _dump_appended(
895
936
  if library_blend is not None:
896
937
  # Typed API: each tree directory re-exports its asset classes, so
897
938
  # ``from <pkg>.<tree_dir> import <Class>`` (or an aliased module
898
- # import) works like the old single-file API.
939
+ # import) works like the old single-file API. A filtered dump only
940
+ # exports modules that exist on disk (a full dump wrote them all).
899
941
  for dirname in set(_TREE_DIRS.values()):
900
942
  exports = sorted(
901
943
  (modules[t.name].split("/")[1], class_names[t.name])
902
944
  for t in asset_trees
903
945
  if modules[t.name].startswith(f"{dirname}/")
946
+ and (output_dir / f"{modules[t.name]}.py").is_file()
904
947
  )
905
948
  if exports:
906
949
  _write_dir_exports(output_dir / dirname, exports)
@@ -1674,7 +1717,15 @@ def _parse_args(argv: list[str] | None = None):
1674
1717
  dump.add_argument(
1675
1718
  "--names",
1676
1719
  nargs="+",
1677
- help="Only dump these asset (node-group or material) names (default: all).",
1720
+ metavar="NAME",
1721
+ help=(
1722
+ "Only dump the assets (node groups or materials) matching these "
1723
+ "names; fnmatch wildcards supported ('Style *', quoted to keep the "
1724
+ "shell from expanding them). Default: all. The _shared helpers "
1725
+ "and materials the selection depends on are written too, and the "
1726
+ "other assets' files are left untouched — for quick iteration on "
1727
+ "a few assets; a full dump (or 'check') catches edits elsewhere."
1728
+ ),
1678
1729
  )
1679
1730
  _add_dump_flags(dump)
1680
1731
  dump.add_argument(
@@ -1856,7 +1907,7 @@ def _dump_command(args) -> None:
1856
1907
  written = dump_library(
1857
1908
  args.blend,
1858
1909
  args.output,
1859
- names=set(args.names) if args.names else None,
1910
+ names=args.names or None,
1860
1911
  nodebpy_pkg=args.nodebpy_pkg,
1861
1912
  snapshot_positions=args.snapshot_positions,
1862
1913
  keep_reroutes=args.keep_reroutes,
File without changes