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,388 @@
|
|
|
1
|
+
"""Frozen support-template registry and exact dependency resolver."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import heapq
|
|
6
|
+
from dataclasses import fields, is_dataclass
|
|
7
|
+
from functools import lru_cache
|
|
8
|
+
from types import MappingProxyType
|
|
9
|
+
from typing import Iterable
|
|
10
|
+
|
|
11
|
+
from pycforge.converter.c_output import CRenderer, validate_c_text
|
|
12
|
+
from pycforge.converter.contracts.configuration import SUPPORTED_TARGET_CONTRACTS
|
|
13
|
+
from pycforge.converter.core.fingerprint import fingerprint
|
|
14
|
+
from pycforge.converter.ir.c_ir import (
|
|
15
|
+
CProvenance,
|
|
16
|
+
CStorage,
|
|
17
|
+
CTranslationUnitBuilder,
|
|
18
|
+
HELPER_SCHEMA_VERSION,
|
|
19
|
+
serialize_translation_unit,
|
|
20
|
+
validate_translation_unit,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
from .factories import builtin_definitions
|
|
24
|
+
from .model import (
|
|
25
|
+
HELPER_INTERFACE_ID,
|
|
26
|
+
HELPER_REGISTRY_VERSION,
|
|
27
|
+
HelperCIRAsset,
|
|
28
|
+
HelperDefinition,
|
|
29
|
+
HelperManifestEntry,
|
|
30
|
+
HelperReference,
|
|
31
|
+
ResolvedHelperPlan,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class HelperRegistryError(ValueError):
|
|
36
|
+
def __init__(self, code: str, message: str) -> None:
|
|
37
|
+
super().__init__(message)
|
|
38
|
+
self.code = code
|
|
39
|
+
self.message = message
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class HelperResolutionCanceled(Exception):
|
|
43
|
+
pass
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class FrozenHelperRegistry:
|
|
47
|
+
"""Immutable project registry; registration order has no semantic effect."""
|
|
48
|
+
|
|
49
|
+
def __init__(
|
|
50
|
+
self,
|
|
51
|
+
definitions: Iterable[HelperDefinition],
|
|
52
|
+
*,
|
|
53
|
+
registry_version: str = HELPER_REGISTRY_VERSION,
|
|
54
|
+
interface_id: str = HELPER_INTERFACE_ID,
|
|
55
|
+
) -> None:
|
|
56
|
+
ordered = tuple(sorted(definitions, key=lambda item: item.reference.canonical))
|
|
57
|
+
references = [item.reference.canonical for item in ordered]
|
|
58
|
+
if len(references) != len(set(references)):
|
|
59
|
+
duplicate = next(item for item in references if references.count(item) > 1)
|
|
60
|
+
raise HelperRegistryError("PYC3307", f"duplicate helper identity: {duplicate}")
|
|
61
|
+
if not registry_version:
|
|
62
|
+
raise HelperRegistryError("PYC3306", "helper registry version is empty")
|
|
63
|
+
self.registry_version = registry_version
|
|
64
|
+
self.interface_id = interface_id
|
|
65
|
+
definitions_by_reference: dict[str, HelperDefinition] = {}
|
|
66
|
+
assets_by_reference: dict[str, HelperCIRAsset] = {}
|
|
67
|
+
entries_by_reference: dict[str, HelperManifestEntry] = {}
|
|
68
|
+
rendered_by_reference: dict[str, str] = {}
|
|
69
|
+
for definition in ordered:
|
|
70
|
+
self._validate_contract(definition)
|
|
71
|
+
try:
|
|
72
|
+
asset = definition.factory()
|
|
73
|
+
except Exception as exc:
|
|
74
|
+
raise HelperRegistryError(
|
|
75
|
+
"PYC3306",
|
|
76
|
+
f"helper factory failed for {definition.reference.canonical}: {type(exc).__name__}",
|
|
77
|
+
) from None
|
|
78
|
+
unit, rendered = self._validate_asset(definition, asset)
|
|
79
|
+
asset_fingerprint = fingerprint(
|
|
80
|
+
"helper-c-ir-asset",
|
|
81
|
+
{
|
|
82
|
+
"contract": definition.contract_dict(),
|
|
83
|
+
"c_ir": serialize_translation_unit(unit),
|
|
84
|
+
},
|
|
85
|
+
).value
|
|
86
|
+
key = definition.reference.canonical
|
|
87
|
+
definitions_by_reference[key] = definition
|
|
88
|
+
assets_by_reference[key] = asset
|
|
89
|
+
entries_by_reference[key] = HelperManifestEntry(definition, asset_fingerprint)
|
|
90
|
+
rendered_by_reference[key] = rendered
|
|
91
|
+
self._definitions = MappingProxyType(definitions_by_reference)
|
|
92
|
+
self._assets = MappingProxyType(assets_by_reference)
|
|
93
|
+
self._entries = MappingProxyType(entries_by_reference)
|
|
94
|
+
self._rendered = MappingProxyType(rendered_by_reference)
|
|
95
|
+
self._manifest = tuple(self._entries[key] for key in sorted(self._entries))
|
|
96
|
+
self._fingerprint = fingerprint(
|
|
97
|
+
"helper-registry-manifest",
|
|
98
|
+
{
|
|
99
|
+
"registry_version": self.registry_version,
|
|
100
|
+
"interface_id": self.interface_id,
|
|
101
|
+
"helpers": [item.to_dict() for item in self._manifest],
|
|
102
|
+
},
|
|
103
|
+
).value
|
|
104
|
+
|
|
105
|
+
@property
|
|
106
|
+
def manifest(self) -> tuple[dict[str, object], ...]:
|
|
107
|
+
return tuple(item.to_dict() for item in self._manifest)
|
|
108
|
+
|
|
109
|
+
@property
|
|
110
|
+
def fingerprint(self) -> str:
|
|
111
|
+
return self._fingerprint
|
|
112
|
+
|
|
113
|
+
def rendered_asset(self, reference: HelperReference | str) -> str:
|
|
114
|
+
parsed = self._reference(reference)
|
|
115
|
+
try:
|
|
116
|
+
return self._rendered[parsed.canonical]
|
|
117
|
+
except KeyError:
|
|
118
|
+
raise HelperRegistryError("PYC3302", f"missing helper: {parsed.canonical}") from None
|
|
119
|
+
|
|
120
|
+
def resolve(
|
|
121
|
+
self,
|
|
122
|
+
requirements: Iterable[HelperReference | str],
|
|
123
|
+
*,
|
|
124
|
+
target_contract: str,
|
|
125
|
+
cancellation: object | None = None,
|
|
126
|
+
) -> ResolvedHelperPlan:
|
|
127
|
+
if target_contract not in SUPPORTED_TARGET_CONTRACTS:
|
|
128
|
+
raise HelperRegistryError(
|
|
129
|
+
"PYC3304",
|
|
130
|
+
f"unknown helper target contract: {target_contract}",
|
|
131
|
+
)
|
|
132
|
+
roots: dict[str, HelperReference] = {}
|
|
133
|
+
for requirement in requirements:
|
|
134
|
+
self._check_cancellation(cancellation)
|
|
135
|
+
reference = self._reference(requirement)
|
|
136
|
+
roots[reference.canonical] = reference
|
|
137
|
+
root_references = tuple(roots[key] for key in sorted(roots))
|
|
138
|
+
|
|
139
|
+
closure: dict[str, HelperDefinition] = {}
|
|
140
|
+
pending = list(roots)
|
|
141
|
+
heapq.heapify(pending)
|
|
142
|
+
while pending:
|
|
143
|
+
self._check_cancellation(cancellation)
|
|
144
|
+
key = heapq.heappop(pending)
|
|
145
|
+
if key in closure:
|
|
146
|
+
continue
|
|
147
|
+
definition = self._definitions.get(key)
|
|
148
|
+
if definition is None:
|
|
149
|
+
raise HelperRegistryError("PYC3302", f"missing helper dependency: {key}")
|
|
150
|
+
if definition.interface_id != self.interface_id:
|
|
151
|
+
raise HelperRegistryError(
|
|
152
|
+
"PYC3305",
|
|
153
|
+
f"helper interface mismatch for {key}: {definition.interface_id}",
|
|
154
|
+
)
|
|
155
|
+
if target_contract not in definition.target_contracts:
|
|
156
|
+
raise HelperRegistryError(
|
|
157
|
+
"PYC3304",
|
|
158
|
+
f"helper {key} is incompatible with target contract {target_contract}",
|
|
159
|
+
)
|
|
160
|
+
closure[key] = definition
|
|
161
|
+
for dependency in definition.dependencies:
|
|
162
|
+
if dependency.canonical not in closure:
|
|
163
|
+
heapq.heappush(pending, dependency.canonical)
|
|
164
|
+
|
|
165
|
+
indegree = {key: 0 for key in closure}
|
|
166
|
+
consumers: dict[str, list[str]] = {key: [] for key in closure}
|
|
167
|
+
for key, definition in closure.items():
|
|
168
|
+
for dependency in definition.dependencies:
|
|
169
|
+
dependency_key = dependency.canonical
|
|
170
|
+
if dependency_key not in closure:
|
|
171
|
+
raise HelperRegistryError(
|
|
172
|
+
"PYC3302",
|
|
173
|
+
f"missing helper dependency: {dependency_key} required by {key}",
|
|
174
|
+
)
|
|
175
|
+
indegree[key] += 1
|
|
176
|
+
consumers[dependency_key].append(key)
|
|
177
|
+
ready = [key for key, count in indegree.items() if count == 0]
|
|
178
|
+
heapq.heapify(ready)
|
|
179
|
+
ordered: list[str] = []
|
|
180
|
+
while ready:
|
|
181
|
+
self._check_cancellation(cancellation)
|
|
182
|
+
key = heapq.heappop(ready)
|
|
183
|
+
ordered.append(key)
|
|
184
|
+
for consumer in sorted(consumers[key]):
|
|
185
|
+
indegree[consumer] -= 1
|
|
186
|
+
if indegree[consumer] == 0:
|
|
187
|
+
heapq.heappush(ready, consumer)
|
|
188
|
+
if len(ordered) != len(closure):
|
|
189
|
+
remaining = {key for key, count in indegree.items() if count > 0}
|
|
190
|
+
cycle = self._cycle_path(remaining, closure)
|
|
191
|
+
raise HelperRegistryError(
|
|
192
|
+
"PYC3303",
|
|
193
|
+
"helper dependency cycle: " + " -> ".join(cycle),
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
assets = tuple(self._assets[key] for key in ordered)
|
|
197
|
+
manifest = tuple(self._entries[key] for key in ordered)
|
|
198
|
+
manifest_dicts = [item.to_dict() for item in manifest]
|
|
199
|
+
manifest_fingerprint = fingerprint(
|
|
200
|
+
"helper-manifest",
|
|
201
|
+
{
|
|
202
|
+
"registry_version": self.registry_version,
|
|
203
|
+
"target_contract": target_contract,
|
|
204
|
+
"requirements": [item.canonical for item in root_references],
|
|
205
|
+
"helpers": manifest_dicts,
|
|
206
|
+
},
|
|
207
|
+
).value
|
|
208
|
+
return ResolvedHelperPlan(
|
|
209
|
+
target_contract,
|
|
210
|
+
self.registry_version,
|
|
211
|
+
root_references,
|
|
212
|
+
assets,
|
|
213
|
+
manifest,
|
|
214
|
+
self.fingerprint,
|
|
215
|
+
manifest_fingerprint,
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
def _validate_contract(self, definition: HelperDefinition) -> None:
|
|
219
|
+
if definition.interface_id != self.interface_id:
|
|
220
|
+
raise HelperRegistryError(
|
|
221
|
+
"PYC3305",
|
|
222
|
+
f"helper interface mismatch for {definition.reference.canonical}: {definition.interface_id}",
|
|
223
|
+
)
|
|
224
|
+
if not definition.target_contracts or tuple(sorted(set(definition.target_contracts))) != definition.target_contracts:
|
|
225
|
+
raise HelperRegistryError("PYC3304", f"helper target contracts must be unique and sorted: {definition.reference.canonical}")
|
|
226
|
+
if any(item not in SUPPORTED_TARGET_CONTRACTS for item in definition.target_contracts):
|
|
227
|
+
raise HelperRegistryError("PYC3304", f"helper declares an unknown target contract: {definition.reference.canonical}")
|
|
228
|
+
dependency_keys = tuple(item.canonical for item in definition.dependencies)
|
|
229
|
+
if dependency_keys != tuple(sorted(set(dependency_keys))):
|
|
230
|
+
raise HelperRegistryError("PYC3306", f"helper dependencies must be exact, unique, and sorted: {definition.reference.canonical}")
|
|
231
|
+
if definition.reference in definition.dependencies:
|
|
232
|
+
raise HelperRegistryError("PYC3303", f"helper directly depends on itself: {definition.reference.canonical}")
|
|
233
|
+
if not definition.semantic_obligations or tuple(sorted(set(definition.semantic_obligations))) != definition.semantic_obligations:
|
|
234
|
+
raise HelperRegistryError("PYC3306", f"helper lacks semantic obligations: {definition.reference.canonical}")
|
|
235
|
+
if not definition.prospective_consumer or not definition.earliest_eligible_phase:
|
|
236
|
+
raise HelperRegistryError("PYC3306", f"helper lacks prospective-consumer gate data: {definition.reference.canonical}")
|
|
237
|
+
if not callable(definition.factory):
|
|
238
|
+
raise HelperRegistryError("PYC3306", f"helper lacks a C IR factory: {definition.reference.canonical}")
|
|
239
|
+
contract_strings = (
|
|
240
|
+
*definition.ownership.to_dict().values(),
|
|
241
|
+
definition.failure.strategy,
|
|
242
|
+
definition.failure.violation_policy,
|
|
243
|
+
definition.failure.runtime_failure_channel,
|
|
244
|
+
definition.cancellation_policy,
|
|
245
|
+
)
|
|
246
|
+
if any(not isinstance(item, str) or not item for item in contract_strings) or not definition.failure.preconditions:
|
|
247
|
+
raise HelperRegistryError("PYC3306", f"helper ownership/failure contract is incomplete: {definition.reference.canonical}")
|
|
248
|
+
|
|
249
|
+
def _validate_asset(
|
|
250
|
+
self,
|
|
251
|
+
definition: HelperDefinition,
|
|
252
|
+
asset: HelperCIRAsset,
|
|
253
|
+
) -> tuple[object, str]:
|
|
254
|
+
reference = definition.reference
|
|
255
|
+
if not isinstance(asset, HelperCIRAsset) or asset.reference != reference:
|
|
256
|
+
raise HelperRegistryError("PYC3306", f"helper factory identity mismatch: {reference.canonical}")
|
|
257
|
+
if asset.prototype.identifier.binding_id != asset.definition.identifier.binding_id:
|
|
258
|
+
raise HelperRegistryError("PYC3306", f"helper prototype/definition binding mismatch: {reference.canonical}")
|
|
259
|
+
if asset.prototype.storage is not CStorage.STATIC or asset.definition.storage is not CStorage.STATIC:
|
|
260
|
+
raise HelperRegistryError("PYC3306", f"helper must use internal static linkage: {reference.canonical}")
|
|
261
|
+
provenance_values = tuple(self._provenance_values(asset))
|
|
262
|
+
if any(
|
|
263
|
+
not isinstance(item, CProvenance)
|
|
264
|
+
or item.origin_kind != "support-template"
|
|
265
|
+
or item.source_document_id is not None
|
|
266
|
+
or item.source_node_ids
|
|
267
|
+
or item.rule_plan_id is not None
|
|
268
|
+
for item in provenance_values
|
|
269
|
+
):
|
|
270
|
+
raise HelperRegistryError("PYC3306", f"helper provenance is not isolated from source provenance: {reference.canonical}")
|
|
271
|
+
primary_unit = None
|
|
272
|
+
primary_rendered = None
|
|
273
|
+
for target_contract in definition.target_contracts:
|
|
274
|
+
builder = CTranslationUnitBuilder(
|
|
275
|
+
target_contract,
|
|
276
|
+
node_id=f"c-helper-tu-{reference.helper_id.replace('.', '-')}-{reference.version.replace('.', '-')}",
|
|
277
|
+
provenance=CProvenance("support-template"),
|
|
278
|
+
schema_version=HELPER_SCHEMA_VERSION,
|
|
279
|
+
)
|
|
280
|
+
for include in asset.includes:
|
|
281
|
+
builder.add_include(include)
|
|
282
|
+
builder.add_declaration(asset.prototype)
|
|
283
|
+
builder.add_declaration(asset.definition)
|
|
284
|
+
unit = builder.build()
|
|
285
|
+
validation = validate_translation_unit(unit)
|
|
286
|
+
if not validation.accepted:
|
|
287
|
+
raise HelperRegistryError(
|
|
288
|
+
"PYC3306",
|
|
289
|
+
f"invalid structured C IR helper {reference.canonical}: {'; '.join(validation.errors)}",
|
|
290
|
+
)
|
|
291
|
+
rendered = CRenderer().render(unit).text
|
|
292
|
+
conformance = validate_c_text(rendered)
|
|
293
|
+
if not conformance.accepted:
|
|
294
|
+
raise HelperRegistryError(
|
|
295
|
+
"PYC3306",
|
|
296
|
+
f"helper text conformance failed for {reference.canonical}: {conformance.message}",
|
|
297
|
+
)
|
|
298
|
+
if primary_unit is None:
|
|
299
|
+
primary_unit = unit
|
|
300
|
+
primary_rendered = rendered
|
|
301
|
+
assert primary_unit is not None and primary_rendered is not None
|
|
302
|
+
return primary_unit, primary_rendered
|
|
303
|
+
|
|
304
|
+
@classmethod
|
|
305
|
+
def _provenance_values(cls, value: object):
|
|
306
|
+
if isinstance(value, CProvenance):
|
|
307
|
+
yield value
|
|
308
|
+
return
|
|
309
|
+
if isinstance(value, tuple):
|
|
310
|
+
for item in value:
|
|
311
|
+
yield from cls._provenance_values(item)
|
|
312
|
+
return
|
|
313
|
+
if is_dataclass(value):
|
|
314
|
+
for field in fields(value):
|
|
315
|
+
yield from cls._provenance_values(getattr(value, field.name))
|
|
316
|
+
|
|
317
|
+
@staticmethod
|
|
318
|
+
def _reference(value: HelperReference | str) -> HelperReference:
|
|
319
|
+
if isinstance(value, HelperReference):
|
|
320
|
+
return value
|
|
321
|
+
try:
|
|
322
|
+
return HelperReference.parse(value)
|
|
323
|
+
except (TypeError, ValueError) as exc:
|
|
324
|
+
raise HelperRegistryError("PYC3301", str(exc)) from None
|
|
325
|
+
|
|
326
|
+
@staticmethod
|
|
327
|
+
def _check_cancellation(cancellation: object | None) -> None:
|
|
328
|
+
if cancellation is not None and bool(getattr(cancellation, "is_canceled", False)):
|
|
329
|
+
raise HelperResolutionCanceled
|
|
330
|
+
|
|
331
|
+
@staticmethod
|
|
332
|
+
def _cycle_path(
|
|
333
|
+
remaining: set[str],
|
|
334
|
+
closure: dict[str, HelperDefinition],
|
|
335
|
+
) -> tuple[str, ...]:
|
|
336
|
+
state: dict[str, int] = {}
|
|
337
|
+
for start in sorted(remaining):
|
|
338
|
+
if state.get(start, 0):
|
|
339
|
+
continue
|
|
340
|
+
path: list[str] = [start]
|
|
341
|
+
positions = {start: 0}
|
|
342
|
+
state[start] = 1
|
|
343
|
+
stack: list[tuple[str, int, tuple[str, ...]]] = [
|
|
344
|
+
(
|
|
345
|
+
start,
|
|
346
|
+
0,
|
|
347
|
+
tuple(
|
|
348
|
+
sorted(
|
|
349
|
+
dependency.canonical
|
|
350
|
+
for dependency in closure[start].dependencies
|
|
351
|
+
if dependency.canonical in remaining
|
|
352
|
+
)
|
|
353
|
+
),
|
|
354
|
+
)
|
|
355
|
+
]
|
|
356
|
+
while stack:
|
|
357
|
+
node, index, dependencies = stack[-1]
|
|
358
|
+
if index >= len(dependencies):
|
|
359
|
+
stack.pop()
|
|
360
|
+
state[node] = 2
|
|
361
|
+
positions.pop(node, None)
|
|
362
|
+
if path and path[-1] == node:
|
|
363
|
+
path.pop()
|
|
364
|
+
continue
|
|
365
|
+
dependency = dependencies[index]
|
|
366
|
+
stack[-1] = (node, index + 1, dependencies)
|
|
367
|
+
dependency_state = state.get(dependency, 0)
|
|
368
|
+
if dependency_state == 1:
|
|
369
|
+
return tuple(path[positions[dependency]:] + [dependency])
|
|
370
|
+
if dependency_state == 2:
|
|
371
|
+
continue
|
|
372
|
+
state[dependency] = 1
|
|
373
|
+
positions[dependency] = len(path)
|
|
374
|
+
path.append(dependency)
|
|
375
|
+
next_dependencies = tuple(
|
|
376
|
+
sorted(
|
|
377
|
+
item.canonical
|
|
378
|
+
for item in closure[dependency].dependencies
|
|
379
|
+
if item.canonical in remaining
|
|
380
|
+
)
|
|
381
|
+
)
|
|
382
|
+
stack.append((dependency, 0, next_dependencies))
|
|
383
|
+
return tuple(sorted(remaining))
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
@lru_cache(maxsize=1)
|
|
387
|
+
def default_helper_registry() -> FrozenHelperRegistry:
|
|
388
|
+
return FrozenHelperRegistry(builtin_definitions())
|
|
File without changes
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from copy import deepcopy
|
|
4
|
+
from threading import Lock
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class TelemetrySink:
|
|
8
|
+
"""Best-effort operational observer isolated from semantic products."""
|
|
9
|
+
|
|
10
|
+
def __init__(self, enabled: bool, limit: int, fail_on_record: bool = False) -> None:
|
|
11
|
+
self.enabled = enabled
|
|
12
|
+
self.limit = limit
|
|
13
|
+
self.fail_on_record = fail_on_record
|
|
14
|
+
self._events: list[dict[str, object]] = []
|
|
15
|
+
self._dropped = 0
|
|
16
|
+
self._failed = False
|
|
17
|
+
self._lock = Lock()
|
|
18
|
+
|
|
19
|
+
def record(self, event: dict[str, object]) -> None:
|
|
20
|
+
if not self.enabled:
|
|
21
|
+
return
|
|
22
|
+
try:
|
|
23
|
+
if self.fail_on_record:
|
|
24
|
+
raise RuntimeError("injected observer failure")
|
|
25
|
+
with self._lock:
|
|
26
|
+
if len(self._events) >= self.limit:
|
|
27
|
+
self._dropped += 1
|
|
28
|
+
else:
|
|
29
|
+
self._events.append(deepcopy(event))
|
|
30
|
+
except Exception:
|
|
31
|
+
self._failed = True
|
|
32
|
+
|
|
33
|
+
def snapshot(self) -> dict[str, object]:
|
|
34
|
+
with self._lock:
|
|
35
|
+
return {
|
|
36
|
+
"events": tuple(deepcopy(self._events)),
|
|
37
|
+
"dropped": self._dropped,
|
|
38
|
+
"observer_failed": self._failed,
|
|
39
|
+
}
|
pycforge/ide/__init__.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from .controller import WorkspaceController
|
|
2
|
+
from .model import WorkspaceDocument, WorkspaceSnapshot, WorkspaceState
|
|
3
|
+
from .qt import QT_AVAILABLE, run
|
|
4
|
+
|
|
5
|
+
WORKSPACE_CONTRACT_VERSION = "pycforge-workspace/0.5"
|
|
6
|
+
ACTION_REGISTRY_VERSION = "pycforge.action-registry/0.2"
|
|
7
|
+
VISUAL_SYSTEM_VERSION = "pycforge.visual-system/0.2"
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"ACTION_REGISTRY_VERSION",
|
|
11
|
+
"VISUAL_SYSTEM_VERSION",
|
|
12
|
+
"WorkspaceController",
|
|
13
|
+
"WorkspaceDocument",
|
|
14
|
+
"WorkspaceSnapshot",
|
|
15
|
+
"WorkspaceState",
|
|
16
|
+
"WORKSPACE_CONTRACT_VERSION",
|
|
17
|
+
"QT_AVAILABLE",
|
|
18
|
+
"run",
|
|
19
|
+
]
|