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/sdk/instant.py ADDED
@@ -0,0 +1,118 @@
1
+ """Morphir.SDK.Instant: a point on the timeline.
2
+
3
+ An `Instant` holds a whole number of milliseconds since 1970-01-01T00:00:00Z.
4
+ This is the form of the Elm runtime, where an `Instant` is a `Time.Posix`, and of
5
+ `morphir.sdk.local_time.LocalTime`. The number has no limit.
6
+
7
+ The type is its own class, not an alias of `datetime.datetime`. A `datetime` can
8
+ be naive (it can have no time zone), and a naive `datetime` does not identify a
9
+ point on the timeline. With a class that holds only the number, a value that is
10
+ not a point on the timeline cannot be made.
11
+
12
+ The IR specification has the type and no functions. All the functions here are
13
+ additions to the specification:
14
+
15
+ >>> from morphir.sdk import instant as Instant
16
+ >>> moment = Instant.from_milliseconds_since_epoch(1643374590000)
17
+ >>> Instant.to_datetime(moment).isoformat()
18
+ '2022-01-28T12:56:30+00:00'
19
+ >>> Instant.to_milliseconds_since_epoch(
20
+ ... Instant.from_datetime(
21
+ ... datetime.datetime(2022, 1, 28, 12, 56, 30, tzinfo=datetime.UTC)
22
+ ... )
23
+ ... )
24
+ 1643374590000
25
+ """
26
+
27
+ import datetime
28
+ from dataclasses import dataclass
29
+
30
+ from morphir.sdk._compare import Order
31
+
32
+ __all__ = [
33
+ "Instant",
34
+ "compare",
35
+ "from_datetime",
36
+ "from_milliseconds_since_epoch",
37
+ "to_datetime",
38
+ "to_milliseconds_since_epoch",
39
+ ]
40
+
41
+ _EPOCH = datetime.datetime(1970, 1, 1, tzinfo=datetime.UTC)
42
+ _MILLISECOND = datetime.timedelta(milliseconds=1)
43
+
44
+
45
+ @dataclass(frozen=True, slots=True, order=True)
46
+ class Instant:
47
+ """A point on the timeline, as milliseconds since 1970-01-01T00:00:00Z.
48
+
49
+ Raises:
50
+ TypeError: If `milliseconds` is not an `int`. A `bool`, a `float` and a
51
+ `datetime` are rejected.
52
+ """
53
+
54
+ milliseconds: int
55
+
56
+ def __post_init__(self) -> None:
57
+ value: object = self.milliseconds
58
+ if not isinstance(value, int) or isinstance(value, bool):
59
+ raise TypeError(
60
+ f"Instant: milliseconds must be an int, not {type(value).__name__}; "
61
+ f"use from_datetime to convert a datetime"
62
+ )
63
+
64
+
65
+ def from_milliseconds_since_epoch(millis: int) -> Instant:
66
+ """Make an instant from a number of milliseconds since the epoch.
67
+
68
+ Args:
69
+ millis: The number of milliseconds since 1970-01-01T00:00:00Z. It can be
70
+ negative.
71
+
72
+ Raises:
73
+ TypeError: If `millis` is not an `int`.
74
+ """
75
+ return Instant(millis)
76
+
77
+
78
+ def to_milliseconds_since_epoch(instant: Instant) -> int:
79
+ """Return the number of milliseconds from the epoch to an instant."""
80
+ return instant.milliseconds
81
+
82
+
83
+ def compare(a: Instant, b: Instant) -> Order:
84
+ """Compare two instants. The earlier instant is less."""
85
+ if a.milliseconds < b.milliseconds:
86
+ return Order.LT
87
+ if a.milliseconds > b.milliseconds:
88
+ return Order.GT
89
+ return Order.EQ
90
+
91
+
92
+ def from_datetime(moment: datetime.datetime) -> Instant:
93
+ """Make an instant from a `datetime` that has a time zone.
94
+
95
+ A part below one millisecond is rounded down.
96
+
97
+ Raises:
98
+ ValueError: If the `datetime` has no time zone. A naive `datetime` does
99
+ not identify a point on the timeline.
100
+ """
101
+ if moment.utcoffset() is None:
102
+ raise ValueError("from_datetime: the datetime has no time zone")
103
+ return Instant((moment - _EPOCH) // _MILLISECOND)
104
+
105
+
106
+ def to_datetime(instant: Instant) -> datetime.datetime:
107
+ """Return the instant as a `datetime` in UTC.
108
+
109
+ Raises:
110
+ OverflowError: If the instant is not in the years 1 to 9999, which is
111
+ the range of a `datetime`.
112
+ """
113
+ try:
114
+ return _EPOCH + datetime.timedelta(milliseconds=instant.milliseconds)
115
+ except OverflowError:
116
+ raise
117
+ except (ValueError, OSError) as error:
118
+ raise OverflowError(str(error)) from error
morphir/sdk/int.py ADDED
@@ -0,0 +1,86 @@
1
+ """Morphir.SDK.Int: fixed-width integers.
2
+
3
+ `Int8`, `Int16`, `Int32` and `Int64` are `NewType` wrappers over `int`. They
4
+ have no run-time cost and a type checker keeps them apart. `to_int8` and the
5
+ other `to_` functions check the range; `from_int8` and the other `from_`
6
+ functions give the plain `int` back.
7
+
8
+ Python integers have no size limit, so `Int64` covers the full signed 64-bit
9
+ range.
10
+ """
11
+
12
+ from typing import NewType
13
+
14
+ from morphir.sdk.maybe import Just, Maybe, Nothing
15
+
16
+ __all__ = [
17
+ "Int8",
18
+ "Int16",
19
+ "Int32",
20
+ "Int64",
21
+ "from_int8",
22
+ "from_int16",
23
+ "from_int32",
24
+ "from_int64",
25
+ "to_int8",
26
+ "to_int16",
27
+ "to_int32",
28
+ "to_int64",
29
+ ]
30
+
31
+ Int8 = NewType("Int8", int)
32
+ """A signed 8-bit integer."""
33
+
34
+ Int16 = NewType("Int16", int)
35
+ """A signed 16-bit integer."""
36
+
37
+ Int32 = NewType("Int32", int)
38
+ """A signed 32-bit integer."""
39
+
40
+ Int64 = NewType("Int64", int)
41
+ """A signed 64-bit integer."""
42
+
43
+
44
+ def _fits(bits: int, n: int) -> bool:
45
+ limit = 1 << (bits - 1)
46
+ return isinstance(n, int) and not isinstance(n, bool) and -limit <= n < limit
47
+
48
+
49
+ def from_int8(n: Int8) -> int:
50
+ """Convert an `Int8` to an `int`."""
51
+ return int(n)
52
+
53
+
54
+ def to_int8(n: int) -> Maybe[Int8]:
55
+ """Convert an `int` to an `Int8`, or `Nothing` when it is out of range."""
56
+ return Just(Int8(n)) if _fits(8, n) else Nothing()
57
+
58
+
59
+ def from_int16(n: Int16) -> int:
60
+ """Convert an `Int16` to an `int`."""
61
+ return int(n)
62
+
63
+
64
+ def to_int16(n: int) -> Maybe[Int16]:
65
+ """Convert an `int` to an `Int16`, or `Nothing` when it is out of range."""
66
+ return Just(Int16(n)) if _fits(16, n) else Nothing()
67
+
68
+
69
+ def from_int32(n: Int32) -> int:
70
+ """Convert an `Int32` to an `int`."""
71
+ return int(n)
72
+
73
+
74
+ def to_int32(n: int) -> Maybe[Int32]:
75
+ """Convert an `int` to an `Int32`, or `Nothing` when it is out of range."""
76
+ return Just(Int32(n)) if _fits(32, n) else Nothing()
77
+
78
+
79
+ def from_int64(n: Int64) -> int:
80
+ """Convert an `Int64` to an `int`."""
81
+ return int(n)
82
+
83
+
84
+ def to_int64(n: int) -> Maybe[Int64]:
85
+ """Convert an `int` to an `Int64`, or `Nothing` when it is out of range."""
86
+ return Just(Int64(n)) if _fits(64, n) else Nothing()