Repository navigation
feat(sdk): add the remaining Morphir SDK modules - #25
Conversation
Add the ten new modules to morphir.sdk, extend the specification coverage test to all 22 modules of the Morphir.SDK package specification, and document them.
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: bff4c3f8b3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| Instant = datetime.datetime | ||
| """The standard library datetime type, used with a time zone.""" |
There was a problem hiding this comment.
Prevent constructing naive Instant values
Because Instant is only an alias for datetime.datetime, callers can construct Instant(2026, 9, 18) and it passes every annotation despite not identifying a point on the timeline. Such a value raises TypeError when compared with an aware instant and ValueError when passed to to_milliseconds_since_epoch; represent Instant with a validated constructor or wrapper that requires a non-None UTC offset.
AGENTS.md reference: AGENTS.md:L96-L99
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Confirmed and fixed in ca82532. Instant is no longer an alias of datetime.datetime. It is a frozen, ordered dataclass that holds milliseconds since the epoch, the form of Elm's Time.Posix and of local_time.LocalTime. The constructor rejects anything that is not an int, so Instant(datetime.datetime(2026, 9, 18)) raises TypeError, and a naive value cannot exist. from_datetime raises ValueError for a naive datetime; to_datetime yields UTC. Tests: tests/unit/sdk/test_instant.py.
Instant was an alias of datetime.datetime, so a naive datetime passed every annotation and failed later on comparison or conversion. Instant is now a frozen dataclass that holds milliseconds since the epoch, as Elm's Time.Posix and LocalTime do, so a value that is not a point on the timeline cannot be made. from_datetime rejects a naive datetime; to_datetime yields UTC.
…#847) Move ecosystem/morphir-typescript from 692557c to a5e3be0 (finos/morphir-typescript#28) and ecosystem/morphir-python from 2047614 to dc0b317 (finos/morphir-python#25). Both add the remaining modules of the Morphir.SDK package specification.
Summary
Completes
morphir.sdkagainst theMorphir.SDKpackage specification in morphir-elm. #24 added the twelve core modules; this adds the other ten:local_date,local_time,instant,uuid,regex,aggregate,rule,key,stateful_appandresult_list.test_spec_coverage.pynow lists all 372 values of the 22Morphir.IR.SDK.*module specifications and checks that each exists under its Python name. TheJsonmodules are not part of the SDK package specification and are not included. The design mirrors finos/morphir-typescript#28.Design
Same conventions as the core modules: snake_case Elm names, Elm argument order with the data last, immutable data, standard library only.
local_date.LocalDateisdatetime.date, with no time zone.MonthandDayOfWeekare Enums with the Elm constructor names. Month-end clamping matches justinmimbs/date, which the Elm runtime uses.local_time.LocalTimeis a frozen dataclass holding milliseconds from the epoch. In Elm it is an alias ofTime.Posix, so it is a point in time and does not go around midnight; the Elm tests requirefrom_iso("12:56:30")to beNothing.instant.Instantis adatetime.datetimewith a time zone. The specification gives it a type and no functions; two conversion helpers are marked as additions.uuid.UUIDis the standard libraryuuid.UUID.parsefollows the elm-uuid rules and error order;for_nameis version 5.regex.Regexwraps theremodule.result_list.ResultList[E, A]istuple[Result[E, A], ...]andStatefulAppholdslogic: (state, command) -> (state, event), both exactly as the Elm types are.key.Key2toKey16are flat tuples, so they work asDictkeys. Elm nests them only because its tuples stop at three elements; the ordering is the same.Name mapping additions:
isbecomesis_, and the initialismstoISOStringandfromISObecometo_iso_stringandfrom_iso.Deliberate departures from the Elm runtime
Each is documented in the module docstring.
regexpatterns use Pythonresyntax, which differs from JavaScript in places: named groups are(?P<name>...), and\d,\wand\bfollow Unicode.Match.indexcounts code points.splitterminates on a pattern that matches the empty string, where Elm does not stop.aggregateomitsconstructAggregationCalland its types. They inspect Morphir IR values, belong to tooling, and have no entry in the specification.aggregateyields rows in ascending key order, because it uses the SDKDictwhere Elm uses an insertion-orderedAssocList.countis a function,count(), so the type checker can infer the row type.local_date.diff_in_monthsanddiff_in_yearsuse integer arithmetic. Elm's float formula loses a month when the month count crosses a power of two.datetime.dateholds the years 1 to 9999 only.local_time.from_isorejects out-of-range fields that the Elm library accepts, and subtracts the minutes of a negative offset, which Elm adds.uuid.parseaccepts versions 1 to 8, as elm-uuid 4.3.1 does.Test plan
uv run pytest tests/: 1579 passed. Every Elm test case for these modules is ported.uv run behave features/: 19 scenarios passed.uv run ruff check .anduv run ruff format --check .uv run mypy packages/morphir/src packages/morphir-tools/srcuv run pyright packages/morphir/src packages/morphir-tools/srctest_doctests.py.