Skip to content

Contracts (quadkit-contracts)

Protocols, shared types, and the exception hierarchy for Quadkit — with zero runtime dependencies beyond typing-extensions. Every published package depends on contracts; no implementation package defines a protocol another package depends on.

For integration authors who need to bind against Quadkit interfaces without pulling in the framework — thin adapters import only this package.

PackageRole
quadkit-contractszero-dependency protocols, types, exception hierarchy
quadkitthe framework core — DI container, modules, config, logging, Result
quadkit-webASGI layer — controllers, routing, middleware, OpenAPI docs
quadkit-cliproject scaffolding and code generators
quadkit-testingin-process test beds, fakes, fixtures
Terminal window
uv add quadkit-contracts

Requires Python >= 3.11.

Protocols are structural: implement the shape, and the container binds your implementation to the contract.

from typing import Protocol
from quadkit.result import Err, Ok, Result
class UserNotFound(Exception):
"""A domain failure the caller is expected to handle."""
class UserRepository(Protocol):
async def find_name(self, user_id: str) -> Result[str, UserNotFound]: ...
async def find_name_or_unknown(repo: UserRepository, user_id: str) -> str:
result = await repo.find_name(user_id)
return result.match(ok=lambda name: name, err=lambda e: "unknown")

The domain-model side — pydantic-based entities and events:

from quadkit.contracts.domain.events import DomainEvent
from quadkit.domain import AggregateRoot
class UserCreated(DomainEvent):
user_id: str
email: str
class User(AggregateRoot):
email: str

Result is this package’s flagship: expected failures become values the type system can see. pipeline() chains fallible steps fluently; as_result wraps exception-raising calls without swallowing the unexpected ones:

from quadkit.result import Err, Ok, as_result, 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]
)
@as_result(ValueError, TypeError) # only these become Err
async def load_setting(raw: str) -> int:
return int(raw)

collect() gathers many results, partition() splits them into successes and failures, and try_catch() is the sync counterpart of as_result. Full walkthrough: contracts — errors as values.

ExtraContents
quadkit-contracts[dev] / [test]development / test tooling
ModuleContents
quadkit.resultResult[T, E], Ok, Err, as_result(), as_result_sync(), try_catch(), ResultPipeline
quadkit.contracts.core.diContainerRegistrarProtocol, ContainerResolverProtocol
quadkit.contracts.core.providerProviderProtocol, ProviderPriority
quadkit.contracts.core.registryRegistryProtocol, StrategyRegistryProtocol, BackendRegistryProtocol
quadkit.contracts.domain.baseDomainModelProtocol, ID
quadkit.contracts.domain.eventsDomainEvent
quadkit.contracts.exceptionsQuadkitError and the full hierarchy
quadkit.contracts.infra.cacheCacheBackendProtocol
quadkit.contracts.dataDatabaseProviderProtocol
quadkit.contracts.security.secretsSecretStoreProtocol

Deep-dive: contracts in the docs set.

None — this package carries types, not behavior.

The taxonomy lives here: QuadkitError is the root; DomainError subclasses describe expected business failures (NotFoundError, ValidationError, ConflictError, PermissionDeniedError, …). The web layer maps them to HTTP problem responses — see error handling.

Protocols are tested by conformance: implement the shape, run your implementation through the behavior callers rely on. quadkit-testing ships the fakes used by the framework’s own tests.

This package ships interfaces and types only — no network, no I/O, no runtime dependencies. Report vulnerabilities privately per SECURITY.md.

Version 0.0.42 in the 0.x series, released in lockstep with the other four distributions. These protocols carry no compatibility guarantee yet: minor releases may add, rename, or remove members while the framework is pre-1.0. Because this package is published whole, protocols for not-yet-released areas are the most likely to change; the settled ones are those exercised by quadkit, quadkit-web, quadkit-testing, and quadkit-cli. Pin an exact version (quadkit-contracts==0.0.42) when you depend on these types directly. Full policy: stability and compatibility.

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