Skip to main content

craftainer-crud

A generic CRUD framework for FastAPI: router factories (JSON, XML, and a small web form/component set), a CRUD interface, storage-agnostic repositories (SQLAlchemy async and in-memory), and the model and view bases they build on. A host application keeps its own models, views and settings and builds each resource on top of the generic pieces here; this package imports nothing from the host.

It is an instance of template-python-package (so it gets the devcontainer, CI, release workflow and template sync from there); see docs/TEMPLATE.md for the conventions shared by every instance. The package's own layout and rules are in src/craftainer_crud/README.md.

Install

pip install craftainer-crud            # core
pip install "craftainer-crud[mqtt]"    # + MQTT event sink/source (aiomqtt)
pip install "craftainer-crud[xml]"     # + XML request bodies (defusedxml)

The core needs Python 3.14 or newer. Both extras are imported lazily, only where they are used (MQTTEventSink/MQTTEventSource, and the XML codec and XML router's body parsing), so the core imports without them and a missing extra surfaces as an ImportError at first use. Bring your own database driver (for example asyncpg) for SQLAlchemyRepository.

Public API overview

  • craftainer_crud.config: CrudConfig, MQTTConfig, configure(config, session_factory). Call configure once at startup, before the first request.
  • craftainer_crud.models: the declarative Base/IdentifiedBase, the record-lifecycle mixins (Archivable, Draftable, Schedulable, Lockable) and the shared Revision model.
  • craftainer_crud.views: the Pydantic view base (ORMView), bulk, revision and stats response shapes.
  • craftainer_crud.repositories: the Repository protocol, SQLAlchemyRepository, InMemoryRepository and the filter/sort clauses.
  • craftainer_crud.interfaces: CRUDInterface, OwnerScope, revision and event sinks, build_repository_provider and friends.
  • craftainer_crud.controllers: build_json_router, build_xml_router, build_web_router and build_resource_router; crud_manifest_router and api_catalog_router (controllers.crud_manifest).
  • craftainer_crud.manifest: ManifestRegistry, ManifestOptions and build_manifest, the CRUD manifest builder.

CRUD manifest

A server describes its resources, their filters, optional routes and cache hints in one JSON document (docs/manifest.md, schema schemas/crud-manifest/v1.json). Wire it up once:

from craftainer_crud.controllers.crud_manifest import api_catalog_router, crud_manifest_router
from craftainer_crud.controllers.crud_router import build_resource_router
from craftainer_crud.manifest import ManifestOptions, ManifestRegistry

registry = ManifestRegistry()

router = build_resource_router(
    ...,  # as before
    manifest=ManifestOptions(
        registry=registry,
        name="heroes",
        api_version="v2",
        model=HeroModel,
        owner_scoped=True,
        read_roles=["viewer"],
        write_roles=["editor"],
        delete_roles=["maintainer"],
    ),
)
app.include_router(
    crud_manifest_router(
        registry,
        service_name="my-api",
        service_version="1.4.0",
        stats_ttl=30,
        oidc_discovery_url="https://idp/.well-known/openid-configuration",
    )
)  # GET /api/_crud/manifest
app.include_router(
    api_catalog_router(manifest_url="/api/_crud/manifest")
)  # GET /.well-known/api-catalog

The factories register what they build, so the manifest cannot list a route that is not mounted. It is filtered to the caller by running each resource's own read_roles dependency, and carries an ETag over that filtered body.

The catalog's service-desc link follows the serving app's openapi_url (RFC 9727 makes the link optional): FastAPI(openapi_url=None) omits it, a custom path is advertised as is. Pass openapi_url="/spec.json" to override, or openapi_url=None to omit it regardless of the app.

Event stream subscriber ids

GET <prefix>/events hands each subscriber a server-minted, HMAC-signed id in its first frame and resumes a session only for an id it minted for that resource. The key is CrudConfig.subscriber_secret (at least 32 bytes), random per process when unset; set the same value on every instance (and keep it across restarts) for resume to work across workers and restarts. A forged or unknown id is not an error: the stream starts fresh under a new id.

Model changes need an app-side migration

The package ships SQLAlchemy models (Revision, the lifecycle mixins) but no migrations: the host's Alembic setup targets models.base.Base.metadata. A release that changes those models therefore needs a new migration in the host application, and the release notes say so. Migrations stay immutable once applied (NFR-0019 in the host repository).

Versioning

SemVer, starting at 0.1.0. While the major version is 0, a minor release may break the public API; pin craftainer-crud>=0.x,<0.(x+1) until 1.0.0. The version lives only in the git tag release.yml computes; pyproject.toml stays at 0.0.0 and make build stamps RELEASE_VERSION into the build. Releases go to TestPyPI first, then PyPI, through trusted publishing (PYPI_PUBLISH repository variable: testpypi or pypi; unset publishes nothing).

Development

Open the repository in the devcontainer (it starts a Postgres for the integration tests), then run prek run --all-files --hook-stage manual. The pytest hook covers tests/unit and tests/integration under one 100% coverage gate; see tests/README.md.

License

Licensed under the GNU General Public License v3.0.

Metadata

Release files for craftainer-crud 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for craftainer-crud 0.3.0
File Size Uploaded Provenance
craftainer_crud-0.3.0.tar.gz 332.8 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for craftainer-crud 0.3.0
File Interpreter ABI Platform
craftainer_crud-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 460.3 kB

Release files / craftainer_crud-0.3.0.tar.gz

Download URL craftainer_crud-0.3.0.tar.gz
Size 332.8 kB
Tags Source
SHA-256 checksum
How to use checksums
6c518a269dc768d78722c59b862dbf2aa90e1c8ea32d053997e3a9103a04af52
BLAKE2b-256 checksum
How to use checksums
9ad632f45d35677188881596c6476c2ec1423eaa51d31aabea5389106f3d6e3e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.0 {"installer":{"name":"uv","version":"0.13.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"13","id":"trixie","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / craftainer_crud-0.3.0-py3-none-any.whl

Download URL craftainer_crud-0.3.0-py3-none-any.whl
Size 127.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e2c132b9509874b684b10177d1e7aab912c5e8c27821d0294e3bfc729a589699
BLAKE2b-256 checksum
How to use checksums
8814d16a24a9d4f57ebbf0280b4fe07dc29c283e1956b5e297a13a3b2b782bba
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.0 {"installer":{"name":"uv","version":"0.13.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"13","id":"trixie","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Security for craftainer-crud 0.3.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page