Skip to main content

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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

modwire_siren-3.1.0.tar.gz (61.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

modwire_siren-3.1.0-py3-none-any.whl (96.2 kB view details)

Uploaded Python 3

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

Hashes for modwire_siren-3.1.0.tar.gz
Algorithm Hash digest
SHA256 ff1981c683bdac18de7e6c4a4c745bc5b3c22d4cc365d6ff27055f42b0582408
MD5 06ef037120c2bbbe4a8ddc6d9c60965e
BLAKE2b-256 4301c38f2043dca7cca87bc647171dee94c7ee72b0711254c8b161d9b62a6ab0

See more details on using hashes here.

Provenance

The following attestation bundles were made for modwire_siren-3.1.0.tar.gz:

Publisher: release.yml on modwire/modwire-siren

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

Hashes for modwire_siren-3.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 77a912ab5f95db76bc0e1894c601385f6a0936e5e1daf9d99a55b78bc8f6e85a
MD5 60e0e580231f94eab793b5478eb440a5
BLAKE2b-256 01fdb68aa4d2a0726ca85a3575dd196779506b047aa5bcb9081e5c21f3278abe

See more details on using hashes here.

Provenance

The following attestation bundles were made for modwire_siren-3.1.0-py3-none-any.whl:

Publisher: release.yml on modwire/modwire-siren

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page