morphir-python 0.0.1__py3-none-any.whl → 0.0.3__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.
- morphir_python-0.0.3.dist-info/METADATA +98 -0
- morphir_python-0.0.3.dist-info/RECORD +14 -0
- morphir_tools/__init__.py +2 -2
- morphir_tools/_output.py +114 -0
- morphir_tools/cli.py +176 -2
- morphir_tools/python/__init__.py +71 -0
- morphir_tools/python/_api.py +215 -0
- morphir_tools/python/_bridge.py +84 -0
- morphir_tools/python/_types.py +201 -0
- morphir_tools/python/diagnostics.py +33 -0
- morphir/__init__.py +0 -14
- morphir/ir/__init__.py +0 -110
- morphir/ir/access_controlled.py +0 -15
- morphir/ir/decorations.py +0 -19
- morphir/ir/diagnostics.py +0 -105
- morphir/ir/distribution.py +0 -21
- morphir/ir/document.py +0 -37
- morphir/ir/documented.py +0 -11
- morphir/ir/format_version.py +0 -483
- morphir/ir/fqname.py +0 -9
- morphir/ir/json.py +0 -11
- morphir/ir/literal.py +0 -25
- morphir/ir/meta.py +0 -11
- morphir/ir/migrate.py +0 -1267
- morphir/ir/module.py +0 -17
- morphir/ir/name.py +0 -27
- morphir/ir/package.py +0 -15
- morphir/ir/path.py +0 -25
- morphir/ir/qname.py +0 -9
- morphir/ir/ref.py +0 -17
- morphir/ir/type.py +0 -37
- morphir/ir/type_constraints.py +0 -33
- morphir/ir/type_def.py +0 -19
- morphir/ir/type_spec.py +0 -25
- morphir/ir/v3/__init__.py +0 -64
- morphir/ir/v3/_legacy_value.py +0 -201
- morphir/ir/v3/access_controlled.py +0 -31
- morphir/ir/v3/codec.py +0 -1275
- morphir/ir/v3/codec_v1.py +0 -491
- morphir/ir/v3/codec_v2.py +0 -187
- morphir/ir/v3/distribution.py +0 -42
- morphir/ir/v3/documented.py +0 -13
- morphir/ir/v3/fqname.py +0 -30
- morphir/ir/v3/literal.py +0 -46
- morphir/ir/v3/module.py +0 -32
- morphir/ir/v3/name.py +0 -42
- morphir/ir/v3/package.py +0 -23
- morphir/ir/v3/path.py +0 -29
- morphir/ir/v3/qname.py +0 -16
- morphir/ir/v3/type.py +0 -126
- morphir/ir/v3/value.py +0 -235
- morphir/ir/v4/__init__.py +0 -160
- morphir/ir/v4/access_controlled.py +0 -17
- morphir/ir/v4/annotation.py +0 -75
- morphir/ir/v4/codec.py +0 -2161
- morphir/ir/v4/decorations.py +0 -88
- morphir/ir/v4/distribution.py +0 -56
- morphir/ir/v4/document.py +0 -69
- morphir/ir/v4/documented.py +0 -10
- morphir/ir/v4/fqname.py +0 -69
- morphir/ir/v4/incompleteness.py +0 -78
- morphir/ir/v4/json.py +0 -103
- morphir/ir/v4/literal.py +0 -51
- morphir/ir/v4/meta.py +0 -27
- morphir/ir/v4/module.py +0 -30
- morphir/ir/v4/name.py +0 -69
- morphir/ir/v4/package.py +0 -19
- morphir/ir/v4/path.py +0 -50
- morphir/ir/v4/qname.py +0 -34
- morphir/ir/v4/ref.py +0 -35
- morphir/ir/v4/type.py +0 -140
- morphir/ir/v4/type_constraints.py +0 -91
- morphir/ir/v4/type_def.py +0 -38
- morphir/ir/v4/type_spec.py +0 -64
- morphir/ir/v4/value.py +0 -480
- morphir/ir/value.py +0 -81
- morphir/py.typed +0 -0
- morphir/sdk/__init__.py +0 -110
- morphir/sdk/_compare.py +0 -187
- morphir/sdk/aggregate.py +0 -421
- morphir/sdk/basics.py +0 -504
- morphir/sdk/char.py +0 -110
- morphir/sdk/decimal.py +0 -311
- morphir/sdk/dict.py +0 -336
- morphir/sdk/instant.py +0 -118
- morphir/sdk/int.py +0 -86
- morphir/sdk/key.py +0 -522
- morphir/sdk/list.py +0 -356
- morphir/sdk/local_date.py +0 -436
- morphir/sdk/local_time.py +0 -267
- morphir/sdk/maybe.py +0 -135
- morphir/sdk/number.py +0 -237
- morphir/sdk/regex.py +0 -287
- morphir/sdk/result.py +0 -162
- morphir/sdk/result_list.py +0 -169
- morphir/sdk/rule.py +0 -103
- morphir/sdk/set.py +0 -214
- morphir/sdk/stateful_app.py +0 -49
- morphir/sdk/string.py +0 -427
- morphir/sdk/tuple.py +0 -43
- morphir/sdk/uuid.py +0 -209
- morphir_python-0.0.1.dist-info/METADATA +0 -269
- morphir_python-0.0.1.dist-info/RECORD +0 -99
- {morphir_python-0.0.1.dist-info → morphir_python-0.0.3.dist-info}/WHEEL +0 -0
- {morphir_python-0.0.1.dist-info → morphir_python-0.0.3.dist-info}/entry_points.txt +0 -0
- {morphir_python-0.0.1.dist-info → morphir_python-0.0.3.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"""Compile Python sources to Morphir IR and generate Python from IR."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from typing import TYPE_CHECKING, cast
|
|
7
|
+
|
|
8
|
+
from morphir.ir import format_version as fv
|
|
9
|
+
from morphir.ir.diagnostics import DiagnosticError
|
|
10
|
+
from morphir.ir.v3 import codec as v3_codec
|
|
11
|
+
from morphir.ir.v3 import distribution as v3_dist
|
|
12
|
+
from morphir.ir.v4 import codec as v4_codec
|
|
13
|
+
from morphir.ir.v4 import distribution as v4_dist
|
|
14
|
+
|
|
15
|
+
from . import _bridge
|
|
16
|
+
from . import diagnostics as codes
|
|
17
|
+
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
from collections.abc import Callable, Iterable, Mapping, Sequence
|
|
20
|
+
|
|
21
|
+
from ._types import (
|
|
22
|
+
CompiledV3,
|
|
23
|
+
CompiledV4,
|
|
24
|
+
CompileFailure,
|
|
25
|
+
CompilerDiagnostic,
|
|
26
|
+
CompileResult,
|
|
27
|
+
Generated,
|
|
28
|
+
GeneratedArtifact,
|
|
29
|
+
GenerateResult,
|
|
30
|
+
GenerationFailure,
|
|
31
|
+
IrVersion,
|
|
32
|
+
LibraryModel,
|
|
33
|
+
NativeCompilerError,
|
|
34
|
+
NativeCompilerInfo,
|
|
35
|
+
Severity,
|
|
36
|
+
SourceDocument,
|
|
37
|
+
SourceLocation,
|
|
38
|
+
SourcePosition,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def native_compiler() -> NativeCompilerInfo:
|
|
43
|
+
"""Load the native compiler and report its identity.
|
|
44
|
+
|
|
45
|
+
Raises:
|
|
46
|
+
NativeCompilerUnavailable: When it is missing or incompatible.
|
|
47
|
+
"""
|
|
48
|
+
return _bridge.load()
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def compile_sources(
|
|
52
|
+
sources: Iterable[SourceDocument],
|
|
53
|
+
*,
|
|
54
|
+
package: str,
|
|
55
|
+
exposed_modules: Sequence[str] | None = None,
|
|
56
|
+
ir_version: IrVersion = IrVersion.V3,
|
|
57
|
+
) -> CompileResult:
|
|
58
|
+
"""Compile Python source documents to a Morphir IR Library.
|
|
59
|
+
|
|
60
|
+
The sources are parsed statically: nothing is imported or executed. The
|
|
61
|
+
order of ``sources`` does not affect the result.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
sources: The documents of one package, with paths relative to its root.
|
|
65
|
+
package: The Morphir package name, such as ``"acme/example"``.
|
|
66
|
+
exposed_modules: Module paths to make public, such as
|
|
67
|
+
``("acme/models",)``. ``None`` exposes every module; an empty
|
|
68
|
+
sequence exposes none.
|
|
69
|
+
ir_version: The IR version to write. Defaults to stable v3.
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
A `CompiledV3` or `CompiledV4` holding the decoded Library, or a
|
|
73
|
+
`CompileFailure` with at least one diagnostic and no model.
|
|
74
|
+
|
|
75
|
+
Raises:
|
|
76
|
+
ValueError: When ``package`` is empty or two documents share a path.
|
|
77
|
+
NativeCompilerUnavailable: When the native compiler is missing or
|
|
78
|
+
incompatible.
|
|
79
|
+
NativeCompilerError: When the native compiler fails internally.
|
|
80
|
+
"""
|
|
81
|
+
documents = sorted(sources, key=lambda document: document.path)
|
|
82
|
+
if not package:
|
|
83
|
+
raise ValueError("package must name a Morphir package")
|
|
84
|
+
paths = [document.path for document in documents]
|
|
85
|
+
if len(set(paths)) != len(paths):
|
|
86
|
+
raise ValueError("source documents contain a duplicate path")
|
|
87
|
+
request = {
|
|
88
|
+
"package": package,
|
|
89
|
+
"sources": [{"path": d.path, "text": d.text} for d in documents],
|
|
90
|
+
"exposedModules": None if exposed_modules is None else list(exposed_modules),
|
|
91
|
+
"irVersion": ir_version.value,
|
|
92
|
+
}
|
|
93
|
+
output = _call(_bridge.module().compile, json.dumps(request))
|
|
94
|
+
diagnostics = _diagnostics(output)
|
|
95
|
+
ir = output.get("ir")
|
|
96
|
+
if not output.get("success") or not isinstance(ir, str):
|
|
97
|
+
return CompileFailure(diagnostics or (_missing_diagnostic(),))
|
|
98
|
+
try:
|
|
99
|
+
if ir_version is IrVersion.V3:
|
|
100
|
+
classic = v3_codec.decode_versioned_distribution(ir)
|
|
101
|
+
if isinstance(classic, v3_dist.Library):
|
|
102
|
+
return CompiledV3(classic, diagnostics)
|
|
103
|
+
else:
|
|
104
|
+
draft = v4_codec.decode_versioned_distribution(ir)
|
|
105
|
+
if isinstance(draft, v4_dist.LibraryDistribution):
|
|
106
|
+
return CompiledV4(draft, diagnostics)
|
|
107
|
+
except DiagnosticError as error:
|
|
108
|
+
return CompileFailure(_ir_diagnostics(error))
|
|
109
|
+
raise NativeCompilerError("the native compiler returned a non-Library model")
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def generate_python(distribution: LibraryModel | object) -> GenerateResult:
|
|
113
|
+
"""Generate Python modules from a v3 or draft v4 Library model.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
distribution: A `morphir.ir.v3.distribution.Library` or
|
|
117
|
+
`morphir.ir.v4.distribution.LibraryDistribution`. Specs and
|
|
118
|
+
Application distributions are rejected with a diagnostic.
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
`Generated` with one artifact per module, in path order, or a
|
|
122
|
+
`GenerationFailure` with at least one diagnostic and no artifacts.
|
|
123
|
+
|
|
124
|
+
Raises:
|
|
125
|
+
NativeCompilerUnavailable: When the native compiler is missing or
|
|
126
|
+
incompatible.
|
|
127
|
+
NativeCompilerError: When the native compiler fails internally.
|
|
128
|
+
"""
|
|
129
|
+
try:
|
|
130
|
+
match distribution:
|
|
131
|
+
case v3_dist.Library():
|
|
132
|
+
document = v3_codec.encode_versioned_distribution(distribution)
|
|
133
|
+
case v4_dist.LibraryDistribution():
|
|
134
|
+
document = v4_codec.encode_versioned_distribution(distribution)
|
|
135
|
+
case _:
|
|
136
|
+
kind = type(distribution).__name__
|
|
137
|
+
return GenerationFailure(
|
|
138
|
+
(
|
|
139
|
+
CompilerDiagnostic(
|
|
140
|
+
codes.UNSUPPORTED_DISTRIBUTION,
|
|
141
|
+
Severity.ERROR,
|
|
142
|
+
"Python generation needs a Library distribution, "
|
|
143
|
+
f"not {kind}",
|
|
144
|
+
),
|
|
145
|
+
)
|
|
146
|
+
)
|
|
147
|
+
except DiagnosticError as error:
|
|
148
|
+
return GenerationFailure(_ir_diagnostics(error))
|
|
149
|
+
output = _call(_bridge.module().generate, fv.dump_json(document))
|
|
150
|
+
diagnostics = _diagnostics(output)
|
|
151
|
+
raw = output.get("artifacts")
|
|
152
|
+
if not output.get("success") or not isinstance(raw, list):
|
|
153
|
+
return GenerationFailure(diagnostics or (_missing_diagnostic(),))
|
|
154
|
+
artifacts = tuple(
|
|
155
|
+
sorted(
|
|
156
|
+
(
|
|
157
|
+
GeneratedArtifact(str(item["path"]), str(item["content"]))
|
|
158
|
+
for item in cast("list[Mapping[str, object]]", raw)
|
|
159
|
+
),
|
|
160
|
+
key=lambda artifact: artifact.path,
|
|
161
|
+
)
|
|
162
|
+
)
|
|
163
|
+
return Generated(artifacts, diagnostics)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def _call(function: Callable[[str], str], argument: str) -> Mapping[str, object]:
|
|
167
|
+
output = cast("Mapping[str, object]", json.loads(function(argument)))
|
|
168
|
+
internal = output.get("internalError")
|
|
169
|
+
if internal is not None:
|
|
170
|
+
raise NativeCompilerError(str(internal))
|
|
171
|
+
return output
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def _missing_diagnostic() -> CompilerDiagnostic:
|
|
175
|
+
return CompilerDiagnostic(
|
|
176
|
+
codes.PY_SERIALIZATION,
|
|
177
|
+
Severity.ERROR,
|
|
178
|
+
"the native compiler failed without a diagnostic",
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def _diagnostics(output: Mapping[str, object]) -> tuple[CompilerDiagnostic, ...]:
|
|
183
|
+
items = cast("list[Mapping[str, object]]", output.get("diagnostics") or [])
|
|
184
|
+
return tuple(_diagnostic(item) for item in items)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def _diagnostic(item: Mapping[str, object]) -> CompilerDiagnostic:
|
|
188
|
+
location = cast("Mapping[str, object] | None", item.get("location"))
|
|
189
|
+
source = None
|
|
190
|
+
if location is not None:
|
|
191
|
+
start = cast("Mapping[str, int]", location["start"])
|
|
192
|
+
end = cast("Mapping[str, int]", location["end"])
|
|
193
|
+
source = SourceLocation(
|
|
194
|
+
str(location["path"]),
|
|
195
|
+
SourcePosition(start["line"], start["character"]),
|
|
196
|
+
SourcePosition(end["line"], end["character"]),
|
|
197
|
+
)
|
|
198
|
+
return CompilerDiagnostic(
|
|
199
|
+
code=str(item.get("code") or codes.PY_SERIALIZATION),
|
|
200
|
+
severity=Severity(str(item["severity"])),
|
|
201
|
+
message=str(item["message"]),
|
|
202
|
+
source=source,
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def _ir_diagnostics(error: DiagnosticError) -> tuple[CompilerDiagnostic, ...]:
|
|
207
|
+
return tuple(
|
|
208
|
+
CompilerDiagnostic(
|
|
209
|
+
code=item.code,
|
|
210
|
+
severity=Severity.ERROR,
|
|
211
|
+
message=item.message,
|
|
212
|
+
ir_path=item.location,
|
|
213
|
+
)
|
|
214
|
+
for item in error.diagnostics
|
|
215
|
+
)
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""Lazy loading and compatibility checks for the private native compiler."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import functools
|
|
6
|
+
from importlib import import_module, metadata
|
|
7
|
+
from typing import Protocol, cast
|
|
8
|
+
|
|
9
|
+
from ._types import NativeCompilerInfo, NativeCompilerUnavailable
|
|
10
|
+
|
|
11
|
+
EXPECTED_NATIVE_VERSION = "0.1.0"
|
|
12
|
+
"""The exact ``morphir-python-native`` version this release was qualified with.
|
|
13
|
+
|
|
14
|
+
It must equal the pin in this distribution's ``pyproject.toml``.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
EXPECTED_BRIDGE_API = 1
|
|
18
|
+
"""The bridge contract revision this release speaks."""
|
|
19
|
+
|
|
20
|
+
_DISTRIBUTION = "morphir-python-native"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class NativeModule(Protocol):
|
|
24
|
+
"""The private ``morphir_python_native._native`` module."""
|
|
25
|
+
|
|
26
|
+
BRIDGE_API: int
|
|
27
|
+
COMPILER_VERSION: str
|
|
28
|
+
__version__: str
|
|
29
|
+
|
|
30
|
+
def compile(self, request: str) -> str:
|
|
31
|
+
"""Compile a JSON request."""
|
|
32
|
+
...
|
|
33
|
+
|
|
34
|
+
def generate(self, ir: str) -> str:
|
|
35
|
+
"""Generate Python from IR JSON text."""
|
|
36
|
+
...
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _unavailable(detail: str) -> NativeCompilerUnavailable:
|
|
40
|
+
return NativeCompilerUnavailable(
|
|
41
|
+
f"the Morphir Python compiler is unavailable: {detail}. Reinstall "
|
|
42
|
+
f"morphir-python, which requires {_DISTRIBUTION}=={EXPECTED_NATIVE_VERSION}."
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@functools.cache
|
|
47
|
+
def load() -> NativeCompilerInfo:
|
|
48
|
+
"""Import the native module once and check that this release can use it.
|
|
49
|
+
|
|
50
|
+
Raises:
|
|
51
|
+
NativeCompilerUnavailable: When the module is missing, or its
|
|
52
|
+
distribution version, module version or bridge revision differ from
|
|
53
|
+
what this release expects.
|
|
54
|
+
"""
|
|
55
|
+
try:
|
|
56
|
+
installed = metadata.version(_DISTRIBUTION)
|
|
57
|
+
except metadata.PackageNotFoundError:
|
|
58
|
+
raise _unavailable(f"{_DISTRIBUTION} is not installed") from None
|
|
59
|
+
try:
|
|
60
|
+
native = cast("NativeModule", import_module("morphir_python_native._native"))
|
|
61
|
+
except ImportError as error:
|
|
62
|
+
raise _unavailable(f"its native module cannot be imported ({error})") from None
|
|
63
|
+
if installed != EXPECTED_NATIVE_VERSION:
|
|
64
|
+
raise _unavailable(
|
|
65
|
+
f"{_DISTRIBUTION} {installed} is installed but this release needs "
|
|
66
|
+
f"{EXPECTED_NATIVE_VERSION}"
|
|
67
|
+
)
|
|
68
|
+
if native.__version__ != installed:
|
|
69
|
+
raise _unavailable(
|
|
70
|
+
f"the native module reports {native.__version__} but its distribution "
|
|
71
|
+
f"is {installed}"
|
|
72
|
+
)
|
|
73
|
+
if native.BRIDGE_API != EXPECTED_BRIDGE_API:
|
|
74
|
+
raise _unavailable(
|
|
75
|
+
f"bridge revision {native.BRIDGE_API} differs from the expected "
|
|
76
|
+
f"{EXPECTED_BRIDGE_API}"
|
|
77
|
+
)
|
|
78
|
+
return NativeCompilerInfo(installed, native.BRIDGE_API, native.COMPILER_VERSION)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def module() -> NativeModule:
|
|
82
|
+
"""Return the checked native module, loading it on first use."""
|
|
83
|
+
load()
|
|
84
|
+
return cast("NativeModule", import_module("morphir_python_native._native"))
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
"""Immutable inputs and results of the Python compiler API."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from enum import Enum
|
|
7
|
+
|
|
8
|
+
from morphir.ir.v3 import distribution as v3_dist
|
|
9
|
+
from morphir.ir.v4 import distribution as v4_dist
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class IrVersion(Enum):
|
|
13
|
+
"""The Morphir IR version a compilation writes."""
|
|
14
|
+
|
|
15
|
+
V3 = "3"
|
|
16
|
+
"""Stable format 3.0.0."""
|
|
17
|
+
V4 = "4"
|
|
18
|
+
"""Draft format 4.0.0."""
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class Severity(Enum):
|
|
22
|
+
"""How serious a compiler diagnostic is."""
|
|
23
|
+
|
|
24
|
+
ERROR = "error"
|
|
25
|
+
WARNING = "warning"
|
|
26
|
+
INFO = "info"
|
|
27
|
+
HINT = "hint"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass(frozen=True)
|
|
31
|
+
class SourceDocument:
|
|
32
|
+
"""One Python source file to compile.
|
|
33
|
+
|
|
34
|
+
Attributes:
|
|
35
|
+
path: Path relative to the source root, with ``/`` separators, such as
|
|
36
|
+
``"acme/rules.py"``. It decides the Morphir module name.
|
|
37
|
+
text: The complete source text. It is parsed, never imported or run.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
path: str
|
|
41
|
+
text: str
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass(frozen=True)
|
|
45
|
+
class SourcePosition:
|
|
46
|
+
"""A zero-based line and UTF-16 character offset in a source document."""
|
|
47
|
+
|
|
48
|
+
line: int
|
|
49
|
+
character: int
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@dataclass(frozen=True)
|
|
53
|
+
class SourceLocation:
|
|
54
|
+
"""A half-open range in one source document."""
|
|
55
|
+
|
|
56
|
+
path: str
|
|
57
|
+
start: SourcePosition
|
|
58
|
+
end: SourcePosition
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@dataclass(frozen=True)
|
|
62
|
+
class CompilerDiagnostic:
|
|
63
|
+
"""A problem reported by compilation or generation.
|
|
64
|
+
|
|
65
|
+
Attributes:
|
|
66
|
+
code: Stable code from `morphir_tools.python.diagnostics` or
|
|
67
|
+
`morphir.ir.diagnostics`.
|
|
68
|
+
severity: How serious the problem is.
|
|
69
|
+
message: Human-readable explanation. Callers must not parse it.
|
|
70
|
+
source: The source range, when the problem is in a source document.
|
|
71
|
+
ir_path: Path from the IR document root, when the problem is in IR.
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
code: str
|
|
75
|
+
severity: Severity
|
|
76
|
+
message: str
|
|
77
|
+
source: SourceLocation | None = None
|
|
78
|
+
ir_path: tuple[str | int, ...] = ()
|
|
79
|
+
|
|
80
|
+
def render(self) -> str:
|
|
81
|
+
"""Render as ``code at location: message``, like IR diagnostics.
|
|
82
|
+
|
|
83
|
+
Source locations are written ``path:line:column`` with one-based line and
|
|
84
|
+
column numbers.
|
|
85
|
+
"""
|
|
86
|
+
if self.source is not None:
|
|
87
|
+
start = self.source.start
|
|
88
|
+
where = f"{self.source.path}:{start.line + 1}:{start.character + 1}"
|
|
89
|
+
elif self.ir_path:
|
|
90
|
+
where = "/".join(str(part) for part in self.ir_path)
|
|
91
|
+
else:
|
|
92
|
+
where = "<request>"
|
|
93
|
+
return f"{self.code} at {where}: {self.message}"
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _warnings_only(diagnostics: tuple[CompilerDiagnostic, ...]) -> None:
|
|
97
|
+
if any(item.severity is Severity.ERROR for item in diagnostics):
|
|
98
|
+
raise ValueError("a successful result cannot carry error diagnostics")
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _not_empty(diagnostics: tuple[CompilerDiagnostic, ...]) -> None:
|
|
102
|
+
if not diagnostics:
|
|
103
|
+
raise ValueError("a failure needs at least one diagnostic")
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
@dataclass(frozen=True)
|
|
107
|
+
class CompiledV3:
|
|
108
|
+
"""A successful compilation to a format 3.0.0 Library."""
|
|
109
|
+
|
|
110
|
+
distribution: v3_dist.Library
|
|
111
|
+
warnings: tuple[CompilerDiagnostic, ...] = ()
|
|
112
|
+
|
|
113
|
+
def __post_init__(self) -> None:
|
|
114
|
+
_warnings_only(self.warnings)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@dataclass(frozen=True)
|
|
118
|
+
class CompiledV4:
|
|
119
|
+
"""A successful compilation to a draft format 4.0.0 Library."""
|
|
120
|
+
|
|
121
|
+
distribution: v4_dist.LibraryDistribution
|
|
122
|
+
warnings: tuple[CompilerDiagnostic, ...] = ()
|
|
123
|
+
|
|
124
|
+
def __post_init__(self) -> None:
|
|
125
|
+
_warnings_only(self.warnings)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
@dataclass(frozen=True)
|
|
129
|
+
class CompileFailure:
|
|
130
|
+
"""A compilation that produced no model."""
|
|
131
|
+
|
|
132
|
+
diagnostics: tuple[CompilerDiagnostic, ...]
|
|
133
|
+
|
|
134
|
+
def __post_init__(self) -> None:
|
|
135
|
+
_not_empty(self.diagnostics)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
type CompileResult = CompiledV3 | CompiledV4 | CompileFailure
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
@dataclass(frozen=True)
|
|
142
|
+
class GeneratedArtifact:
|
|
143
|
+
"""One generated Python module.
|
|
144
|
+
|
|
145
|
+
Attributes:
|
|
146
|
+
path: Path relative to the output root, with ``/`` separators.
|
|
147
|
+
text: The module source.
|
|
148
|
+
"""
|
|
149
|
+
|
|
150
|
+
path: str
|
|
151
|
+
text: str
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
@dataclass(frozen=True)
|
|
155
|
+
class Generated:
|
|
156
|
+
"""Successful generation: every module of the input Library."""
|
|
157
|
+
|
|
158
|
+
artifacts: tuple[GeneratedArtifact, ...]
|
|
159
|
+
warnings: tuple[CompilerDiagnostic, ...] = ()
|
|
160
|
+
|
|
161
|
+
def __post_init__(self) -> None:
|
|
162
|
+
_warnings_only(self.warnings)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
@dataclass(frozen=True)
|
|
166
|
+
class GenerationFailure:
|
|
167
|
+
"""A generation that produced no artifacts."""
|
|
168
|
+
|
|
169
|
+
diagnostics: tuple[CompilerDiagnostic, ...]
|
|
170
|
+
|
|
171
|
+
def __post_init__(self) -> None:
|
|
172
|
+
_not_empty(self.diagnostics)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
type GenerateResult = Generated | GenerationFailure
|
|
176
|
+
|
|
177
|
+
type LibraryModel = v3_dist.Library | v4_dist.LibraryDistribution
|
|
178
|
+
"""A Library model that generation accepts."""
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
@dataclass(frozen=True)
|
|
182
|
+
class NativeCompilerInfo:
|
|
183
|
+
"""Identity of the loaded native compiler.
|
|
184
|
+
|
|
185
|
+
Attributes:
|
|
186
|
+
version: Version of the ``morphir-python-native`` distribution and module.
|
|
187
|
+
bridge_api: Revision of the private bridge contract.
|
|
188
|
+
compiler_version: Version of the linked ``morphir-python-binding`` crate.
|
|
189
|
+
"""
|
|
190
|
+
|
|
191
|
+
version: str
|
|
192
|
+
bridge_api: int
|
|
193
|
+
compiler_version: str
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
class NativeCompilerUnavailable(ImportError): # noqa: N818 - reads as a state
|
|
197
|
+
"""The native compiler is missing or incompatible with this release."""
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
class NativeCompilerError(RuntimeError):
|
|
201
|
+
"""The native compiler failed internally; this is a bug, not a user error."""
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""Diagnostic codes reported by the Python compiler API.
|
|
2
|
+
|
|
3
|
+
Every code the API can report lives in this module, as `morphir.ir.diagnostics`
|
|
4
|
+
does for the IR readers and codecs.
|
|
5
|
+
|
|
6
|
+
- ``PY001`` to ``PY006`` come from the native ``morphir-python-binding`` compiler.
|
|
7
|
+
They are the same codes its MEP extension reports, so Rust, WASM and Python
|
|
8
|
+
callers see one vocabulary.
|
|
9
|
+
- ``unsupported_distribution`` is local to this API: generation accepts only a
|
|
10
|
+
Library distribution.
|
|
11
|
+
- Decoding and encoding failures keep the `morphir.ir.diagnostics` code that the
|
|
12
|
+
codec reported, such as ``unknown_node`` or ``no_v4_spelling``.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
# Native compiler (morphir-python-binding).
|
|
18
|
+
PY_REQUEST = "PY001"
|
|
19
|
+
"""Unsupported request or option."""
|
|
20
|
+
PY_SYNTAX = "PY002"
|
|
21
|
+
"""Python syntax error, with a source range."""
|
|
22
|
+
PY_NAME = "PY003"
|
|
23
|
+
"""Invalid name or naming collision."""
|
|
24
|
+
PY_UNSUPPORTED = "PY004"
|
|
25
|
+
"""Unsupported declaration, annotation or IR feature."""
|
|
26
|
+
PY_SERIALIZATION = "PY005"
|
|
27
|
+
"""IR decoding or serialization, or generated syntax failure."""
|
|
28
|
+
PY_PARSE_STAGE = "PY006"
|
|
29
|
+
"""Parse-stage output was requested but is unavailable."""
|
|
30
|
+
|
|
31
|
+
# Python API (morphir-python).
|
|
32
|
+
UNSUPPORTED_DISTRIBUTION = "unsupported_distribution"
|
|
33
|
+
"""Generation received a Specs or Application distribution."""
|
morphir/__init__.py
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
"""Morphir - Functional domain modeling for Python.
|
|
2
|
-
|
|
3
|
-
Morphir enables you to write business logic once and use it across multiple
|
|
4
|
-
platforms, languages, and runtimes. It provides a strongly-typed intermediate
|
|
5
|
-
representation (IR) that captures the semantics of your domain model.
|
|
6
|
-
|
|
7
|
-
Example:
|
|
8
|
-
>>> import morphir
|
|
9
|
-
>>> morphir.__version__
|
|
10
|
-
'0.0.1'
|
|
11
|
-
"""
|
|
12
|
-
|
|
13
|
-
__version__ = "0.0.1"
|
|
14
|
-
__all__ = ["__version__"]
|
morphir/ir/__init__.py
DELETED
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
"""Morphir IR.
|
|
2
|
-
|
|
3
|
-
The default model is IR v4 (`morphir.ir.v4`); every name here is the same object
|
|
4
|
-
as its `morphir.ir.v4` counterpart. Format 3.0.0 lives in `morphir.ir.v3`.
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
from . import module, package
|
|
8
|
-
from .v4.access_controlled import AccessControlled
|
|
9
|
-
from .v4.decorations import (
|
|
10
|
-
DecorationFormat,
|
|
11
|
-
DecorationValuesFile,
|
|
12
|
-
LayerManifest,
|
|
13
|
-
SchemaRef,
|
|
14
|
-
)
|
|
15
|
-
from .v4.distribution import (
|
|
16
|
-
ApplicationDistribution,
|
|
17
|
-
Distribution,
|
|
18
|
-
EntryPoint,
|
|
19
|
-
EntryPointKind,
|
|
20
|
-
LibraryDistribution,
|
|
21
|
-
PackageInfo,
|
|
22
|
-
SpecsDistribution,
|
|
23
|
-
)
|
|
24
|
-
from .v4.document import (
|
|
25
|
-
DocArray,
|
|
26
|
-
DocBool,
|
|
27
|
-
DocFloat,
|
|
28
|
-
DocInt,
|
|
29
|
-
DocNull,
|
|
30
|
-
DocObject,
|
|
31
|
-
DocString,
|
|
32
|
-
Document,
|
|
33
|
-
)
|
|
34
|
-
from .v4.documented import Documented
|
|
35
|
-
from .v4.fqname import FQName
|
|
36
|
-
from .v4.literal import (
|
|
37
|
-
BoolLiteral,
|
|
38
|
-
CharLiteral,
|
|
39
|
-
DecimalLiteral,
|
|
40
|
-
FloatLiteral,
|
|
41
|
-
IntegerLiteral,
|
|
42
|
-
Literal,
|
|
43
|
-
StringLiteral,
|
|
44
|
-
)
|
|
45
|
-
from .v4.meta import FileMeta, SourceRange
|
|
46
|
-
from .v4.name import Name
|
|
47
|
-
from .v4.path import Path
|
|
48
|
-
from .v4.qname import QName
|
|
49
|
-
from .v4.ref import DefRef, FileWithDefs, PointerRef, Ref
|
|
50
|
-
from .v4.type import Field, Type, TypeAttributes
|
|
51
|
-
from .v4.type_constraints import TypeConstraints
|
|
52
|
-
from .v4.type_def import Definition as TypeDefinition
|
|
53
|
-
from .v4.type_spec import Specification as TypeSpecification
|
|
54
|
-
from .v4.value import Definition as ValueDefinition
|
|
55
|
-
from .v4.value import Pattern, Value, ValueAttributes
|
|
56
|
-
from .v4.value import Specification as ValueSpecification
|
|
57
|
-
|
|
58
|
-
__all__ = [
|
|
59
|
-
"AccessControlled",
|
|
60
|
-
"ApplicationDistribution",
|
|
61
|
-
"BoolLiteral",
|
|
62
|
-
"CharLiteral",
|
|
63
|
-
"DecimalLiteral",
|
|
64
|
-
"DecorationFormat",
|
|
65
|
-
"DecorationValuesFile",
|
|
66
|
-
"DefRef",
|
|
67
|
-
"Distribution",
|
|
68
|
-
"DocArray",
|
|
69
|
-
"DocBool",
|
|
70
|
-
"DocFloat",
|
|
71
|
-
"DocInt",
|
|
72
|
-
"DocNull",
|
|
73
|
-
"DocObject",
|
|
74
|
-
"DocString",
|
|
75
|
-
"Document",
|
|
76
|
-
"Documented",
|
|
77
|
-
"EntryPoint",
|
|
78
|
-
"EntryPointKind",
|
|
79
|
-
"FQName",
|
|
80
|
-
"Field",
|
|
81
|
-
"FileMeta",
|
|
82
|
-
"FileWithDefs",
|
|
83
|
-
"FloatLiteral",
|
|
84
|
-
"IntegerLiteral",
|
|
85
|
-
"LayerManifest",
|
|
86
|
-
"LibraryDistribution",
|
|
87
|
-
"Literal",
|
|
88
|
-
"Name",
|
|
89
|
-
"PackageInfo",
|
|
90
|
-
"Path",
|
|
91
|
-
"Pattern",
|
|
92
|
-
"PointerRef",
|
|
93
|
-
"QName",
|
|
94
|
-
"Ref",
|
|
95
|
-
"SchemaRef",
|
|
96
|
-
"SourceRange",
|
|
97
|
-
"SpecsDistribution",
|
|
98
|
-
"StringLiteral",
|
|
99
|
-
"Type",
|
|
100
|
-
"TypeAttributes",
|
|
101
|
-
"TypeConstraints",
|
|
102
|
-
"TypeDefinition",
|
|
103
|
-
"TypeSpecification",
|
|
104
|
-
"Value",
|
|
105
|
-
"ValueAttributes",
|
|
106
|
-
"ValueDefinition",
|
|
107
|
-
"ValueSpecification",
|
|
108
|
-
"module",
|
|
109
|
-
"package",
|
|
110
|
-
]
|
morphir/ir/access_controlled.py
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
"""Compatibility shim: re-exports `morphir.ir.v4.access_controlled`."""
|
|
2
|
-
|
|
3
|
-
from morphir.ir.v4.access_controlled import (
|
|
4
|
-
AccessControlled,
|
|
5
|
-
Private,
|
|
6
|
-
Public,
|
|
7
|
-
T,
|
|
8
|
-
)
|
|
9
|
-
|
|
10
|
-
__all__ = [
|
|
11
|
-
"AccessControlled",
|
|
12
|
-
"Private",
|
|
13
|
-
"Public",
|
|
14
|
-
"T",
|
|
15
|
-
]
|
morphir/ir/decorations.py
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
"""Compatibility shim: re-exports `morphir.ir.v4.decorations`."""
|
|
2
|
-
|
|
3
|
-
from morphir.ir.v4.decorations import (
|
|
4
|
-
DecorationFormat,
|
|
5
|
-
DecorationValuesFile,
|
|
6
|
-
LayerManifest,
|
|
7
|
-
SchemaRef,
|
|
8
|
-
deep_merge,
|
|
9
|
-
merge_decoration_values,
|
|
10
|
-
)
|
|
11
|
-
|
|
12
|
-
__all__ = [
|
|
13
|
-
"DecorationFormat",
|
|
14
|
-
"DecorationValuesFile",
|
|
15
|
-
"LayerManifest",
|
|
16
|
-
"SchemaRef",
|
|
17
|
-
"deep_merge",
|
|
18
|
-
"merge_decoration_values",
|
|
19
|
-
]
|