Skip to main content

quadkit

Quadkit

PyPI Python License

Async-first application framework for Python: dependency-injection container, application lifecycle, typed configuration, and the Result error model. This is the core package; everything else published from this repository builds on it.

For application developers — write providers and modules, bind contracts, and let one container boot them in order.

What's in the box:

  • DI container — singleton, scoped, and transient bindings, async factories, override support for tests; the graph resolves once, at boot.
  • Provider & module system — providers register bindings, boot resources, and shut them down in reverse order; modules compose providers into reusable units.
  • Typed configuration — application.yaml validated against dataclass models at boot (unknown keys fail fast), every key overridable via QK_* environment variables, profiles via QK_PROFILE.
  • The Result model — expected failures are values (Ok/Err), not exceptions; ResultPipeline chains them fluently while keeping the error type visible to mypy.
  • Structured logging — get_logger() gives every service a keyed, structlog-style logger out of the box.
  • Domain primitives — AggregateRoot, Entity, DomainEvent, AbstractUnitOfWork for DDD-style modelling without a framework tax.

The quadkit family

Package Role
quadkit-contracts zero-dependency protocols, types, exception hierarchy
quadkit the framework core — DI container, modules, config, logging, Result
quadkit-web ASGI layer — controllers, routing, middleware, OpenAPI docs
quadkit-cli project scaffolding and code generators
quadkit-testing in-process test beds, fakes, fixtures

Installation

uv add quadkit
# batteries-included web stack:
uv add "quadkit[web]"

Requires Python >= 3.11.

Minimal working example

import asyncio

from quadkit import Application
from quadkit.contracts.core.di import ContainerRegistrarProtocol
from quadkit.di.provider import Provider


class Settings:
    greeting = "hello, quadkit"


class SettingsProvider(Provider):
    async def register(self, container: ContainerRegistrarProtocol) -> None:
        container.singleton(Settings, instance=Settings())


async def main() -> None:
    app = Application()
    app.add_provider(SettingsProvider())
    await app.start()
    try:
        settings = await app.container.resolve(Settings)
        print(settings.greeting)
    finally:
        await app.stop()


asyncio.run(main())

For the web quickstart (a real endpoint in minutes), see the docs.

Expected failures are values

Result[T, E] keeps the happy path and the failure path in the type system — pipeline() chains fallible steps without try/except pyramids, and mypy sees the error type at every step:

from quadkit.result import Err, Ok, pipeline


def parse_port(raw: str):
    try:
        port = int(raw)
    except ValueError as exc:
        return Err(exc)
    if not 1 <= port <= 65535:
        return Err(ValueError(f"port out of range: {port}"))
    return Ok(port)


result = (
    pipeline("8080")  # infallible start
    .then(parse_port)  # Result[int, ValueError]
    .map(lambda port: f"listening on :{port}")
    .finalize()  # Result[str, ValueError]
)

In controllers, returning an Err renders as an RFC 7807 problem response automatically — see the error-handling guide.

Optional extras

Extra Contents
quadkit[web] quadkit-web[granian] — the full web stack
quadkit[test] pytest, pytest-asyncio, pytest-cov, pytest-mock
quadkit[security] cryptography (signing/token helpers)
quadkit[codegen] code generation toolchain
quadkit[docs] / [dev] documentation / development tooling

Public API entry points

from quadkit import Application, Result, Ok, Err
from quadkit.di.provider import Provider
from quadkit.di.container import Container
from quadkit.config import BaseConfig, ConfigLoader
from quadkit.logging import get_logger
from quadkit.domain import AggregateRoot, Entity, DomainEvent, AbstractUnitOfWork
from quadkit.contracts.core.di import (
    ContainerRegistrarProtocol,
    ContainerResolverProtocol,
)

Providers can also define an async boot() (acquire resources) and shutdown() (release them, in reverse registration order) — the container drives the whole lifecycle.

Concepts: contracts · dependency injection · modules · lifecycle · async model.

Configuration

application.yaml at the working directory, validated against typed config models at boot (unknown keys fail fast); application metadata lives at the root (name, version, description); every typed key overrides from the environment with QK_<SECTION>__<KEY>. Use QK_PROFILE to select a profile.

QK_QUADKIT__LOGGING__LEVEL=DEBUG   # fold into the root `logging` section
QK_PROFILE=production              # select a profile block

See configuration.

Error handling

Result[T, E] for expected domain failures; the quadkit-contracts exception hierarchy (QuadkitError → DomainError → NotFoundError, ValidationError, ConflictError, ...) for everything else. The web layer renders both as problem responses — see error handling.

Testing

Pair with quadkit-testing: AppTestBed.from_factory(create_app) boots your application in-process (with overrides={Contract: fake} for test doubles), no server required. See testing.

Security

Never put secrets in application.yaml — pass them through QK_* environment variables or a secret store. Unexpected exceptions never leak internals to clients. See secure configuration and report vulnerabilities privately per SECURITY.md.

Stability

Version 0.0.3 in the 0.x series, released in lockstep with the other four distributions; APIs may change between minor versions until 1.0 — pin an exact version (quadkit==0.0.3) or a tight range (>=0.0.3,<0.1.0). Full policy: stability and compatibility.

Apache-2.0 — see LICENSE. "Quadkit" and the Quadkit logo are trademarks of the project — see TRADEMARK.md.

Release files for quadkit 0.0.42

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

Source distribution (sdist)

Source distribution for quadkit 0.0.42
File Size Uploaded
quadkit-0.0.42.tar.gz 2.2 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for quadkit 0.0.42
File Interpreter ABI Platform
quadkit-0.0.42-py3-none-any.whl Python 3 none any Details

Total release size: 2.6 MB

Release files / quadkit-0.0.42.tar.gz

Download URL quadkit-0.0.42.tar.gz
Size 2.2 MB
Tags Source
SHA-256 checksum
How to use checksums
90174ec0f851e78932538eb8dfe742b5134c6c6869d80a93e23443105512d8fe
BLAKE2b-256 checksum
How to use checksums
fc29f8e261344d36479c93216fddb64080921d6c981317da3504f8f7f72f319f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.8.14

Release files / quadkit-0.0.42-py3-none-any.whl

Download URL quadkit-0.0.42-py3-none-any.whl
Size 438.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
63c1498ff31c76e85aeb2f9e62fc860e502963ae05edfded1d369701bd6d0a7f
BLAKE2b-256 checksum
How to use checksums
a5472a8d26f7cdf09546b583518bbeb30201335170c707ee4b871639d3b81229
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.8.14

Release history Release notifications | RSS feed

This release

0.0.42 This release

2 release files

0.0.41

2 release files

0.0.4

2 release files

0.0.3

2 release files

0.0.2

2 release files

0.0.1

2 release files

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