tripack-container
High-level IoC container API of the Tripack framework: declarations, wiring, modules and bootstrap helpers used by application code.
tripack-container is the ergonomic surface most consumers import. It is
built on top of the resolver in
tripack-runtime
and the protocols defined in
tripack-contracts.
Install
uv add tripack-container
# or
pip install tripack-container
Optional extras:
# YAML configuration loader (PyYAML)
pip install 'tripack-container[yaml]'
# FastAPI integration (TripackAPI + Inject)
pip install 'tripack-container[fastapi]'
FastAPI integration
tripack_container.fastapi ships a drop-in FastAPI subclass
that owns the container lifecycle and rewrites
Annotated[T, Inject] parameters to FastAPI Depends at route
registration time.
from pathlib import Path
from typing import Annotated, Protocol
from tripack_container import Inject
from tripack_container.fastapi import TripackAPI
from tripack_container.loaders import load_json
class Clock(Protocol):
def now(self) -> float: ...
app = TripackAPI(
container_factory=lambda: load_json(Path("container.json")),
)
@app.get("/now")
def now(clock: Annotated[Clock, Inject]) -> dict[str, float]:
return {"now": clock.now()}
Three things happen behind the scenes:
- The container is built (sync or async factory both work) at
lifespan startup and
aclosed at shutdown. A user-suppliedlifespan=keyword still runs, layered inside the container's lifecycle. - Every HTTP request runs inside
container.ascope()so SCOPED bindings cache per-request and their teardown fires at request end. - Each
Annotated[T, Inject]parameter is rewritten toAnnotated[T, Depends(...)]at route construction time; FastAPI's own dependency system then resolvesTfromapp.state.containerper request.app.dependency_overridescontinues to work for tests.
The Inject marker
| Form | Meaning |
|---|---|
Annotated[T, Inject] |
Resolve T from the container; raise on miss. |
Annotated[T | None, Inject] |
Resolve T; return None if unbound (implicit optional from the union). |
Annotated[T, Inject(optional=True)] |
Same as the union form, explicit flag. |
Annotated[T, Inject(token="named")] |
Override the resolution token (named bindings). |
Sub-routers: TripackRouter
Sub-routers built with the default APIRouter analyse their
routes at decoration time, before app.include_router runs,
and crash on the bare Inject marker. Use TripackRouter
instead - it defaults its route_class to the inject-aware
variant:
from tripack_container.fastapi import TripackRouter
sub = TripackRouter(prefix="/v2")
@sub.get("/now")
def v2_now(clock: Annotated[Clock, Inject]) -> dict[str, float]:
return {"now": clock.now()}
app.include_router(sub)
Architecture: three layers, one marker
Layer 3 (per-framework adapter) tripack_container.fastapi - TripackAPI subclass
Layer 2 (ASGI-agnostic) tripack_container.asgi - container_lifespan
+ ContainerScopeMiddleware
Layer 1 (framework-agnostic) tripack_container._inject - Inject marker
- Layer 1 -
Injectmarker. A pure data class. No ASGI, no FastAPI, no Starlette. Every adapter reads it the same way throughparse_inject(annotation). - Layer 2 - ASGI primitives.
container_lifespanis an@asynccontextmanageryou can plug into any framework that accepts a lifespan (Starlette, FastAPI, Litestar, raw ASGI).ContainerScopeMiddlewareis a pure ASGI middleware that openscontainer.ascope()per HTTP/WebSocket request. Test coverage includes a Starlette-only path that wires both primitives without importing FastAPI. - Layer 3 - FastAPI adapter.
TripackAPIcomposes the ASGI layer and adds the FastAPI-specific concern (rewritingAnnotated[T, Inject]toDepends). A Starlette adapter would be a similar L3 module that re-uses L1 + L2 verbatim and adds its own route-level resolution mechanism.
The subclass choice for TripackAPI (rather than composite)
is documented in the module docstring; the headline is that
subclassing keeps isinstance(app, FastAPI) truthy so every
FastAPI tool (TestClient, deployment runners,
app.dependency_overrides) keeps working untouched while the
constructor surface mirrors FastAPI(...).
Other ASGI frameworks (Starlette, Litestar, raw ASGI)
The L2 primitives - container_lifespan +
ContainerScopeMiddleware - work without FastAPI. The
ASGI integration guide
ships the Starlette and raw-ASGI wiring templates plus the
custom-accessor recipe for frameworks that keep app state
outside app.state. Handlers there resolve from
request.app.state.container manually - the
Annotated[T, Inject] rewriting in L3 is the only piece
that is FastAPI-specific.
Documentation
Project site: https://goabonga.github.io/tripack/.
License
MIT - see LICENSE.
Metadata
Release files for tripack-container 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| tripack_container-0.3.0.tar.gz | 51.6 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| tripack_container-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 85.2 kB
Release files / tripack_container-0.3.0.tar.gz
| Download URL | tripack_container-0.3.0.tar.gz |
|---|---|
| Size | 51.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ef017f236c3de2bd1eccc57bf4b676752036898e63523379d775ddef39c619b5
|
|
BLAKE2b-256 checksum How to use checksums |
2fba000b715d0a7f0a7c9ea9369f4e482534d8638c19eec4b115c2aee9cbdc65
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 19, 2026.
Transparency logRelease files / tripack_container-0.3.0-py3-none-any.whl
| Download URL | tripack_container-0.3.0-py3-none-any.whl |
|---|---|
| Size | 33.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e9cc8c81e5916235e6ba6c852b26b6709abda6181ac109bba1d8f366fc65646d
|
|
BLAKE2b-256 checksum How to use checksums |
ca1c5e9246e0c3018efc0ad174528ce83830c847273bf34ed641bf58bd740828
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 19, 2026.
Transparency logSecurity for tripack-container 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.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for tripack_container-0.3.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 19, 2026.
Transparency logA 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.