nodebpy 0.10.2__tar.gz → 0.11.1__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.10.2 → nodebpy-0.11.1}/PKG-INFO +3 -3
  2. {nodebpy-0.10.2 → nodebpy-0.11.1}/pyproject.toml +3 -3
  3. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/__init__.py +2 -2
  4. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/builder/__init__.py +3 -0
  5. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/builder/accessor.py +26 -4
  6. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/builder/mixins.py +29 -26
  7. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/builder/node.py +30 -3
  8. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/builder/socket.py +405 -51
  9. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/builder/tree.py +48 -42
  10. nodebpy-0.11.1/src/nodebpy/diagram.py +139 -0
  11. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/compositor/__init__.py +18 -0
  12. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/compositor/converter.py +188 -0
  13. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/compositor/filter.py +67 -18
  14. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/compositor/input.py +50 -116
  15. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/compositor/manual.py +3 -1
  16. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/geometry/__init__.py +26 -0
  17. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/geometry/converter.py +836 -14
  18. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/geometry/geometry.py +201 -79
  19. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/geometry/grid.py +864 -57
  20. nodebpy-0.11.1/src/nodebpy/nodes/geometry/groups.py +205 -0
  21. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/geometry/input.py +81 -0
  22. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/geometry/interface.py +11 -0
  23. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/geometry/manual.py +545 -419
  24. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/geometry/vector.py +8 -0
  25. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/shader/__init__.py +6 -0
  26. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/shader/input.py +88 -0
  27. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/shader/manual.py +2 -1
  28. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/shader/vector.py +20 -0
  29. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/types.py +4 -0
  30. nodebpy-0.10.2/src/nodebpy/nodes/geometry/groups.py +0 -104
  31. nodebpy-0.10.2/src/nodebpy/screenshot.py +0 -260
  32. {nodebpy-0.10.2 → nodebpy-0.11.1}/README.md +0 -0
  33. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/arrange.py +0 -0
  34. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/builder/_registry.py +0 -0
  35. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/builder/_utils.py +0 -0
  36. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/builder/interface.py +0 -0
  37. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/lib/nodearrange/__init__.py +0 -0
  38. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/lib/nodearrange/arrange/graph.py +0 -0
  39. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/lib/nodearrange/arrange/ordering.py +0 -0
  40. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/lib/nodearrange/arrange/ranking.py +0 -0
  41. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/lib/nodearrange/arrange/realize.py +0 -0
  42. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/lib/nodearrange/arrange/stacking.py +0 -0
  43. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/lib/nodearrange/arrange/structs.py +0 -0
  44. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/lib/nodearrange/arrange/sugiyama.py +0 -0
  45. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/lib/nodearrange/arrange/x_coords.py +0 -0
  46. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/lib/nodearrange/arrange/y_coords.py +0 -0
  47. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/lib/nodearrange/config.py +0 -0
  48. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/lib/nodearrange/utils.py +0 -0
  49. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/__init__.py +0 -0
  50. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/compositor/color.py +0 -0
  51. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/compositor/distort.py +0 -0
  52. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/compositor/group.py +0 -0
  53. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/compositor/interface.py +0 -0
  54. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/compositor/matte.py +0 -0
  55. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/compositor/output.py +0 -0
  56. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/compositor/vector.py +0 -0
  57. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/geometry/attribute.py +0 -0
  58. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/geometry/color.py +0 -0
  59. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/geometry/group.py +0 -0
  60. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/geometry/output.py +0 -0
  61. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/geometry/texture.py +0 -0
  62. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/geometry/zone.py +0 -0
  63. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/shader/color.py +0 -0
  64. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/shader/converter.py +0 -0
  65. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/shader/grid.py +0 -0
  66. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/shader/group.py +0 -0
  67. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/shader/interface.py +0 -0
  68. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/shader/output.py +0 -0
  69. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/shader/script.py +0 -0
  70. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/shader/shader.py +0 -0
  71. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/shader/texture.py +0 -0
  72. {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/sockets.py +0 -0
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: nodebpy
3
- Version: 0.10.2
3
+ Version: 0.11.1
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.0.1 ; extra == 'bpy'
8
+ Requires-Dist: bpy>=5.1.0 ; extra == 'bpy'
9
9
  Requires-Dist: networkx>=3.6.1 ; extra == 'networkx'
10
- Requires-Python: >=3.11
10
+ Requires-Python: >=3.13
11
11
  Provides-Extra: bpy
12
12
  Provides-Extra: networkx
13
13
  Description-Content-Type: text/markdown
@@ -1,12 +1,12 @@
1
1
  [project]
2
2
  name = "nodebpy"
3
- version = "0.10.2"
3
+ version = "0.11.1"
4
4
  description = "Build nodes trees in Blender more elegantly with code"
5
5
  readme = "README.md"
6
6
  authors = [
7
7
  { name = "Brady Johnston", email = "brady.johnston@me.com" }
8
8
  ]
9
- requires-python = ">=3.11"
9
+ requires-python = ">=3.13"
10
10
  license = { text = "GPL-3.0-or-later" }
11
11
 
12
12
  [project.scripts]
@@ -17,7 +17,7 @@ networkx = [
17
17
  "networkx>=3.6.1",
18
18
  ]
19
19
  bpy = [
20
- "bpy>=5.0.1",
20
+ "bpy>=5.1.0",
21
21
  ]
22
22
 
23
23
  [build-system]
@@ -1,4 +1,4 @@
1
- from . import nodes, screenshot, sockets
1
+ from . import nodes, diagram, sockets
2
2
  from .builder import TreeBuilder, NodeGroupBuilder
3
3
  from .nodes import compositor, geometry, shader
4
4
 
@@ -8,7 +8,7 @@ __all__ = [
8
8
  "geometry",
9
9
  "shader",
10
10
  "sockets",
11
- "screenshot",
11
+ "diagram",
12
12
  "TreeBuilder",
13
13
  "NodeGroupBuilder",
14
14
  ]
@@ -35,6 +35,7 @@ from .socket import (
35
35
  CollectionSocket,
36
36
  ColorSocket,
37
37
  FloatSocket,
38
+ FontSocket,
38
39
  GeometrySocket,
39
40
  ImageSocket,
40
41
  IntegerSocket,
@@ -93,6 +94,7 @@ __all__ = [
93
94
  "MenuSocket",
94
95
  "GeometrySocket",
95
96
  "ObjectSocket",
97
+ "FontSocket",
96
98
  "MaterialSocket",
97
99
  "ImageSocket",
98
100
  "CollectionSocket",
@@ -120,6 +122,7 @@ __all__ = [
120
122
  "SocketMenu",
121
123
  "SocketObject",
122
124
  "SocketGeometry",
125
+ "SocketFont",
123
126
  "SocketCollection",
124
127
  "SocketImage",
125
128
  "SocketMaterial",
@@ -1,12 +1,17 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import TYPE_CHECKING, Literal
3
+ from typing import TYPE_CHECKING, Literal, overload
4
4
 
5
5
  import bpy
6
6
  from bpy.types import NodeSocket
7
7
 
8
8
  from ._registry import _get_socket_linker
9
- from ._utils import SocketError, _allow_innactive_sockets, denormalize_name
9
+ from ._utils import (
10
+ SocketError,
11
+ _allow_innactive_sockets,
12
+ denormalize_name,
13
+ normalize_name,
14
+ )
10
15
 
11
16
  if TYPE_CHECKING:
12
17
  from .socket import Socket
@@ -43,6 +48,10 @@ class SocketAccessor:
43
48
  for candidate in (key, denorm):
44
49
  if candidate in ids:
45
50
  return ids.index(candidate)
51
+ # Normalized identifier match: 'value_001' matches identifier 'Value_001'
52
+ normalized_ids = [normalize_name(id) for id in ids]
53
+ if key in normalized_ids:
54
+ return normalized_ids.index(key)
46
55
  names = [s.name for s in self._collection]
47
56
  for key in (key, denorm):
48
57
  if key in names:
@@ -58,11 +67,24 @@ class SocketAccessor:
58
67
  f"{self._node.bl_idname}. Available sockets (id: name): {list(zip(ids, names))}"
59
68
  )
60
69
 
61
- def _get(self, key: str | int) -> "Socket":
70
+ @overload
71
+ def _get(self, key: slice) -> "list[Socket]": ...
72
+ @overload
73
+ def _get(self, key: str | int) -> "Socket": ...
74
+ def _get(self, key: str | int | slice) -> "Socket | list[Socket]":
62
75
  """Get a Socket for a socket by identifier, name, or index."""
76
+ if isinstance(key, slice):
77
+ return [
78
+ _get_socket_linker(self._collection[i])
79
+ for i in range(*key.indices(len(self._collection)))
80
+ ]
63
81
  return _get_socket_linker(self._collection[self._index(key)])
64
82
 
65
- def __getitem__(self, key: str | int) -> "Socket":
83
+ @overload
84
+ def __getitem__(self, key: slice) -> "list[Socket]": ...
85
+ @overload
86
+ def __getitem__(self, key: str | int) -> "Socket": ...
87
+ def __getitem__(self, key: str | int | slice) -> "Socket | list[Socket]":
66
88
  """Access by identifier, name, or integer index."""
67
89
  return self._get(key)
68
90
 
@@ -1,14 +1,17 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from types import EllipsisType
4
- from typing import TYPE_CHECKING, Any
4
+ from typing import TYPE_CHECKING, Any, TypeVar
5
5
 
6
6
  from bpy.types import NodeLink, NodeSocket
7
7
 
8
8
  from ._registry import _get_socket_linker
9
9
  from ._utils import SocketError, _resolve_promotion, _SocketLike
10
10
 
11
+ _RShiftT = TypeVar("_RShiftT")
12
+
11
13
  if TYPE_CHECKING:
14
+ from ..nodes.geometry import Compare, Math
12
15
  from ..types import InputLinkable
13
16
  from .node import BaseNode
14
17
  from .socket import Socket
@@ -26,7 +29,7 @@ class OperatorMixin:
26
29
 
27
30
  def _apply_math_operation(
28
31
  self, other: Any, operation: str, reverse: bool = False
29
- ) -> "BaseNode":
32
+ ) -> "Math":
30
33
  socket, other, reverse = _resolve_promotion(
31
34
  self._default_output_socket,
32
35
  other,
@@ -34,43 +37,43 @@ class OperatorMixin:
34
37
  )
35
38
  return _get_socket_linker(socket)._dispatch_math(other, operation, reverse)
36
39
 
37
- def __mul__(self, other: Any) -> "BaseNode":
40
+ def __mul__(self, other: Any) -> "Math":
38
41
  return self._apply_math_operation(other, "multiply")
39
42
 
40
- def __rmul__(self, other: Any) -> "BaseNode":
43
+ def __rmul__(self, other: Any) -> "Math":
41
44
  return self._apply_math_operation(other, "multiply", reverse=True)
42
45
 
43
- def __truediv__(self, other: Any) -> "BaseNode":
46
+ def __truediv__(self, other: Any) -> "Math":
44
47
  return self._apply_math_operation(other, "divide")
45
48
 
46
- def __rtruediv__(self, other: Any) -> "BaseNode":
49
+ def __rtruediv__(self, other: Any) -> "Math":
47
50
  return self._apply_math_operation(other, "divide", reverse=True)
48
51
 
49
- def __add__(self, other: Any) -> "BaseNode":
52
+ def __add__(self, other: Any) -> "Math":
50
53
  return self._apply_math_operation(other, "add")
51
54
 
52
- def __radd__(self, other: Any) -> "BaseNode":
55
+ def __radd__(self, other: Any) -> "Math":
53
56
  return self._apply_math_operation(other, "add", reverse=True)
54
57
 
55
- def __sub__(self, other: Any) -> "BaseNode":
58
+ def __sub__(self, other: Any) -> "Math":
56
59
  return self._apply_math_operation(other, "subtract")
57
60
 
58
- def __rsub__(self, other: Any) -> "BaseNode":
61
+ def __rsub__(self, other: Any) -> "Math":
59
62
  return self._apply_math_operation(other, "subtract", reverse=True)
60
63
 
61
- def __pow__(self, other: Any) -> "BaseNode":
64
+ def __pow__(self, other: Any) -> "Math":
62
65
  return self._apply_math_operation(other, "power")
63
66
 
64
- def __rpow__(self, other: Any) -> "BaseNode":
67
+ def __rpow__(self, other: Any) -> "Math":
65
68
  return self._apply_math_operation(other, "power", reverse=True)
66
69
 
67
- def __mod__(self, other: Any) -> "BaseNode":
70
+ def __mod__(self, other: Any) -> "Math":
68
71
  return self._apply_math_operation(other, "modulo")
69
72
 
70
- def __rmod__(self, other: Any) -> "BaseNode":
73
+ def __rmod__(self, other: Any) -> "Math":
71
74
  return self._apply_math_operation(other, "modulo", reverse=True)
72
75
 
73
- def __floordiv__(self, other: Any) -> "BaseNode":
76
+ def __floordiv__(self, other: Any) -> "Math":
74
77
  socket, other, reverse = _resolve_promotion(
75
78
  self._default_output_socket,
76
79
  other,
@@ -78,7 +81,7 @@ class OperatorMixin:
78
81
  )
79
82
  return _get_socket_linker(socket)._dispatch_floordiv(other, reverse)
80
83
 
81
- def __rfloordiv__(self, other: Any) -> "BaseNode":
84
+ def __rfloordiv__(self, other: Any) -> "Math":
82
85
  socket, other, reverse = _resolve_promotion(
83
86
  self._default_output_socket,
84
87
  other,
@@ -86,37 +89,37 @@ class OperatorMixin:
86
89
  )
87
90
  return _get_socket_linker(socket)._dispatch_floordiv(other, reverse)
88
91
 
89
- def __neg__(self) -> "BaseNode":
92
+ def __neg__(self) -> "Math":
90
93
  return _get_socket_linker(self._default_output_socket)._dispatch_unary( # type: ignore[attr-defined]
91
94
  "negate"
92
95
  )
93
96
 
94
- def __abs__(self) -> "BaseNode":
97
+ def __abs__(self) -> "Math":
95
98
  return _get_socket_linker(self._default_output_socket)._dispatch_unary( # type: ignore[attr-defined]
96
99
  "absolute"
97
100
  )
98
101
 
99
- def _apply_compare_operation(self, other: Any, operation: str) -> "BaseNode":
102
+ def _apply_compare_operation(self, other: Any, operation: str) -> "Math":
100
103
  return _get_socket_linker(self._default_output_socket)._dispatch_compare( # type: ignore[attr-defined]
101
104
  other, operation
102
105
  )
103
106
 
104
- def __lt__(self, other: Any) -> "BaseNode":
107
+ def __lt__(self, other: Any) -> "Compare":
105
108
  return self._apply_compare_operation(other, "less_than")
106
109
 
107
- def __gt__(self, other: Any) -> "BaseNode":
110
+ def __gt__(self, other: Any) -> "Compare":
108
111
  return self._apply_compare_operation(other, "greater_than")
109
112
 
110
- def __le__(self, other: Any) -> "BaseNode":
113
+ def __le__(self, other: Any) -> "Compare":
111
114
  return self._apply_compare_operation(other, "less_equal")
112
115
 
113
- def __ge__(self, other: Any) -> "BaseNode":
116
+ def __ge__(self, other: Any) -> "Compare":
114
117
  return self._apply_compare_operation(other, "greater_equal")
115
118
 
116
- def __eq__(self, other: Any) -> "BaseNode": # type: ignore[override]
119
+ def __eq__(self, other: Any) -> "Compare": # type: ignore[override]
117
120
  return self._apply_compare_operation(other, "equal")
118
121
 
119
- def __ne__(self, other: Any) -> "BaseNode": # type: ignore[override]
122
+ def __ne__(self, other: Any) -> "Compare": # type: ignore[override]
120
123
  return self._apply_compare_operation(other, "not_equal")
121
124
 
122
125
  def _apply_boolean_operation(self, other: Any, operation: str):
@@ -273,7 +276,7 @@ class LinkingMixin:
273
276
  else:
274
277
  self._link(source, input)
275
278
 
276
- def __rshift__(self, other: "BaseNode | Socket") -> "BaseNode | Socket":
279
+ def __rshift__(self, other: _RShiftT) -> _RShiftT:
277
280
  """Chain nodes using >> operator. Links output to input.
278
281
 
279
282
  Usage:
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from abc import ABC, abstractmethod
4
- from typing import TYPE_CHECKING, Iterable, Literal
4
+ from typing import TYPE_CHECKING, Iterable, Literal, Self
5
5
 
6
6
  import bpy
7
7
  from bpy.types import Node, NodeSocket
@@ -25,7 +25,7 @@ class BaseNode(_NodeLike, OperatorMixin, LinkingMixin):
25
25
  _default_output_id: str | None = None
26
26
  _placeholder_inputs: list[str]
27
27
 
28
- def __init__(self):
28
+ def __init__(self, node: bpy.types.Node | None = None):
29
29
  tree = (
30
30
  TreeBuilder._tree_contexts[-1] if len(TreeBuilder._tree_contexts) else None
31
31
  )
@@ -39,7 +39,7 @@ class BaseNode(_NodeLike, OperatorMixin, LinkingMixin):
39
39
 
40
40
  self._tree = tree
41
41
  self._placeholder_inputs = []
42
- self.node: bpy.types.Node = self._tree.add(self.__class__._bl_idname)
42
+ self.node = node if node else self._tree.add(self.__class__._bl_idname)
43
43
 
44
44
  @property
45
45
  def tree(self) -> TreeBuilder:
@@ -71,6 +71,33 @@ class BaseNode(_NodeLike, OperatorMixin, LinkingMixin):
71
71
  socket = self.node.outputs[counter]
72
72
  return socket
73
73
 
74
+ @classmethod
75
+ def _from_node(cls, node: bpy.types.Node) -> Self:
76
+ builder = cls()
77
+ builder.tree.nodes.remove(builder.node)
78
+ builder.node = node
79
+ return builder
80
+
81
+ @classmethod
82
+ def _find_or_create_linked(cls, socket: NodeSocket) -> Self:
83
+ if socket.is_output:
84
+ if socket.links:
85
+ for link in socket.links:
86
+ assert link.to_node
87
+ if link.to_node.bl_idname == cls._bl_idname:
88
+ return cls._from_node(link.to_node)
89
+ return cls(socket)
90
+ else:
91
+ if socket.links:
92
+ for link in socket.links:
93
+ assert link.from_node
94
+ if link.from_node.bl_idname == cls._bl_idname:
95
+ return cls._from_node(link.from_node)
96
+
97
+ node = cls()
98
+ node >> socket
99
+ return node
100
+
74
101
  def _set_input_default_value(self, input, value):
75
102
  """Set the default value for an input socket, handling type conversions."""
76
103
  if (