nodebpy 0.17.0__tar.gz → 520.0.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 (72) hide show
  1. {nodebpy-0.17.0 → nodebpy-520.0.0}/PKG-INFO +3 -3
  2. {nodebpy-0.17.0 → nodebpy-520.0.0}/README.md +1 -1
  3. {nodebpy-0.17.0 → nodebpy-520.0.0}/pyproject.toml +5 -5
  4. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/arrange.py +5 -1
  5. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/builder/__init__.py +51 -0
  6. nodebpy-520.0.0/src/nodebpy/builder/_registry.py +26 -0
  7. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/builder/_utils.py +2 -1
  8. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/builder/accessor.py +7 -5
  9. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/builder/mixins.py +137 -90
  10. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/builder/node.py +30 -13
  11. nodebpy-520.0.0/src/nodebpy/builder/socket.py +2457 -0
  12. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/builder/tree.py +22 -10
  13. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/diagram.py +4 -2
  14. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/lib/nodearrange/arrange/graph.py +3 -3
  15. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/lib/nodearrange/arrange/ordering.py +5 -5
  16. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/lib/nodearrange/arrange/ranking.py +2 -2
  17. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/lib/nodearrange/arrange/realize.py +2 -2
  18. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/lib/nodearrange/arrange/stacking.py +3 -3
  19. nodebpy-520.0.0/src/nodebpy/lib/nodearrange/arrange/structs.py +179 -0
  20. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/lib/nodearrange/arrange/sugiyama.py +2 -1
  21. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/lib/nodearrange/arrange/x_coords.py +3 -3
  22. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/lib/nodearrange/arrange/y_coords.py +1 -1
  23. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/nodes/compositor/__init__.py +92 -2
  24. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/nodes/compositor/color.py +15 -13
  25. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/nodes/compositor/converter.py +367 -548
  26. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/nodes/compositor/distort.py +19 -11
  27. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/nodes/compositor/filter.py +13 -13
  28. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/nodes/compositor/group.py +1 -1
  29. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/nodes/compositor/input.py +177 -17
  30. nodebpy-520.0.0/src/nodebpy/nodes/compositor/interface.py +242 -0
  31. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/nodes/compositor/manual.py +113 -5
  32. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/nodes/compositor/matte.py +11 -11
  33. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/nodes/compositor/output.py +15 -11
  34. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/nodes/compositor/vector.py +5 -1
  35. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/nodes/geometry/__init__.py +61 -1
  36. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/nodes/geometry/attribute.py +137 -26
  37. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/nodes/geometry/color.py +6 -6
  38. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/nodes/geometry/converter.py +2859 -821
  39. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/nodes/geometry/geometry.py +951 -81
  40. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/nodes/geometry/grid.py +238 -221
  41. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/nodes/geometry/group.py +1 -1
  42. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/nodes/geometry/groups.py +86 -10
  43. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/nodes/geometry/input.py +191 -40
  44. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/nodes/geometry/interface.py +98 -57
  45. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/nodes/geometry/manual.py +691 -94
  46. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/nodes/geometry/output.py +1 -1
  47. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/nodes/geometry/texture.py +10 -10
  48. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/nodes/geometry/utilities.py +8 -17
  49. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/nodes/geometry/vector.py +6 -6
  50. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/nodes/geometry/zone.py +16 -16
  51. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/nodes/shader/__init__.py +22 -8
  52. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/nodes/shader/color.py +6 -6
  53. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/nodes/shader/converter.py +179 -30
  54. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/nodes/shader/grid.py +10 -10
  55. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/nodes/shader/group.py +1 -1
  56. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/nodes/shader/input.py +7 -7
  57. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/nodes/shader/output.py +9 -9
  58. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/nodes/shader/script.py +1 -1
  59. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/nodes/shader/shader.py +39 -15
  60. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/nodes/shader/texture.py +11 -9
  61. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/nodes/shader/vector.py +7 -7
  62. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/types.py +114 -13
  63. nodebpy-0.17.0/src/nodebpy/builder/_registry.py +0 -19
  64. nodebpy-0.17.0/src/nodebpy/builder/socket.py +0 -1722
  65. nodebpy-0.17.0/src/nodebpy/lib/nodearrange/arrange/structs.py +0 -139
  66. nodebpy-0.17.0/src/nodebpy/nodes/compositor/interface.py +0 -143
  67. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/__init__.py +0 -0
  68. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/lib/nodearrange/__init__.py +0 -0
  69. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/lib/nodearrange/config.py +0 -0
  70. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/lib/nodearrange/utils.py +0 -0
  71. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/nodes/__init__.py +0 -0
  72. {nodebpy-0.17.0 → nodebpy-520.0.0}/src/nodebpy/nodes/shader/manual.py +0 -0
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: nodebpy
3
- Version: 0.17.0
3
+ Version: 520.0.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>
7
7
  License: GPL-3.0-or-later
8
- Requires-Dist: bpy>=5.1.0 ; extra == 'bpy'
8
+ Requires-Dist: bpy==5.2.* ; extra == 'bpy'
9
9
  Requires-Dist: networkx>=3.6.1 ; extra == 'networkx'
10
10
  Requires-Python: >=3.13
11
11
  Provides-Extra: bpy
@@ -130,7 +130,7 @@ Some nodes are manually specified in the `src/nodebpy/nodes/geometry/manual.py`
130
130
  Run the build & format script as such:
131
131
 
132
132
  ```bash
133
- uv run generate.py && uvx ruff format && uvx ruff check --fix
133
+ uv run generate.py && uvx ruff format && uvx ruff check --fix && uvx ty check --fix src
134
134
  ```
135
135
 
136
136
  ## Other Projects
@@ -116,7 +116,7 @@ Some nodes are manually specified in the `src/nodebpy/nodes/geometry/manual.py`
116
116
  Run the build & format script as such:
117
117
 
118
118
  ```bash
119
- uv run generate.py && uvx ruff format && uvx ruff check --fix
119
+ uv run generate.py && uvx ruff format && uvx ruff check --fix && uvx ty check --fix src
120
120
  ```
121
121
 
122
122
  ## Other Projects
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "nodebpy"
3
- version = "0.17.0"
3
+ version = "520.0.0"
4
4
  description = "Build nodes trees in Blender more elegantly with code"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -17,7 +17,7 @@ networkx = [
17
17
  "networkx>=3.6.1",
18
18
  ]
19
19
  bpy = [
20
- "bpy>=5.1.0",
20
+ "bpy==5.2.*",
21
21
  ]
22
22
 
23
23
  [build-system]
@@ -28,7 +28,7 @@ build-backend = "uv_build"
28
28
  dev = [
29
29
  "ipython>=8.0.0",
30
30
  "networkx>=3.6.1",
31
- "fake-bpy-module>=20260113",
31
+ "fake-bpy-module>=20260501",
32
32
  "jsondiff>=2.2.1",
33
33
  "pytest>=9.0.2",
34
34
  "pytest-cov>=7.0.0",
@@ -36,7 +36,7 @@ dev = [
36
36
  "quartodoc>=0.11.1",
37
37
  "ruff>=0.14.11",
38
38
  "syrupy>=5.0.0",
39
- "tree-clipper>=0.1.1",
40
39
  "pytest-xdist>=3.8.0",
41
- "griffe<2.0.0"
40
+ "griffe<2.0.0",
41
+ "ty>=0.0.35",
42
42
  ]
@@ -117,7 +117,7 @@ def calculate_node_dimensions(
117
117
  inherited_ids = {
118
118
  prop.identifier
119
119
  for base in type(node).__bases__
120
- for prop in base.bl_rna.properties
120
+ for prop in getattr(base, "bl_rna").properties
121
121
  }
122
122
  node_property_count = sum(
123
123
  1 for prop in node.bl_rna.properties if prop.identifier not in inherited_ids
@@ -143,6 +143,7 @@ def calculate_node_dimensions(
143
143
 
144
144
  def _socket_index(socket: bpy.types.NodeSocket) -> int:
145
145
  """Return the index of a socket among its node's enabled sockets."""
146
+ assert socket.node is not None
146
147
  collection = socket.node.inputs if not socket.is_output else socket.node.outputs
147
148
  idx = 0
148
149
  for s in collection:
@@ -208,6 +209,7 @@ def _reduce_crossings(
208
209
  # on the target node (input side).
209
210
  out_count = max(1, sum(1 for s in src.outputs if s.enabled))
210
211
  in_count = max(1, sum(1 for s in dst.inputs if s.enabled))
212
+ assert link.from_socket is not None and link.to_socket is not None
211
213
  src_frac = _socket_index(link.from_socket) / out_count
212
214
  dst_frac = _socket_index(link.to_socket) / in_count
213
215
 
@@ -299,8 +301,10 @@ def position_reroutes(tree: bpy.types.NodeTree) -> None:
299
301
  targets: list[bpy.types.Node] = []
300
302
  for link in tree.links:
301
303
  if link.to_node == node:
304
+ assert link.from_node is not None
302
305
  sources.append(link.from_node)
303
306
  if link.from_node == node:
307
+ assert link.to_node is not None
304
308
  targets.append(link.to_node)
305
309
 
306
310
  neighbours = sources + targets
@@ -11,24 +11,49 @@ from .node import (
11
11
  )
12
12
  from .socket import (
13
13
  BooleanSocket,
14
+ BooleanSocketGrid,
15
+ BooleanSocketList,
14
16
  BundleSocket,
17
+ BundleSocketList,
15
18
  ClosureSocket,
19
+ ClosureSocketList,
16
20
  CollectionSocket,
21
+ CollectionSocketList,
17
22
  ColorSocket,
23
+ ColorSocketList,
18
24
  FloatSocket,
25
+ FloatSocketGrid,
26
+ FloatSocketList,
19
27
  FontSocket,
28
+ FontSocketList,
20
29
  GeometrySocket,
30
+ GeometrySocketList,
21
31
  ImageSocket,
32
+ ImageSocketList,
22
33
  IntegerSocket,
34
+ IntegerSocketGrid,
35
+ IntegerSocketList,
36
+ IntegerVectorSocket,
23
37
  MaterialSocket,
38
+ MaterialSocketList,
24
39
  MatrixSocket,
40
+ MatrixSocketList,
25
41
  MenuSocket,
42
+ MenuSocketList,
26
43
  ObjectSocket,
44
+ ObjectSocketList,
27
45
  RotationSocket,
46
+ RotationSocketList,
28
47
  ShaderSocket,
48
+ ShaderSocketList,
29
49
  Socket,
50
+ SoundSocket,
51
+ SoundSocketList,
30
52
  StringSocket,
53
+ StringSocketList,
31
54
  VectorSocket,
55
+ VectorSocketGrid,
56
+ VectorSocketList,
32
57
  )
33
58
  from .tree import (
34
59
  InputInterfaceContext,
@@ -55,25 +80,51 @@ __all__ = [
55
80
  "CustomCompositorGroup",
56
81
  "CustomGeometryGroup",
57
82
  "CustomShaderGroup",
83
+ "CustomShaderGroup",
58
84
  # Runtime socket types
59
85
  "FloatSocket",
86
+ "FloatSocketGrid",
87
+ "FloatSocketList",
60
88
  "VectorSocket",
89
+ "VectorSocketGrid",
90
+ "VectorSocketList",
61
91
  "ColorSocket",
92
+ "ColorSocketList",
62
93
  "IntegerSocket",
94
+ "IntegerVectorSocket",
95
+ "IntegerSocketGrid",
96
+ "IntegerSocketList",
63
97
  "BooleanSocket",
98
+ "BooleanSocketGrid",
99
+ "BooleanSocketList",
64
100
  "RotationSocket",
101
+ "RotationSocketList",
65
102
  "MatrixSocket",
103
+ "MatrixSocketList",
66
104
  "StringSocket",
105
+ "StringSocketList",
67
106
  "MenuSocket",
107
+ "MenuSocketList",
68
108
  "GeometrySocket",
109
+ "GeometrySocketList",
69
110
  "ObjectSocket",
111
+ "ObjectSocketList",
70
112
  "FontSocket",
113
+ "FontSocketList",
71
114
  "MaterialSocket",
115
+ "MaterialSocketList",
72
116
  "ImageSocket",
117
+ "ImageSocketList",
73
118
  "CollectionSocket",
119
+ "CollectionSocketList",
74
120
  "BundleSocket",
121
+ "BundleSocketList",
75
122
  "ClosureSocket",
123
+ "ClosureSocketList",
76
124
  "ShaderSocket",
125
+ "ShaderSocketList",
126
+ "SoundSocket",
127
+ "SoundSocketList",
77
128
  # Tree context
78
129
  "SocketContext",
79
130
  "PanelContext",
@@ -0,0 +1,26 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from bpy.types import NodeSocket
6
+
7
+ if TYPE_CHECKING:
8
+ from .socket import Socket
9
+
10
+ _SOCKET_REGISTRY: dict[str, "type[Socket]"] = {}
11
+ _SOCKET_LIST_REGISTRY: dict[str, "type[Socket]"] = {}
12
+ _SOCKET_GRID_REGISTRY: dict[str, "type[Socket]"] = {}
13
+
14
+
15
+ def _wrap_socket(socket: NodeSocket) -> "Socket":
16
+ for key, cls in _SOCKET_REGISTRY.items():
17
+ if key in socket.bl_idname:
18
+ structure = getattr(socket, "inferred_structure_type", "SINGLE")
19
+ if structure == "LIST":
20
+ return _SOCKET_LIST_REGISTRY.get(key, cls)(socket)
21
+ elif structure == "GRID":
22
+ return _SOCKET_GRID_REGISTRY.get(key, cls)(socket)
23
+ return cls(socket)
24
+ from .socket import Socket
25
+
26
+ return Socket(socket)
@@ -47,6 +47,7 @@ def _allow_innactive_sockets(node: bpy.types.Node) -> bool:
47
47
  "GeometryNodeIndexSwitch",
48
48
  "GeometryNodeMenuSwitch",
49
49
  "ShaderNodeMixShader",
50
+ # "ShaderNodeMix",
50
51
  "GeometryNodeSwitch",
51
52
  )
52
53
 
@@ -63,7 +64,7 @@ def _resolve_promotion(
63
64
  """
64
65
  other_type = getattr(other, "type", None)
65
66
  self_prec = _TYPE_PRECEDENCE.get(self_socket.type, 1)
66
- other_prec = _TYPE_PRECEDENCE.get(other_type, -1) # type: ignore[arg-type]
67
+ other_prec = _TYPE_PRECEDENCE.get(other_type, -1) if other_type is not None else -1
67
68
 
68
69
  if other_prec > self_prec:
69
70
  # Other side is dominant — swap so the linker wraps the vector/higher socket
@@ -5,7 +5,7 @@ from typing import TYPE_CHECKING, Iterator, Literal, overload
5
5
  import bpy
6
6
  from bpy.types import NodeSocket
7
7
 
8
- from ._registry import _get_socket_linker
8
+ from ._registry import _wrap_socket
9
9
  from ._utils import (
10
10
  SocketError,
11
11
  _allow_innactive_sockets,
@@ -79,14 +79,14 @@ class SocketAccessor:
79
79
  """Get a Socket for a socket by identifier, name, or index."""
80
80
  if isinstance(key, slice):
81
81
  sockets = [
82
- _get_socket_linker(self._collection[i])
82
+ _wrap_socket(self._collection[i])
83
83
  for i in range(*key.indices(len(self._collection)))
84
84
  ]
85
85
  if self._builder is not None:
86
86
  for s in sockets:
87
87
  s._builder_node = self._builder
88
88
  return sockets
89
- socket = _get_socket_linker(self._collection[self._index(key)])
89
+ socket = _wrap_socket(self._collection[self._index(key)])
90
90
  if self._builder is not None:
91
91
  socket._builder_node = self._builder
92
92
  return socket
@@ -103,10 +103,12 @@ class SocketAccessor:
103
103
  def _node(self) -> bpy.types.Node:
104
104
  """The node this accessor is associated with."""
105
105
  if isinstance(self._collection, list):
106
+ assert self._collection[0].node is not None
106
107
  return self._collection[0].node
107
108
  # bpy NodeInputs/NodeOutputs.id_data returns the NodeTree (top-level ID),
108
109
  # not the Node. Retrieve the node via the first socket instead.
109
110
  for s in self._collection:
111
+ assert s.node is not None
110
112
  return s.node
111
113
  return self._collection.data # empty collection fallback
112
114
 
@@ -183,7 +185,7 @@ class SocketAccessor:
183
185
  Uses node-level visibility rules regardless of ``ignore_visibility`` —
184
186
  see ``_visible_sockets`` for rationale.
185
187
  """
186
- return [_get_socket_linker(s) for s in self._visible_sockets()]
188
+ return [_wrap_socket(s) for s in self._visible_sockets()]
187
189
 
188
190
  def _items(self) -> "list[tuple[str, Socket]]":
189
191
  """All visible sockets as (name, Socket) pairs.
@@ -191,7 +193,7 @@ class SocketAccessor:
191
193
  Uses node-level visibility rules regardless of ``ignore_visibility`` —
192
194
  see ``_visible_sockets`` for rationale.
193
195
  """
194
- return [(s.name, _get_socket_linker(s)) for s in self._visible_sockets()]
196
+ return [(s.name, _wrap_socket(s)) for s in self._visible_sockets()]
195
197
 
196
198
  def _keys(self) -> list[str]:
197
199
  """All visible socket names."""