Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .config/mise/tasks/docs/test
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env bash
#MISE description="Check canonical documentation snippets, installed examples and types"
set -euo pipefail
uv run --no-sync python scripts/docs_examples.py check
uv run --no-sync pytest tests/tools/test_python_docs.py tests/unit/test_docs_examples.py
4 changes: 4 additions & 0 deletions .config/mise/tasks/site/build
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env bash
#MISE description="Build the Telescope documentation site"
set -euo pipefail
exec python3 scripts/site_tool.py build "$@"
4 changes: 4 additions & 0 deletions .config/mise/tasks/site/check
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env bash
#MISE description="Check the Telescope documentation site"
set -euo pipefail
exec python3 scripts/site_tool.py check "$@"
4 changes: 4 additions & 0 deletions .config/mise/tasks/site/serve
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env bash
#MISE description="Serve the Telescope documentation site"
set -euo pipefail
exec python3 scripts/site_tool.py serve "$@"
4 changes: 4 additions & 0 deletions .config/mise/tasks/site/setup
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env bash
#MISE description="Setup the Telescope documentation site"
set -euo pipefail
exec python3 scripts/site_tool.py setup "$@"
18 changes: 17 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ on:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:
workflow_call:
inputs:
release-tag:
Expand All @@ -27,6 +28,8 @@ jobs:
outputs:
jobs: ${{ steps.select.outputs.jobs }}
reason: ${{ steps.select.outputs.reason }}
docs-examples: ${{ steps.select.outputs.docs-examples }}
docs-hosts: ${{ steps.select.outputs.docs-hosts }}
steps:
- name: Checkout repository
uses: actions/checkout@v6
Expand Down Expand Up @@ -285,10 +288,23 @@ jobs:
.dev/qualification/*.json
include-hidden-files: true

docs:
name: Documentation
needs: [changes, native-wheel]
if: >-
always() && needs.changes.result == 'success' &&
fromJSON(needs.changes.outputs.jobs).docs &&
(needs.native-wheel.result == 'success' || needs.native-wheel.result == 'skipped')
uses: ./.github/workflows/docs.yml
with:
examples: ${{ needs.changes.outputs.docs-examples == 'true' }}
hosts: ${{ needs.changes.outputs.docs-hosts == 'true' }}
native-wheel: ${{ fromJSON(needs.changes.outputs.jobs)['native-wheel'] }}

all-checks:
name: All Checks
if: always()
needs: [changes, lint, typecheck, native-wheel, test, rust, bundle, build]
needs: [changes, lint, typecheck, native-wheel, test, rust, bundle, build, docs]
runs-on: ubuntu-latest
steps:
- name: Checkout repository
Expand Down
154 changes: 154 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
name: Documentation

on:
workflow_call:
inputs:
examples:
type: boolean
default: false
hosts:
type: boolean
default: false
native-wheel:
type: boolean
default: false

permissions:
contents: read

jobs:
examples:
name: Installed documentation examples
if: inputs.examples
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: '3.14'
- uses: astral-sh/setup-uv@v7
- name: Reuse the selected native producer
if: inputs.native-wheel
uses: actions/download-artifact@v7
with:
name: native-linux-x86_64
path: dist/native
- name: Verify released, candidate and library-only examples
env:
NATIVE_WHEEL: ${{ inputs.native-wheel }}
run: |
args=()
if [[ "$NATIVE_WHEEL" == true ]]; then args+=(--native-wheel-dir dist/native); fi
python scripts/docs_check.py "${args[@]}"
- uses: actions/upload-artifact@v6
with:
name: documentation-examples
path: .dev/docs-examples.json
include-hidden-files: true
if-no-files-found: error

hosts:
name: Host recipe qualification
if: inputs.hosts
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: '3.14'
- uses: actions/setup-node@v6
with:
node-version: '24'
- uses: astral-sh/setup-uv@v7
- uses: jdx/mise-action@v3
with:
install: false
- name: Install the qualified Python pair
run: |
uv venv .dev/docs-host-python
uv pip install --python .dev/docs-host-python/bin/python --only-binary=:all: --refresh morphir==0.1.0 morphir-python==0.1.0
- name: Run displayed recipe, offline bundle checks and MoonBit refusal probes
run: .dev/docs-host-python/bin/python scripts/docs_hosts.py qualify
- uses: actions/upload-artifact@v6
with:
name: documentation-hosts
path: .dev/docs-hosts/report.json
include-hidden-files: true
if-no-files-found: error

site:
name: Site build, checks and browser smoke
needs: [examples, hosts]
if: >-
always() &&
(!inputs.examples || needs.examples.result == 'success') &&
(!inputs.hosts || needs.hosts.result == 'success')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: '3.14'
- uses: actions/setup-node@v6
with:
node-version: '24'
- uses: astral-sh/setup-uv@v7
- name: Check site tooling and failure behavior
env:
MORPHIR_SITE_TEST: '1'
run: |
uv venv .dev/site-checker
uv pip install --python .dev/site-checker/bin/python pytest==9.0.2 ruff==0.14.13
.dev/site-checker/bin/ruff check scripts/docs_*.py scripts/site_*.py tests/unit/test_docs*.py tests/unit/test_site*.py
.dev/site-checker/bin/ruff format --check scripts/docs_*.py scripts/site_*.py tests/unit/test_docs*.py tests/unit/test_site*.py
.dev/site-checker/bin/python -m pytest tests/unit/test_site_tool.py tests/unit/test_site_release.py tests/unit/test_docs_hosts.py tests/unit/test_docs_examples.py tests/unit/test_ci_changes.py
- name: Install browser checks
run: |
npm ci --prefix site/browser
npm exec --prefix site/browser -- playwright install --with-deps chromium
- name: Build once at each base and check the actual browser experience
run: |
npm test --prefix site/browser
python scripts/site_tool.py check
python scripts/site_tool.py check --base / --out .dev/site-root
- name: Download example evidence
if: inputs.examples
uses: actions/download-artifact@v7
with:
name: documentation-examples
path: .dev/site-evidence
- name: Download host evidence
if: inputs.hosts
uses: actions/download-artifact@v7
with:
name: documentation-hosts
path: .dev/site-evidence
- name: Bind checked bytes and required evidence
env:
EXAMPLES: ${{ inputs.examples }}
HOSTS: ${{ inputs.hosts }}
run: |
args=()
if [[ "$EXAMPLES" == true ]]; then args+=(--examples .dev/site-evidence/docs-examples.json); fi
if [[ "$HOSTS" == true ]]; then
args+=(--hosts .dev/site-evidence/report.json)
else
args+=(--hosts site/evidence/hosts.json)
fi
python scripts/site_release.py seal "${args[@]}"
python scripts/site_tool.py check
- name: Retain the exact verified output
uses: actions/upload-artifact@v6
with:
name: documentation-site
path: _site/
if-no-files-found: error
retention-days: 30
- name: Retain browser evidence
if: always()
uses: actions/upload-artifact@v6
with:
name: documentation-browser
path: .dev/site-browser/
include-hidden-files: true
retention-days: 30
73 changes: 73 additions & 0 deletions .github/workflows/site-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
name: Publish documentation

on:
workflow_run:
workflows: [CI]
types: [completed]
branches: [main]
workflow_dispatch:
inputs:
run-id:
description: Successful main CI run whose retained checked site should be deployed or restored
required: true
type: string

permissions:
contents: read
actions: read

concurrency:
group: documentation-pages
cancel-in-progress: false

jobs:
publish:
if: >-
github.event_name == 'workflow_dispatch' ||
(github.event.workflow_run.conclusion == 'success' &&
(github.event.workflow_run.event == 'push' || github.event.workflow_run.event == 'workflow_dispatch'))
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deploy.outputs.page_url }}
permissions:
contents: read
actions: read
pages: write
id-token: write
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
- name: Validate the producer before consuming its artifact
id: producer
env:
GH_TOKEN: ${{ github.token }}
RUN_ID: ${{ github.event.workflow_run.id || inputs.run-id }}
REPOSITORY: ${{ github.repository }}
run: |
[[ "$RUN_ID" =~ ^[0-9]+$ ]]
mkdir -p .dev
gh api "repos/$REPOSITORY/actions/runs/$RUN_ID" > .dev/site-producer.json
python3 scripts/site_release.py producer --run-json .dev/site-producer.json --repository "$REPOSITORY" >> "$GITHUB_OUTPUT"
- name: Checkout the producer's exact source
uses: actions/checkout@v6
with:
ref: ${{ steps.producer.outputs.source }}
path: candidate
persist-credentials: false
- uses: actions/download-artifact@v7
with:
name: documentation-site
path: _site
run-id: ${{ steps.producer.outputs.run }}
github-token: ${{ github.token }}
- name: Verify checked bytes and candidate identity without rebuilding
env:
SOURCE_SHA: ${{ steps.producer.outputs.source }}
run: python3 candidate/scripts/site_release.py verify --site _site --source "$SOURCE_SHA" --deploy
- uses: actions/upload-pages-artifact@v4
with:
path: _site
- id: deploy
uses: actions/deploy-pages@v4
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,8 @@ rust/target/

# Extension bundles built by mise run extension:bundle
.morphir/

# Checked documentation output and browser dependencies
_site/
site/browser/node_modules/
site/downloads/
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@

# Morphir Python

Read the [documentation site](https://finos.github.io/morphir-python/) for
[installation](https://finos.github.io/morphir-python/docs/installation/),
[API examples](https://finos.github.io/morphir-python/docs/python-compiler/) and
[CLI workflows](https://finos.github.io/morphir-python/docs/quickstart/).

Python port of [Morphir](https://morphir.finos.org) - a library of tools for building and working with functional domain models.

Morphir enables you to write business logic once and use it across multiple platforms, languages, and runtimes. It provides a strongly-typed intermediate representation (IR) that captures the semantics of your domain model.
Expand Down
73 changes: 73 additions & 0 deletions docs/api-reference.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Python API reference

Import the public compiler API from `morphir_tools.python`. It loads the native
compiler lazily when you compile or generate. The private bridge module is not
an application API. See the [complete example](python-compiler.md#use-the-python-api)
and [installation requirements](installation.md) first.

## Operations

| Operation | Result |
| --- | --- |
| `compile_sources(sources, *, package, exposed_modules=None, ir_version=IrVersion.V3)` | `CompileResult` |
| `generate_python(distribution)` | `GenerateResult` |
| `native_compiler()` | `NativeCompilerInfo` |

`sources` is a sequence of `SourceDocument(path, text)`. Paths are relative module
paths with `/` separators. Compilation reads source text statically. `package`
is a Morphir package identity; exposure uses module paths. `IrVersion.V3` is the
default; `IrVersion.V4` is draft.

## Immutable results and diagnostics

`CompileResult` is `CompiledV3 | CompiledV4 | CompileFailure`. A success has a
version-specific `distribution` and `warnings`. `GenerateResult` is
`Generated | GenerationFailure`; success contains ordered `GeneratedArtifact`
values with `path` and `text`. `LibraryModel` identifies supported input models.

`CompilerDiagnostic` has `code`, `severity`, `message`, optional `source` and
`ir_path`. `Severity` distinguishes error, warning, info and hint. A
`SourceLocation` contains the `path`, `start` and `end` `SourcePosition` values.
Positions use zero-based lines and UTF-16 characters. `render()` formats a
human-readable location; use fields and codes in application logic.

`NativeCompilerUnavailable` reports a missing or incompatible installation.
`NativeCompilerError` reports an internal compiler failure.
`EXPECTED_BRIDGE_API` and `EXPECTED_NATIVE_VERSION` describe the private package
pair expected by this CLI release. They are diagnostic information, not knobs
to override a version mismatch.

## Checked signatures

This reference is synchronized from the candidate public exports. The declared
release examples verify that their used signatures work against the released
pair. The reference check fails if a public export disappears from this page
or its signature snapshot changes without updating the source.

<!-- reference: api -->
```text
EXPECTED_BRIDGE_API
EXPECTED_NATIVE_VERSION
CompileFailure(diagnostics: 'tuple[CompilerDiagnostic, ...]') -> None
CompileResult
CompiledV3(distribution: 'v3_dist.Library', warnings: 'tuple[CompilerDiagnostic, ...]' = ()) -> None
CompiledV4(distribution: 'v4_dist.LibraryDistribution', warnings: 'tuple[CompilerDiagnostic, ...]' = ()) -> None
CompilerDiagnostic(code: 'str', severity: 'Severity', message: 'str', source: 'SourceLocation | None' = None, ir_path: 'tuple[str | int, ...]' = ()) -> None
GenerateResult
Generated(artifacts: 'tuple[GeneratedArtifact, ...]', warnings: 'tuple[CompilerDiagnostic, ...]' = ()) -> None
GeneratedArtifact(path: 'str', text: 'str') -> None
GenerationFailure(diagnostics: 'tuple[CompilerDiagnostic, ...]') -> None
IrVersion(*values)
LibraryModel
NativeCompilerError
NativeCompilerInfo(version: 'str', bridge_api: 'int', compiler_version: 'str') -> None
NativeCompilerUnavailable
Severity(*values)
SourceDocument(path: 'str', text: 'str') -> None
SourceLocation(path: 'str', start: 'SourcePosition', end: 'SourcePosition') -> None
SourcePosition(line: 'int', character: 'int') -> None
compile_sources(sources: 'Iterable[SourceDocument]', *, package: 'str', exposed_modules: 'Sequence[str] | None' = None, ir_version: 'IrVersion' = <IrVersion.V3: '3'>) -> 'CompileResult'
generate_python(distribution: 'LibraryModel | object') -> 'GenerateResult'
native_compiler() -> 'NativeCompilerInfo'
```
<!-- /reference -->
Loading
Loading