nodebpy 0.13.0__tar.gz → 0.15.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 (71) hide show
  1. {nodebpy-0.13.0 → nodebpy-0.15.0}/PKG-INFO +1 -1
  2. {nodebpy-0.13.0 → nodebpy-0.15.0}/pyproject.toml +1 -1
  3. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/__init__.py +1 -2
  4. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/builder/__init__.py +2 -74
  5. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/builder/accessor.py +1 -2
  6. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/builder/mixins.py +45 -25
  7. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/builder/node.py +9 -17
  8. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/builder/socket.py +218 -70
  9. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/builder/tree.py +157 -198
  10. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/nodes/compositor/color.py +13 -13
  11. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/nodes/compositor/converter.py +16 -16
  12. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/nodes/compositor/distort.py +14 -14
  13. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/nodes/compositor/filter.py +18 -18
  14. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/nodes/compositor/group.py +2 -2
  15. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/nodes/compositor/input.py +13 -13
  16. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/nodes/compositor/interface.py +2 -2
  17. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/nodes/compositor/manual.py +64 -17
  18. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/nodes/compositor/matte.py +13 -13
  19. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/nodes/compositor/output.py +3 -3
  20. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/nodes/compositor/vector.py +2 -2
  21. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/nodes/geometry/__init__.py +6 -5
  22. nodebpy-0.15.0/src/nodebpy/nodes/geometry/attribute.py +291 -0
  23. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/nodes/geometry/color.py +3 -3
  24. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/nodes/geometry/converter.py +55 -460
  25. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/nodes/geometry/geometry.py +93 -93
  26. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/nodes/geometry/grid.py +37 -37
  27. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/nodes/geometry/group.py +2 -2
  28. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/nodes/geometry/input.py +77 -77
  29. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/nodes/geometry/interface.py +8 -8
  30. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/nodes/geometry/manual.py +1189 -254
  31. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/nodes/geometry/output.py +2 -2
  32. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/nodes/geometry/texture.py +11 -11
  33. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/nodes/geometry/utilities.py +2 -2
  34. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/nodes/geometry/vector.py +5 -5
  35. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/nodes/geometry/zone.py +59 -53
  36. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/nodes/shader/color.py +5 -5
  37. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/nodes/shader/converter.py +7 -7
  38. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/nodes/shader/grid.py +6 -6
  39. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/nodes/shader/group.py +2 -2
  40. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/nodes/shader/input.py +20 -20
  41. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/nodes/shader/manual.py +75 -14
  42. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/nodes/shader/output.py +6 -6
  43. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/nodes/shader/script.py +2 -2
  44. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/nodes/shader/shader.py +21 -21
  45. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/nodes/shader/texture.py +5 -5
  46. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/nodes/shader/vector.py +8 -8
  47. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/types.py +90 -23
  48. nodebpy-0.13.0/src/nodebpy/builder/interface.py +0 -571
  49. nodebpy-0.13.0/src/nodebpy/nodes/geometry/attribute.py +0 -723
  50. nodebpy-0.13.0/src/nodebpy/sockets.py +0 -48
  51. {nodebpy-0.13.0 → nodebpy-0.15.0}/README.md +0 -0
  52. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/arrange.py +0 -0
  53. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/builder/_registry.py +0 -0
  54. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/builder/_utils.py +0 -0
  55. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/diagram.py +0 -0
  56. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/lib/nodearrange/__init__.py +0 -0
  57. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/lib/nodearrange/arrange/graph.py +0 -0
  58. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/lib/nodearrange/arrange/ordering.py +0 -0
  59. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/lib/nodearrange/arrange/ranking.py +0 -0
  60. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/lib/nodearrange/arrange/realize.py +0 -0
  61. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/lib/nodearrange/arrange/stacking.py +0 -0
  62. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/lib/nodearrange/arrange/structs.py +0 -0
  63. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/lib/nodearrange/arrange/sugiyama.py +0 -0
  64. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/lib/nodearrange/arrange/x_coords.py +0 -0
  65. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/lib/nodearrange/arrange/y_coords.py +0 -0
  66. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/lib/nodearrange/config.py +0 -0
  67. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/lib/nodearrange/utils.py +0 -0
  68. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/nodes/__init__.py +0 -0
  69. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/nodes/compositor/__init__.py +2 -2
  70. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/nodes/geometry/groups.py +0 -0
  71. {nodebpy-0.13.0 → nodebpy-0.15.0}/src/nodebpy/nodes/shader/__init__.py +2 -2
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: nodebpy
3
- Version: 0.13.0
3
+ Version: 0.15.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 = "0.13.0"
3
+ version = "0.15.0"
4
4
  description = "Build nodes trees in Blender more elegantly with code"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -1,4 +1,4 @@
1
- from . import diagram, nodes, sockets
1
+ from . import diagram, nodes
2
2
  from .builder import (
3
3
  TreeBuilder,
4
4
  )
@@ -9,7 +9,6 @@ __all__ = [
9
9
  "compositor",
10
10
  "geometry",
11
11
  "shader",
12
- "sockets",
13
12
  "diagram",
14
13
  "TreeBuilder",
15
14
  ]
@@ -1,31 +1,5 @@
1
- """nodebpy.builder — node tree construction API.
2
-
3
- Public names are re-exported here. Old names (NodeBuilder, SocketLinker,
4
- SocketBase) are kept as aliases for backward compatibility.
5
- """
6
-
7
1
  from ._utils import SocketError, denormalize_name, normalize_name
8
2
  from .accessor import SocketAccessor
9
- from .interface import (
10
- InterfaceSocket,
11
- SocketBoolean,
12
- SocketBundle,
13
- SocketClosure,
14
- SocketCollection,
15
- SocketColor,
16
- SocketFloat,
17
- SocketGeometry,
18
- SocketImage,
19
- SocketInteger,
20
- SocketMaterial,
21
- SocketMatrix,
22
- SocketMenu,
23
- SocketObject,
24
- SocketRotation,
25
- SocketShader,
26
- SocketString,
27
- SocketVector,
28
- )
29
3
  from .mixins import LinkingMixin, OperatorMixin
30
4
  from .node import (
31
5
  BaseNode,
@@ -55,12 +29,6 @@ from .socket import (
55
29
  Socket,
56
30
  StringSocket,
57
31
  VectorSocket,
58
- _BooleanMixin,
59
- _ColorMixin,
60
- _IntegerMixin,
61
- _MatrixMixin,
62
- _RotationMixin,
63
- _VectorMixin,
64
32
  )
65
33
  from .tree import (
66
34
  InputInterfaceContext,
@@ -71,11 +39,6 @@ from .tree import (
71
39
  TreeBuilder,
72
40
  )
73
41
 
74
- # Backward-compatible aliases for hand-written code that uses the old names.
75
- NodeBuilder = BaseNode
76
- SocketLinker = Socket
77
- SocketBase = InterfaceSocket
78
-
79
42
  __all__ = [
80
43
  # Core
81
44
  "TreeBuilder",
@@ -92,10 +55,7 @@ __all__ = [
92
55
  "CustomCompositorGroup",
93
56
  "CustomGeometryGroup",
94
57
  "CustomShaderGroup",
95
- "GeometryNodeGroup",
96
- "ShaderNodeGroup",
97
- "CompositorNodeGroup",
98
- # Type-specific socket classes (runtime)
58
+ # Runtime socket types
99
59
  "FloatSocket",
100
60
  "VectorSocket",
101
61
  "ColorSocket",
@@ -114,35 +74,7 @@ __all__ = [
114
74
  "BundleSocket",
115
75
  "ClosureSocket",
116
76
  "ShaderSocket",
117
- # Type-specific behaviour mixins
118
- "_VectorMixin",
119
- "_ColorMixin",
120
- "_IntegerMixin",
121
- "_BooleanMixin",
122
- "_RotationMixin",
123
- "_MatrixMixin",
124
- # Interface socket base
125
- "InterfaceSocket",
126
- # Interface socket types
127
- "SocketFloat",
128
- "SocketInteger",
129
- "SocketBoolean",
130
- "SocketVector",
131
- "SocketColor",
132
- "SocketRotation",
133
- "SocketMatrix",
134
- "SocketString",
135
- "SocketMenu",
136
- "SocketObject",
137
- "SocketGeometry",
138
- "SocketFont",
139
- "SocketCollection",
140
- "SocketImage",
141
- "SocketMaterial",
142
- "SocketBundle",
143
- "SocketClosure",
144
- "SocketShader",
145
- # Tree context helpers
77
+ # Tree context
146
78
  "SocketContext",
147
79
  "PanelContext",
148
80
  "InputInterfaceContext",
@@ -151,8 +83,4 @@ __all__ = [
151
83
  "SocketError",
152
84
  "normalize_name",
153
85
  "denormalize_name",
154
- # Backward-compatible aliases
155
- "NodeBuilder",
156
- "SocketLinker",
157
- "SocketBase",
158
86
  ]
@@ -190,8 +190,7 @@ class SocketAccessor:
190
190
  return len(self._items())
191
191
 
192
192
  def __iter__(self):
193
- """Iterate over socket names (enables ``**node.outputs`` unpacking)."""
194
- return iter(self._keys())
193
+ return iter(self._values())
195
194
 
196
195
  def __getattr__(self, name: str) -> "Socket":
197
196
  """Dynamic socket access by normalised attribute name.
@@ -11,10 +11,11 @@ from ._utils import SocketError, _resolve_promotion, _SocketLike
11
11
  _RShiftT = TypeVar("_RShiftT")
12
12
 
13
13
  if TYPE_CHECKING:
14
- from ..nodes.geometry import Compare, Math
14
+ from ..nodes.geometry import Compare, Math, MultiplyMatrices, TransformPoint
15
15
  from ..types import InputLinkable
16
16
  from .node import BaseNode
17
- from .socket import Socket
17
+ from .socket import MatrixSocket, Socket
18
+ from .tree import TreeBuilder
18
19
 
19
20
 
20
21
  class OperatorMixin:
@@ -160,46 +161,46 @@ class OperatorMixin:
160
161
  return BooleanMath.l_not(self)
161
162
 
162
163
  @staticmethod
163
- def _cast_to_matrix(value):
164
+ def _cast_to_matrix(value) -> MatrixSocket:
164
165
  from ..nodes.geometry.converter import CombineMatrix
165
166
 
166
167
  if hasattr(value, "shape") and value.shape == (4, 4):
167
- return CombineMatrix(*value.ravel())
168
+ return CombineMatrix(*value.ravel()).o.matrix
168
169
  else:
169
170
  return value
170
171
 
171
- def __matmul__(self, other: Any):
172
+ def __matmul__(self, other: Any) -> "MultiplyMatrices | TransformPoint":
172
173
  from ..nodes.geometry.converter import MultiplyMatrices, TransformPoint
173
174
 
174
175
  other = self._cast_to_matrix(other)
175
- socket = self._default_output_socket # type: ignore[attr-defined]
176
- other_type = getattr(other, "type", None)
176
+ socket = self._default_output_socket
177
177
 
178
- if socket.type == "MATRIX" and other_type == "VECTOR":
178
+ if socket.type == "MATRIX" and other.type == "VECTOR":
179
179
  return TransformPoint(other, socket)
180
180
 
181
- return MultiplyMatrices(self, other)
181
+ return MultiplyMatrices(socket, other)
182
182
 
183
- def __rmatmul__(self, other: Any):
183
+ def __rmatmul__(self, other: Any) -> "MultiplyMatrices | TransformPoint":
184
184
  from ..nodes.geometry.converter import MultiplyMatrices, TransformPoint
185
185
 
186
186
  other = self._cast_to_matrix(other)
187
- socket = self._default_output_socket # type: ignore[attr-defined]
188
- other_type = getattr(other, "type", None)
187
+ socket = self._default_output_socket
189
188
 
190
- if socket.type == "VECTOR" and other_type == "MATRIX":
189
+ if socket.type == "VECTOR" and getattr(other, "type", None) == "MATRIX":
191
190
  return TransformPoint(socket, other)
192
191
 
193
- return MultiplyMatrices(other, self)
192
+ return MultiplyMatrices(other, socket)
194
193
 
195
194
 
196
195
  class LinkingMixin:
197
196
  """Node/socket linking logic: ``>>``, ``_link``, best-socket matching.
198
197
 
199
- Requires ``tree``, ``inputs``, ``outputs``, ``_default_output_socket``,
198
+ Requires ``tree``, ``i``, ``o``, ``_default_output_socket``,
200
199
  and ``_default_input_socket`` on the concrete class.
201
200
  """
202
201
 
202
+ tree: "TreeBuilder"
203
+
203
204
  def _source_socket(self, node: "InputLinkable | Socket | NodeSocket") -> NodeSocket:
204
205
  assert node
205
206
  if isinstance(node, NodeSocket):
@@ -224,18 +225,22 @@ class LinkingMixin:
224
225
  target: "BaseNode | Socket | NodeSocket | EllipsisType | LinkingMixin",
225
226
  ) -> tuple[NodeSocket, NodeSocket]:
226
227
  """Find the best compatible pair of sockets between two nodes/sockets."""
227
- from ..types import SOCKET_COMPATIBILITY
228
+ from ..builder.node import BaseNode
229
+ from ..builder.socket import Socket
230
+ from ..types import PREFER_FIRST_SOCKET, SOCKET_COMPATIBILITY
228
231
 
229
232
  possible_combos = []
230
- if hasattr(source, "outputs"):
231
- outputs = source.outputs._available # type: ignore[union-attr]
233
+ if isinstance(source, BaseNode):
234
+ outputs = source.o._available
232
235
  elif isinstance(source, NodeSocket):
233
236
  outputs = [source]
237
+ elif isinstance(source, Socket):
238
+ outputs = [source.socket]
234
239
  else:
235
240
  raise TypeError(f"Cannot get outputs from {type(source)}")
236
241
 
237
- if hasattr(target, "inputs"):
238
- inputs = target.inputs._available # type: ignore[union-attr]
242
+ if isinstance(target, BaseNode):
243
+ inputs = target.i._available
239
244
  else:
240
245
  inputs = [target]
241
246
 
@@ -244,6 +249,21 @@ class LinkingMixin:
244
249
  if outputs and inputs:
245
250
  return inputs[0], outputs[0]
246
251
 
252
+ # Try first available input first — if the output type matches it exactly,
253
+ # or is a "preferred" implicit conversion (e.g. float→color, vector→color),
254
+ # use the first socket rather than searching for a better-typed later one.
255
+ # This keeps float→Image working in the compositor instead of drifting to
256
+ # a float Factor socket that scores higher on raw compatibility.
257
+ # Pairs not in PREFER_FIRST_SOCKET (e.g. VALUE→BOOLEAN, VECTOR→ROTATION)
258
+ # fall through to the ranked search below.
259
+ if inputs:
260
+ first_input = inputs[0]
261
+ for output in outputs:
262
+ if first_input.type == output.type:
263
+ return first_input, output
264
+ if (output.type, first_input.type) in PREFER_FIRST_SOCKET:
265
+ return first_input, output
266
+
247
267
  for output in outputs:
248
268
  compat_sockets = SOCKET_COMPATIBILITY.get(output.type, ())
249
269
  for input in inputs:
@@ -269,7 +289,7 @@ class LinkingMixin:
269
289
  ) -> NodeLink:
270
290
  source_socket = self._source_socket(source)
271
291
  target_socket = self._target_socket(target)
272
- return self.tree.link(source_socket, target_socket) # type: ignore[attr-defined]
292
+ return self.tree.link(source_socket, target_socket)
273
293
 
274
294
  def _link_from(
275
295
  self,
@@ -278,9 +298,9 @@ class LinkingMixin:
278
298
  ):
279
299
  if isinstance(input, str):
280
300
  try:
281
- self._link(source, self.node.inputs[input]) # type: ignore[attr-defined]
301
+ self._link(source, self.node.inputs[input])
282
302
  except KeyError:
283
- self._link(source, self.node.inputs[self.inputs._index(input)]) # type: ignore[attr-defined]
303
+ self._link(source, self.node.inputs[self.i._index(input)])
284
304
  else:
285
305
  self._link(source, input)
286
306
 
@@ -304,8 +324,8 @@ class LinkingMixin:
304
324
  try:
305
325
  target = other.node.inputs[name]
306
326
  except KeyError:
307
- target = other.node.inputs[other.inputs._index(name)]
308
- source = self.outputs._best_match(target.type)
327
+ target = other.node.inputs[other.i._index(name)]
328
+ source = self.o._best_match(target.type) if hasattr(self, "o") else self
309
329
  else:
310
330
  try:
311
331
  source, target = self._find_best_socket_pair(self, other)
@@ -41,7 +41,7 @@ if TYPE_CHECKING:
41
41
  def _add_inputs(self, *args: Any, **kwargs: Any) -> dict[str, NodeSocket]: ...
42
42
 
43
43
  @property
44
- def inputs(self) -> SocketAccessor: ...
44
+ def i(self) -> SocketAccessor: ...
45
45
 
46
46
 
47
47
  class BaseNode(_NodeLike, OperatorMixin, LinkingMixin):
@@ -84,13 +84,13 @@ class BaseNode(_NodeLike, OperatorMixin, LinkingMixin):
84
84
  @property
85
85
  def _default_input_socket(self) -> NodeSocket:
86
86
  if self._default_input_id is not None:
87
- return self.node.inputs[self.inputs._index(self._default_input_id)]
87
+ return self.node.inputs[self.i._index(self._default_input_id)]
88
88
  return self.node.inputs[0]
89
89
 
90
90
  @property
91
91
  def _default_output_socket(self) -> NodeSocket:
92
92
  if self._default_output_id is not None:
93
- return self.node.outputs[self.outputs._index(self._default_output_id)]
93
+ return self.node.outputs[self.o._index(self._default_output_id)]
94
94
 
95
95
  counter = 0
96
96
  socket = self.node.outputs[counter]
@@ -115,7 +115,7 @@ class BaseNode(_NodeLike, OperatorMixin, LinkingMixin):
115
115
  if link.to_node.bl_idname == cls._bl_idname:
116
116
  return cls._from_node(link.to_node)
117
117
  node = cls()
118
- node.tree.link(socket, node.inputs._best_match(socket.type))
118
+ node.tree.link(socket, node.i._best_match(socket.type))
119
119
  return node
120
120
  else:
121
121
  if socket.links:
@@ -163,9 +163,7 @@ class BaseNode(_NodeLike, OperatorMixin, LinkingMixin):
163
163
  elif isinstance(value, NodeSocket):
164
164
  self._link_from(value, name)
165
165
  elif isinstance(value, _NodeLike):
166
- self._link_from(
167
- value.outputs._best_match(self.inputs._get(name).type), name
168
- )
166
+ self._link_from(value.o._best_match(self.i._get(name).type), name)
169
167
  else:
170
168
  if name in input_ids:
171
169
  input = self.node.inputs[input_ids.index(name)]
@@ -177,14 +175,6 @@ class BaseNode(_NodeLike, OperatorMixin, LinkingMixin):
177
175
  input = self.node.inputs[name.replace("_", " ").title()]
178
176
  self._set_input_default_value(input, value)
179
177
 
180
- @property
181
- def outputs(self) -> SocketAccessor:
182
- return SocketAccessor(self.node.outputs, "output")
183
-
184
- @property
185
- def inputs(self) -> SocketAccessor:
186
- return SocketAccessor(self.node.inputs, "input")
187
-
188
178
  @property
189
179
  def o(self) -> SocketAccessor:
190
180
  """Output socket accessor. Subclasses narrow the return type via TYPE_CHECKING."""
@@ -225,7 +215,7 @@ class DynamicInputsMixin(ABC):
225
215
  except SocketError:
226
216
  dyn = cast("_DynamicTarget", target)
227
217
  target_name, source_socket = list(dyn._add_inputs(source).items())[0]
228
- return (source_socket, dyn.inputs[target_name].socket)
218
+ return (source_socket, dyn.i[target_name].socket)
229
219
 
230
220
  @abstractmethod
231
221
  def _add_socket(self, name: str, *args: Any, **kwargs: Any) -> NodeSocket: ...
@@ -238,7 +228,9 @@ class DynamicInputsMixin(ABC):
238
228
  items[arg._default_output_socket.name] = arg
239
229
  items.update(kwargs)
240
230
  for key, source in items.items():
241
- socket_source, type = self._match_compatible_data(source.outputs._available)
231
+ socket_source, type = self._match_compatible_data(
232
+ source.o._available if hasattr(source, "o") else [source]
233
+ )
242
234
  if type in self._type_map:
243
235
  type = self._type_map[type]
244
236
  socket = self._add_socket(name=key, type=type)