morphir-python 0.0.1__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.
Files changed (99) hide show
  1. morphir/__init__.py +14 -0
  2. morphir/ir/__init__.py +110 -0
  3. morphir/ir/access_controlled.py +15 -0
  4. morphir/ir/decorations.py +19 -0
  5. morphir/ir/diagnostics.py +105 -0
  6. morphir/ir/distribution.py +21 -0
  7. morphir/ir/document.py +37 -0
  8. morphir/ir/documented.py +11 -0
  9. morphir/ir/format_version.py +483 -0
  10. morphir/ir/fqname.py +9 -0
  11. morphir/ir/json.py +11 -0
  12. morphir/ir/literal.py +25 -0
  13. morphir/ir/meta.py +11 -0
  14. morphir/ir/migrate.py +1267 -0
  15. morphir/ir/module.py +17 -0
  16. morphir/ir/name.py +27 -0
  17. morphir/ir/package.py +15 -0
  18. morphir/ir/path.py +25 -0
  19. morphir/ir/qname.py +9 -0
  20. morphir/ir/ref.py +17 -0
  21. morphir/ir/type.py +37 -0
  22. morphir/ir/type_constraints.py +33 -0
  23. morphir/ir/type_def.py +19 -0
  24. morphir/ir/type_spec.py +25 -0
  25. morphir/ir/v3/__init__.py +64 -0
  26. morphir/ir/v3/_legacy_value.py +201 -0
  27. morphir/ir/v3/access_controlled.py +31 -0
  28. morphir/ir/v3/codec.py +1275 -0
  29. morphir/ir/v3/codec_v1.py +491 -0
  30. morphir/ir/v3/codec_v2.py +187 -0
  31. morphir/ir/v3/distribution.py +42 -0
  32. morphir/ir/v3/documented.py +13 -0
  33. morphir/ir/v3/fqname.py +30 -0
  34. morphir/ir/v3/literal.py +46 -0
  35. morphir/ir/v3/module.py +32 -0
  36. morphir/ir/v3/name.py +42 -0
  37. morphir/ir/v3/package.py +23 -0
  38. morphir/ir/v3/path.py +29 -0
  39. morphir/ir/v3/qname.py +16 -0
  40. morphir/ir/v3/type.py +126 -0
  41. morphir/ir/v3/value.py +235 -0
  42. morphir/ir/v4/__init__.py +160 -0
  43. morphir/ir/v4/access_controlled.py +17 -0
  44. morphir/ir/v4/annotation.py +75 -0
  45. morphir/ir/v4/codec.py +2161 -0
  46. morphir/ir/v4/decorations.py +88 -0
  47. morphir/ir/v4/distribution.py +56 -0
  48. morphir/ir/v4/document.py +69 -0
  49. morphir/ir/v4/documented.py +10 -0
  50. morphir/ir/v4/fqname.py +69 -0
  51. morphir/ir/v4/incompleteness.py +78 -0
  52. morphir/ir/v4/json.py +103 -0
  53. morphir/ir/v4/literal.py +51 -0
  54. morphir/ir/v4/meta.py +27 -0
  55. morphir/ir/v4/module.py +30 -0
  56. morphir/ir/v4/name.py +69 -0
  57. morphir/ir/v4/package.py +19 -0
  58. morphir/ir/v4/path.py +50 -0
  59. morphir/ir/v4/qname.py +34 -0
  60. morphir/ir/v4/ref.py +35 -0
  61. morphir/ir/v4/type.py +140 -0
  62. morphir/ir/v4/type_constraints.py +91 -0
  63. morphir/ir/v4/type_def.py +38 -0
  64. morphir/ir/v4/type_spec.py +64 -0
  65. morphir/ir/v4/value.py +480 -0
  66. morphir/ir/value.py +81 -0
  67. morphir/py.typed +0 -0
  68. morphir/sdk/__init__.py +110 -0
  69. morphir/sdk/_compare.py +187 -0
  70. morphir/sdk/aggregate.py +421 -0
  71. morphir/sdk/basics.py +504 -0
  72. morphir/sdk/char.py +110 -0
  73. morphir/sdk/decimal.py +311 -0
  74. morphir/sdk/dict.py +336 -0
  75. morphir/sdk/instant.py +118 -0
  76. morphir/sdk/int.py +86 -0
  77. morphir/sdk/key.py +522 -0
  78. morphir/sdk/list.py +356 -0
  79. morphir/sdk/local_date.py +436 -0
  80. morphir/sdk/local_time.py +267 -0
  81. morphir/sdk/maybe.py +135 -0
  82. morphir/sdk/number.py +237 -0
  83. morphir/sdk/regex.py +287 -0
  84. morphir/sdk/result.py +162 -0
  85. morphir/sdk/result_list.py +169 -0
  86. morphir/sdk/rule.py +103 -0
  87. morphir/sdk/set.py +214 -0
  88. morphir/sdk/stateful_app.py +49 -0
  89. morphir/sdk/string.py +427 -0
  90. morphir/sdk/tuple.py +43 -0
  91. morphir/sdk/uuid.py +209 -0
  92. morphir_python-0.0.1.dist-info/METADATA +269 -0
  93. morphir_python-0.0.1.dist-info/RECORD +99 -0
  94. morphir_python-0.0.1.dist-info/WHEEL +4 -0
  95. morphir_python-0.0.1.dist-info/entry_points.txt +2 -0
  96. morphir_python-0.0.1.dist-info/licenses/LICENSE +201 -0
  97. morphir_tools/__init__.py +13 -0
  98. morphir_tools/cli.py +223 -0
  99. morphir_tools/py.typed +0 -0
morphir/ir/v4/path.py ADDED
@@ -0,0 +1,50 @@
1
+ from .name import Name
2
+ from .name import from_string as name_from_string
3
+
4
+
5
+ class Path(tuple[Name, ...]):
6
+ pass
7
+
8
+
9
+ def from_list(names: list[Name]) -> Path:
10
+ return Path(tuple(names))
11
+
12
+
13
+ def to_list(path: Path) -> list[Name]:
14
+ return list(path)
15
+
16
+
17
+ def from_string(s: str) -> Path:
18
+ if not s:
19
+ return Path(tuple())
20
+ # Support both "/" (v4) and "." (legacy) as separators
21
+ # Use regex split to handle both
22
+ import re
23
+
24
+ parts = re.split(r"[/\.]", s)
25
+ return Path(tuple(name_from_string(p) for p in parts if p))
26
+
27
+
28
+ def to_string(path: Path, separator: str = "/") -> str:
29
+ # Default separator per IR v4 is "/"
30
+ from .name import to_kebab_case
31
+
32
+ return separator.join(to_kebab_case(name) for name in path)
33
+
34
+
35
+ def empty() -> Path:
36
+ return Path(tuple())
37
+
38
+
39
+ def append(path: Path, name: Name) -> Path:
40
+ return Path(path + (name,))
41
+
42
+
43
+ def concat(path1: Path, path2: Path) -> Path:
44
+ return Path(path1 + path2)
45
+
46
+
47
+ def is_prefix_of(prefix: Path, path: Path) -> bool:
48
+ if len(prefix) > len(path):
49
+ return False
50
+ return path[: len(prefix)] == prefix
morphir/ir/v4/qname.py ADDED
@@ -0,0 +1,34 @@
1
+ from dataclasses import dataclass
2
+
3
+ from . import name, path
4
+ from .name import Name
5
+ from .path import Path
6
+
7
+
8
+ @dataclass(frozen=True)
9
+ class QName:
10
+ module_path: Path
11
+ local_name: Name
12
+
13
+ @staticmethod
14
+ def from_tuple(t: tuple[Path, Name]) -> QName:
15
+ return QName(t[0], t[1])
16
+
17
+ def to_tuple(self) -> tuple[Path, Name]:
18
+ return (self.module_path, self.local_name)
19
+
20
+ @staticmethod
21
+ def from_name(n: Name) -> QName:
22
+ return QName(path.empty(), n)
23
+
24
+ def to_string(self) -> str:
25
+ module_str = path.to_string(self.module_path, ".")
26
+ local_str = name.to_camel_case(self.local_name)
27
+ return f"{module_str}:{local_str}"
28
+
29
+ @staticmethod
30
+ def from_string(s: str) -> QName:
31
+ parts = s.split(":")
32
+ if len(parts) == 2:
33
+ return QName(path.from_string(parts[0]), name.from_string(parts[1]))
34
+ return QName(path.empty(), name.from_string(s))
morphir/ir/v4/ref.py ADDED
@@ -0,0 +1,35 @@
1
+ from dataclasses import dataclass, field
2
+ from typing import Any, Generic, TypeVar, Union
3
+
4
+
5
+ @dataclass(frozen=True)
6
+ class DefRef:
7
+ """Shorthand reference to an entry in $defs."""
8
+
9
+ name: str
10
+
11
+
12
+ @dataclass(frozen=True)
13
+ class PointerRef:
14
+ """Full JSON Pointer reference."""
15
+
16
+ pointer: list[str]
17
+
18
+ @staticmethod
19
+ def from_string(pointer_str: str) -> PointerRef:
20
+ if pointer_str.startswith("#/"):
21
+ # Remove #/ and split
22
+ path = pointer_str[2:].split("/")
23
+ return PointerRef(pointer=path)
24
+ raise ValueError(f"Invalid pointer string: {pointer_str}")
25
+
26
+
27
+ Ref = Union[DefRef, PointerRef]
28
+
29
+ T = TypeVar("T")
30
+
31
+
32
+ @dataclass(frozen=True)
33
+ class FileWithDefs(Generic[T]):
34
+ content: T
35
+ defs: dict[str, Any] = field(default_factory=dict)
morphir/ir/v4/type.py ADDED
@@ -0,0 +1,140 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+ from typing import Any
5
+ from typing import Union as TUnion
6
+
7
+ from .fqname import FQName
8
+ from .name import Name
9
+ from .type_constraints import TypeConstraints
10
+
11
+
12
+ @dataclass(frozen=True)
13
+ class SourceLocation:
14
+ start_line: int
15
+ start_column: int
16
+ end_line: int
17
+ end_column: int
18
+
19
+
20
+ @dataclass(frozen=True)
21
+ class TypeAttributes:
22
+ source: SourceLocation | None = None
23
+ constraints: TypeConstraints | None = None
24
+ extensions: dict[FQName, Any] = field(
25
+ default_factory=dict
26
+ ) # Any for extensions due to circular dep with Value
27
+
28
+
29
+ EMPTY_TYPE_ATTRIBUTES = TypeAttributes()
30
+
31
+
32
+ @dataclass(frozen=True)
33
+ class Variable:
34
+ attributes: TypeAttributes
35
+ name: Name
36
+
37
+
38
+ @dataclass(frozen=True)
39
+ class Reference:
40
+ attributes: TypeAttributes
41
+ fqname: FQName
42
+ args: list[Type] = field(default_factory=list)
43
+
44
+
45
+ @dataclass(frozen=True)
46
+ class Tuple:
47
+ attributes: TypeAttributes
48
+ elements: list[Type] = field(default_factory=list)
49
+
50
+
51
+ @dataclass(frozen=True)
52
+ class Record:
53
+ attributes: TypeAttributes
54
+ fields: list[Field] = field(default_factory=list)
55
+
56
+
57
+ @dataclass(frozen=True)
58
+ class ExtensibleRecord:
59
+ attributes: TypeAttributes
60
+ variable: Name
61
+ fields: list[Field] = field(default_factory=list)
62
+
63
+
64
+ @dataclass(frozen=True)
65
+ class Function:
66
+ attributes: TypeAttributes
67
+ argument_type: Type
68
+ return_type: Type
69
+
70
+
71
+ @dataclass(frozen=True)
72
+ class Unit:
73
+ attributes: TypeAttributes
74
+
75
+
76
+ @dataclass(frozen=True)
77
+ class Union:
78
+ """An untagged union: values of at least one of the alternative types.
79
+
80
+ Draft v4 type expression (finos/morphir ``docs/spec/draft/types.md``). It
81
+ introduces no constructor or tag. Alternative order, duplicates and nesting
82
+ are content and are kept.
83
+
84
+ Attributes:
85
+ attributes: The type attributes.
86
+ alternatives: Two or more alternative types.
87
+ """
88
+
89
+ attributes: TypeAttributes
90
+ alternatives: list[Type] = field(default_factory=list)
91
+
92
+
93
+ Type = TUnion[
94
+ Variable, Reference, Tuple, Record, ExtensibleRecord, Function, Unit, Union
95
+ ]
96
+
97
+
98
+ @dataclass(frozen=True)
99
+ class Field:
100
+ name: Name
101
+ tpe: Type
102
+
103
+
104
+ def get_attributes(tpe: Type) -> TypeAttributes:
105
+ return tpe.attributes
106
+
107
+
108
+ def map_attributes(tpe: Type, f: Any) -> Type:
109
+ # f should be Callable[[TypeAttributes], TypeAttributes]
110
+ # But doing precise typing here might be verbose with Union destructuring
111
+ # For now, simplistic implementation
112
+ ta = tpe.attributes
113
+ new_ta = f(ta)
114
+
115
+ if isinstance(tpe, Variable):
116
+ return Variable(new_ta, tpe.name)
117
+ elif isinstance(tpe, Reference):
118
+ return Reference(new_ta, tpe.fqname, [map_attributes(a, f) for a in tpe.args])
119
+ elif isinstance(tpe, Tuple):
120
+ return Tuple(new_ta, [map_attributes(e, f) for e in tpe.elements])
121
+ elif isinstance(tpe, Record):
122
+ return Record(
123
+ new_ta, [Field(fl.name, map_attributes(fl.tpe, f)) for fl in tpe.fields]
124
+ )
125
+ elif isinstance(tpe, ExtensibleRecord):
126
+ return ExtensibleRecord(
127
+ new_ta,
128
+ tpe.variable,
129
+ [Field(fl.name, map_attributes(fl.tpe, f)) for fl in tpe.fields],
130
+ )
131
+ elif isinstance(tpe, Function):
132
+ return Function(
133
+ new_ta,
134
+ map_attributes(tpe.argument_type, f),
135
+ map_attributes(tpe.return_type, f),
136
+ )
137
+ elif isinstance(tpe, Union):
138
+ return Union(new_ta, [map_attributes(a, f) for a in tpe.alternatives])
139
+ else:
140
+ return Unit(new_ta)
@@ -0,0 +1,91 @@
1
+ from dataclasses import dataclass, field
2
+ from enum import Enum, auto
3
+ from typing import Literal, Union
4
+
5
+ from .fqname import FQName
6
+
7
+ # Numeric Constraints
8
+ IntWidth = Literal[8, 16, 32, 64]
9
+ FloatWidth = Literal[32, 64]
10
+
11
+
12
+ @dataclass(frozen=True)
13
+ class Signed:
14
+ bits: IntWidth
15
+
16
+
17
+ @dataclass(frozen=True)
18
+ class Unsigned:
19
+ bits: IntWidth
20
+
21
+
22
+ @dataclass(frozen=True)
23
+ class FloatingPoint:
24
+ bits: FloatWidth
25
+
26
+
27
+ @dataclass(frozen=True)
28
+ class Bounded:
29
+ min: int | None = None
30
+ max: int | None = None
31
+
32
+
33
+ @dataclass(frozen=True)
34
+ class Decimal:
35
+ precision: int
36
+ scale: int
37
+
38
+
39
+ NumericConstraint = Union[Signed, Unsigned, FloatingPoint, Bounded, Decimal]
40
+
41
+
42
+ # String Constraints
43
+ class StringEncoding(Enum):
44
+ UTF8 = auto()
45
+ UTF16 = auto()
46
+ ASCII = auto()
47
+ LATIN1 = auto()
48
+
49
+
50
+ @dataclass(frozen=True)
51
+ class StringConstraint:
52
+ encoding: StringEncoding | None = None
53
+ min_length: int | None = None
54
+ max_length: int | None = None
55
+ pattern: str | None = None
56
+
57
+
58
+ # Collection Constraints
59
+ @dataclass(frozen=True)
60
+ class CollectionConstraint:
61
+ min_length: int | None = None
62
+ max_length: int | None = None
63
+ unique_items: bool = False
64
+
65
+
66
+ # Custom Constraints
67
+ # We use 'Any' for arguments to avoid circular dependency with Value for now
68
+ # Ideally this should be Value, but Value depends on Type (sometimes)
69
+ from typing import Any
70
+
71
+
72
+ @dataclass(frozen=True)
73
+ class CustomConstraint:
74
+ predicate: FQName
75
+ arguments: list[Any]
76
+
77
+
78
+ @dataclass(frozen=True)
79
+ class TypeConstraints:
80
+ """Structured constraints on a type.
81
+
82
+ Deprecated: the upstream v4 schema keeps ``TypeAttributes.constraints`` as a
83
+ placeholder with no defined members, so these constraints have no v4 spelling
84
+ and encoding fails with ``no_v4_spelling``. Keep constraints in
85
+ ``TypeAttributes.extensions`` until upstream defines them.
86
+ """
87
+
88
+ numeric: NumericConstraint | None = None
89
+ string: StringConstraint | None = None
90
+ collection: CollectionConstraint | None = None
91
+ custom: list[CustomConstraint] = field(default_factory=list)
@@ -0,0 +1,38 @@
1
+ from dataclasses import dataclass
2
+ from typing import Union
3
+
4
+ from .access_controlled import AccessControlled
5
+ from .incompleteness import Incompleteness
6
+ from .name import Name
7
+ from .type import Type
8
+ from .type_spec import Constructors
9
+
10
+
11
+ @dataclass(frozen=True)
12
+ class TypeAliasDefinition:
13
+ type_params: list[Name]
14
+ tpe: Type
15
+
16
+
17
+ @dataclass(frozen=True)
18
+ class CustomTypeDefinition:
19
+ type_params: list[Name]
20
+ constructors: AccessControlled[Constructors]
21
+
22
+
23
+ @dataclass(frozen=True)
24
+ class IncompleteTypeDefinition:
25
+ """A type definition that is not complete yet.
26
+
27
+ Attributes:
28
+ type_params: The declared type parameters.
29
+ incompleteness: Why the definition is incomplete.
30
+ partial_type_exp: The type expression written so far, if any.
31
+ """
32
+
33
+ type_params: list[Name]
34
+ incompleteness: Incompleteness
35
+ partial_type_exp: Type | None = None
36
+
37
+
38
+ Definition = Union[TypeAliasDefinition, CustomTypeDefinition, IncompleteTypeDefinition]
@@ -0,0 +1,64 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+ from typing import Union
5
+
6
+ from .annotation import Annotation
7
+ from .fqname import FQName
8
+ from .name import Name
9
+ from .type import Type
10
+
11
+ ConstructorArgs = list[tuple[Name, Type]]
12
+ Constructors = dict[Name, ConstructorArgs]
13
+
14
+
15
+ @dataclass(frozen=True)
16
+ class TypeAliasSpecification:
17
+ """Specification for a type alias."""
18
+
19
+ type_params: list[Name]
20
+ tpe: Type
21
+ annotations: list[Annotation] = field(default_factory=list)
22
+
23
+
24
+ @dataclass(frozen=True)
25
+ class OpaqueTypeSpecification:
26
+ """Specification for an opaque type."""
27
+
28
+ type_params: list[Name]
29
+ annotations: list[Annotation] = field(default_factory=list)
30
+
31
+
32
+ @dataclass(frozen=True)
33
+ class CustomTypeSpecification:
34
+ """Specification for a custom type (ADT)."""
35
+
36
+ type_params: list[Name]
37
+ constructors: Constructors
38
+ annotations: list[Annotation] = field(default_factory=list)
39
+
40
+
41
+ @dataclass(frozen=True)
42
+ class DerivedTypeSpecificationDetails:
43
+ """Details for a derived type."""
44
+
45
+ base_type: Type
46
+ from_base_type: FQName
47
+ to_base_type: FQName
48
+
49
+
50
+ @dataclass(frozen=True)
51
+ class DerivedTypeSpecification:
52
+ """Specification for a derived type."""
53
+
54
+ type_params: list[Name]
55
+ details: DerivedTypeSpecificationDetails
56
+ annotations: list[Annotation] = field(default_factory=list)
57
+
58
+
59
+ Specification = Union[
60
+ TypeAliasSpecification,
61
+ OpaqueTypeSpecification,
62
+ CustomTypeSpecification,
63
+ DerivedTypeSpecification,
64
+ ]