rocket-engine 0.0.2b1__py3-none-any.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.
- rocket_engine/__init__.py +8 -0
- rocket_engine/__main__.py +12 -0
- rocket_engine/adapters/README.md +54 -0
- rocket_engine/adapters/__init__.py +7 -0
- rocket_engine/adapters/api.py +39 -0
- rocket_engine/adapters/base.py +46 -0
- rocket_engine/adapters/cace/__init__.py +163 -0
- rocket_engine/adapters/circulax/README.md +10 -0
- rocket_engine/adapters/circulax/__init__.py +112 -0
- rocket_engine/adapters/circulax/_driver.py +190 -0
- rocket_engine/adapters/circulax/adapter.py +538 -0
- rocket_engine/adapters/circulax/results.py +291 -0
- rocket_engine/adapters/cli/README.md +34 -0
- rocket_engine/adapters/cli/__init__.py +305 -0
- rocket_engine/adapters/connectivity/__init__.py +196 -0
- rocket_engine/adapters/connectivity/results.py +214 -0
- rocket_engine/adapters/cosim/README.md +9 -0
- rocket_engine/adapters/cosim/__init__.py +1390 -0
- rocket_engine/adapters/cosim/results.py +214 -0
- rocket_engine/adapters/devsim/__init__.py +171 -0
- rocket_engine/adapters/digital_flow/README.md +9 -0
- rocket_engine/adapters/digital_flow/__init__.py +1121 -0
- rocket_engine/adapters/digital_flow/results.py +227 -0
- rocket_engine/adapters/digital_flow/state.py +462 -0
- rocket_engine/adapters/drc/README.klayout.md +7 -0
- rocket_engine/adapters/drc/README.md +7 -0
- rocket_engine/adapters/drc/__init__.py +1 -0
- rocket_engine/adapters/drc/klayout.py +952 -0
- rocket_engine/adapters/drc/magic.py +287 -0
- rocket_engine/adapters/drc/results.py +184 -0
- rocket_engine/adapters/drc/results_kl.py +188 -0
- rocket_engine/adapters/elmer/__init__.py +163 -0
- rocket_engine/adapters/envelope.py +120 -0
- rocket_engine/adapters/equivalence/__init__.py +156 -0
- rocket_engine/adapters/errors.py +181 -0
- rocket_engine/adapters/extract_magic/README.md +15 -0
- rocket_engine/adapters/extract_magic/__init__.py +425 -0
- rocket_engine/adapters/extract_magic/results.py +209 -0
- rocket_engine/adapters/formal_sby/README.md +7 -0
- rocket_engine/adapters/formal_sby/__init__.py +773 -0
- rocket_engine/adapters/formal_sby/formal_edge.py +195 -0
- rocket_engine/adapters/formal_sby/results.py +185 -0
- rocket_engine/adapters/gdsfactory/__init__.py +194 -0
- rocket_engine/adapters/gdsfactory/_driver.py +459 -0
- rocket_engine/adapters/gdsfactory/adapter.py +609 -0
- rocket_engine/adapters/gdsfactory/results.py +181 -0
- rocket_engine/adapters/hdl_sim/README.md +7 -0
- rocket_engine/adapters/hdl_sim/__init__.py +369 -0
- rocket_engine/adapters/hdl_sim/ghdl.py +155 -0
- rocket_engine/adapters/hdl_sim/results.py +178 -0
- rocket_engine/adapters/hdl_sim/verilator.py +167 -0
- rocket_engine/adapters/http/README.md +15 -0
- rocket_engine/adapters/http/__init__.py +458 -0
- rocket_engine/adapters/http/events.py +63 -0
- rocket_engine/adapters/ipc/README.md +9 -0
- rocket_engine/adapters/ipc/__init__.py +111 -0
- rocket_engine/adapters/lvs_netgen/README.md +20 -0
- rocket_engine/adapters/lvs_netgen/__init__.py +889 -0
- rocket_engine/adapters/lvs_netgen/results.py +168 -0
- rocket_engine/adapters/mcp/README.md +19 -0
- rocket_engine/adapters/mcp/__init__.py +85 -0
- rocket_engine/adapters/mcp/api-schema.json +6107 -0
- rocket_engine/adapters/mcp/pull.py +246 -0
- rocket_engine/adapters/mcp/server.py +794 -0
- rocket_engine/adapters/meep/__init__.py +167 -0
- rocket_engine/adapters/meow/README.md +7 -0
- rocket_engine/adapters/meow/__init__.py +190 -0
- rocket_engine/adapters/meow/_driver.py +227 -0
- rocket_engine/adapters/meow/_envelope.py +108 -0
- rocket_engine/adapters/meow/adapter.py +491 -0
- rocket_engine/adapters/meow/results.py +218 -0
- rocket_engine/adapters/mutation/__init__.py +192 -0
- rocket_engine/adapters/ngspice/README.md +28 -0
- rocket_engine/adapters/ngspice/__init__.py +627 -0
- rocket_engine/adapters/ngspice/results.py +215 -0
- rocket_engine/adapters/postlayout/README.md +28 -0
- rocket_engine/adapters/postlayout/__init__.py +17 -0
- rocket_engine/adapters/postlayout/adapter.py +1062 -0
- rocket_engine/adapters/postlayout/results.py +330 -0
- rocket_engine/adapters/postlayout/translate.py +71 -0
- rocket_engine/adapters/profile.py +220 -0
- rocket_engine/adapters/sax/README.md +23 -0
- rocket_engine/adapters/sax/__init__.py +516 -0
- rocket_engine/adapters/sax/_driver.py +148 -0
- rocket_engine/adapters/sax/results.py +291 -0
- rocket_engine/adapters/stdio_console.py +1151 -0
- rocket_engine/adapters/syntax/README.md +5 -0
- rocket_engine/adapters/syntax/__init__.py +366 -0
- rocket_engine/adapters/syntax/results.py +181 -0
- rocket_engine/adapters/synth_yosys/README.md +7 -0
- rocket_engine/adapters/synth_yosys/__init__.py +655 -0
- rocket_engine/adapters/synth_yosys/amaranth.py +120 -0
- rocket_engine/adapters/synth_yosys/ghdl.py +128 -0
- rocket_engine/adapters/synth_yosys/results.py +173 -0
- rocket_engine/adapters/timing_sta/README.md +9 -0
- rocket_engine/adapters/timing_sta/__init__.py +726 -0
- rocket_engine/adapters/view_sync/__init__.py +185 -0
- rocket_engine/adapters/view_sync/results.py +189 -0
- rocket_engine/adapters/vlsir/__init__.py +142 -0
- rocket_engine/adapters/xschem/__init__.py +440 -0
- rocket_engine/adapters/xschem/deck.py +266 -0
- rocket_engine/clients/__init__.py +5 -0
- rocket_engine/clients/runtime.py +255 -0
- rocket_engine/composition.py +2279 -0
- rocket_engine/execution/README.md +30 -0
- rocket_engine/execution/__init__.py +150 -0
- rocket_engine/execution/apig/__init__.py +1 -0
- rocket_engine/execution/apig/pool.py +463 -0
- rocket_engine/execution/apig/submit.py +124 -0
- rocket_engine/execution/block_runs_scope.py +479 -0
- rocket_engine/execution/coalesce.py +414 -0
- rocket_engine/execution/console/__init__.py +1 -0
- rocket_engine/execution/console/apply_session.py +318 -0
- rocket_engine/execution/console/lifecycle.py +265 -0
- rocket_engine/execution/dispatch.py +205 -0
- rocket_engine/execution/errors.py +31 -0
- rocket_engine/execution/ports.py +797 -0
- rocket_engine/execution/pyexec/__init__.py +1 -0
- rocket_engine/execution/pyexec/apply_context.py +547 -0
- rocket_engine/execution/pyexec/approvals.py +173 -0
- rocket_engine/execution/pyexec/runner.py +404 -0
- rocket_engine/execution/pyexec/support.py +464 -0
- rocket_engine/execution/python_exec.py +897 -0
- rocket_engine/execution/queue.py +85 -0
- rocket_engine/execution/replay_safe.py +601 -0
- rocket_engine/execution/routing/__init__.py +1 -0
- rocket_engine/execution/routing/calls.py +364 -0
- rocket_engine/execution/routing/namespace.py +785 -0
- rocket_engine/execution/routing/ports.py +102 -0
- rocket_engine/execution/routing/tools.py +346 -0
- rocket_engine/execution/runner.py +311 -0
- rocket_engine/execution/runtime.py +1149 -0
- rocket_engine/execution/service.py +279 -0
- rocket_engine/execution/worker.py +464 -0
- rocket_engine/interfaces/README.md +26 -0
- rocket_engine/interfaces/__init__.py +58 -0
- rocket_engine/interfaces/apig_assets.py +407 -0
- rocket_engine/interfaces/celllink.py +461 -0
- rocket_engine/interfaces/documents.py +92 -0
- rocket_engine/interfaces/envelopes.py +131 -0
- rocket_engine/interfaces/errors.py +16 -0
- rocket_engine/interfaces/layout.py +410 -0
- rocket_engine/interfaces/layout_schema.yaml +278 -0
- rocket_engine/interfaces/paths.py +223 -0
- rocket_engine/interfaces/pdks.py +550 -0
- rocket_engine/interfaces/ports.py +72 -0
- rocket_engine/interfaces/references.py +115 -0
- rocket_engine/interfaces/refs.py +85 -0
- rocket_engine/interfaces/runparams.py +304 -0
- rocket_engine/interfaces/setup.py +552 -0
- rocket_engine/interfaces/setup_catalogue.yaml +243 -0
- rocket_engine/interfaces/setup_probes.py +66 -0
- rocket_engine/interfaces/vocabulary.py +58 -0
- rocket_engine/interfaces/yaml_dump.py +31 -0
- rocket_engine/library/README.md +22 -0
- rocket_engine/library/__init__.py +13 -0
- rocket_engine/library/apig_catalog.py +371 -0
- rocket_engine/library/catalog.py +185 -0
- rocket_engine/library/errors.py +16 -0
- rocket_engine/mutate/__init__.py +776 -0
- rocket_engine/mutate/operators.py +700 -0
- rocket_engine/ops/__init__.py +1 -0
- rocket_engine/ops/deferred.py +279 -0
- rocket_engine/ops/editors.py +404 -0
- rocket_engine/ops/evidence.py +194 -0
- rocket_engine/ops/gates.py +262 -0
- rocket_engine/ops/ports.py +76 -0
- rocket_engine/ops/queries.py +417 -0
- rocket_engine/ops/runs.py +553 -0
- rocket_engine/ops/submit.py +614 -0
- rocket_engine/project/README.md +27 -0
- rocket_engine/project/__init__.py +6 -0
- rocket_engine/project/canvas/__init__.py +55 -0
- rocket_engine/project/canvas/compile.py +109 -0
- rocket_engine/project/canvas/integrate.py +48 -0
- rocket_engine/project/canvas/schema.py +241 -0
- rocket_engine/project/canvas/templates.py +46 -0
- rocket_engine/project/canvas/validate.py +603 -0
- rocket_engine/project/edit/__init__.py +5 -0
- rocket_engine/project/edit/cas.py +87 -0
- rocket_engine/project/edit/transactions.py +282 -0
- rocket_engine/project/errors.py +16 -0
- rocket_engine/project/frontend13.py +685 -0
- rocket_engine/project/geometry/__init__.py +23 -0
- rocket_engine/project/geometry/ops.py +451 -0
- rocket_engine/project/geometry/snap.py +103 -0
- rocket_engine/project/ip/__init__.py +1 -0
- rocket_engine/project/ip/blocks.py +195 -0
- rocket_engine/project/ip/entry.schema.json +78 -0
- rocket_engine/project/ip/lifecycle.py +402 -0
- rocket_engine/project/ip/verbs.py +123 -0
- rocket_engine/project/layers/__init__.py +6 -0
- rocket_engine/project/layers/bindings.py +115 -0
- rocket_engine/project/layers/catalog.py +228 -0
- rocket_engine/project/layers/presets.py +43 -0
- rocket_engine/project/ledger/__init__.py +1 -0
- rocket_engine/project/ledger/docs.py +167 -0
- rocket_engine/project/ledger/entities.py +176 -0
- rocket_engine/project/ledger/events.py +29 -0
- rocket_engine/project/ledger/runs.py +257 -0
- rocket_engine/project/ledger/shared.py +174 -0
- rocket_engine/project/ledger/snapshots.py +385 -0
- rocket_engine/project/ledger/wires.py +552 -0
- rocket_engine/project/process/__init__.py +14 -0
- rocket_engine/project/process/model.py +193 -0
- rocket_engine/project/process/recipes.py +121 -0
- rocket_engine/project/schematic/__init__.py +1 -0
- rocket_engine/project/schematic/hierarchy.py +312 -0
- rocket_engine/project/schematic/instances.py +393 -0
- rocket_engine/project/schematic/sheets.py +198 -0
- rocket_engine/project/service.py +2941 -0
- rocket_engine/project/symbols/__init__.py +1 -0
- rocket_engine/project/symbols/cells.py +324 -0
- rocket_engine/project/symbols/linkage.py +356 -0
- rocket_engine/project/symbols/symbols.py +352 -0
- rocket_engine/project/templates/__init__.py +303 -0
- rocket_engine/project/templates/errors.py +25 -0
- rocket_engine/project/testbench/__init__.py +49 -0
- rocket_engine/project/testbench/blocks.py +619 -0
- rocket_engine/project/testbench/templates.py +134 -0
- rocket_engine/stores/README.md +12 -0
- rocket_engine/stores/__init__.py +6 -0
- rocket_engine/stores/ecs.py +1508 -0
- rocket_engine/stores/layers.py +1023 -0
- rocket_engine/stores/settings.py +1188 -0
- rocket_engine/stores/wireplace.py +1324 -0
- rocket_engine/workflows/README.md +18 -0
- rocket_engine/workflows/__init__.py +53 -0
- rocket_engine/workflows/capability_plan.py +102 -0
- rocket_engine/workflows/completion.py +228 -0
- rocket_engine/workflows/connectgraph.py +734 -0
- rocket_engine/workflows/connectgraph_emit.py +336 -0
- rocket_engine/workflows/gates.py +144 -0
- rocket_engine/workflows/photonic_wire.py +76 -0
- rocket_engine/workflows/stages.py +324 -0
- rocket_engine/workflows/viewsync.py +399 -0
- rocket_engine-0.0.2b1.dist-info/METADATA +200 -0
- rocket_engine-0.0.2b1.dist-info/RECORD +241 -0
- rocket_engine-0.0.2b1.dist-info/WHEEL +4 -0
- rocket_engine-0.0.2b1.dist-info/entry_points.txt +2 -0
- rocket_engine-0.0.2b1.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
"""Wave-3 deferred runtime seam (EngineRuntime integration).
|
|
2
|
+
|
|
3
|
+
Engine owns one EngineRuntime bound to the solver lane: deferred
|
|
4
|
+
submits acknowledge promptly with a run id and no verdict, the
|
|
5
|
+
pump executes through the ledger-admitting runner, and the
|
|
6
|
+
terminal completion merges back with merge_ack_completion. The
|
|
7
|
+
synchronous submit path is untouched — deferred runs correlate
|
|
8
|
+
with ledger admissions through the shared request id.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from ..execution.apig.submit import merge_ack_completion
|
|
12
|
+
from ..interfaces.yaml_dump import dump_yaml_text
|
|
13
|
+
from ..project.errors import ProjectError
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class _DeferredMixin:
|
|
17
|
+
"""Deferred prompt-ack runtime surface (Wave-3 EngineRuntime)."""
|
|
18
|
+
|
|
19
|
+
def _deferred_runner(
|
|
20
|
+
self, operation, arguments, snapshot_id, request_id,
|
|
21
|
+
run_id):
|
|
22
|
+
"""Execute one deferred run through ledger admission.
|
|
23
|
+
|
|
24
|
+
Admission gates exactly like a synchronous submit
|
|
25
|
+
(targets, request knobs, confinement, budget, verdict
|
|
26
|
+
gates, block scope); the ledger ack run id executes while
|
|
27
|
+
the runtime tracks its own run id. Replay hits return the
|
|
28
|
+
stored completion without re-executing.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
operation: Cataloged solver operation name.
|
|
32
|
+
arguments: Operation arguments mapping.
|
|
33
|
+
snapshot_id: Evidence snapshot the run pins.
|
|
34
|
+
request_id: Client envelope id.
|
|
35
|
+
run_id: Runtime run id tracking this run.
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
Terminal completion for the run.
|
|
39
|
+
"""
|
|
40
|
+
from .gates import check_submit_gates
|
|
41
|
+
|
|
42
|
+
args = dict(arguments or {})
|
|
43
|
+
args = self._apply_targets(operation, args)
|
|
44
|
+
args = self._apply_run_request(args)
|
|
45
|
+
if operation == "run_spice":
|
|
46
|
+
args = self.confine_spice_arguments(
|
|
47
|
+
args, self._project_dir)
|
|
48
|
+
from .runs import refuse_smuggled_budget
|
|
49
|
+
|
|
50
|
+
refuse_smuggled_budget(args)
|
|
51
|
+
check_submit_gates(operation, args, self._project)
|
|
52
|
+
scope_request = self._admit_block_scope(operation, args)
|
|
53
|
+
command_text = dump_yaml_text(
|
|
54
|
+
{"operation": operation, "arguments": args})
|
|
55
|
+
ack = self._pool.submit_request(
|
|
56
|
+
command_text, str(request_id),
|
|
57
|
+
expected_revision=args.get("expected_revision"),
|
|
58
|
+
)
|
|
59
|
+
self._deferred.apply_edit()
|
|
60
|
+
ledger_id = ack["run_id"]
|
|
61
|
+
stored = self._stored_completion(ledger_id)
|
|
62
|
+
if stored is not None:
|
|
63
|
+
self._pool.discard(ledger_id)
|
|
64
|
+
return {
|
|
65
|
+
"run_id": ledger_id, "state": stored["state"],
|
|
66
|
+
"verdict": stored.get("verdict"),
|
|
67
|
+
"operation": stored.get("operation"),
|
|
68
|
+
}
|
|
69
|
+
self._run_jobs.setdefault(
|
|
70
|
+
ledger_id,
|
|
71
|
+
{
|
|
72
|
+
"operation": operation,
|
|
73
|
+
"arguments": args,
|
|
74
|
+
"snapshot_id": ack["snapshot_id"],
|
|
75
|
+
"request_id": str(request_id),
|
|
76
|
+
},
|
|
77
|
+
)
|
|
78
|
+
if scope_request is not None:
|
|
79
|
+
from ..execution.block_runs_scope import (
|
|
80
|
+
build_scope_record,
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
pending = self._run_jobs[ledger_id]
|
|
84
|
+
if "block_scope" not in pending:
|
|
85
|
+
pending["block_scope"] = build_scope_record(
|
|
86
|
+
run_id=ledger_id,
|
|
87
|
+
revision=self._project.snapshot()[
|
|
88
|
+
"current_revision"],
|
|
89
|
+
block_id=scope_request["block_id"],
|
|
90
|
+
deck=args.get("deck"),
|
|
91
|
+
closure=scope_request["closure"],
|
|
92
|
+
)
|
|
93
|
+
if not self._execution.known(ledger_id):
|
|
94
|
+
self._execution.dispatch(
|
|
95
|
+
operation, {"cell": args.get("cell")},
|
|
96
|
+
ack["snapshot_id"], run_id=ledger_id,
|
|
97
|
+
)
|
|
98
|
+
try:
|
|
99
|
+
completion = self._pool_execute(
|
|
100
|
+
operation, args, ack["snapshot_id"],
|
|
101
|
+
str(request_id), ledger_id)
|
|
102
|
+
finally:
|
|
103
|
+
self._pool.discard(ledger_id)
|
|
104
|
+
self._maybe_record_evidence(operation, completion, args)
|
|
105
|
+
return completion
|
|
106
|
+
|
|
107
|
+
def deferred_submit(self, operation, arguments, request_id=None):
|
|
108
|
+
"""Admit one deferred run with a prompt acknowledgement.
|
|
109
|
+
|
|
110
|
+
The receipt carries the run id with no verdict; pump (or
|
|
111
|
+
auto-dispatch) executes, then merge the terminal
|
|
112
|
+
completion back with merge_ack_completion.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
operation: Cataloged solver operation name.
|
|
116
|
+
arguments: Operation arguments mapping.
|
|
117
|
+
request_id: Client envelope id; minted when omitted.
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
DeferredHandle watching the admitted run.
|
|
121
|
+
|
|
122
|
+
Raises:
|
|
123
|
+
ProjectError: unsupported_operation for an unknown
|
|
124
|
+
name; the runtime refusal code otherwise.
|
|
125
|
+
"""
|
|
126
|
+
from ..execution.errors import ExecutionError
|
|
127
|
+
from ..interfaces.vocabulary import OPERATIONS
|
|
128
|
+
|
|
129
|
+
if operation not in OPERATIONS:
|
|
130
|
+
raise ProjectError(
|
|
131
|
+
"unsupported_operation",
|
|
132
|
+
f"Unsupported operation: {operation}.",
|
|
133
|
+
)
|
|
134
|
+
import uuid as _uuid
|
|
135
|
+
|
|
136
|
+
try:
|
|
137
|
+
handle = self._deferred.submit(
|
|
138
|
+
operation, dict(arguments or {}),
|
|
139
|
+
request_id=str(request_id or _uuid.uuid4().hex),
|
|
140
|
+
)
|
|
141
|
+
except ExecutionError as exc:
|
|
142
|
+
raise ProjectError(exc.code, str(exc)) from exc
|
|
143
|
+
self._deferred_receipts[handle.run_id] = dict(
|
|
144
|
+
handle.receipt)
|
|
145
|
+
return handle
|
|
146
|
+
|
|
147
|
+
def deferred_pump(self, limit=None):
|
|
148
|
+
"""Dispatch deferred runs oldest first through the runner.
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
limit: Maximum entries to drain; the whole backlog
|
|
152
|
+
when omitted.
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
Completions in dispatch order.
|
|
156
|
+
"""
|
|
157
|
+
return self._deferred.pump(limit=limit)
|
|
158
|
+
|
|
159
|
+
def deferred_status(self, run_id):
|
|
160
|
+
"""Read one deferred run's state, verdict, and operation.
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
run_id: Deferred run id to inspect.
|
|
164
|
+
|
|
165
|
+
Returns:
|
|
166
|
+
Status with run_id, operation, state, and verdict
|
|
167
|
+
once judged.
|
|
168
|
+
"""
|
|
169
|
+
from ..execution.errors import ExecutionError
|
|
170
|
+
|
|
171
|
+
try:
|
|
172
|
+
return self._deferred.status(run_id)
|
|
173
|
+
except ExecutionError as exc:
|
|
174
|
+
raise ProjectError(exc.code, str(exc)) from exc
|
|
175
|
+
|
|
176
|
+
def deferred_wait(self, run_id, timeout=None):
|
|
177
|
+
"""Block until one deferred run reaches a terminal state.
|
|
178
|
+
|
|
179
|
+
Args:
|
|
180
|
+
run_id: Deferred run id to wait for.
|
|
181
|
+
timeout: Maximum seconds to wait; waits with the run
|
|
182
|
+
when omitted.
|
|
183
|
+
|
|
184
|
+
Returns:
|
|
185
|
+
Terminal completion for the run.
|
|
186
|
+
"""
|
|
187
|
+
from ..execution.errors import ExecutionError
|
|
188
|
+
|
|
189
|
+
try:
|
|
190
|
+
return self._deferred.wait_for(
|
|
191
|
+
run_id, timeout=timeout)
|
|
192
|
+
except ExecutionError as exc:
|
|
193
|
+
raise ProjectError(exc.code, str(exc)) from exc
|
|
194
|
+
|
|
195
|
+
def runtime_events(self, cursor=0):
|
|
196
|
+
"""Read ordered deferred lifecycle events after a cursor.
|
|
197
|
+
|
|
198
|
+
Args:
|
|
199
|
+
cursor: Opaque position from a previous read; 0
|
|
200
|
+
replays the retained log.
|
|
201
|
+
|
|
202
|
+
Returns:
|
|
203
|
+
Events appended after the cursor, oldest first,
|
|
204
|
+
plus the current cursor.
|
|
205
|
+
"""
|
|
206
|
+
from ..execution.errors import ExecutionError
|
|
207
|
+
|
|
208
|
+
try:
|
|
209
|
+
return self._deferred.watch(cursor)
|
|
210
|
+
except ExecutionError as exc:
|
|
211
|
+
raise ProjectError(exc.code, str(exc)) from exc
|
|
212
|
+
|
|
213
|
+
def runtime_cancel(self, run_id, actor=None):
|
|
214
|
+
"""Cancel one queued or running deferred run.
|
|
215
|
+
|
|
216
|
+
Args:
|
|
217
|
+
run_id: Deferred run id to cancel.
|
|
218
|
+
actor: Attribution for the lifecycle event.
|
|
219
|
+
|
|
220
|
+
Returns:
|
|
221
|
+
Outcome with run_id, state, actor, and verdict
|
|
222
|
+
when judged.
|
|
223
|
+
"""
|
|
224
|
+
from ..execution.errors import ExecutionError
|
|
225
|
+
|
|
226
|
+
try:
|
|
227
|
+
return self._deferred.cancel(
|
|
228
|
+
run_id,
|
|
229
|
+
actor=actor if actor is not None else self._actor,
|
|
230
|
+
)
|
|
231
|
+
except ExecutionError as exc:
|
|
232
|
+
raise ProjectError(exc.code, str(exc)) from exc
|
|
233
|
+
|
|
234
|
+
def replay_pending(self):
|
|
235
|
+
"""Resume crash-surviving RETRYABLE admissions, oldest first.
|
|
236
|
+
|
|
237
|
+
Explicit-call compat over the gated batch: same survivor
|
|
238
|
+
set as the boot report, but only RETRYABLE runs execute —
|
|
239
|
+
ambiguous and poison survivors are skipped loudly in the
|
|
240
|
+
report instead of re-running.
|
|
241
|
+
|
|
242
|
+
Returns:
|
|
243
|
+
Report with replayed run ids (the resumed set, seq
|
|
244
|
+
order), their completions, and skipped entries.
|
|
245
|
+
"""
|
|
246
|
+
batch = self._replay.resume_all_retryable()
|
|
247
|
+
return {
|
|
248
|
+
"replayed": batch["resumed"],
|
|
249
|
+
"completions": batch["completions"],
|
|
250
|
+
"skipped": batch["skipped"],
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
def deferred_fetch(self, run_id):
|
|
254
|
+
"""Fetch one deferred run's merged ack-plus-completion.
|
|
255
|
+
|
|
256
|
+
Args:
|
|
257
|
+
run_id: Deferred run id to fetch.
|
|
258
|
+
|
|
259
|
+
Returns:
|
|
260
|
+
Merged receipt-plus-completion; verdict present once
|
|
261
|
+
the run finished.
|
|
262
|
+
|
|
263
|
+
Raises:
|
|
264
|
+
ProjectError: unknown_run for an untracked id.
|
|
265
|
+
"""
|
|
266
|
+
from ..execution.errors import ExecutionError
|
|
267
|
+
|
|
268
|
+
try:
|
|
269
|
+
receipt = self._deferred_receipts[run_id]
|
|
270
|
+
except (KeyError, AttributeError):
|
|
271
|
+
raise ProjectError(
|
|
272
|
+
"unknown_run", f"Unknown run: {run_id}.") from None
|
|
273
|
+
try:
|
|
274
|
+
completion = self._deferred.wait_for(run_id, timeout=0)
|
|
275
|
+
except TimeoutError:
|
|
276
|
+
return dict(receipt)
|
|
277
|
+
except ExecutionError as exc:
|
|
278
|
+
raise ProjectError(exc.code, str(exc)) from exc
|
|
279
|
+
return merge_ack_completion(receipt, completion)
|
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
"""Wave-3 editor seam: shared admission helpers plus Engine dispatch.
|
|
2
|
+
|
|
3
|
+
Service-side endpoint methods stay thin: every Wave-3 mutation
|
|
4
|
+
rides the wire envelope through editor_mutation, every computed
|
|
5
|
+
read serves through editor_read. Receipts carry I28 closure pins
|
|
6
|
+
via pin_receipt. The Engine-side _EditorMixin (query dispatch)
|
|
7
|
+
lives here too, so both sides of the seam share one module.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from ..interfaces.errors import ContractError
|
|
11
|
+
from ..interfaces.refs import pin_closure
|
|
12
|
+
from ..project.errors import ProjectError
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def editor_value_error(func):
|
|
16
|
+
"""Run editor logic, mapping value errors to ProjectError.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
func: Zero-argument callable running the domain op.
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
Whatever the domain op returns.
|
|
23
|
+
|
|
24
|
+
Raises:
|
|
25
|
+
ProjectError: invalid_command (or the carried code).
|
|
26
|
+
"""
|
|
27
|
+
try:
|
|
28
|
+
return func()
|
|
29
|
+
except ValueError as exc:
|
|
30
|
+
raise ProjectError(
|
|
31
|
+
getattr(exc, "code", "invalid_command"), str(exc)
|
|
32
|
+
) from exc
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def pin_receipt(service, receipt):
|
|
36
|
+
"""Attach the I28 closure pin to one editor receipt.
|
|
37
|
+
|
|
38
|
+
Entity ids carried by the receipt pin their live revisions;
|
|
39
|
+
unresolvable names are skipped, never guessed. Prior partial
|
|
40
|
+
pins (template, profile) are preserved and folded in.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
service: ProjectService handle to read through.
|
|
44
|
+
receipt: Editor receipt mapping (mutated in place).
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
The same receipt with an i28 closure pin.
|
|
48
|
+
"""
|
|
49
|
+
from ..project.edit import cas as _cas
|
|
50
|
+
|
|
51
|
+
revisions = {}
|
|
52
|
+
prior = {}
|
|
53
|
+
if isinstance(receipt, dict):
|
|
54
|
+
prior = receipt.get("i28") or {}
|
|
55
|
+
for key in (
|
|
56
|
+
"entity", "sheet", "instance", "binding",
|
|
57
|
+
"symbol", "cell", "ip", "testbench", "shape",
|
|
58
|
+
):
|
|
59
|
+
candidate = receipt.get(key)
|
|
60
|
+
if not isinstance(candidate, str):
|
|
61
|
+
continue
|
|
62
|
+
try:
|
|
63
|
+
revisions[candidate] = _cas.read_entity_revision(
|
|
64
|
+
service, candidate)
|
|
65
|
+
except Exception: # noqa: BLE001 — names skip
|
|
66
|
+
continue
|
|
67
|
+
receipt["i28"] = pin_closure(
|
|
68
|
+
service.snapshot()["current_revision"], entities=revisions,
|
|
69
|
+
template=prior.get("template"),
|
|
70
|
+
profile=prior.get("profile"))
|
|
71
|
+
return receipt
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def editor_mutation(service, operation, request_id, command_text,
|
|
75
|
+
expected_revision, run, describe):
|
|
76
|
+
"""Admit one Wave-3 editor op through the wire envelope.
|
|
77
|
+
|
|
78
|
+
Inner envelopes derive from the outer request id, so a
|
|
79
|
+
retried envelope replays the verbs that already landed
|
|
80
|
+
instead of minting duplicates.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
service: ProjectService handle to write through.
|
|
84
|
+
operation: Editor operation name for the event data.
|
|
85
|
+
request_id: Client envelope id for idempotent replay.
|
|
86
|
+
command_text: YAML command text for identity comparison.
|
|
87
|
+
expected_revision: Revision the writer read.
|
|
88
|
+
run: Callable (store, inner_id, inner_text, live)
|
|
89
|
+
returning the domain receipt.
|
|
90
|
+
describe: Callable (receipt) -> diff summary text.
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
Mutation mapping with the pinned receipt, revision
|
|
94
|
+
transition, actor, events, cursor, and request id.
|
|
95
|
+
|
|
96
|
+
Raises:
|
|
97
|
+
ProjectError: request_conflict, revision_conflict,
|
|
98
|
+
or the domain refusal code.
|
|
99
|
+
"""
|
|
100
|
+
def _run(store):
|
|
101
|
+
return pin_receipt(service, editor_value_error(
|
|
102
|
+
lambda: run(
|
|
103
|
+
store, f"{request_id}:{operation}",
|
|
104
|
+
f"{command_text}\n#inner {operation}",
|
|
105
|
+
service.snapshot()["current_revision"])))
|
|
106
|
+
return service._wire_mutation(
|
|
107
|
+
operation, request_id, command_text,
|
|
108
|
+
expected_revision, _run, describe)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def editor_read(service, operation, func):
|
|
112
|
+
"""Serve one Wave-3 computed read with revision and I28.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
service: ProjectService handle to read through.
|
|
116
|
+
operation: Read name for the entity-store seam tag.
|
|
117
|
+
func: Callable (store) returning the result mapping.
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
Result mapping plus head revision and I28 pin.
|
|
121
|
+
|
|
122
|
+
Raises:
|
|
123
|
+
ProjectError: The domain refusal code.
|
|
124
|
+
"""
|
|
125
|
+
store = service._entity_store(f"read:{operation}")
|
|
126
|
+
try:
|
|
127
|
+
result = func(store)
|
|
128
|
+
except ContractError as exc:
|
|
129
|
+
raise ProjectError(exc.code, str(exc)) from exc
|
|
130
|
+
return {
|
|
131
|
+
**result,
|
|
132
|
+
"current_revision": service.snapshot()["current_revision"],
|
|
133
|
+
"i28": pin_closure(service.snapshot()["current_revision"]),
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
#: Wave-3 computed reads served through ProjectService verbs.
|
|
138
|
+
_COMPUTED_READS = frozenset({
|
|
139
|
+
"read_entity_revision", "connection_graph", "net_occurrences",
|
|
140
|
+
"xprobe", "resolve_pin", "plot_export", "erc_check",
|
|
141
|
+
"write_netlist", "update_netlist", "check_capability",
|
|
142
|
+
"describe_testbench",
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
class _EditorMixin:
|
|
147
|
+
"""Wave-3 editor/testbench query dispatch (Engine side)."""
|
|
148
|
+
|
|
149
|
+
def _query_editor(self, name, arguments):
|
|
150
|
+
"""Serve one Wave-3 editor/testbench read query.
|
|
151
|
+
|
|
152
|
+
Generic entity reads ride the component seams; computed
|
|
153
|
+
reports delegate to the matching ProjectService verb.
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
name: Wave-3 read query name.
|
|
157
|
+
arguments: Query arguments mapping.
|
|
158
|
+
|
|
159
|
+
Returns:
|
|
160
|
+
Read mapping with head revision and I28 pin.
|
|
161
|
+
|
|
162
|
+
Raises:
|
|
163
|
+
ProjectError: invalid_command for missing arguments
|
|
164
|
+
or the domain refusal code.
|
|
165
|
+
"""
|
|
166
|
+
project = self._project
|
|
167
|
+
if name == "read_entity_revision":
|
|
168
|
+
if "entity" not in arguments:
|
|
169
|
+
raise ProjectError(
|
|
170
|
+
"invalid_command",
|
|
171
|
+
"read_entity_revision needs an entity.",
|
|
172
|
+
)
|
|
173
|
+
return project.entity_revision(arguments["entity"])
|
|
174
|
+
if name in ("connection_graph", "net_occurrences"):
|
|
175
|
+
return project.graph_report(
|
|
176
|
+
sheet=arguments.get("sheet"),
|
|
177
|
+
net=arguments.get("net"),
|
|
178
|
+
net_pins=arguments.get("net_pins"))
|
|
179
|
+
if name in ("xprobe", "resolve_pin"):
|
|
180
|
+
for key in ("instance", "pin"):
|
|
181
|
+
if key not in arguments:
|
|
182
|
+
raise ProjectError(
|
|
183
|
+
"invalid_command",
|
|
184
|
+
f"{name} needs {key}.",
|
|
185
|
+
)
|
|
186
|
+
return project.probe_report(
|
|
187
|
+
arguments["instance"], arguments["pin"],
|
|
188
|
+
net_pins=arguments.get("net_pins"))
|
|
189
|
+
if name == "plot_export":
|
|
190
|
+
return project.plot_report(
|
|
191
|
+
format=arguments.get("format", "svg"),
|
|
192
|
+
sheet=arguments.get("sheet"))
|
|
193
|
+
if name == "erc_check":
|
|
194
|
+
return project.erc_report(
|
|
195
|
+
severities=arguments.get("severities"),
|
|
196
|
+
pin_conflict=arguments.get("pin_conflict"),
|
|
197
|
+
net_pins=arguments.get("net_pins"))
|
|
198
|
+
if name in ("write_netlist", "update_netlist"):
|
|
199
|
+
if "language" not in arguments:
|
|
200
|
+
raise ProjectError(
|
|
201
|
+
"invalid_command",
|
|
202
|
+
f"{name} needs a language.",
|
|
203
|
+
)
|
|
204
|
+
return project.netlist_report(
|
|
205
|
+
arguments["language"],
|
|
206
|
+
sheet=arguments.get("sheet"),
|
|
207
|
+
title=arguments.get("title", "top"),
|
|
208
|
+
net_pins=arguments.get("net_pins"),
|
|
209
|
+
prior_text=arguments.get("prior_text"),
|
|
210
|
+
adopted=arguments.get("adopted", False))
|
|
211
|
+
if name == "check_capability":
|
|
212
|
+
for key in ("profile", "operation"):
|
|
213
|
+
if key not in arguments:
|
|
214
|
+
raise ProjectError(
|
|
215
|
+
"invalid_command",
|
|
216
|
+
f"check_capability needs {key}.",
|
|
217
|
+
)
|
|
218
|
+
return project.capability_report(
|
|
219
|
+
arguments["profile"], arguments["operation"],
|
|
220
|
+
bindings=arguments.get("bindings"))
|
|
221
|
+
if name == "describe_testbench":
|
|
222
|
+
if "testbench" not in arguments:
|
|
223
|
+
raise ProjectError(
|
|
224
|
+
"invalid_command",
|
|
225
|
+
"describe_testbench needs a testbench.",
|
|
226
|
+
)
|
|
227
|
+
return project.testbench_report(
|
|
228
|
+
arguments["testbench"])
|
|
229
|
+
return self._query_entities(name, arguments)
|
|
230
|
+
|
|
231
|
+
def _query_entities(self, name, arguments):
|
|
232
|
+
"""Serve Wave-3 reads through the component seams.
|
|
233
|
+
|
|
234
|
+
Args:
|
|
235
|
+
name: Wave-3 entity read query name.
|
|
236
|
+
arguments: Query arguments mapping.
|
|
237
|
+
|
|
238
|
+
Returns:
|
|
239
|
+
Entity data with head revision and I28 pin.
|
|
240
|
+
|
|
241
|
+
Raises:
|
|
242
|
+
ProjectError: invalid_command for missing arguments
|
|
243
|
+
or the seam refusal code.
|
|
244
|
+
"""
|
|
245
|
+
from ..interfaces.refs import pin_closure as _pin
|
|
246
|
+
|
|
247
|
+
project = self._project
|
|
248
|
+
head = project.snapshot()["current_revision"]
|
|
249
|
+
|
|
250
|
+
def _wrap(payload):
|
|
251
|
+
"""Attach head revision and I28 pin to one payload.
|
|
252
|
+
|
|
253
|
+
Args:
|
|
254
|
+
payload: Read payload mapping.
|
|
255
|
+
|
|
256
|
+
Returns:
|
|
257
|
+
Payload with current_revision and i28 pin.
|
|
258
|
+
"""
|
|
259
|
+
return {
|
|
260
|
+
**payload, "current_revision": head,
|
|
261
|
+
"i28": _pin(head),
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if name == "list_sheets":
|
|
265
|
+
return _wrap(project.entities_with("Sheet"))
|
|
266
|
+
if name == "get_sheet":
|
|
267
|
+
if "entity" not in arguments:
|
|
268
|
+
raise ProjectError(
|
|
269
|
+
"invalid_command",
|
|
270
|
+
"get_sheet needs an entity.",
|
|
271
|
+
)
|
|
272
|
+
return _wrap(project.get_component(
|
|
273
|
+
arguments["entity"], "Sheet"))
|
|
274
|
+
if name == "get_instance":
|
|
275
|
+
if "entity" not in arguments:
|
|
276
|
+
raise ProjectError(
|
|
277
|
+
"invalid_command",
|
|
278
|
+
"get_instance needs an entity.",
|
|
279
|
+
)
|
|
280
|
+
return _wrap(project.get_component(
|
|
281
|
+
arguments["entity"], "Instance"))
|
|
282
|
+
if name == "instances_on_sheet":
|
|
283
|
+
if "sheet" not in arguments:
|
|
284
|
+
raise ProjectError(
|
|
285
|
+
"invalid_command",
|
|
286
|
+
"instances_on_sheet needs a sheet.",
|
|
287
|
+
)
|
|
288
|
+
sheet = arguments["sheet"]
|
|
289
|
+
found = []
|
|
290
|
+
for entity in project.entities_with(
|
|
291
|
+
"Instance")["entities"]:
|
|
292
|
+
data = project.get_component(
|
|
293
|
+
entity, "Instance")["data"]
|
|
294
|
+
if isinstance(data, dict) and data.get(
|
|
295
|
+
"sheet") == sheet:
|
|
296
|
+
found.append(entity)
|
|
297
|
+
return _wrap({"sheet": sheet, "instances": found})
|
|
298
|
+
if name == "list_symbols":
|
|
299
|
+
return _wrap(project.entities_with("Symbol"))
|
|
300
|
+
if name == "get_symbol":
|
|
301
|
+
if "entity" not in arguments:
|
|
302
|
+
raise ProjectError(
|
|
303
|
+
"invalid_command",
|
|
304
|
+
"get_symbol needs an entity.",
|
|
305
|
+
)
|
|
306
|
+
return _wrap(project.get_component(
|
|
307
|
+
arguments["entity"], "Symbol"))
|
|
308
|
+
if name == "get_cell":
|
|
309
|
+
if "entity" not in arguments:
|
|
310
|
+
raise ProjectError(
|
|
311
|
+
"invalid_command",
|
|
312
|
+
"get_cell needs an entity.",
|
|
313
|
+
)
|
|
314
|
+
return _wrap(project.get_component(
|
|
315
|
+
arguments["entity"], "Cell"))
|
|
316
|
+
if name == "list_layers":
|
|
317
|
+
try:
|
|
318
|
+
doc = project.read_doc("layers/bindings.yaml")
|
|
319
|
+
except ProjectError as exc:
|
|
320
|
+
if exc.code == "unknown_document":
|
|
321
|
+
raise ProjectError(
|
|
322
|
+
"invalid_command",
|
|
323
|
+
"No bindings saved yet: run bind_layers.",
|
|
324
|
+
) from exc
|
|
325
|
+
raise
|
|
326
|
+
return _wrap({"bindings_path": doc["path"],
|
|
327
|
+
"content": doc["content"]})
|
|
328
|
+
if name == "list_testbenches":
|
|
329
|
+
return _wrap(project.entities_with("Testbench"))
|
|
330
|
+
if name == "ip_search":
|
|
331
|
+
if "pdk" not in arguments:
|
|
332
|
+
raise ProjectError(
|
|
333
|
+
"invalid_command",
|
|
334
|
+
"ip_search needs a pdk.",
|
|
335
|
+
)
|
|
336
|
+
wanted = arguments.get("name_filter")
|
|
337
|
+
found = []
|
|
338
|
+
for entity in project.entities_with(
|
|
339
|
+
"IpBlock")["entities"]:
|
|
340
|
+
data = project.get_component(
|
|
341
|
+
entity, "IpBlock")["data"]
|
|
342
|
+
if not isinstance(data, dict):
|
|
343
|
+
continue
|
|
344
|
+
if data.get("pdk") != arguments["pdk"]:
|
|
345
|
+
continue
|
|
346
|
+
if wanted and wanted not in data.get("name", ""):
|
|
347
|
+
continue
|
|
348
|
+
item = dict(data)
|
|
349
|
+
item["id"] = entity
|
|
350
|
+
found.append(item)
|
|
351
|
+
found.sort(key=lambda item: item.get("version", ""))
|
|
352
|
+
return _wrap({"pdk": arguments["pdk"],
|
|
353
|
+
"blocks": found})
|
|
354
|
+
if name == "ip_get":
|
|
355
|
+
for key in ("pdk", "name"):
|
|
356
|
+
if key not in arguments:
|
|
357
|
+
raise ProjectError(
|
|
358
|
+
"invalid_command",
|
|
359
|
+
f"ip_get needs {key}.",
|
|
360
|
+
)
|
|
361
|
+
return _wrap(self._select_ip(
|
|
362
|
+
arguments["pdk"], arguments["name"],
|
|
363
|
+
arguments.get("version")))
|
|
364
|
+
raise ProjectError(
|
|
365
|
+
"unsupported_operation", f"Operation not wired yet: {name}.")
|
|
366
|
+
|
|
367
|
+
def _select_ip(self, pdk, name, version):
|
|
368
|
+
"""Select one registered IP block, latest when unpinned.
|
|
369
|
+
|
|
370
|
+
Args:
|
|
371
|
+
pdk: Owning PDK id (shared blocks qualify too).
|
|
372
|
+
name: Block name within the registry.
|
|
373
|
+
version: Revision label, or None for the greatest.
|
|
374
|
+
|
|
375
|
+
Returns:
|
|
376
|
+
Full block mapping with id.
|
|
377
|
+
|
|
378
|
+
Raises:
|
|
379
|
+
ProjectError: unknown_ip when no block matches.
|
|
380
|
+
"""
|
|
381
|
+
project = self._project
|
|
382
|
+
candidates = []
|
|
383
|
+
for entity in project.entities_with("IpBlock")["entities"]:
|
|
384
|
+
data = project.get_component(entity, "IpBlock")["data"]
|
|
385
|
+
if not isinstance(data, dict):
|
|
386
|
+
continue
|
|
387
|
+
if data.get("name") != name:
|
|
388
|
+
continue
|
|
389
|
+
if data.get("pdk") != pdk and data.get(
|
|
390
|
+
"scope") != "shared":
|
|
391
|
+
continue
|
|
392
|
+
item = dict(data)
|
|
393
|
+
item["id"] = entity
|
|
394
|
+
candidates.append(item)
|
|
395
|
+
if version is not None:
|
|
396
|
+
for item in candidates:
|
|
397
|
+
if item.get("version") == version:
|
|
398
|
+
return item
|
|
399
|
+
elif candidates:
|
|
400
|
+
return sorted(
|
|
401
|
+
candidates, key=lambda item: item.get(
|
|
402
|
+
"version", ""))[-1]
|
|
403
|
+
raise ProjectError(
|
|
404
|
+
"unknown_ip", f"No such IP block: {pdk}/{name}.")
|