quadkit
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.yamlvalidated against dataclass models at boot (unknown keys fail fast), every key overridable viaQK_*environment variables, profiles viaQK_PROFILE. - The
Resultmodel — expected failures are values (Ok/Err), not exceptions;ResultPipelinechains 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,AbstractUnitOfWorkfor 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.
Links
- Documentation — https://dbtinoy-.github.io/quadkit/
- Getting started — https://dbtinoy-.github.io/quadkit/getting-started/installation/
- Changelog — https://github.com/dbtinoy-/quadkit/blob/main/CHANGELOG.md
- Issues — https://github.com/dbtinoy-/quadkit/issues
- Security — report privately per SECURITY.md
- Contributing — CONTRIBUTING.md
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)
| File | Size | Uploaded | |
|---|---|---|---|
| quadkit-0.0.42.tar.gz | 2.2 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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
|