umf-codegen 0.10.8__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.
- umf_codegen/__init__.py +198 -0
- umf_codegen/__main__.py +47 -0
- umf_codegen/_embedded.py +2 -0
- umf_codegen/_native.py +445 -0
- umf_codegen/_release.py +8 -0
- umf_codegen/_resources/actions.package.json +1 -0
- umf_codegen/_resources/binding.package.json +384 -0
- umf_codegen/_resources/core.constraints.json +6134 -0
- umf_codegen/_resources/core.domain-policy.json +610 -0
- umf_codegen/_resources/core.package-schema.json +1907 -0
- umf_codegen/_resources/core.reference-schema.json +1907 -0
- umf_codegen/_resources/core.schema.json +1908 -0
- umf_codegen/_resources/manifest.json +1 -0
- umf_codegen/_resources/saga.package.json +616 -0
- umf_codegen/_resources/stable-binding.package.json +385 -0
- umf_codegen/_resources/tables.package.json +1031 -0
- umf_codegen/_writer.py +194 -0
- umf_codegen/py.typed +0 -0
- umf_codegen-0.10.8.dist-info/METADATA +105 -0
- umf_codegen-0.10.8.dist-info/RECORD +25 -0
- umf_codegen-0.10.8.dist-info/WHEEL +4 -0
- umf_codegen-0.10.8.dist-info/entry_points.txt +2 -0
- umf_codegen-0.10.8.dist-info/licenses/LICENSE-APACHE +201 -0
- umf_codegen-0.10.8.dist-info/licenses/LICENSE-MIT +21 -0
- umf_codegen-0.10.8.dist-info/licenses/LICENSE.md +239 -0
umf_codegen/__init__.py
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT OR Apache-2.0
|
|
2
|
+
"""Generate the same typed Python client as UMF's portable TypeScript API."""
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Literal, TypedDict, cast
|
|
6
|
+
|
|
7
|
+
from ._native import GenerationError, execute
|
|
8
|
+
|
|
9
|
+
__version__ = "0.10.8"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class RecordSelection(TypedDict):
|
|
13
|
+
module: str
|
|
14
|
+
element: str
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ActionSelection(TypedDict):
|
|
18
|
+
module: str
|
|
19
|
+
action: str
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class GeneratedFile(TypedDict):
|
|
23
|
+
path: str
|
|
24
|
+
text: str
|
|
25
|
+
sha256: str
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class PythonClientResult(TypedDict):
|
|
29
|
+
sourceDigest: str
|
|
30
|
+
description: dict[str, object]
|
|
31
|
+
files: list[GeneratedFile]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class TableDeploymentConfiguration(TypedDict):
|
|
35
|
+
module: str
|
|
36
|
+
schema: str
|
|
37
|
+
ownerRole: str
|
|
38
|
+
personRole: str
|
|
39
|
+
batchRole: str
|
|
40
|
+
groupRoles: dict[str, str]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class TableDeploymentDiagnostic(TypedDict):
|
|
44
|
+
code: str
|
|
45
|
+
path: str
|
|
46
|
+
message: str
|
|
47
|
+
severity: str
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class CompiledTableDeployment(TypedDict):
|
|
51
|
+
status: Literal["compiled"]
|
|
52
|
+
bundle: dict[str, object]
|
|
53
|
+
diagnostics: list[TableDeploymentDiagnostic]
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class BlockedTableDeployment(TypedDict):
|
|
57
|
+
status: Literal["blocked"]
|
|
58
|
+
diagnostics: list[TableDeploymentDiagnostic]
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class TableMigrationPredecessor(TypedDict):
|
|
62
|
+
id: str
|
|
63
|
+
digest: str
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class CompiledTableMigration(TypedDict):
|
|
67
|
+
status: Literal["compiled"]
|
|
68
|
+
plan: dict[str, object]
|
|
69
|
+
diagnostics: list[TableDeploymentDiagnostic]
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class BlockedTableMigration(TypedDict):
|
|
73
|
+
status: Literal["blocked"]
|
|
74
|
+
diagnostics: list[TableDeploymentDiagnostic]
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class ReviewedChangeCandidate(TypedDict):
|
|
78
|
+
id: str
|
|
79
|
+
kind: str
|
|
80
|
+
path: str
|
|
81
|
+
oldDigest: str
|
|
82
|
+
newDigest: str
|
|
83
|
+
oldValue: object
|
|
84
|
+
newValue: object
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class TableMigrationDiscoveryOptions(TypedDict):
|
|
88
|
+
version: Literal["umf.table-migration-discovery/0.2"]
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def compile_table_migration(*, migration_id: str, predecessor: TableMigrationPredecessor | None,
|
|
92
|
+
previous_bundle: dict[str, object] | None, next_bundle: dict[str, object],
|
|
93
|
+
options: dict[str, object] | None = None,
|
|
94
|
+
timeout_seconds: float = 30) -> CompiledTableMigration | BlockedTableMigration:
|
|
95
|
+
"""Compile an inert forward plan; native ledger and approval remain required."""
|
|
96
|
+
return cast(CompiledTableMigration | BlockedTableMigration,
|
|
97
|
+
execute({"id": migration_id, "predecessor": predecessor,
|
|
98
|
+
"previous": previous_bundle, "next": next_bundle,
|
|
99
|
+
**({"options": options} if options is not None else {})},
|
|
100
|
+
timeout_seconds, operation="migration"))
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def verify_table_migration(plan: dict[str, object], *, timeout_seconds: float = 30) -> dict[str, object]:
|
|
104
|
+
"""Recompile a complete plan; consistency cannot authenticate native history."""
|
|
105
|
+
return execute({"plan": plan}, timeout_seconds, operation="verify_migration")
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def discover_table_migration_changes(previous_bundle: dict[str, object], next_bundle: dict[str, object],
|
|
109
|
+
*, options: TableMigrationDiscoveryOptions | None = None,
|
|
110
|
+
timeout_seconds: float = 30) -> list[ReviewedChangeCandidate]:
|
|
111
|
+
"""Discover exact source-bound review candidates from two verified deployment bundles.
|
|
112
|
+
|
|
113
|
+
Returned values explain each delta. Copy only id, kind, path, oldDigest and
|
|
114
|
+
newDigest into `reviewedChanges`; a supported type conversion must also be
|
|
115
|
+
selected explicitly as `pg-cast-v1`.
|
|
116
|
+
Omitting `options`, or passing `None`, retains the historical discovery
|
|
117
|
+
profile. G03 candidates require the explicit discovery 0.2 selector.
|
|
118
|
+
"""
|
|
119
|
+
request = {"previous": previous_bundle, "next": next_bundle}
|
|
120
|
+
if options is not None:
|
|
121
|
+
request["options"] = options
|
|
122
|
+
result = execute(request, timeout_seconds,
|
|
123
|
+
operation="discover_changes")
|
|
124
|
+
changes = result.get("changes")
|
|
125
|
+
if not isinstance(changes, list) or any(not isinstance(item, dict) for item in changes):
|
|
126
|
+
raise GenerationError("PAYLOAD_INVALID", "Embedded compiler returned invalid reviewed changes")
|
|
127
|
+
return cast(list[ReviewedChangeCandidate], changes)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def compile_table_deployment(text: str, *, configuration: TableDeploymentConfiguration,
|
|
131
|
+
format: Literal["json", "yaml"] = "json", timeout_seconds: float = 30
|
|
132
|
+
) -> CompiledTableDeployment | BlockedTableDeployment:
|
|
133
|
+
"""Compile inert initial DDL using UMF's original portable implementation."""
|
|
134
|
+
return cast(CompiledTableDeployment | BlockedTableDeployment,
|
|
135
|
+
execute({"text": text, "format": format, "configuration": configuration},
|
|
136
|
+
timeout_seconds, operation="deployment"))
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def verify_table_deployment(bundle: dict[str, object], *, timeout_seconds: float = 30) -> dict[str, object]:
|
|
140
|
+
"""Recompile a bundle; hosts must additionally verify their trusted digest."""
|
|
141
|
+
return execute({"bundle": bundle}, timeout_seconds, operation="verify_deployment")
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def normalize_row_scope(input: dict[str, object], *, context: dict[str, object],
|
|
145
|
+
timeout_seconds: float = 30) -> dict[str, object]:
|
|
146
|
+
"""Normalize row_scope with exact host bindings and lifecycle/native admission."""
|
|
147
|
+
return execute({"input": input, "context": context}, timeout_seconds, operation="row_scope")
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def generate_python_client(text: str, *, package: str, format: Literal["json", "yaml"] = "json",
|
|
151
|
+
records: list[RecordSelection] | None = None,
|
|
152
|
+
actions: list[ActionSelection] | None = None,
|
|
153
|
+
timeout_seconds: float = 30) -> PythonClientResult:
|
|
154
|
+
request: dict[str, object] = {"text": text, "package": package, "format": format}
|
|
155
|
+
if records is not None:
|
|
156
|
+
request["records"] = records
|
|
157
|
+
if actions is not None:
|
|
158
|
+
request["actions"] = actions
|
|
159
|
+
return cast(PythonClientResult, execute(request, timeout_seconds))
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def generate_pack_tools(request: dict[str, object], *, timeout_seconds: float = 30) -> dict[str, object]:
|
|
163
|
+
"""Compile the complete ``umf.pack-tools/1`` request through pinned Rust.
|
|
164
|
+
|
|
165
|
+
The result's generated, partial, and blocked statuses are ordinary compiler
|
|
166
|
+
outcomes. This wrapper preserves the request and result profiles and adds
|
|
167
|
+
no source parsing or generation behavior in Python.
|
|
168
|
+
"""
|
|
169
|
+
if type(request) is not dict:
|
|
170
|
+
raise TypeError("request must be a dict")
|
|
171
|
+
return execute(request, timeout_seconds, operation="pack_tools")
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
__all__ = [
|
|
175
|
+
"ActionSelection",
|
|
176
|
+
"BlockedTableDeployment",
|
|
177
|
+
"BlockedTableMigration",
|
|
178
|
+
"CompiledTableDeployment",
|
|
179
|
+
"CompiledTableMigration",
|
|
180
|
+
"GeneratedFile",
|
|
181
|
+
"GenerationError",
|
|
182
|
+
"PythonClientResult",
|
|
183
|
+
"RecordSelection",
|
|
184
|
+
"ReviewedChangeCandidate",
|
|
185
|
+
"TableDeploymentConfiguration",
|
|
186
|
+
"TableDeploymentDiagnostic",
|
|
187
|
+
"TableMigrationDiscoveryOptions",
|
|
188
|
+
"TableMigrationPredecessor",
|
|
189
|
+
"__version__",
|
|
190
|
+
"compile_table_deployment",
|
|
191
|
+
"compile_table_migration",
|
|
192
|
+
"discover_table_migration_changes",
|
|
193
|
+
"generate_python_client",
|
|
194
|
+
"generate_pack_tools",
|
|
195
|
+
"normalize_row_scope",
|
|
196
|
+
"verify_table_deployment",
|
|
197
|
+
"verify_table_migration",
|
|
198
|
+
]
|
umf_codegen/__main__.py
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT OR Apache-2.0
|
|
2
|
+
"""Installed generate/check CLI. No executable discovery or external JS process."""
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import json
|
|
7
|
+
import sys
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from . import generate_python_client
|
|
11
|
+
from ._writer import check_package, write_package
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def main(argv: list[str] | None = None) -> int:
|
|
15
|
+
parser = argparse.ArgumentParser(prog="umf-codegen", allow_abbrev=False)
|
|
16
|
+
parser.add_argument("command", choices=("generate", "check"))
|
|
17
|
+
parser.add_argument("--schema", required=True, type=Path)
|
|
18
|
+
parser.add_argument("--package", required=True)
|
|
19
|
+
parser.add_argument("--output", required=True, type=Path)
|
|
20
|
+
argv = sys.argv[1:] if argv is None else argv
|
|
21
|
+
for flag in ("--schema", "--package", "--output"):
|
|
22
|
+
if sum(item.split("=", 1)[0] == flag for item in argv) > 1:
|
|
23
|
+
parser.error("option may only appear once: " + flag)
|
|
24
|
+
args = parser.parse_args(argv)
|
|
25
|
+
try:
|
|
26
|
+
result = generate_python_client(args.schema.read_bytes().decode("utf-8"), package=args.package,
|
|
27
|
+
format="yaml" if args.schema.suffix.lower() in (".yaml", ".yml") else "json")
|
|
28
|
+
if args.command == "check":
|
|
29
|
+
differences = check_package(args.output, result)
|
|
30
|
+
for path in differences:
|
|
31
|
+
print(path, file=sys.stderr)
|
|
32
|
+
return int(bool(differences))
|
|
33
|
+
write_package(args.output, result)
|
|
34
|
+
print(json.dumps({"package": args.package, "sourceDigest": result["sourceDigest"], "files": len(result["files"])}))
|
|
35
|
+
return 0
|
|
36
|
+
except Exception as error: # noqa: BLE001 -- sanitize errors at the CLI process boundary
|
|
37
|
+
response = {"code": getattr(error, "code", "GENERATION_FAILED"), "message": str(error)}
|
|
38
|
+
for name in ("path", "written", "recovery"):
|
|
39
|
+
value = getattr(error, name, None)
|
|
40
|
+
if value is not None:
|
|
41
|
+
response[name] = value
|
|
42
|
+
print(json.dumps(response), file=sys.stderr)
|
|
43
|
+
return 1
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
if __name__ == "__main__":
|
|
47
|
+
sys.exit(main())
|
umf_codegen/_embedded.py
ADDED