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,343 @@
|
|
|
1
|
+
"""Strict plain-data mapping for converter results and progress."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from pycforge.converter.contracts.versions import RESULT_SCHEMA_VERSION
|
|
8
|
+
from pycforge.converter.core.artifact_io import (
|
|
9
|
+
ArtifactCompatibilityError,
|
|
10
|
+
artifact_from_dict,
|
|
11
|
+
artifact_to_dict,
|
|
12
|
+
)
|
|
13
|
+
from pycforge.converter.core.diagnostics import Diagnostic
|
|
14
|
+
from pycforge.converter.core.enums import ResultStatus, Severity
|
|
15
|
+
from pycforge.converter.core.fingerprint import Fingerprint, fingerprint
|
|
16
|
+
from pycforge.converter.core.progress import ConversionProgress
|
|
17
|
+
from pycforge.converter.core.result import ConversionResult
|
|
18
|
+
from pycforge.converter.core.stage_artifact import freeze_value
|
|
19
|
+
|
|
20
|
+
from ._worker_protocol_json import (
|
|
21
|
+
_exact_fields,
|
|
22
|
+
_list,
|
|
23
|
+
_nonempty_string,
|
|
24
|
+
_nonnegative_integer,
|
|
25
|
+
_object,
|
|
26
|
+
_optional_object,
|
|
27
|
+
_string,
|
|
28
|
+
_string_list,
|
|
29
|
+
)
|
|
30
|
+
from ._worker_protocol_types import MAX_DIAGNOSTICS, WorkerProtocolError
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
_FINGERPRINT_FIELDS = frozenset(
|
|
34
|
+
{"domain", "schema_version", "canonicalization_version", "algorithm", "value"}
|
|
35
|
+
)
|
|
36
|
+
_DIAGNOSTIC_FIELDS = frozenset(
|
|
37
|
+
{
|
|
38
|
+
"diagnostic_id",
|
|
39
|
+
"code",
|
|
40
|
+
"severity",
|
|
41
|
+
"stage",
|
|
42
|
+
"message",
|
|
43
|
+
"effect_on_status",
|
|
44
|
+
"source_span",
|
|
45
|
+
"related_spans",
|
|
46
|
+
"causal_diagnostic_id",
|
|
47
|
+
"target_contract",
|
|
48
|
+
"semantic_policy",
|
|
49
|
+
"rule_id",
|
|
50
|
+
"fact_references",
|
|
51
|
+
"obligation_references",
|
|
52
|
+
"explanation",
|
|
53
|
+
"remediation",
|
|
54
|
+
"approximation_code",
|
|
55
|
+
"semantic_delta",
|
|
56
|
+
"source_module_id",
|
|
57
|
+
"source_logical_name",
|
|
58
|
+
}
|
|
59
|
+
)
|
|
60
|
+
_RESULT_FIELDS = frozenset(
|
|
61
|
+
{
|
|
62
|
+
"schema_version",
|
|
63
|
+
"status",
|
|
64
|
+
"generated_c",
|
|
65
|
+
"diagnostics",
|
|
66
|
+
"request_fingerprint",
|
|
67
|
+
"resource_fingerprint",
|
|
68
|
+
"output_fingerprint",
|
|
69
|
+
"last_completed_stage",
|
|
70
|
+
"stage_order",
|
|
71
|
+
"decision_trace",
|
|
72
|
+
"telemetry",
|
|
73
|
+
"stage_artifact",
|
|
74
|
+
"conversion_summary",
|
|
75
|
+
}
|
|
76
|
+
)
|
|
77
|
+
_PUBLISHABLE_STATUSES = frozenset(
|
|
78
|
+
{
|
|
79
|
+
ResultStatus.CONVERTED,
|
|
80
|
+
ResultStatus.CONVERTED_WITH_WARNINGS,
|
|
81
|
+
ResultStatus.CONVERTED_WITH_APPROXIMATIONS,
|
|
82
|
+
}
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _result_to_data(result: ConversionResult) -> dict[str, object]:
|
|
87
|
+
return {
|
|
88
|
+
"schema_version": RESULT_SCHEMA_VERSION,
|
|
89
|
+
"status": result.status.value,
|
|
90
|
+
"generated_c": result.generated_c,
|
|
91
|
+
"diagnostics": [item.to_dict() for item in result.diagnostics],
|
|
92
|
+
"request_fingerprint": (
|
|
93
|
+
None
|
|
94
|
+
if result.request_fingerprint is None
|
|
95
|
+
else result.request_fingerprint.to_dict()
|
|
96
|
+
),
|
|
97
|
+
"resource_fingerprint": (
|
|
98
|
+
None
|
|
99
|
+
if result.resource_fingerprint is None
|
|
100
|
+
else result.resource_fingerprint.to_dict()
|
|
101
|
+
),
|
|
102
|
+
"output_fingerprint": (
|
|
103
|
+
None
|
|
104
|
+
if result.output_fingerprint is None
|
|
105
|
+
else result.output_fingerprint.to_dict()
|
|
106
|
+
),
|
|
107
|
+
"last_completed_stage": result.last_completed_stage,
|
|
108
|
+
"stage_order": list(result.stage_order),
|
|
109
|
+
"decision_trace": result.decision_trace,
|
|
110
|
+
"telemetry": result.telemetry,
|
|
111
|
+
"stage_artifact": (
|
|
112
|
+
None if result.stage_artifact is None else artifact_to_dict(result.stage_artifact)
|
|
113
|
+
),
|
|
114
|
+
"conversion_summary": result.conversion_summary,
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _result_from_data(value: object) -> ConversionResult:
|
|
119
|
+
data = _object(value, "conversion result")
|
|
120
|
+
_exact_fields(data, _RESULT_FIELDS, "conversion result")
|
|
121
|
+
if data["schema_version"] != RESULT_SCHEMA_VERSION:
|
|
122
|
+
raise WorkerProtocolError("incompatible conversion result schema")
|
|
123
|
+
try:
|
|
124
|
+
status = ResultStatus(data["status"])
|
|
125
|
+
except (TypeError, ValueError) as exc:
|
|
126
|
+
raise WorkerProtocolError("unknown conversion result status") from exc
|
|
127
|
+
generated_c = data["generated_c"]
|
|
128
|
+
if generated_c is not None:
|
|
129
|
+
generated_c = _string(generated_c, "generated C")
|
|
130
|
+
raw_diagnostics = _list(data["diagnostics"], "conversion diagnostics")
|
|
131
|
+
if len(raw_diagnostics) > MAX_DIAGNOSTICS:
|
|
132
|
+
raise WorkerProtocolError("conversion diagnostic count exceeds protocol bounds")
|
|
133
|
+
diagnostics = tuple(_diagnostic_from_data(item) for item in raw_diagnostics)
|
|
134
|
+
request_fingerprint = _fingerprint_from_data(
|
|
135
|
+
data["request_fingerprint"], "request", "conversion-request", False
|
|
136
|
+
)
|
|
137
|
+
resource_fingerprint = _fingerprint_from_data(
|
|
138
|
+
data["resource_fingerprint"], "resource", "resource-policy", False
|
|
139
|
+
)
|
|
140
|
+
output_fingerprint = _fingerprint_from_data(
|
|
141
|
+
data["output_fingerprint"], "output", "generated-output", False
|
|
142
|
+
)
|
|
143
|
+
last_completed_stage = data["last_completed_stage"]
|
|
144
|
+
if last_completed_stage is not None:
|
|
145
|
+
last_completed_stage = _nonempty_string(
|
|
146
|
+
last_completed_stage, "last completed stage"
|
|
147
|
+
)
|
|
148
|
+
stage_order = _string_list(data["stage_order"], "stage order")
|
|
149
|
+
decision_trace = _optional_object(data["decision_trace"], "decision trace")
|
|
150
|
+
telemetry = _optional_object(data["telemetry"], "telemetry")
|
|
151
|
+
conversion_summary = _optional_object(
|
|
152
|
+
data["conversion_summary"], "conversion summary"
|
|
153
|
+
)
|
|
154
|
+
artifact_data = data["stage_artifact"]
|
|
155
|
+
if artifact_data is None:
|
|
156
|
+
artifact = None
|
|
157
|
+
else:
|
|
158
|
+
try:
|
|
159
|
+
artifact = artifact_from_dict(
|
|
160
|
+
_object(artifact_data, "stage artifact")
|
|
161
|
+
)
|
|
162
|
+
except ArtifactCompatibilityError as exc:
|
|
163
|
+
raise WorkerProtocolError("conversion stage artifact is invalid") from exc
|
|
164
|
+
|
|
165
|
+
if generated_c is None:
|
|
166
|
+
if output_fingerprint is not None:
|
|
167
|
+
raise WorkerProtocolError(
|
|
168
|
+
"unpublished conversion carries an output fingerprint"
|
|
169
|
+
)
|
|
170
|
+
if status in _PUBLISHABLE_STATUSES:
|
|
171
|
+
raise WorkerProtocolError(
|
|
172
|
+
"publishable conversion result lacks generated C"
|
|
173
|
+
)
|
|
174
|
+
else:
|
|
175
|
+
expected_output = fingerprint("generated-output", generated_c)
|
|
176
|
+
if output_fingerprint != expected_output:
|
|
177
|
+
raise WorkerProtocolError("conversion output fingerprint mismatch")
|
|
178
|
+
if status not in _PUBLISHABLE_STATUSES:
|
|
179
|
+
raise WorkerProtocolError(
|
|
180
|
+
"non-publishable conversion carries generated C"
|
|
181
|
+
)
|
|
182
|
+
if artifact is not None:
|
|
183
|
+
artifact_order = artifact.payload.get("stage_order", ())
|
|
184
|
+
if tuple(artifact_order) != stage_order:
|
|
185
|
+
raise WorkerProtocolError("conversion stage order mismatch")
|
|
186
|
+
if (
|
|
187
|
+
request_fingerprint is None
|
|
188
|
+
or artifact.conversion_id != request_fingerprint.value
|
|
189
|
+
):
|
|
190
|
+
raise WorkerProtocolError("conversion artifact request identity mismatch")
|
|
191
|
+
artifact_c = artifact.payload.get("generated_c")
|
|
192
|
+
if generated_c is not None and artifact_c != generated_c:
|
|
193
|
+
raise WorkerProtocolError("conversion artifact output mismatch")
|
|
194
|
+
elif stage_order:
|
|
195
|
+
raise WorkerProtocolError("conversion without artifact carries stage order")
|
|
196
|
+
|
|
197
|
+
return ConversionResult(
|
|
198
|
+
status,
|
|
199
|
+
generated_c,
|
|
200
|
+
diagnostics,
|
|
201
|
+
request_fingerprint,
|
|
202
|
+
resource_fingerprint,
|
|
203
|
+
output_fingerprint,
|
|
204
|
+
last_completed_stage,
|
|
205
|
+
stage_order,
|
|
206
|
+
freeze_value(decision_trace),
|
|
207
|
+
freeze_value(telemetry),
|
|
208
|
+
artifact,
|
|
209
|
+
freeze_value(conversion_summary),
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def _diagnostic_from_data(value: object) -> Diagnostic:
|
|
214
|
+
data = _object(value, "diagnostic")
|
|
215
|
+
_exact_fields(data, _DIAGNOSTIC_FIELDS, "diagnostic")
|
|
216
|
+
try:
|
|
217
|
+
severity = Severity(data["severity"])
|
|
218
|
+
except (TypeError, ValueError) as exc:
|
|
219
|
+
raise WorkerProtocolError("diagnostic severity is invalid") from exc
|
|
220
|
+
for key in ("code", "stage", "message"):
|
|
221
|
+
_nonempty_string(data[key], f"diagnostic {key}")
|
|
222
|
+
if not isinstance(data["effect_on_status"], bool):
|
|
223
|
+
raise WorkerProtocolError("diagnostic effect_on_status must be Boolean")
|
|
224
|
+
source_span = data["source_span"]
|
|
225
|
+
if source_span is not None:
|
|
226
|
+
source_span = _object(source_span, "diagnostic source span")
|
|
227
|
+
raw_related = _list(data["related_spans"], "diagnostic related spans")
|
|
228
|
+
related = tuple(_object(item, "diagnostic related span") for item in raw_related)
|
|
229
|
+
optional_strings = {}
|
|
230
|
+
for key in (
|
|
231
|
+
"causal_diagnostic_id",
|
|
232
|
+
"target_contract",
|
|
233
|
+
"semantic_policy",
|
|
234
|
+
"rule_id",
|
|
235
|
+
"explanation",
|
|
236
|
+
"remediation",
|
|
237
|
+
"approximation_code",
|
|
238
|
+
"semantic_delta",
|
|
239
|
+
"source_module_id",
|
|
240
|
+
"source_logical_name",
|
|
241
|
+
):
|
|
242
|
+
raw = data[key]
|
|
243
|
+
optional_strings[key] = (
|
|
244
|
+
None if raw is None else _string(raw, f"diagnostic {key}")
|
|
245
|
+
)
|
|
246
|
+
diagnostic = Diagnostic(
|
|
247
|
+
code=data["code"],
|
|
248
|
+
severity=severity,
|
|
249
|
+
stage=data["stage"],
|
|
250
|
+
message=data["message"],
|
|
251
|
+
effect_on_status=data["effect_on_status"],
|
|
252
|
+
source_span=source_span,
|
|
253
|
+
related_spans=related,
|
|
254
|
+
causal_diagnostic_id=optional_strings["causal_diagnostic_id"],
|
|
255
|
+
target_contract=optional_strings["target_contract"],
|
|
256
|
+
semantic_policy=optional_strings["semantic_policy"],
|
|
257
|
+
rule_id=optional_strings["rule_id"],
|
|
258
|
+
fact_references=_string_list(
|
|
259
|
+
data["fact_references"], "diagnostic fact references"
|
|
260
|
+
),
|
|
261
|
+
obligation_references=_string_list(
|
|
262
|
+
data["obligation_references"], "diagnostic obligation references"
|
|
263
|
+
),
|
|
264
|
+
explanation=optional_strings["explanation"],
|
|
265
|
+
remediation=optional_strings["remediation"],
|
|
266
|
+
approximation_code=optional_strings["approximation_code"],
|
|
267
|
+
semantic_delta=optional_strings["semantic_delta"],
|
|
268
|
+
source_module_id=optional_strings["source_module_id"],
|
|
269
|
+
source_logical_name=optional_strings["source_logical_name"],
|
|
270
|
+
)
|
|
271
|
+
supplied_id = _nonempty_string(data["diagnostic_id"], "diagnostic ID")
|
|
272
|
+
if supplied_id != diagnostic.diagnostic_id:
|
|
273
|
+
raise WorkerProtocolError("diagnostic identity mismatch")
|
|
274
|
+
return diagnostic
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def _fingerprint_from_data(
|
|
278
|
+
value: object,
|
|
279
|
+
role: str,
|
|
280
|
+
domain: str,
|
|
281
|
+
required: bool,
|
|
282
|
+
) -> Fingerprint | None:
|
|
283
|
+
if value is None:
|
|
284
|
+
if required:
|
|
285
|
+
raise WorkerProtocolError(f"missing {role} fingerprint")
|
|
286
|
+
return None
|
|
287
|
+
data = _object(value, f"{role} fingerprint")
|
|
288
|
+
_exact_fields(data, _FINGERPRINT_FIELDS, f"{role} fingerprint")
|
|
289
|
+
if not all(isinstance(item, str) for item in data.values()):
|
|
290
|
+
raise WorkerProtocolError(f"{role} fingerprint fields must be strings")
|
|
291
|
+
result = Fingerprint(**data)
|
|
292
|
+
if (
|
|
293
|
+
result.domain != domain
|
|
294
|
+
or result.schema_version != "0.1"
|
|
295
|
+
or result.canonicalization_version != "canonical-json-v1"
|
|
296
|
+
or result.algorithm != "sha256"
|
|
297
|
+
or len(result.value) != 64
|
|
298
|
+
or any(character not in "0123456789abcdef" for character in result.value)
|
|
299
|
+
):
|
|
300
|
+
raise WorkerProtocolError(f"{role} fingerprint metadata is incompatible")
|
|
301
|
+
return result
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
def _progress_from_data(value: object) -> ConversionProgress:
|
|
305
|
+
data = _object(value, "conversion progress")
|
|
306
|
+
_exact_fields(
|
|
307
|
+
data,
|
|
308
|
+
{"state", "stage_id", "completed_stages", "total_stages"},
|
|
309
|
+
"conversion progress",
|
|
310
|
+
)
|
|
311
|
+
state = _nonempty_string(data["state"], "conversion progress state")
|
|
312
|
+
stage_id = data["stage_id"]
|
|
313
|
+
if stage_id is not None:
|
|
314
|
+
stage_id = _nonempty_string(stage_id, "conversion progress stage")
|
|
315
|
+
completed = _nonnegative_integer(
|
|
316
|
+
data["completed_stages"], "completed conversion stages"
|
|
317
|
+
)
|
|
318
|
+
total = _nonnegative_integer(data["total_stages"], "total conversion stages")
|
|
319
|
+
try:
|
|
320
|
+
return ConversionProgress(state, stage_id, completed, total)
|
|
321
|
+
except ValueError as exc:
|
|
322
|
+
raise WorkerProtocolError("conversion progress is inconsistent") from exc
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
def _mappings_from_data(value: object) -> tuple[dict[str, Any], ...]:
|
|
326
|
+
items = _list(value, "source/output mappings")
|
|
327
|
+
return tuple(
|
|
328
|
+
freeze_value(_object(item, "source/output mapping")) for item in items
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
def _validate_result_mappings(
|
|
333
|
+
result: ConversionResult,
|
|
334
|
+
mappings: tuple[dict[str, Any], ...],
|
|
335
|
+
) -> None:
|
|
336
|
+
artifact = result.stage_artifact
|
|
337
|
+
if artifact is None:
|
|
338
|
+
if mappings:
|
|
339
|
+
raise WorkerProtocolError("conversion without artifact carries mappings")
|
|
340
|
+
return
|
|
341
|
+
raw = artifact.payload.get("source_output_mappings", ())
|
|
342
|
+
if not isinstance(raw, (tuple, list)) or tuple(raw) != mappings:
|
|
343
|
+
raise WorkerProtocolError("terminal source/output mappings mismatch")
|