PyDecisionGraph 0.3.2.post1__cp315-cp315-win_amd64.whl

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 (148) hide show
  1. decision_graph/__infra__.pxd +1 -0
  2. decision_graph/__init__.pxd +1 -0
  3. decision_graph/__init__.py +39 -0
  4. decision_graph/decision_tree/__init__.pxd +2 -0
  5. decision_graph/decision_tree/__init__.py +85 -0
  6. decision_graph/decision_tree/bake/__infra__.pxd +502 -0
  7. decision_graph/decision_tree/bake/__init__.pxd +502 -0
  8. decision_graph/decision_tree/bake/__init__.py +29 -0
  9. decision_graph/decision_tree/bake/c_action.cp315-win_amd64.pyd +0 -0
  10. decision_graph/decision_tree/bake/c_action.h +324 -0
  11. decision_graph/decision_tree/bake/c_action.pxd +54 -0
  12. decision_graph/decision_tree/bake/c_action.pyi +126 -0
  13. decision_graph/decision_tree/bake/c_action.pyx +65 -0
  14. decision_graph/decision_tree/bake/c_allocator_protocol.cp315-win_amd64.pyd +0 -0
  15. decision_graph/decision_tree/bake/c_allocator_protocol.pxd +24 -0
  16. decision_graph/decision_tree/bake/c_allocator_protocol.pyi +61 -0
  17. decision_graph/decision_tree/bake/c_allocator_protocol.pyx +109 -0
  18. decision_graph/decision_tree/bake/c_bake.cp315-win_amd64.pyd +0 -0
  19. decision_graph/decision_tree/bake/c_bake.h +629 -0
  20. decision_graph/decision_tree/bake/c_bake.pxd +52 -0
  21. decision_graph/decision_tree/bake/c_bake.pyi +151 -0
  22. decision_graph/decision_tree/bake/c_bake.pyx +142 -0
  23. decision_graph/decision_tree/bake/c_collections.cp315-win_amd64.pyd +0 -0
  24. decision_graph/decision_tree/bake/c_collections.h +692 -0
  25. decision_graph/decision_tree/bake/c_collections.pxd +52 -0
  26. decision_graph/decision_tree/bake/c_collections.pyi +193 -0
  27. decision_graph/decision_tree/bake/c_collections.pyx +161 -0
  28. decision_graph/decision_tree/bake/c_const.cp315-win_amd64.pyd +0 -0
  29. decision_graph/decision_tree/bake/c_const.h +395 -0
  30. decision_graph/decision_tree/bake/c_const.pxd +56 -0
  31. decision_graph/decision_tree/bake/c_const.pyi +305 -0
  32. decision_graph/decision_tree/bake/c_const.pyx +278 -0
  33. decision_graph/decision_tree/bake/c_edge.cp315-win_amd64.pyd +0 -0
  34. decision_graph/decision_tree/bake/c_edge.h +511 -0
  35. decision_graph/decision_tree/bake/c_edge.pxd +117 -0
  36. decision_graph/decision_tree/bake/c_edge.pyi +192 -0
  37. decision_graph/decision_tree/bake/c_edge.pyx +270 -0
  38. decision_graph/decision_tree/bake/c_eval.h +848 -0
  39. decision_graph/decision_tree/bake/c_eval.pxd +40 -0
  40. decision_graph/decision_tree/bake/c_expr.cp315-win_amd64.pyd +0 -0
  41. decision_graph/decision_tree/bake/c_expr.h +1900 -0
  42. decision_graph/decision_tree/bake/c_expr.pxd +129 -0
  43. decision_graph/decision_tree/bake/c_expr.pyi +280 -0
  44. decision_graph/decision_tree/bake/c_expr.pyx +272 -0
  45. decision_graph/decision_tree/bake/c_hierarchy.cp315-win_amd64.pyd +0 -0
  46. decision_graph/decision_tree/bake/c_hierarchy.h +731 -0
  47. decision_graph/decision_tree/bake/c_hierarchy.pxd +71 -0
  48. decision_graph/decision_tree/bake/c_hierarchy.pyi +409 -0
  49. decision_graph/decision_tree/bake/c_hierarchy.pyx +277 -0
  50. decision_graph/decision_tree/bake/c_logic_group.cp315-win_amd64.pyd +0 -0
  51. decision_graph/decision_tree/bake/c_logic_group.h +1226 -0
  52. decision_graph/decision_tree/bake/c_logic_group.pxd +162 -0
  53. decision_graph/decision_tree/bake/c_logic_group.pyi +264 -0
  54. decision_graph/decision_tree/bake/c_logic_group.pyx +308 -0
  55. decision_graph/decision_tree/bake/c_node.cp315-win_amd64.pyd +0 -0
  56. decision_graph/decision_tree/bake/c_node.h +2660 -0
  57. decision_graph/decision_tree/bake/c_node.pxd +381 -0
  58. decision_graph/decision_tree/bake/c_node.pyi +438 -0
  59. decision_graph/decision_tree/bake/c_node.pyx +639 -0
  60. decision_graph/decision_tree/bake/c_reconstruct.cp315-win_amd64.pyd +0 -0
  61. decision_graph/decision_tree/bake/c_reconstruct.pxd +63 -0
  62. decision_graph/decision_tree/bake/c_reconstruct.pyi +60 -0
  63. decision_graph/decision_tree/bake/c_reconstruct.pyx +252 -0
  64. decision_graph/decision_tree/bake/c_var.cp315-win_amd64.pyd +0 -0
  65. decision_graph/decision_tree/bake/c_var.h +2054 -0
  66. decision_graph/decision_tree/bake/c_var.pxd +205 -0
  67. decision_graph/decision_tree/bake/c_var.pyi +271 -0
  68. decision_graph/decision_tree/bake/c_var.pyx +228 -0
  69. decision_graph/decision_tree/capi/__infra__.pxd +33 -0
  70. decision_graph/decision_tree/capi/__init__.pxd +33 -0
  71. decision_graph/decision_tree/capi/__init__.py +57 -0
  72. decision_graph/decision_tree/capi/c_abc.cp315-win_amd64.pyd +0 -0
  73. decision_graph/decision_tree/capi/c_abc.pxd +246 -0
  74. decision_graph/decision_tree/capi/c_abc.pyi +770 -0
  75. decision_graph/decision_tree/capi/c_abc.pyx +1532 -0
  76. decision_graph/decision_tree/capi/c_collection.cp315-win_amd64.pyd +0 -0
  77. decision_graph/decision_tree/capi/c_collection.pxd +22 -0
  78. decision_graph/decision_tree/capi/c_collection.pyi +245 -0
  79. decision_graph/decision_tree/capi/c_collection.pyx +138 -0
  80. decision_graph/decision_tree/capi/c_node.cp315-win_amd64.pyd +0 -0
  81. decision_graph/decision_tree/capi/c_node.pxd +95 -0
  82. decision_graph/decision_tree/capi/c_node.pyi +492 -0
  83. decision_graph/decision_tree/capi/c_node.pyx +551 -0
  84. decision_graph/decision_tree/exc.py +189 -0
  85. decision_graph/decision_tree/native/__init__.py +57 -0
  86. decision_graph/decision_tree/native/abc.py +1167 -0
  87. decision_graph/decision_tree/native/collection.py +134 -0
  88. decision_graph/decision_tree/native/node.py +521 -0
  89. decision_graph/includes/decision_graph/decision_tree/bake/c_action.c +16320 -0
  90. decision_graph/includes/decision_graph/decision_tree/bake/c_action.h +324 -0
  91. decision_graph/includes/decision_graph/decision_tree/bake/c_allocator_protocol.c +12473 -0
  92. decision_graph/includes/decision_graph/decision_tree/bake/c_bake.c +16485 -0
  93. decision_graph/includes/decision_graph/decision_tree/bake/c_bake.h +629 -0
  94. decision_graph/includes/decision_graph/decision_tree/bake/c_collections.c +17381 -0
  95. decision_graph/includes/decision_graph/decision_tree/bake/c_collections.h +692 -0
  96. decision_graph/includes/decision_graph/decision_tree/bake/c_const.c +20815 -0
  97. decision_graph/includes/decision_graph/decision_tree/bake/c_const.h +395 -0
  98. decision_graph/includes/decision_graph/decision_tree/bake/c_edge.c +20696 -0
  99. decision_graph/includes/decision_graph/decision_tree/bake/c_edge.h +511 -0
  100. decision_graph/includes/decision_graph/decision_tree/bake/c_eval.h +848 -0
  101. decision_graph/includes/decision_graph/decision_tree/bake/c_expr.c +22065 -0
  102. decision_graph/includes/decision_graph/decision_tree/bake/c_expr.h +1900 -0
  103. decision_graph/includes/decision_graph/decision_tree/bake/c_hierarchy.c +23267 -0
  104. decision_graph/includes/decision_graph/decision_tree/bake/c_hierarchy.h +731 -0
  105. decision_graph/includes/decision_graph/decision_tree/bake/c_logic_group.c +22434 -0
  106. decision_graph/includes/decision_graph/decision_tree/bake/c_logic_group.h +1226 -0
  107. decision_graph/includes/decision_graph/decision_tree/bake/c_node.c +28385 -0
  108. decision_graph/includes/decision_graph/decision_tree/bake/c_node.h +2660 -0
  109. decision_graph/includes/decision_graph/decision_tree/bake/c_reconstruct.c +15955 -0
  110. decision_graph/includes/decision_graph/decision_tree/bake/c_var.c +16881 -0
  111. decision_graph/includes/decision_graph/decision_tree/bake/c_var.h +2054 -0
  112. decision_graph/includes/decision_graph/decision_tree/capi/c_abc.c +59196 -0
  113. decision_graph/includes/decision_graph/decision_tree/capi/c_collection.c +19967 -0
  114. decision_graph/includes/decision_graph/decision_tree/capi/c_node.c +39229 -0
  115. decision_graph/logic_group/__init__.py +25 -0
  116. decision_graph/logic_group/base.py +100 -0
  117. decision_graph/logic_group/pending_request.py +384 -0
  118. decision_graph/webui/__init__.py +24 -0
  119. decision_graph/webui/bake/__init__.py +65 -0
  120. decision_graph/webui/bake/app.py +488 -0
  121. decision_graph/webui/bake/static/d3.v7.min.js +2 -0
  122. decision_graph/webui/bake/static/script.js +1440 -0
  123. decision_graph/webui/bake/static/style.css +920 -0
  124. decision_graph/webui/bake/templates/index.html +183 -0
  125. decision_graph/webui/bake/templates/offline.html +184 -0
  126. decision_graph/webui/capi/__init__.py +54 -0
  127. decision_graph/webui/capi/app.py +366 -0
  128. decision_graph/webui/capi/main.py +53 -0
  129. decision_graph/webui/capi/static/d3.v7.min.js +2 -0
  130. decision_graph/webui/capi/static/script.js +1026 -0
  131. decision_graph/webui/capi/static/style.css +656 -0
  132. decision_graph/webui/capi/templates/index.html +93 -0
  133. decision_graph/webui/capi/templates/offline.html +94 -0
  134. decision_graph/webui/native/__init__.py +54 -0
  135. decision_graph/webui/native/app.py +366 -0
  136. decision_graph/webui/native/main.py +53 -0
  137. decision_graph/webui/native/static/d3.v7.min.js +2 -0
  138. decision_graph/webui/native/static/script.js +1026 -0
  139. decision_graph/webui/native/static/style.css +656 -0
  140. decision_graph/webui/native/templates/index.html +93 -0
  141. decision_graph/webui/native/templates/offline.html +94 -0
  142. pydecisiongraph-0.3.2.post1.dist-info/METADATA +136 -0
  143. pydecisiongraph-0.3.2.post1.dist-info/RECORD +148 -0
  144. pydecisiongraph-0.3.2.post1.dist-info/WHEEL +5 -0
  145. pydecisiongraph-0.3.2.post1.dist-info/licenses/LICENSE +373 -0
  146. pydecisiongraph-0.3.2.post1.dist-info/top_level.txt +2 -0
  147. tests/bake_c_evaluator.cp315-win_amd64.pyd +0 -0
  148. tests/bake_eval_probe.cp315-win_amd64.pyd +0 -0
@@ -0,0 +1,438 @@
1
+ """The node layer: the graph's nodes, their edges, and the wrapper registry.
2
+
3
+ A node is a block in C and a wrapper here, and the two are held together by
4
+ address: ``NODE_REGISTRY`` is keyed by the block's address, so one node is one
5
+ Python object for as long as the layer holds it.
6
+
7
+ A node that is entered with the ``with`` statement is a scope a build descends
8
+ into - the manager reserves the arms its branches will fill - and the children
9
+ dict is the graph as Python sees it, keyed by the edge each child hangs by.
10
+ """
11
+
12
+ from typing import Any, Self
13
+ from uuid import UUID
14
+
15
+ from cbase.bytemap import BoundByteMap
16
+
17
+ from .c_edge import AUTO_CONDITION, NodeEdgeCondition
18
+ from .c_logic_group import LogicGroupManager
19
+ from .c_var import VarView
20
+
21
+
22
+ class LogicNode:
23
+ """A node of a bake graph: a block in C, wrapped here.
24
+
25
+ Building is the ``with`` statement: entering a node reserves the arms its
26
+ branches will be filled into, and whatever a build leaves reserved is closed
27
+ by the C layer on the way out. A node reached from C - a child the C layer
28
+ grew, a node named by an address - is turned back into a wrapper by
29
+ ``c_reconstruct``, which is the only place a node's class is decided.
30
+
31
+ **The evaluation protocol.** Evaluating a graph walks it from the top down,
32
+ one node at a time, and comes to rest on a single leaf: each node's value is
33
+ produced (from its type, or from a hook), the value selects which edge the
34
+ walk follows, and the node that had no edge to follow is where the walk
35
+ stops. A node off that path keeps the value it had.
36
+
37
+ A subclass may take part in that, at any of three points around the value:
38
+
39
+ - ``pre_eval_fn`` runs before the node's value is produced. A refusal here
40
+ stops the node where it stands.
41
+ - ``eval_fn`` produces the node's value, and is what a subclass overrides to
42
+ compute rather than to react. A subclass that overrides neither this nor
43
+ its Cython counterpart evaluates by the rule for its type.
44
+ - ``post_eval_fn`` runs after the value is produced, so it reacts to a value
45
+ that is already in the slot.
46
+
47
+ Each hook answers with the code the node ends with: return ``None`` to let
48
+ the evaluation continue, or a negative ``DCG_ERR_*`` code to refuse it. An
49
+ exception raised inside a hook is reported as that exception rather than as a
50
+ code - the hook's failure is the hook's to describe.
51
+
52
+ Hooks are installed **once, when the node is built**, and only for the hooks
53
+ a subclass has actually overridden: a node that overrides none carries no
54
+ hooks at all, so the evaluator has nothing to call. What that means is that a
55
+ hook cannot be added to a node that is already built, and that a wrapper
56
+ which does **not own** its node cannot carry one at all - the C node would
57
+ end up holding a pointer to a Python object nothing keeps alive. A read built
58
+ by a store is the store's, so it is the one kind of node that cannot hook.
59
+
60
+ The same three points exist one level down, as ``cdef`` hooks
61
+ (``c_pre_eval_fn``, ``c_eval_fn``, ``c_post_eval_fn``), which a subclass
62
+ written in Cython overrides to keep the hook on the C side of the call. Those
63
+ answer with a ``DCG_ERR_*`` code too, and a node whose class overrides both
64
+ runs the Cython one first.
65
+
66
+ A hook is an OVERRIDE of what the node would do anyway, and most types have
67
+ no work to do at all: a literal's value, a read's entry, an action's
68
+ self-reference and a root's truth are all settled when the node is BUILT, so
69
+ evaluating one is nothing. What an evaluation actually runs is an operator
70
+ node's rule - and a hook, where a subclass has one, is reached instead.
71
+ """
72
+
73
+ def __init__(self, *, repr: str | None = None, uid: Any | None = None, **kwargs: Any) -> None:
74
+ """Initialize a node of no particular type, which is refused.
75
+
76
+ Args:
77
+ repr: Display text to copy.
78
+ uid: Stable identity, minted when it is not given.
79
+
80
+ Raises:
81
+ NodeTypeError: Always - the base has no node type to build.
82
+ """
83
+ ...
84
+
85
+ @staticmethod
86
+ def get_manager() -> LogicGroupManager:
87
+ """The manager this layer builds through.
88
+
89
+ Returns:
90
+ The process-wide logic group manager.
91
+ """
92
+ ...
93
+
94
+ def __repr__(self) -> str:
95
+ """The wrapper's own text: its class and its display text."""
96
+ ...
97
+
98
+ def __rshift__(self, other: LogicNode) -> LogicNode:
99
+ """Link a child under the inherited edge, and answer with the child.
100
+
101
+ Args:
102
+ other: Node to link under this one.
103
+
104
+ Returns:
105
+ The child, so a shift chain reads as the graph it builds.
106
+ """
107
+ ...
108
+
109
+ def __enter__(self) -> Self:
110
+ """Enter the node as a scope a build continues inside.
111
+
112
+ Returns:
113
+ This node.
114
+ """
115
+ ...
116
+
117
+ def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> bool:
118
+ """Leave the node, closing whatever the build left reserved.
119
+
120
+ Returns:
121
+ False, so an exception raised inside the block keeps travelling.
122
+ """
123
+ ...
124
+
125
+ def append(self, child: LogicNode, condition: NodeEdgeCondition = AUTO_CONDITION) -> None:
126
+ """Link a child under an explicit edge, or under an inferred one.
127
+
128
+ The default is the auto edge, which hands the arm to the PARENT to work
129
+ out - the same edge ``>>`` links by - so ``node.append(child)`` and
130
+ ``node >> child`` add a child the same way. A build that knows which arm
131
+ it is filling passes the edge, and one that is filling a branch's second
132
+ arm normally should not have to say so.
133
+
134
+ The edge is what a build settles, not what a graph runs: an auto edge is
135
+ resolved when the child joins, and what the parent ends up holding is the
136
+ arm itself - which is what ``children`` is keyed by and what a
137
+ reconstruction restores.
138
+
139
+ Args:
140
+ child: Node to link.
141
+ condition: Edge to link it by; the auto edge when not given.
142
+
143
+ Raises:
144
+ RuntimeError: When the C layer refuses the link - an arm the parent
145
+ has no room for, or one the child cannot take.
146
+ TypeError: When ``condition`` is not a ``NodeEdgeCondition``.
147
+ """
148
+ ...
149
+
150
+ def overwrite(self, new_node: LogicNode, condition: NodeEdgeCondition) -> None:
151
+ """Put a node into an edge another node already holds.
152
+
153
+ Args:
154
+ new_node: Node to place.
155
+ condition: The edge to place it on.
156
+
157
+ Raises:
158
+ KeyError: When no child hangs by that edge.
159
+ """
160
+ ...
161
+
162
+ def replace(self, original_node: LogicNode, new_node: LogicNode) -> None:
163
+ """Put a node where another one is, inheriting its edge and position.
164
+
165
+ Args:
166
+ original_node: Node being displaced.
167
+ new_node: Node taking its place.
168
+ """
169
+ ...
170
+
171
+ def detach(self) -> None:
172
+ """Unlink this node from its parent, keeping its own subtree."""
173
+ ...
174
+
175
+ def label(self, name: str) -> None:
176
+ """Add a label to this node.
177
+
178
+ Args:
179
+ name: Label to add.
180
+ """
181
+ ...
182
+
183
+ def unlabel(self, name: str) -> None:
184
+ """Remove a label from this node.
185
+
186
+ Args:
187
+ name: Label to remove.
188
+ """
189
+ ...
190
+
191
+ def has_label(self, name: str) -> bool:
192
+ """Whether this node carries a label.
193
+
194
+ Args:
195
+ name: Label to look for.
196
+
197
+ Returns:
198
+ True when the label is on this node.
199
+ """
200
+ ...
201
+
202
+ def render(self, max_depth: int = 0, show_labels: bool = True, show_out: bool = False, style: str = 'unicode') -> str:
203
+ """Render the subtree as text.
204
+
205
+ Args:
206
+ max_depth: How deep to walk; 0 for the layer's own limit.
207
+ show_labels: Whether to print each node's labels.
208
+ show_out: Whether to print each node's value.
209
+ style: ``'unicode'`` for box-drawing, anything else for ASCII.
210
+
211
+ Returns:
212
+ The tree, one node per line.
213
+
214
+ Raises:
215
+ BufferError: When the tree does not fit the render buffer.
216
+ """
217
+ ...
218
+
219
+ def validate(self) -> tuple[int, int, int, int] | None:
220
+ """Check the graph this node heads.
221
+
222
+ Returns:
223
+ None when the graph is well formed, else the report as
224
+ ``(code, errors, nodes, depth)``.
225
+ """
226
+ ...
227
+
228
+ def pre_eval_fn(self) -> None:
229
+ """Run before this node's value is produced.
230
+
231
+ Override to set a node up, or to refuse it: returning a negative
232
+ ``DCG_ERR_*`` code stops the node where it stands, and so does raising.
233
+ The default does nothing.
234
+
235
+ Returns:
236
+ None, so the evaluation goes on to produce the value.
237
+ """
238
+ ...
239
+
240
+ def eval_fn(self) -> None:
241
+ """Produce this node's value.
242
+
243
+ Override to compute the value rather than to take it from the node's
244
+ type. A hook written in Python watches the value rather than setting it -
245
+ the slot is the C layer's to write, which is what the ``c_eval_fn`` hook
246
+ next to this one is for. The default does nothing, which lets the
247
+ built-in rule for the node's type produce the value.
248
+
249
+ Returns:
250
+ None, so the evaluation goes on to the post stage.
251
+ """
252
+ ...
253
+
254
+ def post_eval_fn(self) -> None:
255
+ """Run after this node's value has been produced.
256
+
257
+ Override to react to a value that is already in the slot. The default
258
+ does nothing.
259
+
260
+ Returns:
261
+ None, so the evaluation goes on to select the next edge.
262
+ """
263
+ ...
264
+
265
+ def eval(self) -> Any:
266
+ """Evaluate this node - and only this node - for the value it holds.
267
+
268
+ The node's three stages run and the slot keeps what they produced, but
269
+ nothing else is written and no child is reached: the node's stage bits,
270
+ error code, run id, depth and visit count are left exactly as they were,
271
+ so a node can be asked between two walks without disturbing them.
272
+
273
+ Whether the value makes sense on its own is the node's own business. A
274
+ literal answers with itself, an action with the node itself (an action
275
+ *is* what it decides), a read with what its store entry holds now, and an
276
+ expression with its operands evaluated and applied - which is why a read
277
+ of an entry nothing has landed in reports ``UNBOUND`` rather than a
278
+ value, and an expression over one of those stops at the read.
279
+
280
+ Returns:
281
+ The value the node came to hold, as a Python object.
282
+
283
+ Raises:
284
+ EvalFailureError: When the node cannot produce a value - an
285
+ unevaluable type, an operand outside its operator's domain, a
286
+ read of an entry with no value in it, or a hook that refused the
287
+ node. It carries the node that refused, the code it ended with,
288
+ the stage that failed, what was running then and the run it
289
+ happened in, and a hook's own exception travels as its cause.
290
+ """
291
+ ...
292
+
293
+ def dry_run(self) -> Any:
294
+ """Ask this node what it WOULD evaluate to, and leave it as it was.
295
+
296
+ The same three stages run as in ``eval``, and then the node is put back
297
+ the way it was found: the value the evaluation produced comes back to the
298
+ caller instead of staying in the slot, and the slot keeps what it held.
299
+ The node's own outcome is still written - how far it got and what it
300
+ ended with - since that is about this evaluation; nothing about a run is,
301
+ because this is not one.
302
+
303
+ This is the question a build asks about a node it does not want to
304
+ disturb: what a branch says before a subtree under it is walked, and what
305
+ a node a walk has already valued says now.
306
+
307
+ It is ONE node, like ``eval`` on every node but a root: asking a root this
308
+ way asks for the root's own value, not for the decision its graph reaches.
309
+ A walk is ``RootLogicNode.eval``.
310
+
311
+ Returns:
312
+ The value the node would come to hold, as a Python object.
313
+
314
+ Raises:
315
+ EvalFailureError: When the node cannot produce a value, with the
316
+ fields and the message ``eval`` reports.
317
+ """
318
+ ...
319
+
320
+ @property
321
+ def repr(self) -> str:
322
+ """The node's display text."""
323
+ ...
324
+
325
+ @property
326
+ def type(self) -> str:
327
+ """The node's type name, as the C layer reports it."""
328
+ ...
329
+
330
+ @property
331
+ def uuid(self) -> UUID:
332
+ """The node's stable identity, minted when the node was built."""
333
+ ...
334
+
335
+ @property
336
+ def autogen(self) -> bool:
337
+ """Whether the builder generated this node rather than a caller."""
338
+ ...
339
+
340
+ @property
341
+ def is_leaf(self) -> bool:
342
+ """Whether the node has no children."""
343
+ ...
344
+
345
+ @property
346
+ def labels(self) -> list[str]:
347
+ """The labels on this node, in the order they were added."""
348
+ ...
349
+
350
+ @property
351
+ def size(self) -> int:
352
+ """How many nodes the subtree under this one holds, itself included."""
353
+ ...
354
+
355
+ @property
356
+ def out(self) -> VarView:
357
+ """A read-only view of the value this node holds.
358
+
359
+ Every node has one output slot, and what is in it depends on the node: a
360
+ literal holds its value there (which is what makes a constant a valid
361
+ place to take a view from), an evaluated node holds what it last produced,
362
+ and a node that has not run holds nothing. The view reads the slot live
363
+ and keeps it alive only through this node.
364
+ """
365
+ ...
366
+
367
+ @property
368
+ def eval_hooks(self) -> tuple[str, ...]:
369
+ """The eval hooks this node carries, by the name of the method that runs.
370
+
371
+ What is installed is decided once, when the node is built: the hooks a
372
+ subclass overrode, and only those. The names tell the two levels apart -
373
+ ``'pre_eval_fn'`` is the Python hook and ``'c_pre_eval_fn'`` its
374
+ Cython counterpart - and an empty tuple means the node has no hooks at all, so
375
+ the built-in rule for its type is what produces its value.
376
+ """
377
+ ...
378
+
379
+ @property
380
+ def address(self) -> int:
381
+ """The C block's address, which is what the registry is keyed by."""
382
+ ...
383
+
384
+ @property
385
+ def parent(self) -> LogicNode | None:
386
+ """The node this one hangs from, or None for a parentless node."""
387
+ ...
388
+
389
+ @property
390
+ def children(self) -> dict[NodeEdgeCondition, LogicNode]:
391
+ """The children, keyed by the edge each one hangs by."""
392
+ ...
393
+
394
+ @property
395
+ def condition_to_parent(self) -> NodeEdgeCondition:
396
+ """The edge this node hangs by - unconditional when it has no parent."""
397
+ ...
398
+
399
+
400
+ class PlaceholderNode(LogicNode):
401
+ """A reserved arm: where a branch goes if a build gives it one.
402
+
403
+ A placeholder is the C layer's stand-in for a branch that has not been built
404
+ yet. A branch that is built replaces it, and one that never is becomes an
405
+ auto-generated no-action when the node around it is left.
406
+ """
407
+
408
+ def __init__(self, **kwargs: Any) -> None:
409
+ """Allocate a stand-in.
410
+
411
+ Raises:
412
+ MemoryError: When the block cannot be allocated.
413
+ """
414
+ ...
415
+
416
+
417
+ class LogicNodeRegistry(BoundByteMap):
418
+ """The wrapper every node is looked up by, keyed by its block's address.
419
+
420
+ A lookup that finds the address answers with the wrapper the layer holds for
421
+ it; one that misses hands the node to the reconstruction, which is what makes
422
+ a node reached from C come back as the class its type names.
423
+ """
424
+
425
+ def __getitem__(self, key: int) -> LogicNode:
426
+ """The wrapper at an address, rebuilding it when it is not held.
427
+
428
+ Args:
429
+ key: Address of the C node block.
430
+
431
+ Returns:
432
+ The wrapper for that address.
433
+ """
434
+ ...
435
+
436
+
437
+ # The registry every node is looked up by.
438
+ NODE_REGISTRY: LogicNodeRegistry