pycforge 0.15.2__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.
- pycforge/__init__.py +8 -0
- pycforge/__main__.py +2 -0
- pycforge/_version.py +1 -0
- pycforge/converter/__init__.py +0 -0
- pycforge/converter/analysis/__init__.py +5 -0
- pycforge/converter/analysis/functions.py +723 -0
- pycforge/converter/analysis/model.py +158 -0
- pycforge/converter/analysis/planning.py +464 -0
- pycforge/converter/analysis/stage.py +1976 -0
- pycforge/converter/analysis/symbols.py +237 -0
- pycforge/converter/analysis/validation.py +2166 -0
- pycforge/converter/c_output/__init__.py +2 -0
- pycforge/converter/c_output/conformance.py +297 -0
- pycforge/converter/c_output/renderer.py +220 -0
- pycforge/converter/conditional_regions/__init__.py +71 -0
- pycforge/converter/conditional_regions/analysis.py +481 -0
- pycforge/converter/conditional_regions/lowering.py +579 -0
- pycforge/converter/conditional_regions/model.py +243 -0
- pycforge/converter/conditional_regions/validation.py +690 -0
- pycforge/converter/containers/__init__.py +19 -0
- pycforge/converter/containers/analysis.py +542 -0
- pycforge/converter/containers/lowering.py +290 -0
- pycforge/converter/containers/model.py +146 -0
- pycforge/converter/contracts/__init__.py +0 -0
- pycforge/converter/contracts/configuration.py +178 -0
- pycforge/converter/contracts/identifiers.py +68 -0
- pycforge/converter/contracts/versions.py +71 -0
- pycforge/converter/core/__init__.py +0 -0
- pycforge/converter/core/artifact_io.py +87 -0
- pycforge/converter/core/cancellation.py +7 -0
- pycforge/converter/core/canonicalization.py +254 -0
- pycforge/converter/core/context.py +13 -0
- pycforge/converter/core/diagnostics.py +95 -0
- pycforge/converter/core/enums.py +23 -0
- pycforge/converter/core/fingerprint.py +22 -0
- pycforge/converter/core/pipeline.py +8 -0
- pycforge/converter/core/progress.py +25 -0
- pycforge/converter/core/request.py +50 -0
- pycforge/converter/core/resource_policy.py +31 -0
- pycforge/converter/core/result.py +23 -0
- pycforge/converter/core/serialization.py +36 -0
- pycforge/converter/core/stage.py +10 -0
- pycforge/converter/core/stage_artifact.py +80 -0
- pycforge/converter/core/stage_outcome.py +15 -0
- pycforge/converter/decision_trace/__init__.py +0 -0
- pycforge/converter/decision_trace/recorder.py +52 -0
- pycforge/converter/facade.py +459 -0
- pycforge/converter/frontend/__init__.py +4 -0
- pycforge/converter/frontend/normalizer.py +59 -0
- pycforge/converter/frontend/parser.py +20 -0
- pycforge/converter/frontend/source_document.py +93 -0
- pycforge/converter/frontend/stages.py +518 -0
- pycforge/converter/frontend/validation.py +76 -0
- pycforge/converter/io/__init__.py +0 -0
- pycforge/converter/io/atomic_writer.py +33 -0
- pycforge/converter/ir/__init__.py +1 -0
- pycforge/converter/ir/c_ir/__init__.py +4 -0
- pycforge/converter/ir/c_ir/builder.py +26 -0
- pycforge/converter/ir/c_ir/model.py +318 -0
- pycforge/converter/ir/c_ir/serialization.py +88 -0
- pycforge/converter/ir/c_ir/validation.py +834 -0
- pycforge/converter/ir/python_ir/__init__.py +17 -0
- pycforge/converter/ir/python_ir/nodes.py +86 -0
- pycforge/converter/keyword_calls/__init__.py +68 -0
- pycforge/converter/keyword_calls/analysis.py +458 -0
- pycforge/converter/keyword_calls/lowering.py +271 -0
- pycforge/converter/keyword_calls/model.py +304 -0
- pycforge/converter/keyword_calls/validation.py +802 -0
- pycforge/converter/keyword_only_calls/__init__.py +68 -0
- pycforge/converter/keyword_only_calls/analysis.py +640 -0
- pycforge/converter/keyword_only_calls/lowering.py +376 -0
- pycforge/converter/keyword_only_calls/model.py +328 -0
- pycforge/converter/keyword_only_calls/validation.py +1357 -0
- pycforge/converter/lowering.py +1000 -0
- pycforge/converter/modules/__init__.py +13 -0
- pycforge/converter/modules/analysis.py +1250 -0
- pycforge/converter/modules/lowering.py +200 -0
- pycforge/converter/modules/model.py +73 -0
- pycforge/converter/modules/stage.py +192 -0
- pycforge/converter/numeric_semantics/__init__.py +23 -0
- pycforge/converter/numeric_semantics/analysis.py +420 -0
- pycforge/converter/numeric_semantics/lowering.py +153 -0
- pycforge/converter/numeric_semantics/model.py +123 -0
- pycforge/converter/records/__init__.py +34 -0
- pycforge/converter/records/analysis.py +1394 -0
- pycforge/converter/records/lowering.py +393 -0
- pycforge/converter/records/model.py +319 -0
- pycforge/converter/rules/__init__.py +3 -0
- pycforge/converter/support_templates/__init__.py +49 -0
- pycforge/converter/support_templates/assembly.py +106 -0
- pycforge/converter/support_templates/factories.py +348 -0
- pycforge/converter/support_templates/model.py +159 -0
- pycforge/converter/support_templates/registry.py +388 -0
- pycforge/converter/telemetry/__init__.py +0 -0
- pycforge/converter/telemetry/sink.py +39 -0
- pycforge/ide/__init__.py +19 -0
- pycforge/ide/__main__.py +5 -0
- pycforge/ide/_worker_event_codec.py +276 -0
- pycforge/ide/_worker_protocol_json.py +197 -0
- pycforge/ide/_worker_protocol_types.py +90 -0
- pycforge/ide/_worker_request_codec.py +200 -0
- pycforge/ide/_worker_request_data.py +260 -0
- pycforge/ide/_worker_result_data.py +343 -0
- pycforge/ide/action_contract.py +469 -0
- pycforge/ide/action_validation.py +229 -0
- pycforge/ide/command_palette.py +223 -0
- pycforge/ide/controller.py +904 -0
- pycforge/ide/controller_conversion.py +546 -0
- pycforge/ide/controller_io.py +205 -0
- pycforge/ide/editor.py +951 -0
- pycforge/ide/editor_commands_qt.py +274 -0
- pycforge/ide/editor_lexical.py +43 -0
- pycforge/ide/editor_sidebars.py +236 -0
- pycforge/ide/editor_syntax.py +243 -0
- pycforge/ide/find_replace.py +535 -0
- pycforge/ide/icons.py +91 -0
- pycforge/ide/io_service.py +196 -0
- pycforge/ide/model.py +164 -0
- pycforge/ide/panels.py +903 -0
- pycforge/ide/positions.py +220 -0
- pycforge/ide/process_worker.py +130 -0
- pycforge/ide/qt.py +230 -0
- pycforge/ide/qt_actions.py +418 -0
- pycforge/ide/qt_close.py +169 -0
- pycforge/ide/qt_command_palette.py +297 -0
- pycforge/ide/qt_contract.py +220 -0
- pycforge/ide/qt_documents.py +590 -0
- pycforge/ide/qt_editor_buffers.py +113 -0
- pycforge/ide/qt_editor_surfaces.py +112 -0
- pycforge/ide/qt_menus.py +324 -0
- pycforge/ide/qt_projection.py +500 -0
- pycforge/ide/qt_shell.py +348 -0
- pycforge/ide/qt_shell_interactions.py +320 -0
- pycforge/ide/qt_state.py +393 -0
- pycforge/ide/qt_workspace_features.py +398 -0
- pycforge/ide/qt_workspace_navigation.py +208 -0
- pycforge/ide/qt_workspace_observers.py +173 -0
- pycforge/ide/qt_workspace_panels.py +545 -0
- pycforge/ide/qt_workspace_widgets.py +228 -0
- pycforge/ide/resources/icons/about.svg +1 -0
- pycforge/ide/resources/icons/add-document.svg +1 -0
- pycforge/ide/resources/icons/brand-mark.svg +1 -0
- pycforge/ide/resources/icons/bundle-search.svg +1 -0
- pycforge/ide/resources/icons/cancel.svg +1 -0
- pycforge/ide/resources/icons/check.svg +1 -0
- pycforge/ide/resources/icons/chevron-right.svg +1 -0
- pycforge/ide/resources/icons/close.svg +1 -0
- pycforge/ide/resources/icons/collapse-all.svg +1 -0
- pycforge/ide/resources/icons/command-palette.svg +1 -0
- pycforge/ide/resources/icons/convert.svg +1 -0
- pycforge/ide/resources/icons/copy.svg +1 -0
- pycforge/ide/resources/icons/cut.svg +1 -0
- pycforge/ide/resources/icons/decision-trace.svg +1 -0
- pycforge/ide/resources/icons/details.svg +1 -0
- pycforge/ide/resources/icons/diagnostics.svg +1 -0
- pycforge/ide/resources/icons/duplicate-line.svg +1 -0
- pycforge/ide/resources/icons/exit.svg +1 -0
- pycforge/ide/resources/icons/expand-all.svg +1 -0
- pycforge/ide/resources/icons/export.svg +1 -0
- pycforge/ide/resources/icons/find.svg +1 -0
- pycforge/ide/resources/icons/go-to-line.svg +1 -0
- pycforge/ide/resources/icons/go-to-output.svg +1 -0
- pycforge/ide/resources/icons/go-to-source.svg +1 -0
- pycforge/ide/resources/icons/history.svg +1 -0
- pycforge/ide/resources/icons/indent.svg +1 -0
- pycforge/ide/resources/icons/link-c.svg +1 -0
- pycforge/ide/resources/icons/mappings.svg +1 -0
- pycforge/ide/resources/icons/module.svg +1 -0
- pycforge/ide/resources/icons/move-down.svg +1 -0
- pycforge/ide/resources/icons/move-line-down.svg +1 -0
- pycforge/ide/resources/icons/move-line-up.svg +1 -0
- pycforge/ide/resources/icons/move-up.svg +1 -0
- pycforge/ide/resources/icons/next.svg +1 -0
- pycforge/ide/resources/icons/open.svg +1 -0
- pycforge/ide/resources/icons/outdent.svg +1 -0
- pycforge/ide/resources/icons/outline.svg +1 -0
- pycforge/ide/resources/icons/paste.svg +1 -0
- pycforge/ide/resources/icons/previous.svg +1 -0
- pycforge/ide/resources/icons/primary-module.svg +1 -0
- pycforge/ide/resources/icons/redo.svg +1 -0
- pycforge/ide/resources/icons/remove-document.svg +1 -0
- pycforge/ide/resources/icons/replace.svg +1 -0
- pycforge/ide/resources/icons/save-as.svg +1 -0
- pycforge/ide/resources/icons/save.svg +1 -0
- pycforge/ide/resources/icons/select-all.svg +1 -0
- pycforge/ide/resources/icons/settings.svg +1 -0
- pycforge/ide/resources/icons/show-c.svg +1 -0
- pycforge/ide/resources/icons/split-view.svg +1 -0
- pycforge/ide/resources/icons/summary.svg +1 -0
- pycforge/ide/resources/icons/telemetry.svg +1 -0
- pycforge/ide/resources/icons/toggle-comment.svg +1 -0
- pycforge/ide/resources/icons/toggle-fold.svg +1 -0
- pycforge/ide/resources/icons/undo.svg +1 -0
- pycforge/ide/resources/icons/whitespace.svg +1 -0
- pycforge/ide/revisions.py +400 -0
- pycforge/ide/search_service.py +280 -0
- pycforge/ide/session_history.py +150 -0
- pycforge/ide/source_editing.py +417 -0
- pycforge/ide/source_structure.py +558 -0
- pycforge/ide/source_structure_async.py +191 -0
- pycforge/ide/supervisor.py +503 -0
- pycforge/ide/theme.py +119 -0
- pycforge/ide/theme_stylesheet.py +598 -0
- pycforge/ide/theme_workspace_stylesheet.py +155 -0
- pycforge/ide/visual_tokens.py +142 -0
- pycforge/ide/worker_protocol.py +80 -0
- pycforge/ide/workspace_search.py +462 -0
- pycforge/ide/workspace_session.py +256 -0
- pycforge/laboratory/__init__.py +6 -0
- pycforge/laboratory/audits.py +2244 -0
- pycforge/laboratory/checkpoint_e.py +2685 -0
- pycforge/laboratory/cli.py +119 -0
- pycforge/laboratory/exit_codes.py +11 -0
- pycforge/laboratory/keyword_audit.py +661 -0
- pycforge/laboratory/keyword_only_audit.py +481 -0
- pycforge-0.15.2.dist-info/METADATA +209 -0
- pycforge-0.15.2.dist-info/RECORD +221 -0
- pycforge-0.15.2.dist-info/WHEEL +5 -0
- pycforge-0.15.2.dist-info/entry_points.txt +5 -0
- pycforge-0.15.2.dist-info/licenses/LICENSE +674 -0
- pycforge-0.15.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
"""Request and cancellation envelope codec."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pycforge.converter.core.fingerprint import fingerprint
|
|
6
|
+
from pycforge.converter.core.request import ConversionRequest, ObservationOptions
|
|
7
|
+
|
|
8
|
+
from ._worker_protocol_json import (
|
|
9
|
+
_decode_bounded,
|
|
10
|
+
_encode_bounded,
|
|
11
|
+
_exact_fields,
|
|
12
|
+
_generation,
|
|
13
|
+
_object,
|
|
14
|
+
_receive_frame,
|
|
15
|
+
_send_frame,
|
|
16
|
+
_sha256,
|
|
17
|
+
)
|
|
18
|
+
from ._worker_protocol_types import (
|
|
19
|
+
MAX_CONTROL_BYTES,
|
|
20
|
+
MAX_REQUEST_BYTES,
|
|
21
|
+
PROTOCOL_SCHEMA,
|
|
22
|
+
ByteConnection,
|
|
23
|
+
WorkerControl,
|
|
24
|
+
WorkerProtocolError,
|
|
25
|
+
WorkerRequest,
|
|
26
|
+
)
|
|
27
|
+
from ._worker_request_data import (
|
|
28
|
+
_observation_from_data,
|
|
29
|
+
_observation_to_data,
|
|
30
|
+
_request_from_data,
|
|
31
|
+
_request_to_data,
|
|
32
|
+
bundle_fingerprint_for_request,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def create_worker_request(
|
|
37
|
+
generation: int,
|
|
38
|
+
bundle_fingerprint: str,
|
|
39
|
+
request: ConversionRequest,
|
|
40
|
+
observation: ObservationOptions | None = None,
|
|
41
|
+
) -> WorkerRequest:
|
|
42
|
+
observation = observation or ObservationOptions()
|
|
43
|
+
core = _request_envelope_core(
|
|
44
|
+
generation, bundle_fingerprint, request, observation
|
|
45
|
+
)
|
|
46
|
+
return WorkerRequest(
|
|
47
|
+
generation,
|
|
48
|
+
bundle_fingerprint,
|
|
49
|
+
request,
|
|
50
|
+
observation,
|
|
51
|
+
_transport_fingerprint(core),
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def encode_request(request: WorkerRequest) -> bytes:
|
|
56
|
+
_validate_worker_request(request)
|
|
57
|
+
core = _request_envelope_core(
|
|
58
|
+
request.generation,
|
|
59
|
+
request.bundle_fingerprint,
|
|
60
|
+
request.request,
|
|
61
|
+
request.observation,
|
|
62
|
+
)
|
|
63
|
+
if request.transport_fingerprint != _transport_fingerprint(core):
|
|
64
|
+
raise WorkerProtocolError("worker request transport fingerprint mismatch")
|
|
65
|
+
return _encode_bounded(
|
|
66
|
+
{**core, "transport_fingerprint": request.transport_fingerprint},
|
|
67
|
+
MAX_REQUEST_BYTES,
|
|
68
|
+
"request",
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def decode_request(frame: bytes) -> WorkerRequest:
|
|
73
|
+
value = _decode_bounded(frame, MAX_REQUEST_BYTES, "request")
|
|
74
|
+
data = _object(value, "request envelope")
|
|
75
|
+
_exact_fields(
|
|
76
|
+
data,
|
|
77
|
+
{
|
|
78
|
+
"schema",
|
|
79
|
+
"kind",
|
|
80
|
+
"generation",
|
|
81
|
+
"bundle_fingerprint",
|
|
82
|
+
"request",
|
|
83
|
+
"observation",
|
|
84
|
+
"transport_fingerprint",
|
|
85
|
+
},
|
|
86
|
+
"request envelope",
|
|
87
|
+
)
|
|
88
|
+
if data["schema"] != PROTOCOL_SCHEMA or data["kind"] != "request":
|
|
89
|
+
raise WorkerProtocolError("incompatible worker request envelope")
|
|
90
|
+
generation = _generation(data["generation"])
|
|
91
|
+
bundle_fingerprint = _sha256(data["bundle_fingerprint"], "bundle fingerprint")
|
|
92
|
+
conversion_request = _request_from_data(data["request"])
|
|
93
|
+
observation = _observation_from_data(data["observation"])
|
|
94
|
+
supplied = _sha256(data["transport_fingerprint"], "transport fingerprint")
|
|
95
|
+
core = {
|
|
96
|
+
"schema": PROTOCOL_SCHEMA,
|
|
97
|
+
"kind": "request",
|
|
98
|
+
"generation": generation,
|
|
99
|
+
"bundle_fingerprint": bundle_fingerprint,
|
|
100
|
+
"request": _request_to_data(conversion_request),
|
|
101
|
+
"observation": _observation_to_data(observation),
|
|
102
|
+
}
|
|
103
|
+
expected = _transport_fingerprint(core)
|
|
104
|
+
if supplied != expected:
|
|
105
|
+
raise WorkerProtocolError("worker request transport fingerprint mismatch")
|
|
106
|
+
result = WorkerRequest(
|
|
107
|
+
generation,
|
|
108
|
+
bundle_fingerprint,
|
|
109
|
+
conversion_request,
|
|
110
|
+
observation,
|
|
111
|
+
supplied,
|
|
112
|
+
)
|
|
113
|
+
_validate_worker_request(result)
|
|
114
|
+
return result
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def encode_cancel(request: WorkerRequest) -> bytes:
|
|
118
|
+
_validate_worker_request(request)
|
|
119
|
+
return _encode_bounded(
|
|
120
|
+
{
|
|
121
|
+
"schema": PROTOCOL_SCHEMA,
|
|
122
|
+
"kind": "cancel",
|
|
123
|
+
"generation": request.generation,
|
|
124
|
+
"bundle_fingerprint": request.bundle_fingerprint,
|
|
125
|
+
"transport_fingerprint": request.transport_fingerprint,
|
|
126
|
+
},
|
|
127
|
+
MAX_CONTROL_BYTES,
|
|
128
|
+
"control",
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def decode_control(frame: bytes) -> WorkerControl:
|
|
133
|
+
value = _decode_bounded(frame, MAX_CONTROL_BYTES, "control")
|
|
134
|
+
data = _object(value, "control envelope")
|
|
135
|
+
_exact_fields(
|
|
136
|
+
data,
|
|
137
|
+
{
|
|
138
|
+
"schema",
|
|
139
|
+
"kind",
|
|
140
|
+
"generation",
|
|
141
|
+
"bundle_fingerprint",
|
|
142
|
+
"transport_fingerprint",
|
|
143
|
+
},
|
|
144
|
+
"control envelope",
|
|
145
|
+
)
|
|
146
|
+
if data["schema"] != PROTOCOL_SCHEMA or data["kind"] != "cancel":
|
|
147
|
+
raise WorkerProtocolError("incompatible worker control envelope")
|
|
148
|
+
return WorkerControl(
|
|
149
|
+
_generation(data["generation"]),
|
|
150
|
+
_sha256(data["bundle_fingerprint"], "bundle fingerprint"),
|
|
151
|
+
_sha256(data["transport_fingerprint"], "transport fingerprint"),
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def send_request(connection: ByteConnection, request: WorkerRequest) -> None:
|
|
156
|
+
_send_frame(connection, encode_request(request), MAX_REQUEST_BYTES, "request")
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def receive_request(connection: ByteConnection) -> WorkerRequest:
|
|
160
|
+
return decode_request(_receive_frame(connection, MAX_REQUEST_BYTES, "request"))
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def send_cancel(connection: ByteConnection, request: WorkerRequest) -> None:
|
|
164
|
+
_send_frame(connection, encode_cancel(request), MAX_CONTROL_BYTES, "control")
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def receive_control(connection: ByteConnection) -> WorkerControl:
|
|
168
|
+
return decode_control(_receive_frame(connection, MAX_CONTROL_BYTES, "control"))
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def _request_envelope_core(
|
|
172
|
+
generation: int,
|
|
173
|
+
bundle_fingerprint: str,
|
|
174
|
+
request: ConversionRequest,
|
|
175
|
+
observation: ObservationOptions,
|
|
176
|
+
) -> dict[str, object]:
|
|
177
|
+
return {
|
|
178
|
+
"schema": PROTOCOL_SCHEMA,
|
|
179
|
+
"kind": "request",
|
|
180
|
+
"generation": generation,
|
|
181
|
+
"bundle_fingerprint": bundle_fingerprint,
|
|
182
|
+
"request": _request_to_data(request),
|
|
183
|
+
"observation": _observation_to_data(observation),
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def _transport_fingerprint(value: object) -> str:
|
|
188
|
+
return fingerprint("worker-request-transport", value).value
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def _validate_worker_request(request: WorkerRequest) -> None:
|
|
192
|
+
if not isinstance(request, WorkerRequest):
|
|
193
|
+
raise WorkerProtocolError("worker request envelope is malformed")
|
|
194
|
+
_generation(request.generation)
|
|
195
|
+
_sha256(request.bundle_fingerprint, "bundle fingerprint")
|
|
196
|
+
_sha256(request.transport_fingerprint, "transport fingerprint")
|
|
197
|
+
_request_to_data(request.request)
|
|
198
|
+
_observation_to_data(request.observation)
|
|
199
|
+
if request.bundle_fingerprint != bundle_fingerprint_for_request(request.request):
|
|
200
|
+
raise WorkerProtocolError("worker request bundle fingerprint mismatch")
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
"""Strict plain-data mapping for conversion requests and observations."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pycforge.converter.core.fingerprint import fingerprint
|
|
6
|
+
from pycforge.converter.core.request import (
|
|
7
|
+
ConversionRequest,
|
|
8
|
+
ObservationOptions,
|
|
9
|
+
SourceBundle,
|
|
10
|
+
SourceDocumentInput,
|
|
11
|
+
)
|
|
12
|
+
from pycforge.converter.core.resource_policy import ResourcePolicy
|
|
13
|
+
|
|
14
|
+
from ._worker_protocol_json import (
|
|
15
|
+
_contains_surrogate,
|
|
16
|
+
_exact_fields,
|
|
17
|
+
_list,
|
|
18
|
+
_nonempty_string,
|
|
19
|
+
_nonnegative_integer,
|
|
20
|
+
_object,
|
|
21
|
+
_string,
|
|
22
|
+
_string_list,
|
|
23
|
+
)
|
|
24
|
+
from ._worker_protocol_types import (
|
|
25
|
+
MAX_SOURCE_BYTES,
|
|
26
|
+
MAX_SOURCE_DOCUMENTS,
|
|
27
|
+
WorkerProtocolError,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
_REQUEST_FIELDS = frozenset(
|
|
32
|
+
{
|
|
33
|
+
"source_bundle",
|
|
34
|
+
"python_version",
|
|
35
|
+
"target_contract",
|
|
36
|
+
"semantic_policy",
|
|
37
|
+
"approximation_allowlist",
|
|
38
|
+
"rule_set_version",
|
|
39
|
+
"renderer_version",
|
|
40
|
+
"helper_policy_version",
|
|
41
|
+
"container_policy_version",
|
|
42
|
+
"resource_policy",
|
|
43
|
+
"module_policy_version",
|
|
44
|
+
"record_policy_version",
|
|
45
|
+
"numeric_policy_version",
|
|
46
|
+
}
|
|
47
|
+
)
|
|
48
|
+
_RESOURCE_FIELDS = frozenset(ResourcePolicy.__slots__)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def bundle_fingerprint_for_request(request: ConversionRequest) -> str:
|
|
52
|
+
"""Return the workspace bundle identity represented by ``request``."""
|
|
53
|
+
|
|
54
|
+
if not isinstance(request, ConversionRequest):
|
|
55
|
+
raise WorkerProtocolError("worker request must contain ConversionRequest")
|
|
56
|
+
bundle = request.source_bundle
|
|
57
|
+
if not isinstance(bundle, SourceBundle):
|
|
58
|
+
raise WorkerProtocolError("worker request source bundle is malformed")
|
|
59
|
+
documents = (bundle.primary,) + tuple(bundle.companions)
|
|
60
|
+
if not documents or any(not isinstance(item, SourceDocumentInput) for item in documents):
|
|
61
|
+
raise WorkerProtocolError("worker request source documents are malformed")
|
|
62
|
+
semantic = [
|
|
63
|
+
{
|
|
64
|
+
"module_id": document.module_id,
|
|
65
|
+
"logical_name": document.logical_name,
|
|
66
|
+
"text": document.text,
|
|
67
|
+
"is_primary": ordinal == 0,
|
|
68
|
+
}
|
|
69
|
+
for ordinal, document in enumerate(documents)
|
|
70
|
+
]
|
|
71
|
+
return fingerprint("workspace-source-bundle", semantic).value
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _request_to_data(request: ConversionRequest) -> dict[str, object]:
|
|
75
|
+
if not isinstance(request, ConversionRequest):
|
|
76
|
+
raise WorkerProtocolError("worker request must contain ConversionRequest")
|
|
77
|
+
bundle = request.source_bundle
|
|
78
|
+
if not isinstance(bundle, SourceBundle):
|
|
79
|
+
raise WorkerProtocolError("worker request source bundle is malformed")
|
|
80
|
+
documents = (bundle.primary,) + tuple(bundle.companions)
|
|
81
|
+
if (
|
|
82
|
+
not documents
|
|
83
|
+
or len(documents) > MAX_SOURCE_DOCUMENTS
|
|
84
|
+
or any(not isinstance(item, SourceDocumentInput) for item in documents)
|
|
85
|
+
):
|
|
86
|
+
raise WorkerProtocolError("worker request document count is outside bounds")
|
|
87
|
+
total_bytes = 0
|
|
88
|
+
|
|
89
|
+
def document_data(document: SourceDocumentInput) -> dict[str, object]:
|
|
90
|
+
nonlocal total_bytes
|
|
91
|
+
if (
|
|
92
|
+
not isinstance(document.logical_name, str)
|
|
93
|
+
or not document.logical_name
|
|
94
|
+
or _contains_surrogate(document.logical_name)
|
|
95
|
+
or not isinstance(document.text, str)
|
|
96
|
+
or _contains_surrogate(document.text)
|
|
97
|
+
or (
|
|
98
|
+
document.module_id is not None
|
|
99
|
+
and (
|
|
100
|
+
not isinstance(document.module_id, str)
|
|
101
|
+
or _contains_surrogate(document.module_id)
|
|
102
|
+
)
|
|
103
|
+
)
|
|
104
|
+
):
|
|
105
|
+
raise WorkerProtocolError("worker request source document is malformed")
|
|
106
|
+
try:
|
|
107
|
+
total_bytes += len(document.text.encode("utf-8"))
|
|
108
|
+
except UnicodeEncodeError as exc:
|
|
109
|
+
raise WorkerProtocolError(
|
|
110
|
+
"worker request source text is not valid UTF-8"
|
|
111
|
+
) from exc
|
|
112
|
+
return {
|
|
113
|
+
"logical_name": document.logical_name,
|
|
114
|
+
"text": document.text,
|
|
115
|
+
"module_id": document.module_id,
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
primary = document_data(documents[0])
|
|
119
|
+
companions = [document_data(item) for item in documents[1:]]
|
|
120
|
+
if total_bytes > MAX_SOURCE_BYTES:
|
|
121
|
+
raise WorkerProtocolError("worker request source bytes exceed protocol bounds")
|
|
122
|
+
if not isinstance(request.approximation_allowlist, tuple) or any(
|
|
123
|
+
not isinstance(item, str) or _contains_surrogate(item)
|
|
124
|
+
for item in request.approximation_allowlist
|
|
125
|
+
):
|
|
126
|
+
raise WorkerProtocolError("worker request approximation allowlist is malformed")
|
|
127
|
+
string_fields = (
|
|
128
|
+
request.python_version,
|
|
129
|
+
request.target_contract,
|
|
130
|
+
request.semantic_policy,
|
|
131
|
+
request.rule_set_version,
|
|
132
|
+
request.renderer_version,
|
|
133
|
+
request.helper_policy_version,
|
|
134
|
+
request.container_policy_version,
|
|
135
|
+
request.module_policy_version,
|
|
136
|
+
request.record_policy_version,
|
|
137
|
+
request.numeric_policy_version,
|
|
138
|
+
)
|
|
139
|
+
if any(
|
|
140
|
+
not isinstance(item, str) or not item or _contains_surrogate(item)
|
|
141
|
+
for item in string_fields
|
|
142
|
+
):
|
|
143
|
+
raise WorkerProtocolError("worker request configuration is malformed")
|
|
144
|
+
if not isinstance(request.resource_policy, ResourcePolicy):
|
|
145
|
+
raise WorkerProtocolError("worker request resource policy is malformed")
|
|
146
|
+
resource = request.resource_policy.to_dict()
|
|
147
|
+
if set(resource) != _RESOURCE_FIELDS or any(
|
|
148
|
+
not isinstance(item, int) or isinstance(item, bool) or item < 0
|
|
149
|
+
for item in resource.values()
|
|
150
|
+
):
|
|
151
|
+
raise WorkerProtocolError("worker request resource policy is malformed")
|
|
152
|
+
return {
|
|
153
|
+
"source_bundle": {
|
|
154
|
+
"primary": primary,
|
|
155
|
+
"companions": companions,
|
|
156
|
+
},
|
|
157
|
+
"python_version": request.python_version,
|
|
158
|
+
"target_contract": request.target_contract,
|
|
159
|
+
"semantic_policy": request.semantic_policy,
|
|
160
|
+
"approximation_allowlist": list(request.approximation_allowlist),
|
|
161
|
+
"rule_set_version": request.rule_set_version,
|
|
162
|
+
"renderer_version": request.renderer_version,
|
|
163
|
+
"helper_policy_version": request.helper_policy_version,
|
|
164
|
+
"container_policy_version": request.container_policy_version,
|
|
165
|
+
"resource_policy": resource,
|
|
166
|
+
"module_policy_version": request.module_policy_version,
|
|
167
|
+
"record_policy_version": request.record_policy_version,
|
|
168
|
+
"numeric_policy_version": request.numeric_policy_version,
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def _request_from_data(value: object) -> ConversionRequest:
|
|
173
|
+
data = _object(value, "conversion request")
|
|
174
|
+
_exact_fields(data, _REQUEST_FIELDS, "conversion request")
|
|
175
|
+
source_bundle_data = _object(data["source_bundle"], "source bundle")
|
|
176
|
+
_exact_fields(source_bundle_data, {"primary", "companions"}, "source bundle")
|
|
177
|
+
primary = _document_from_data(source_bundle_data["primary"])
|
|
178
|
+
raw_companions = _list(source_bundle_data["companions"], "source companions")
|
|
179
|
+
if len(raw_companions) + 1 > MAX_SOURCE_DOCUMENTS:
|
|
180
|
+
raise WorkerProtocolError("worker request document count is outside bounds")
|
|
181
|
+
companions = tuple(_document_from_data(item) for item in raw_companions)
|
|
182
|
+
allowlist = _string_list(
|
|
183
|
+
data["approximation_allowlist"], "approximation allowlist"
|
|
184
|
+
)
|
|
185
|
+
resource_data = _object(data["resource_policy"], "resource policy")
|
|
186
|
+
_exact_fields(resource_data, _RESOURCE_FIELDS, "resource policy")
|
|
187
|
+
resource_values = {
|
|
188
|
+
key: _nonnegative_integer(value, f"resource policy {key}")
|
|
189
|
+
for key, value in resource_data.items()
|
|
190
|
+
}
|
|
191
|
+
string_values = {}
|
|
192
|
+
for key in (
|
|
193
|
+
"python_version",
|
|
194
|
+
"target_contract",
|
|
195
|
+
"semantic_policy",
|
|
196
|
+
"rule_set_version",
|
|
197
|
+
"renderer_version",
|
|
198
|
+
"helper_policy_version",
|
|
199
|
+
"container_policy_version",
|
|
200
|
+
"module_policy_version",
|
|
201
|
+
"record_policy_version",
|
|
202
|
+
"numeric_policy_version",
|
|
203
|
+
):
|
|
204
|
+
string_values[key] = _nonempty_string(data[key], key)
|
|
205
|
+
request = ConversionRequest(
|
|
206
|
+
SourceBundle(primary, companions),
|
|
207
|
+
python_version=string_values["python_version"],
|
|
208
|
+
target_contract=string_values["target_contract"],
|
|
209
|
+
semantic_policy=string_values["semantic_policy"],
|
|
210
|
+
approximation_allowlist=allowlist,
|
|
211
|
+
rule_set_version=string_values["rule_set_version"],
|
|
212
|
+
renderer_version=string_values["renderer_version"],
|
|
213
|
+
helper_policy_version=string_values["helper_policy_version"],
|
|
214
|
+
container_policy_version=string_values["container_policy_version"],
|
|
215
|
+
resource_policy=ResourcePolicy(**resource_values),
|
|
216
|
+
module_policy_version=string_values["module_policy_version"],
|
|
217
|
+
record_policy_version=string_values["record_policy_version"],
|
|
218
|
+
numeric_policy_version=string_values["numeric_policy_version"],
|
|
219
|
+
)
|
|
220
|
+
# Reuse the encoder's aggregate byte and structural bounds.
|
|
221
|
+
_request_to_data(request)
|
|
222
|
+
return request
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def _document_from_data(value: object) -> SourceDocumentInput:
|
|
226
|
+
data = _object(value, "source document")
|
|
227
|
+
_exact_fields(data, {"logical_name", "text", "module_id"}, "source document")
|
|
228
|
+
logical_name = _nonempty_string(data["logical_name"], "source logical name")
|
|
229
|
+
text = _string(data["text"], "source text")
|
|
230
|
+
module_id = data["module_id"]
|
|
231
|
+
if module_id is not None:
|
|
232
|
+
module_id = _string(module_id, "source module ID")
|
|
233
|
+
return SourceDocumentInput(logical_name, text, module_id)
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def _observation_to_data(observation: ObservationOptions) -> dict[str, object]:
|
|
237
|
+
if (
|
|
238
|
+
not isinstance(observation, ObservationOptions)
|
|
239
|
+
or observation.trace_level not in {"None", "Summary", "Decisions", "Full"}
|
|
240
|
+
or not isinstance(observation.telemetry_enabled, bool)
|
|
241
|
+
):
|
|
242
|
+
raise WorkerProtocolError("worker observation options are malformed")
|
|
243
|
+
return {
|
|
244
|
+
"trace_level": observation.trace_level,
|
|
245
|
+
"telemetry_enabled": observation.telemetry_enabled,
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def _observation_from_data(value: object) -> ObservationOptions:
|
|
250
|
+
data = _object(value, "observation options")
|
|
251
|
+
_exact_fields(
|
|
252
|
+
data, {"trace_level", "telemetry_enabled"}, "observation options"
|
|
253
|
+
)
|
|
254
|
+
trace_level = data["trace_level"]
|
|
255
|
+
telemetry_enabled = data["telemetry_enabled"]
|
|
256
|
+
if trace_level not in {"None", "Summary", "Decisions", "Full"} or not isinstance(
|
|
257
|
+
telemetry_enabled, bool
|
|
258
|
+
):
|
|
259
|
+
raise WorkerProtocolError("worker observation options are malformed")
|
|
260
|
+
return ObservationOptions(trace_level, telemetry_enabled)
|