Compile OpenAPI API graphs into Siren hypermedia documents.
Project description
modwire-siren
modwire-siren compiles a complete OpenAPI 3.1 document into a reusable Siren engine. At request
time, the engine turns application data and permissions into a Siren response with concrete links
and authorized actions.
Requires Python 3.12 or later.
Install
python -m pip install modwire-siren
For local development, install uv and use the locked environment:
UV_CACHE_DIR=.dump/uv-cache uv sync --locked --all-groups
make verify
Version 2 is a breaking rewrite. See MIGRATION.md when upgrading from version 1.
Usage
This section is generated from the docstrings of the supported root imports. Run make docs after changing a public API example or its guidance.
siren
Compile a complete OpenAPI 3.1 document into a reusable Siren engine.
Call this once during application startup, then call engine.project(context) for each
negotiated Siren response. OpenAPI defines links, methods, and candidate fields; the context's
capabilities decide which candidate actions are present in that response.
Example
from modwire_siren import SirenContext, siren
openapi = {
"openapi": "3.1.1",
"info": {"title": "Records API", "version": "1.0"},
"paths": {
"/records": {"get": {"operationId": "list_records", "responses": {"200": {"description": "OK"}}}},
"/records/{record_id}": {
"parameters": [{"name": "record_id", "in": "path", "required": True, "schema": {"type": "string"}}],
"get": {"operationId": "get_record", "responses": {"200": {"description": "OK"}}},
"patch": {
"operationId": "rename_record",
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {"title": {"type": "string"}},
}
}
}
},
"responses": {"200": {"description": "OK"}},
},
},
},
}
engine = siren(openapi)
document = engine.project(
SirenContext(
base_url="https://api.example.com",
resource="record",
value={"id": "42", "title": "Architecture"},
capabilities=frozenset({"get_record", "rename_record"}),
)
)
payload = document.model_dump(by_alias=True, mode="json", exclude_none=True)
assert payload["actions"][0] == {
"name": "get_record",
"href": "https://api.example.com/records/42",
"method": "GET",
}
OpenAPI requirements
The final plural static segment of a route is a collection; adding one path parameter forms
its entity route. Prefixes and nested collections are supported. Every non-root HTTP operation
needs a unique operationId. Local #/components/parameters, #/components/requestBodies,
and #/components/schemas references are resolved; external and path-item references are not.
Action field support matrix
Path parameters substitute into action URLs and never become fields. Query parameters and
properties of an application/json object body become fields:
| OpenAPI schema | Siren field type |
|---|---|
string, including uuid |
text |
formatted string |
matching Siren field type |
integer or number |
number |
boolean |
checkbox |
| flat primitive array or repeated query parameter | text |
scalar enum |
radio with selectable values |
flat array with an item enum |
checkbox with selectable values |
| object, map, or nested array | delegated; no synthetic field |
| header or cookie parameter | delegated; no synthetic field |
| one non-JSON request media type | delegated action with that media type |
email, uri, date, date-time, and time map to email, url, date,
datetime-local, and time, respectively.
Required and nullable controls compile as ordinary standard Siren fields: validation remains
server-enforced because official Siren has no required or nullable members. A flat array
has one named text field; the OpenAPI serialization contract remains authoritative for
submission. allOf scalar fragments and a oneOf or anyOf containing one scalar plus
null are accepted when they normalize unambiguously.
Structured values, header and cookie parameters, and one non-JSON request body are delegated
to the API contract and client transport; official Siren has no standard members for their
paths, serialization, or placement. Multiple non-JSON media types, ambiguous compositions,
unsupported string formats, and HEAD, OPTIONS, or TRACE operations are rejected during
this startup call.
Call audit(openapi) first when a consumer needs a deterministic list of every current
incompatibility before using this strict fail-fast entry point.
Framework integration is one startup call
Give the framework-generated document directly to siren() after routes are registered:
engine = siren(app.openapi()) # FastAPI
engine = siren(api.get_openapi_schema()) # Django Ninja / Django Ninja Extra
HTTP response contract
engine.project(context) returns a SirenDocument, not a dictionary. Serialize it with
document.model_dump(by_alias=True, mode="json", exclude_none=True) and send that payload as
application/vnd.siren+json. The document contains only official Siren members; action fields
never include the non-standard required member.
Set root_path when the Siren entry point is mounted away from /.
audit
Inspect a valid OpenAPI document against the current official-Siren support boundary.
Call this during startup before siren(openapi) when a consumer needs every currently
unsupported construct at once. The report exposes typed findings and render() for terminal
or CI output; siren(openapi) remains the strict fail-fast compilation entry point.
SirenRelationship
Describe a runtime relationship to another OpenAPI resource.
A relationship projects as a navigational link by default. Set embedded when the related
resource values should be included as a Siren embedded representation instead.
SirenLink
Describe a navigational Siren link.
SirenFieldValue
Describe a selectable Siren action field value.
SirenField
Describe an official Siren action field.
SirenEmbeddedRepresentation
Represent a Siren sub-entity embedded in full.
SirenEmbeddedLink
Represent a Siren sub-entity linked by URI.
SirenDocument
Represent an official Siren entity document.
Project an engine request into this immutable public value, then serialize it with
model_dump(by_alias=True, mode="json", exclude_none=True) for an
application/vnd.siren+json response. Navigation belongs in links; embedded sub-entities
belong in entities.
SirenContext
Supply runtime state used to project a Siren document.
Use the default "entity" scope for one resource, "collection" for a list, and "root"
for an API entry point. A resource is required outside root scope and is the singular name
derived from the collection route: "record" for /records. If the same resource appears
in multiple nested routes, path_values selects the route with matching parent parameters.
| Field | Purpose |
|---|---|
base_url |
Public origin joined with OpenAPI paths. |
scope |
"root", "collection", or "entity". |
resource |
Derived singular resource name; required outside root. |
value |
Entity or collection properties and entity path parameters. |
items |
Entity mappings for a collection. |
item_capabilities |
Optional permitted operation IDs for each collection item. |
relationships |
Linked or embedded related resources for this document. |
path_values |
Missing path parameters, such as a parent resource ID or a root command target. |
query |
Ordered query pairs for self and action links. |
capabilities |
Permitted OpenAPI operationId values. |
SirenCompatibilityReport
Expose deterministic OpenAPI-to-Siren compatibility findings.
SirenCompatibilityFinding
Describe one OpenAPI construct outside the current official-Siren boundary.
SirenAction
Describe an available Siren action.
ModwireSirenError
Indicate a Modwire Siren operation failure.
Public API
The supported root imports below are generated from modwire_siren.__all__.
| Symbol | Purpose | Primary API |
|---|---|---|
ModwireSirenError |
Indicate a Modwire Siren operation failure. | — |
SirenAction |
Describe an available Siren action. | — |
SirenCompatibilityFinding |
Describe one OpenAPI construct outside the current official-Siren boundary. | — |
SirenCompatibilityReport |
Expose deterministic OpenAPI-to-Siren compatibility findings. | compatible: <class 'bool'>render() -> <class 'str'> |
SirenContext |
Supply runtime state used to project a Siren document. | — |
SirenDocument |
Represent an official Siren entity document. | — |
SirenEmbeddedLink |
Represent a Siren sub-entity linked by URI. | — |
SirenEmbeddedRepresentation |
Represent a Siren sub-entity embedded in full. | — |
SirenField |
Describe an official Siren action field. | — |
SirenFieldValue |
Describe a selectable Siren action field value. | — |
SirenLink |
Describe a navigational Siren link. | — |
SirenRelationship |
Describe a runtime relationship to another OpenAPI resource. | — |
audit |
Inspect a valid OpenAPI document against the current official-Siren support boundary. | — |
siren |
Compile a complete OpenAPI 3.1 document into a reusable Siren engine. | — |
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file modwire_siren-3.1.0.tar.gz.
File metadata
- Download URL: modwire_siren-3.1.0.tar.gz
- Upload date:
- Size: 61.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ff1981c683bdac18de7e6c4a4c745bc5b3c22d4cc365d6ff27055f42b0582408
|
|
| MD5 |
06ef037120c2bbbe4a8ddc6d9c60965e
|
|
| BLAKE2b-256 |
4301c38f2043dca7cca87bc647171dee94c7ee72b0711254c8b161d9b62a6ab0
|
Provenance
The following attestation bundles were made for modwire_siren-3.1.0.tar.gz:
Publisher:
release.yml on modwire/modwire-siren
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
modwire_siren-3.1.0.tar.gz -
Subject digest:
ff1981c683bdac18de7e6c4a4c745bc5b3c22d4cc365d6ff27055f42b0582408 - Sigstore transparency entry: 2250374719
- Sigstore integration time:
-
Permalink:
modwire/modwire-siren@9cb8886b4764aad0ec1a652430d3637c396e5a1f -
Branch / Tag:
refs/tags/v3.1.0 - Owner: https://github.com/modwire
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@9cb8886b4764aad0ec1a652430d3637c396e5a1f -
Trigger Event:
release
-
Statement type:
File details
Details for the file modwire_siren-3.1.0-py3-none-any.whl.
File metadata
- Download URL: modwire_siren-3.1.0-py3-none-any.whl
- Upload date:
- Size: 96.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
77a912ab5f95db76bc0e1894c601385f6a0936e5e1daf9d99a55b78bc8f6e85a
|
|
| MD5 |
60e0e580231f94eab793b5478eb440a5
|
|
| BLAKE2b-256 |
01fdb68aa4d2a0726ca85a3575dd196779506b047aa5bcb9081e5c21f3278abe
|
Provenance
The following attestation bundles were made for modwire_siren-3.1.0-py3-none-any.whl:
Publisher:
release.yml on modwire/modwire-siren
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
modwire_siren-3.1.0-py3-none-any.whl -
Subject digest:
77a912ab5f95db76bc0e1894c601385f6a0936e5e1daf9d99a55b78bc8f6e85a - Sigstore transparency entry: 2250374780
- Sigstore integration time:
-
Permalink:
modwire/modwire-siren@9cb8886b4764aad0ec1a652430d3637c396e5a1f -
Branch / Tag:
refs/tags/v3.1.0 - Owner: https://github.com/modwire
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@9cb8886b4764aad0ec1a652430d3637c396e5a1f -
Trigger Event:
release
-
Statement type: