nodebpy 520.13.0__tar.gz → 520.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.
- {nodebpy-520.13.0 → nodebpy-520.15.0}/PKG-INFO +1 -1
- {nodebpy-520.13.0 → nodebpy-520.15.0}/pyproject.toml +23 -2
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/assets/_codegen.py +12 -2
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/builder/__init__.py +52 -58
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/builder/_registry.py +4 -4
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/builder/_utils.py +2 -2
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/builder/accessor.py +15 -12
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/builder/arrange.py +5 -3
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/builder/asset.py +6 -5
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/builder/items.py +27 -3
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/builder/mixins.py +66 -52
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/builder/node.py +17 -15
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/builder/socket.py +339 -306
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/builder/tree.py +46 -39
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/export/codegen.py +109 -82
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/export/diagram.py +15 -8
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/export/web_render.py +2 -2
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/lib/nodearrange/arrange/graph.py +7 -6
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/lib/nodearrange/arrange/ordering.py +22 -14
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/lib/nodearrange/arrange/ranking.py +8 -7
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/lib/nodearrange/arrange/realize.py +11 -4
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/lib/nodearrange/arrange/stacking.py +11 -13
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/lib/nodearrange/arrange/x_coords.py +3 -6
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/lib/nodearrange/arrange/y_coords.py +2 -2
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/lib/nodearrange/utils.py +5 -10
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/nodes/_mixins.py +13 -9
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/nodes/compositor/assets.py +67 -98
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/nodes/compositor/color.py +6 -9
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/nodes/compositor/converter.py +31 -35
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/nodes/compositor/distort.py +8 -10
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/nodes/compositor/filter.py +8 -10
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/nodes/compositor/input.py +9 -11
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/nodes/compositor/interface.py +22 -26
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/nodes/compositor/manual.py +12 -11
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/nodes/compositor/matte.py +8 -10
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/nodes/compositor/output.py +3 -5
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/nodes/compositor/vector.py +3 -5
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/nodes/geometry/__init__.py +208 -209
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/nodes/geometry/assets.py +452 -542
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/nodes/geometry/attribute.py +22 -26
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/nodes/geometry/color.py +4 -6
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/nodes/geometry/converter.py +158 -162
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/nodes/geometry/geometry.py +50 -56
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/nodes/geometry/grid.py +115 -119
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/nodes/geometry/input.py +18 -24
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/nodes/geometry/interface.py +29 -33
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/nodes/geometry/manual.py +281 -130
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/nodes/geometry/texture.py +7 -9
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/nodes/geometry/utilities.py +3 -5
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/nodes/geometry/vector.py +4 -6
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/nodes/geometry/zone.py +16 -13
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/nodes/shader/assets.py +5 -4
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/nodes/shader/color.py +4 -6
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/nodes/shader/converter.py +21 -25
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/nodes/shader/grid.py +6 -8
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/nodes/shader/input.py +5 -7
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/nodes/shader/manual.py +17 -16
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/nodes/shader/output.py +6 -8
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/nodes/shader/shader.py +7 -9
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/nodes/shader/texture.py +4 -7
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/nodes/shader/vector.py +5 -7
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/types.py +44 -49
- {nodebpy-520.13.0 → nodebpy-520.15.0}/README.md +0 -0
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/__init__.py +4 -4
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/assets/__init__.py +2 -2
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/assets/__main__.py +0 -0
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/export/__init__.py +1 -1
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/lib/nodearrange/__init__.py +0 -0
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/lib/nodearrange/arrange/__init__.py +0 -0
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/lib/nodearrange/arrange/structs.py +0 -0
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/lib/nodearrange/arrange/sugiyama.py +0 -0
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/lib/nodearrange/config.py +0 -0
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/nodes/__init__.py +0 -0
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/nodes/compositor/__init__.py +67 -67
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/nodes/compositor/group.py +0 -0
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/nodes/geometry/group.py +0 -0
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/nodes/geometry/groups.py +0 -0
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/nodes/geometry/output.py +0 -0
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/nodes/shader/__init__.py +61 -61
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/nodes/shader/group.py +0 -0
- {nodebpy-520.13.0 → nodebpy-520.15.0}/src/nodebpy/nodes/shader/script.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "nodebpy"
|
|
3
|
-
version = "520.
|
|
3
|
+
version = "520.15.0"
|
|
4
4
|
description = "Build nodes trees in Blender more elegantly with code"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [
|
|
@@ -44,5 +44,26 @@ dev = [
|
|
|
44
44
|
"syrupy>=5.0.0",
|
|
45
45
|
"pytest-xdist>=3.8.0",
|
|
46
46
|
"griffe<2.0.0",
|
|
47
|
-
"ty==0.0.
|
|
47
|
+
"ty==0.0.74",
|
|
48
|
+
"types-networkx>=3.6.1.20260728",
|
|
48
49
|
]
|
|
50
|
+
|
|
51
|
+
# The bpy stubs mark most collections/pointers as `| None` and cannot type
|
|
52
|
+
# dynamic attributes (e.g. `socket.default_value`), which test code exercises
|
|
53
|
+
# constantly under a real Blender runtime. Relax only those rules for tests;
|
|
54
|
+
# src/ passes `ty check` with no per-directory relaxation.
|
|
55
|
+
[[tool.ty.overrides]]
|
|
56
|
+
include = ["tests/**"]
|
|
57
|
+
|
|
58
|
+
[tool.ty.overrides.rules]
|
|
59
|
+
unresolved-attribute = "ignore"
|
|
60
|
+
not-subscriptable = "ignore"
|
|
61
|
+
invalid-assignment = "ignore"
|
|
62
|
+
invalid-argument-type = "ignore"
|
|
63
|
+
|
|
64
|
+
[tool.ruff.lint]
|
|
65
|
+
ignore = ["SIM117", "RUF059"]
|
|
66
|
+
|
|
67
|
+
[tool.ruff.lint.per-file-ignores]
|
|
68
|
+
"tests/test_codegen.py" = ["S102"]
|
|
69
|
+
"tests/test_assets.py" = ["S102"]
|
|
@@ -17,6 +17,7 @@ from collections import Counter
|
|
|
17
17
|
from collections.abc import Sequence
|
|
18
18
|
from dataclasses import dataclass
|
|
19
19
|
from pathlib import Path
|
|
20
|
+
from typing import cast
|
|
20
21
|
|
|
21
22
|
import bpy
|
|
22
23
|
|
|
@@ -220,6 +221,7 @@ def _introspect_group(group, name: str, library_source: str) -> _AssetClass:
|
|
|
220
221
|
``node_groups.new`` too narrowly for the runtime attributes used here.
|
|
221
222
|
"""
|
|
222
223
|
host = bpy.data.node_groups.new("_introspect_host", group.bl_idname)
|
|
224
|
+
assert host is not None
|
|
223
225
|
try:
|
|
224
226
|
node_type = {
|
|
225
227
|
"GeometryNodeTree": "GeometryNodeGroup",
|
|
@@ -227,7 +229,12 @@ def _introspect_group(group, name: str, library_source: str) -> _AssetClass:
|
|
|
227
229
|
"CompositorNodeTree": "CompositorNodeGroup",
|
|
228
230
|
}[group.bl_idname]
|
|
229
231
|
node = host.nodes.new(node_type)
|
|
230
|
-
node
|
|
232
|
+
assert node is not None
|
|
233
|
+
node = cast(
|
|
234
|
+
"bpy.types.GeometryNodeGroup | bpy.types.ShaderNodeGroup | bpy.types.CompositorNodeGroup",
|
|
235
|
+
node,
|
|
236
|
+
)
|
|
237
|
+
node.node_tree = group
|
|
231
238
|
# Tooltips live on the tree *interface* items, not on the node's
|
|
232
239
|
# sockets — collect them by identifier so the generated docstrings
|
|
233
240
|
# can use the asset author's own wording.
|
|
@@ -442,7 +449,10 @@ def generate_asset_api(
|
|
|
442
449
|
editors show documentation alongside the type hints. Defaults to
|
|
443
450
|
``True``; pass ``False`` for a terser module.
|
|
444
451
|
|
|
445
|
-
Returns
|
|
452
|
+
Returns
|
|
453
|
+
-------
|
|
454
|
+
list[str]
|
|
455
|
+
The list of generated class names.
|
|
446
456
|
"""
|
|
447
457
|
if isinstance(libraries, AssetLibrary):
|
|
448
458
|
libraries = [libraries]
|
|
@@ -9,7 +9,7 @@ from .asset import (
|
|
|
9
9
|
PackageLibrary,
|
|
10
10
|
asset_group_base,
|
|
11
11
|
)
|
|
12
|
-
from .items import Item, ItemsMixin
|
|
12
|
+
from .items import Item, ItemsMixin, MenuItem
|
|
13
13
|
from .mixins import LinkingMixin, OperatorMixin
|
|
14
14
|
from .node import (
|
|
15
15
|
BaseNode,
|
|
@@ -75,82 +75,76 @@ from .tree import (
|
|
|
75
75
|
)
|
|
76
76
|
|
|
77
77
|
__all__ = [
|
|
78
|
-
|
|
79
|
-
"
|
|
80
|
-
"
|
|
78
|
+
"AssetCompositorGroup",
|
|
79
|
+
"AssetGeometryGroup",
|
|
80
|
+
"AssetLibrary",
|
|
81
|
+
"AssetShaderGroup",
|
|
81
82
|
"BaseNode",
|
|
82
|
-
"
|
|
83
|
-
"
|
|
84
|
-
|
|
85
|
-
"
|
|
86
|
-
"
|
|
87
|
-
"
|
|
88
|
-
"
|
|
89
|
-
"
|
|
90
|
-
|
|
91
|
-
"
|
|
83
|
+
"BooleanSocket",
|
|
84
|
+
"BooleanSocketGrid",
|
|
85
|
+
"BooleanSocketList",
|
|
86
|
+
"BundleSocket",
|
|
87
|
+
"BundleSocketList",
|
|
88
|
+
"BundledLibrary",
|
|
89
|
+
"ClosureSocket",
|
|
90
|
+
"ClosureSocketList",
|
|
91
|
+
"CollectionSocket",
|
|
92
|
+
"CollectionSocketList",
|
|
93
|
+
"ColorSocket",
|
|
94
|
+
"ColorSocketList",
|
|
92
95
|
"CustomCompositorGroup",
|
|
93
96
|
"CustomGeometryGroup",
|
|
94
97
|
"CustomShaderGroup",
|
|
95
|
-
|
|
96
|
-
"AssetLibrary",
|
|
97
|
-
"BundledLibrary",
|
|
98
|
-
"PackageLibrary",
|
|
99
|
-
"AssetGeometryGroup",
|
|
100
|
-
"AssetShaderGroup",
|
|
101
|
-
"AssetCompositorGroup",
|
|
102
|
-
"asset_group_base",
|
|
103
|
-
# Runtime socket types
|
|
98
|
+
"DynamicInputsMixin",
|
|
104
99
|
"FloatSocket",
|
|
105
100
|
"FloatSocketGrid",
|
|
106
101
|
"FloatSocketList",
|
|
107
|
-
"
|
|
108
|
-
"
|
|
109
|
-
"
|
|
110
|
-
"
|
|
111
|
-
"
|
|
102
|
+
"FontSocket",
|
|
103
|
+
"FontSocketList",
|
|
104
|
+
"GeometrySocket",
|
|
105
|
+
"GeometrySocketList",
|
|
106
|
+
"ImageSocket",
|
|
107
|
+
"ImageSocketList",
|
|
108
|
+
"InputInterfaceContext",
|
|
112
109
|
"IntegerSocket",
|
|
113
|
-
"IntegerVectorSocket",
|
|
114
110
|
"IntegerSocketGrid",
|
|
115
111
|
"IntegerSocketList",
|
|
116
|
-
"
|
|
117
|
-
"
|
|
118
|
-
"
|
|
119
|
-
"
|
|
120
|
-
"
|
|
112
|
+
"IntegerVectorSocket",
|
|
113
|
+
"Item",
|
|
114
|
+
"ItemsMixin",
|
|
115
|
+
"LinkingMixin",
|
|
116
|
+
"MaterialBuilder",
|
|
117
|
+
"MaterialSocket",
|
|
118
|
+
"MaterialSocketList",
|
|
121
119
|
"MatrixSocket",
|
|
122
120
|
"MatrixSocketList",
|
|
123
|
-
"
|
|
124
|
-
"StringSocketList",
|
|
121
|
+
"MenuItem",
|
|
125
122
|
"MenuSocket",
|
|
126
123
|
"MenuSocketList",
|
|
127
|
-
"
|
|
128
|
-
"GeometrySocketList",
|
|
124
|
+
"NodeGroupBuilder",
|
|
129
125
|
"ObjectSocket",
|
|
130
126
|
"ObjectSocketList",
|
|
131
|
-
"
|
|
132
|
-
"
|
|
133
|
-
"
|
|
134
|
-
"
|
|
135
|
-
"
|
|
136
|
-
"
|
|
137
|
-
"CollectionSocket",
|
|
138
|
-
"CollectionSocketList",
|
|
139
|
-
"BundleSocket",
|
|
140
|
-
"BundleSocketList",
|
|
141
|
-
"ClosureSocket",
|
|
142
|
-
"ClosureSocketList",
|
|
127
|
+
"OperatorMixin",
|
|
128
|
+
"OutputInterfaceContext",
|
|
129
|
+
"PackageLibrary",
|
|
130
|
+
"PanelContext",
|
|
131
|
+
"RotationSocket",
|
|
132
|
+
"RotationSocketList",
|
|
143
133
|
"ShaderSocket",
|
|
144
134
|
"ShaderSocketList",
|
|
145
|
-
"
|
|
146
|
-
"
|
|
147
|
-
# Tree context
|
|
135
|
+
"Socket",
|
|
136
|
+
"SocketAccessor",
|
|
148
137
|
"SocketContext",
|
|
149
|
-
"PanelContext",
|
|
150
|
-
"InputInterfaceContext",
|
|
151
|
-
"OutputInterfaceContext",
|
|
152
|
-
# Utilities
|
|
153
138
|
"SocketError",
|
|
154
|
-
"
|
|
139
|
+
"SoundSocket",
|
|
140
|
+
"SoundSocketList",
|
|
141
|
+
"StringSocket",
|
|
142
|
+
"StringSocketList",
|
|
143
|
+
"TreeBuilder",
|
|
144
|
+
"VectorSocket",
|
|
145
|
+
"VectorSocketGrid",
|
|
146
|
+
"VectorSocketList",
|
|
147
|
+
"asset_group_base",
|
|
155
148
|
"denormalize_name",
|
|
149
|
+
"normalize_name",
|
|
156
150
|
]
|
|
@@ -7,12 +7,12 @@ from bpy.types import NodeSocket
|
|
|
7
7
|
if TYPE_CHECKING:
|
|
8
8
|
from .socket import Socket
|
|
9
9
|
|
|
10
|
-
_SOCKET_REGISTRY: dict[str,
|
|
11
|
-
_SOCKET_LIST_REGISTRY: dict[str,
|
|
12
|
-
_SOCKET_GRID_REGISTRY: dict[str,
|
|
10
|
+
_SOCKET_REGISTRY: dict[str, type[Socket]] = {}
|
|
11
|
+
_SOCKET_LIST_REGISTRY: dict[str, type[Socket]] = {}
|
|
12
|
+
_SOCKET_GRID_REGISTRY: dict[str, type[Socket]] = {}
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
def _wrap_socket(socket: NodeSocket) ->
|
|
15
|
+
def _wrap_socket(socket: NodeSocket) -> Socket:
|
|
16
16
|
for key, cls in _SOCKET_REGISTRY.items():
|
|
17
17
|
if key in socket.bl_idname:
|
|
18
18
|
structure = getattr(socket, "inferred_structure_type", "SINGLE")
|
|
@@ -77,7 +77,7 @@ def _allow_innactive_sockets(node: bpy.types.Node) -> bool:
|
|
|
77
77
|
|
|
78
78
|
def _resolve_promotion(
|
|
79
79
|
self_socket: NodeSocket, other: Any, reverse: bool
|
|
80
|
-
) ->
|
|
80
|
+
) -> tuple[NodeSocket, Any, bool]:
|
|
81
81
|
"""Determine the dominant socket for operator dispatch.
|
|
82
82
|
|
|
83
83
|
When both operands have a socket type, the higher-precedence type wins.
|
|
@@ -114,7 +114,7 @@ class _SocketLike(Protocol):
|
|
|
114
114
|
socket: NodeSocket
|
|
115
115
|
|
|
116
116
|
|
|
117
|
-
def _output_socket_type(value: Any) ->
|
|
117
|
+
def _output_socket_type(value: Any) -> SOCKET_TYPES | None:
|
|
118
118
|
"""The Blender socket ``type`` of *value*'s default output socket.
|
|
119
119
|
|
|
120
120
|
Resolves a raw ``NodeSocket`` or any socket/node wrapper to the type string
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from collections.abc import Iterator
|
|
4
|
+
from typing import TYPE_CHECKING, Literal, cast, overload
|
|
4
5
|
|
|
5
6
|
import bpy
|
|
6
7
|
from bpy.types import NodeSocket
|
|
@@ -84,10 +85,10 @@ class SocketAccessor:
|
|
|
84
85
|
)
|
|
85
86
|
|
|
86
87
|
@overload
|
|
87
|
-
def _get(self, key: slice) ->
|
|
88
|
+
def _get(self, key: slice) -> list[Socket]: ...
|
|
88
89
|
@overload
|
|
89
|
-
def _get(self, key: str | int) ->
|
|
90
|
-
def _get(self, key: str | int | slice) ->
|
|
90
|
+
def _get(self, key: str | int) -> Socket: ...
|
|
91
|
+
def _get(self, key: str | int | slice) -> Socket | list[Socket]:
|
|
91
92
|
"""Get a Socket for a socket by identifier, name, or index."""
|
|
92
93
|
if isinstance(key, slice):
|
|
93
94
|
sockets = [
|
|
@@ -104,10 +105,10 @@ class SocketAccessor:
|
|
|
104
105
|
return socket
|
|
105
106
|
|
|
106
107
|
@overload
|
|
107
|
-
def __getitem__(self, key: slice) ->
|
|
108
|
+
def __getitem__(self, key: slice) -> list[Socket]: ...
|
|
108
109
|
@overload
|
|
109
|
-
def __getitem__(self, key: str | int) ->
|
|
110
|
-
def __getitem__(self, key: str | int | slice) ->
|
|
110
|
+
def __getitem__(self, key: str | int) -> Socket: ...
|
|
111
|
+
def __getitem__(self, key: str | int | slice) -> Socket | list[Socket]:
|
|
111
112
|
"""Access by identifier, name, or integer index."""
|
|
112
113
|
return self._get(key)
|
|
113
114
|
|
|
@@ -122,7 +123,9 @@ class SocketAccessor:
|
|
|
122
123
|
for s in self._collection:
|
|
123
124
|
assert s.node is not None
|
|
124
125
|
return s.node
|
|
125
|
-
return
|
|
126
|
+
return cast(
|
|
127
|
+
"bpy.types.Node", self._collection.data
|
|
128
|
+
) # empty collection fallback
|
|
126
129
|
|
|
127
130
|
@property
|
|
128
131
|
def _ignore_visibility(self) -> bool:
|
|
@@ -191,7 +194,7 @@ class SocketAccessor:
|
|
|
191
194
|
f"{socket_type} on {self._node.name}"
|
|
192
195
|
)
|
|
193
196
|
|
|
194
|
-
def _values(self) ->
|
|
197
|
+
def _values(self) -> list[Socket]:
|
|
195
198
|
"""All visible sockets as Sockets.
|
|
196
199
|
|
|
197
200
|
Uses node-level visibility rules regardless of ``ignore_visibility`` —
|
|
@@ -199,7 +202,7 @@ class SocketAccessor:
|
|
|
199
202
|
"""
|
|
200
203
|
return [_wrap_socket(s) for s in self._visible_sockets()]
|
|
201
204
|
|
|
202
|
-
def _items(self) ->
|
|
205
|
+
def _items(self) -> list[tuple[str, Socket]]:
|
|
203
206
|
"""All visible sockets as (name, Socket) pairs.
|
|
204
207
|
|
|
205
208
|
Uses node-level visibility rules regardless of ``ignore_visibility`` —
|
|
@@ -214,10 +217,10 @@ class SocketAccessor:
|
|
|
214
217
|
def __len__(self) -> int:
|
|
215
218
|
return len(self._items())
|
|
216
219
|
|
|
217
|
-
def __iter__(self) -> Iterator[
|
|
220
|
+
def __iter__(self) -> Iterator[Socket]:
|
|
218
221
|
return iter(self._values())
|
|
219
222
|
|
|
220
|
-
def __getattr__(self, name: str) ->
|
|
223
|
+
def __getattr__(self, name: str) -> Socket:
|
|
221
224
|
"""Dynamic socket access by normalised attribute name.
|
|
222
225
|
|
|
223
226
|
Converts ``node.o.base_color`` to ``self._get("Base Color")``.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from collections import Counter, deque
|
|
4
|
+
from typing import Any, cast
|
|
4
5
|
|
|
5
6
|
import bpy
|
|
6
7
|
|
|
@@ -114,10 +115,12 @@ def calculate_node_dimensions(
|
|
|
114
115
|
enabled_outputs = sum(1 for s in node.outputs if s.enabled)
|
|
115
116
|
|
|
116
117
|
# count properties specific to this node type (not inherited)
|
|
118
|
+
# ``bl_rna`` exists on bpy classes via their metaclass, invisible to type
|
|
119
|
+
# checkers looking at plain ``type``.
|
|
117
120
|
inherited_ids = {
|
|
118
121
|
prop.identifier
|
|
119
122
|
for base in type(node).__bases__
|
|
120
|
-
for prop in
|
|
123
|
+
for prop in cast(Any, base).bl_rna.properties
|
|
121
124
|
}
|
|
122
125
|
node_property_count = sum(
|
|
123
126
|
1 for prop in node.bl_rna.properties if prop.identifier not in inherited_ids
|
|
@@ -275,8 +278,7 @@ def position_nodes_in_columns(
|
|
|
275
278
|
|
|
276
279
|
width, height = calculate_node_dimensions(node, connection_counts, 1.0)
|
|
277
280
|
|
|
278
|
-
|
|
279
|
-
col_width = width
|
|
281
|
+
col_width = max(col_width, width)
|
|
280
282
|
|
|
281
283
|
node.location = (x, y)
|
|
282
284
|
|
|
@@ -12,6 +12,7 @@ from __future__ import annotations
|
|
|
12
12
|
import os
|
|
13
13
|
from abc import ABC, abstractmethod
|
|
14
14
|
from pathlib import Path
|
|
15
|
+
from typing import Any
|
|
15
16
|
|
|
16
17
|
import bpy
|
|
17
18
|
|
|
@@ -122,19 +123,19 @@ _ASSET_GROUP_BASE = {
|
|
|
122
123
|
}
|
|
123
124
|
|
|
124
125
|
|
|
125
|
-
def asset_group_base(tree_idname: str) -> type:
|
|
126
|
+
def asset_group_base(tree_idname: str) -> type[Any]:
|
|
126
127
|
"""The ``Asset*Group`` base class for a tree-type bl_idname."""
|
|
127
128
|
return _ASSET_GROUP_BASE[tree_idname]
|
|
128
129
|
|
|
129
130
|
|
|
130
131
|
__all__ = [
|
|
132
|
+
"AssetCompositorGroup",
|
|
133
|
+
"AssetGeometryGroup",
|
|
131
134
|
"AssetLibrary",
|
|
132
|
-
"BundledLibrary",
|
|
133
|
-
"PackageLibrary",
|
|
134
135
|
"AssetNodeGroup",
|
|
135
|
-
"AssetGeometryGroup",
|
|
136
136
|
"AssetShaderGroup",
|
|
137
|
-
"
|
|
137
|
+
"BundledLibrary",
|
|
138
|
+
"PackageLibrary",
|
|
138
139
|
"asset_group_base",
|
|
139
140
|
]
|
|
140
141
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from collections.abc import Mapping
|
|
4
|
+
from typing import TYPE_CHECKING, Any, Generic, TypeVar, cast
|
|
4
5
|
|
|
5
6
|
from bpy.types import ID, Node, NodeSocket
|
|
6
7
|
from mathutils import Euler
|
|
@@ -9,7 +10,7 @@ from ..types import _is_default_value
|
|
|
9
10
|
from ._registry import _wrap_socket
|
|
10
11
|
from ._utils import _SocketLike
|
|
11
12
|
from .node import DynamicInputsMixin
|
|
12
|
-
from .socket import Socket
|
|
13
|
+
from .socket import BaseSocket, Socket
|
|
13
14
|
|
|
14
15
|
if TYPE_CHECKING:
|
|
15
16
|
from ..types import (
|
|
@@ -95,7 +96,7 @@ def _infer_value_type(value: Any) -> str | None:
|
|
|
95
96
|
return None
|
|
96
97
|
|
|
97
98
|
|
|
98
|
-
_SocketT = TypeVar("_SocketT", bound=
|
|
99
|
+
_SocketT = TypeVar("_SocketT", bound=BaseSocket, default=Socket)
|
|
99
100
|
|
|
100
101
|
|
|
101
102
|
class Item(Generic[_SocketT]):
|
|
@@ -151,6 +152,29 @@ class Item(Generic[_SocketT]):
|
|
|
151
152
|
)
|
|
152
153
|
|
|
153
154
|
|
|
155
|
+
class MenuItem(Item[_SocketT]):
|
|
156
|
+
"""Handle for one Menu Switch enum item.
|
|
157
|
+
|
|
158
|
+
``input`` is the item's input socket (typed to the switch's data
|
|
159
|
+
type), ``is_selected`` the item's boolean output socket, and
|
|
160
|
+
``description`` the tooltip Blender shows for the item in the menu.
|
|
161
|
+
"""
|
|
162
|
+
|
|
163
|
+
@property
|
|
164
|
+
def is_selected(self) -> BooleanSocket:
|
|
165
|
+
"""Boolean output socket — True when this item is selected."""
|
|
166
|
+
return cast("BooleanSocket", self.output)
|
|
167
|
+
|
|
168
|
+
@property
|
|
169
|
+
def description(self) -> str:
|
|
170
|
+
"""Tooltip shown for this item in the drop-down menu."""
|
|
171
|
+
return self._item.description
|
|
172
|
+
|
|
173
|
+
@description.setter
|
|
174
|
+
def description(self, value: str) -> None:
|
|
175
|
+
self._item.description = value
|
|
176
|
+
|
|
177
|
+
|
|
154
178
|
class ItemsMixin(DynamicInputsMixin):
|
|
155
179
|
"""Socket machinery for nodes whose sockets are driven by a bpy item
|
|
156
180
|
collection (``capture_items``, ``bake_items``, ``format_items``, ...).
|