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,71 @@
1
+ from libc.stdint cimport uint64_t
2
+ from libcpp cimport bool as c_bool
3
+
4
+ from cbase.allocator_protocol cimport allocator_protocol
5
+
6
+ from .c_node cimport LogicNode, dcg_logic_group_manager, dcg_node
7
+ from .c_var cimport dcg_ret_code, dcg_var_t
8
+
9
+
10
+ cdef extern from "decision_graph/decision_tree/bake/c_logic_group.h":
11
+ ctypedef struct dcg_logic_group:
12
+ pass
13
+
14
+ int c_dcg_lgm_install_breakpoint(dcg_logic_group_manager* mgr, dcg_breakpoint_node* breakpoint) noexcept nogil
15
+
16
+
17
+ cdef extern from "decision_graph/decision_tree/bake/c_hierarchy.h":
18
+ ctypedef struct dcg_node_eval_path:
19
+ dcg_node** node
20
+ dcg_var_t* eval_val
21
+ size_t capacity
22
+ size_t n_nodes
23
+ dcg_ret_code code
24
+ dcg_node* leaf
25
+ dcg_node* failed
26
+ uint64_t seq_id
27
+
28
+ ctypedef struct dcg_root_node:
29
+ dcg_node base
30
+ dcg_node_eval_path eval_path
31
+ c_bool inherit_contexts
32
+
33
+ ctypedef struct dcg_breakpoint_node:
34
+ dcg_node base
35
+ dcg_logic_group* break_from
36
+ c_bool await_connection
37
+
38
+ dcg_root_node* c_dcg_node_new_root(const char* repr, allocator_protocol* allocator) noexcept nogil
39
+ dcg_breakpoint_node* c_dcg_node_new_breakpoint(dcg_logic_group* break_from, const char* repr, allocator_protocol* allocator) noexcept nogil
40
+ int c_dcg_node_ctx_enter_breakpoint(dcg_node* node, dcg_logic_group_manager* mgr) noexcept nogil
41
+ dcg_breakpoint_node* c_dcg_node_get_breakpoint(const dcg_node* node) noexcept nogil
42
+ void c_dcg_node_free_root(dcg_root_node* node) noexcept nogil
43
+ void c_dcg_node_free_breakpoint(dcg_breakpoint_node* node) noexcept nogil
44
+
45
+ void c_dcg_node_free_generic(dcg_node* node) noexcept nogil
46
+ int c_dcg_node_teardown_root(dcg_node* node) noexcept nogil
47
+ int c_dcg_node_remove(dcg_node* node) noexcept nogil
48
+ int c_dcg_node_clear_children(dcg_node* node) noexcept nogil
49
+ int c_dcg_node_clean(dcg_node* node) noexcept nogil
50
+
51
+
52
+ cdef extern from "decision_graph/decision_tree/bake/c_eval.h":
53
+ int c_dcg_root_node_eval(dcg_root_node* root) noexcept nogil
54
+
55
+
56
+ cdef class NodeEvalPathView:
57
+ cdef const dcg_node_eval_path* header
58
+
59
+ @staticmethod
60
+ cdef inline NodeEvalPathView c_from_header(const dcg_node_eval_path* path)
61
+
62
+
63
+ cdef class RootLogicNode(LogicNode):
64
+ cdef readonly str name
65
+ cdef readonly NodeEvalPathView eval_path
66
+
67
+ cpdef object bake(self, bint validate_only=?)
68
+
69
+
70
+ cdef class BreakpointNode(LogicNode):
71
+ cdef readonly object break_from
@@ -0,0 +1,409 @@
1
+ """The hierarchy layer: the graph's entry point, and the inspection sink.
2
+
3
+ Two nodes that are not part of the decision the graph computes. A root is where
4
+ a graph is entered: it takes one branch, and entering it opens a context of its
5
+ own - the scopes around it are shelved while a build is inside. A breakpoint is
6
+ where an inspection stops: it breaks out of a group, and it resumes into the
7
+ next node entered outside it.
8
+ """
9
+
10
+ from collections.abc import Iterator
11
+ from typing import Any
12
+
13
+ from .c_bake import BakeReport
14
+ from .c_logic_group import LogicGroup
15
+ from .c_node import LogicNode
16
+
17
+
18
+ class RootLogicNode(LogicNode):
19
+ """The graph's entry point: the only node that has no parent.
20
+
21
+ A root takes exactly ONE branch - its single arm is the entry edge - and
22
+ entering it shelves the contexts around it, so the graph a build enters is
23
+ built in a context of its own.
24
+
25
+ It is also where evaluating a graph starts: ``eval`` walks from here to a
26
+ single leaf and keeps the record of how it got there, so the root is what a
27
+ caller asks to make a decision.
28
+
29
+ Attributes:
30
+ name: The root's name, as the build gave it.
31
+ """
32
+
33
+ def __init__(self, *, name: str = 'Entry Point', inherit_contexts: bool = False, **kwargs: Any) -> None:
34
+ """Build a root.
35
+
36
+ Args:
37
+ name: The root's name.
38
+ inherit_contexts: Whether it takes the contexts of the groups around
39
+ it rather than shelving them.
40
+
41
+ Raises:
42
+ MemoryError: When the block cannot be allocated.
43
+ """
44
+ ...
45
+
46
+ def eval(self) -> Any:
47
+ """Evaluate the graph from this root, and record how it decided.
48
+
49
+ The whole walk, not one node - which is what a root does differently
50
+ from every other node, whose ``eval`` runs its own rule and nothing else.
51
+ Every node on the way is evaluated, the record of the last walk is
52
+ replaced, and the answer is where the walk came to rest: the leaf itself
53
+ when that leaf is an action, because the action IS the decision, and the
54
+ value in the leaf's slot otherwise.
55
+
56
+ The value a walk reads is the value AT THE MOMENT OF THE WALK: a read is
57
+ live, so the same graph decides differently when the store it reads
58
+ changes, and evaluating again is how a caller asks it to. What a walk
59
+ pins is the TYPE: a read is resolved to its entry's type at its first
60
+ evaluation, and a value of another type written to that entry afterwards
61
+ is not one it reports.
62
+
63
+ Returns:
64
+ The leaf's wrapper when the walk landed on an action, else the value
65
+ that leaf holds.
66
+
67
+ Raises:
68
+ EvalFailureError: When the walk could not reach a leaf - a read of an
69
+ entry with no value in it, a branch whose value selected no arm
70
+ and which has no fallback, or a node a hook refused. It names the
71
+ node that refused, with the code, the stage and what was running
72
+ (``node`` is the node the record reports as ``failed``), and a
73
+ hook's own exception travels as its cause. The record still holds
74
+ the nodes reached before it stopped.
75
+ """
76
+ ...
77
+
78
+ def __call__(self) -> Any:
79
+ """Evaluate the graph - the same walk ``eval`` runs, written as a call.
80
+
81
+ A root is what a caller asks to make a decision, and asking it is the
82
+ call: ``root()`` and ``root.eval()`` answer with the same value for the
83
+ same graph and leave the same record. The call form is what a graph reads
84
+ as at the place a decision is wanted, and it is the one door the two
85
+ layers spell the same way.
86
+
87
+ Returns:
88
+ The leaf's wrapper when the walk landed on an action, else the value
89
+ that leaf holds.
90
+
91
+ Raises:
92
+ EvalFailureError: When the walk could not reach a leaf - the same
93
+ failures, reported the same way, as ``eval``.
94
+ """
95
+ ...
96
+
97
+ def bake(self, validate_only: bool = False) -> BakeReport:
98
+ """Bake the graph: verify what an evaluation assumes, lock it, prepare it.
99
+
100
+ The last thing done to a graph before it is walked, and the pass every
101
+ evaluation is written assuming has run. What an evaluation assumes is
102
+ established here and nowhere else, because the hot path carries no check
103
+ for a malformed node:
104
+
105
+ - the graph's STRUCTURE - one parentless root, the arms in reading order,
106
+ an else last, an action below nothing, no cycles;
107
+ - every operand an operator takes has a component bound to it, and its
108
+ operand array is exactly as long as its arity - the rules run their
109
+ operands and read the workspace they filled with nothing checked in
110
+ between;
111
+ - every operator belongs to its node's arity, and no node in the graph is
112
+ one that has no evaluation at all (a call).
113
+
114
+ What the pass then does is hold the graph to what it verified: every node
115
+ it walked is frozen, so the structure is what it was, and every store the
116
+ graph reads is sealed, so no entry can appear in it afterwards. That
117
+ second half is the one an evaluation depends on - a resolved read holds a
118
+ reference into its store's block, and a block moves only when the store
119
+ grows. A store is sealed in SHAPE and not in contents: the entries it has
120
+ are the entries it will have, and a caller goes on writing values into
121
+ them, because a graph is baked once and fed many times.
122
+
123
+ Finally the record a walk fills has its room made in advance, sized for
124
+ what a walk from this root can need, so evaluating a baked graph
125
+ allocates nothing.
126
+
127
+ A bake is all or nothing: a graph that fails is not locked and not
128
+ prepared, so nothing about it changes. A bake that succeeds is
129
+ idempotent - asked again it locks nothing and seals nothing, which the
130
+ report says.
131
+
132
+ Args:
133
+ validate_only: Whether to verify the graph and report without
134
+ locking or preparing it - the question "would this bake?" asked
135
+ without the commitment. Nothing about the graph changes.
136
+
137
+ Returns:
138
+ The report: what the pass found, and what it affected.
139
+
140
+ Raises:
141
+ BakeFailureError: When the graph cannot be baked - the code, the node
142
+ that produced it and the problems found are in the report the
143
+ failure carries, and nothing was locked or prepared.
144
+ """
145
+ ...
146
+
147
+ def get_breakpoint(self) -> BreakpointNode | None:
148
+ """The breakpoint this graph left waiting, or None if it left none.
149
+
150
+ A breakpoint that has not resumed into anything yet IS a leaf - it is
151
+ where a branch stopped - so what this looks for is the first breakpoint
152
+ among the leaves: the one a build can enter to carry the graph on from
153
+ where it broke out.
154
+
155
+ That is what makes a graph assemblable from several places. One function
156
+ builds up to a break, this hands the break back, and the next function
157
+ takes it and builds the rest - so the two halves never have to be in the
158
+ same scope, or even the same function.
159
+
160
+ Returns:
161
+ The waiting breakpoint, or None when the graph has none.
162
+ """
163
+ ...
164
+
165
+ def to_html(self, file_name: str | None = None, with_eval: bool = True) -> None:
166
+ """Render this graph to a standalone HTML file.
167
+
168
+ The file carries the stylesheet, the script and D3 inside it, so it
169
+ opens anywhere with no server and no network.
170
+
171
+ Args:
172
+ file_name: Output HTML file name; when omitted, the file is named
173
+ after the root's ``repr``.
174
+ with_eval: Whether to include the last evaluation in the rendering.
175
+ """
176
+ ...
177
+
178
+ def show(self, **kwargs: Any) -> None:
179
+ """Serve this graph in the layer's own web viewer and open a browser.
180
+
181
+ Arguments:
182
+ **kwargs: keyword arguments passed into ``decision_graph.webui.bake.show`` method.
183
+ """
184
+ ...
185
+
186
+ def watch(self, **kwargs: Any) -> None:
187
+ """Stream this graph's changes to the layer's own web viewer.
188
+
189
+ The root is re-evaluated on a timer, and what changed between runs is
190
+ sent to the page as it happens.
191
+
192
+ Arguments:
193
+ **kwargs: keyword arguments passed into ``decision_graph.webui.bake.watch`` method.
194
+ """
195
+ ...
196
+
197
+ @property
198
+ def inherit_contexts(self) -> bool:
199
+ """Whether this root takes the groups around it rather than shelving them.
200
+
201
+ Recorded on the root when it is built, and read back off the block. Note
202
+ that this layer's root does not shelve yet - its entering reserves its arm
203
+ and nothing else - so the flag is carried and reported but has no effect
204
+ on a build. The capi's root does shelve, which is where the two differ.
205
+ """
206
+ ...
207
+
208
+ @property
209
+ def name(self) -> str | None:
210
+ """The root's name - None for a root rebuilt from C, which carries none."""
211
+ ...
212
+
213
+ @property
214
+ def eval_path(self) -> NodeEvalPathView:
215
+ """The record of how the last decision was made, as a view over it.
216
+
217
+ The root first and the leaf it came to rest on last, with every branch it
218
+ descended through in between - the very wrappers the build made, not
219
+ fresh views of them - plus the outcome: which code the walk ended with,
220
+ where it landed, what failed if anything did, and which run wrote it. It
221
+ is replaced by each walk, so what it holds is the last one; a walk that
222
+ could not finish leaves the nodes it got through and no leaf.
223
+
224
+ The view is taken once, when the root is built, and it reads the record
225
+ the root holds - so a root rebuilt from C carries one too, over the very
226
+ record the block has. It is a field rather than a computed value: asking
227
+ for it twice answers with the same view.
228
+ """
229
+ ...
230
+
231
+
232
+ class NodeEvalPathView:
233
+ """A read-only window onto ``dcg_node_eval_path``: the record of a walk.
234
+
235
+ A view is an ADDRESS, not a copy: it holds the record it was taken over, and
236
+ every read answers for what that record holds now. The record belongs to the
237
+ root whose walk wrote it - it is embedded in that root and its blocks are
238
+ nested under it - so the view must not outlive the root, which the layer
239
+ guarantees by handing one out only from the root it belongs to
240
+ (``RootLogicNode.eval_path``).
241
+
242
+ It cannot write: the view holds the record **const**, and every reader it
243
+ exposes reads through that pointer, so a write through a view does not
244
+ compile.
245
+
246
+ Attributes:
247
+ _header: The C record this view reads through.
248
+ """
249
+
250
+ def __init__(self, address: int) -> None:
251
+ """View the record at an address.
252
+
253
+ Args:
254
+ address: Address of a ``dcg_node_eval_path``. The layer hands these
255
+ out itself (``RootLogicNode.eval_path``); nothing here checks that
256
+ the address is a record, and the view keeps nothing alive.
257
+ """
258
+ ...
259
+
260
+ def __repr__(self) -> str:
261
+ """The view's own text: the count, the outcome and where the walk landed."""
262
+ ...
263
+
264
+ def __len__(self) -> int:
265
+ """How many nodes the walk reached."""
266
+ ...
267
+
268
+ def __iter__(self) -> Iterator[LogicNode]:
269
+ """Walk the entries: the nodes, in the order the walk reached them."""
270
+ ...
271
+
272
+ def __getitem__(self, index: int) -> LogicNode:
273
+ """The entry at an index, counting from the front or the back.
274
+
275
+ Args:
276
+ index: Which entry; negative counts from the end.
277
+
278
+ Returns:
279
+ The wrapper for the node the walk reached there.
280
+
281
+ Raises:
282
+ IndexError: When there is no such entry.
283
+ """
284
+ ...
285
+
286
+ @property
287
+ def address(self) -> int:
288
+ """The record's address - what this view reads through."""
289
+ ...
290
+
291
+ @property
292
+ def nodes(self) -> list[LogicNode]:
293
+ """Every entry, as a list: the nodes in the order the walk reached them."""
294
+ ...
295
+
296
+ @property
297
+ def capacity(self) -> int:
298
+ """The room the record has - how many entries it can hold before it grows."""
299
+ ...
300
+
301
+ @property
302
+ def code(self) -> int:
303
+ """The outcome, as the ``DCG_ERR_*`` code the walk ended with."""
304
+ ...
305
+
306
+ @property
307
+ def code_name(self) -> str:
308
+ """The outcome's stable name: ``'OK'``, ``'UNBOUND'``, ``'NO_MATCH'``, ..."""
309
+ ...
310
+
311
+ @property
312
+ def leaf(self) -> LogicNode | None:
313
+ """The node the walk came to rest on, or None when it never landed."""
314
+ ...
315
+
316
+ @property
317
+ def failed(self) -> LogicNode | None:
318
+ """The node that reported the failure, or None when nothing failed.
319
+
320
+ In a walk this is the node whose own evaluation refused - the one whose
321
+ stages say how far it got - which need not be the node a caller asked to
322
+ evaluate: a root that reached a branch which then failed did not fail.
323
+ """
324
+ ...
325
+
326
+ @property
327
+ def seq_id(self) -> int:
328
+ """The id of the run the record belongs to: which walk wrote it."""
329
+ ...
330
+
331
+
332
+ class BreakpointNode(LogicNode):
333
+ """An inspection sink: evaluation stops here and resumes outside the group.
334
+
335
+ It borrows the group it breaks out of, and this wrapper is what holds that
336
+ group alive while the breakpoint stands - which is why a reconstruction
337
+ restores the group with it.
338
+ """
339
+
340
+ def __init__(self, *, break_from: LogicGroup | None = None, repr: str | None = None, autogen: bool = True, **kwargs: Any) -> None:
341
+ """Build a breakpoint.
342
+
343
+ Args:
344
+ break_from: The group it breaks out of, if any.
345
+ repr: Display text to copy.
346
+ autogen: Whether the builder generated it rather than a caller.
347
+
348
+ Raises:
349
+ MemoryError: When the block cannot be allocated.
350
+ """
351
+ ...
352
+
353
+ @classmethod
354
+ def break_(cls, break_from: LogicGroup, **kwargs: Any) -> BreakpointNode:
355
+ """Break out of a group, and hand back the breakpoint that did it.
356
+
357
+ A break is a node: it takes the arm the build was about to fill, and the
358
+ build continues from IT rather than from the group it left. That is what
359
+ makes this the door for assembling a graph in pieces - one function
360
+ builds up to the break and returns, and the break it made is taken here
361
+ to carry on outside the group.
362
+
363
+ The breakpoint is built first and then installed, rather than made by the
364
+ install: the block that lands in the graph has to be the one this wrapper
365
+ holds, or the caller is handed a handle on a node that is not in the
366
+ graph.
367
+
368
+ Args:
369
+ break_from: The group being broken out of.
370
+ **kwargs: Passed to the constructor.
371
+
372
+ Returns:
373
+ The breakpoint, now in the graph and waiting to resume into the next
374
+ node built.
375
+
376
+ Raises:
377
+ RuntimeError: When the C layer cannot place it.
378
+ MemoryError: When the block cannot be allocated.
379
+ """
380
+ ...
381
+
382
+ def connect(self, child: LogicNode) -> None:
383
+ """Connect a node as what this breakpoint resumes into.
384
+
385
+ A breakpoint resumes into exactly one node - which is what its arm is
386
+ for - so a second connection is refused rather than replacing the first.
387
+
388
+ Args:
389
+ child: Node to connect.
390
+
391
+ Raises:
392
+ RuntimeError: When the C layer refuses the link.
393
+ """
394
+ ...
395
+
396
+ @property
397
+ def linked_to(self) -> LogicNode | None:
398
+ """The node this breakpoint resumes into, or None while it waits."""
399
+ ...
400
+
401
+ @property
402
+ def break_from(self) -> LogicGroup | None:
403
+ """The group this breakpoint breaks out of, or None when it names none."""
404
+ ...
405
+
406
+ @property
407
+ def await_connection(self) -> bool:
408
+ """Whether the breakpoint is waiting for the node it resumes into."""
409
+ ...