Skip to main content

Lightweight, container-agnostic dependency injection for Python — typed, explicit, composable.

Project description

typewirepy

CI Python 3.10+ License: MIT

Lightweight, container-agnostic dependency injection for Python — typed, explicit, composable.

Features

  • Zero runtime dependencies — only stdlib
  • Fully typed — strict mypy + pyright out of the box
  • Async-first — native async/await; sync wrappers included
  • Immutable wires — safe to share across threads and modules
  • Scopes — singleton (default) and transient
  • Generator lifecycleyield-based creators with automatic cleanup
  • FastAPI integrationWireDepends() bridges wires to FastAPI's Depends()

Installation

pip install typewirepy

With FastAPI support:

pip install typewirepy[fastapi]

Quick Start

Async (primary)

from typewirepy import Scope, TypeWireContainer, type_wire_group_of, type_wire_of

config_wire = type_wire_of(token="Config", creator=lambda: {"db_url": "sqlite://"})

db_wire = type_wire_of(
    token="Database",
    imports={"config": config_wire},
    create_with=lambda *, config: f"db({config['db_url']})",
)

app_wires = type_wire_group_of([config_wire, db_wire])

async def main():
    async with TypeWireContainer() as container:
        await app_wires.apply(container)
        db = await db_wire.get_instance(container)
        print(db)  # "db(sqlite://)"

Sync

from typewirepy import TypeWireContainer, type_wire_of

wire = type_wire_of(token="Greeting", creator=lambda: "hello")

with TypeWireContainer.sync() as container:
    wire.apply_sync(container)
    print(wire.get_instance_sync(container))  # "hello"

Key Concepts

Wires

A wire is an immutable description of a dependency — its token (name), how to create it, and what it depends on. Create wires with type_wire_of():

  • Leaf wire — uses creator (zero-arg callable)
  • Composed wire — uses create_with + imports to receive resolved dependencies

Imports

Imports declare which other wires a composed wire depends on. They're passed as a dict[str, TypeWire] and delivered to create_with as keyword arguments:

# Keyword-only parameters (works with lambdas too)
type_wire_of(token="Svc", imports={"db": db_wire}, create_with=lambda *, db: Service(db))

# Named function
def create_svc(*, db: Database) -> Service: ...
type_wire_of(token="Svc", imports={"db": db_wire}, create_with=create_svc)

Scopes

  • Scope.SINGLETON (default) — resolved once, cached for the container's lifetime
  • Scope.TRANSIENT — resolved fresh on every call

Groups

A TypeWireGroup bundles wires together for batch application:

group = type_wire_group_of([config_wire, db_wire, service_wire])
await group.apply(container)

Override wires for testing with with_extra_wires():

test_group = group.with_extra_wires([service_wire.with_creator(lambda _ctx: mock_svc)])

Use the 2-arg form of with_creator to spy on or decorate the original creator:

async def spy(ctx, original_creator):
    instance = await original_creator()  # zero-arg closure
    instance.log = MagicMock(wraps=instance.log)
    return instance

test_group = group.with_extra_wires([service_wire.with_creator(spy)])

Introspection

Every wire exposes read-only properties for inspecting the dependency graph at runtime:

wire = type_wire_of(
    token="UserService",
    imports={"db": db_wire},
    create_with=lambda *, db: UserService(db),
    scope=Scope.TRANSIENT,
)

wire.token_label  # "UserService"
wire.scope        # Scope.TRANSIENT
wire.imports      # {"db": TypeWire(...)}  (shallow copy — safe to mutate)

Use these to build dependency graphs, generate documentation, or debug resolution order.

FastAPI Integration

from contextlib import asynccontextmanager

from fastapi import FastAPI
from typewirepy import TypeWireContainer, type_wire_group_of, type_wire_of
from typewirepy.integrations.fastapi import WireDepends

db_wire = type_wire_of(token="DB", creator=lambda: "db_connection")
app_wires = type_wire_group_of([db_wire])

@asynccontextmanager
async def lifespan(app: FastAPI):
    async with TypeWireContainer() as container:
        await app_wires.apply(container)
        app.state.typewire_container = container
        yield

app = FastAPI(lifespan=lifespan)

@app.get("/")
async def root(db: str = WireDepends(db_wire)):
    return {"db": db}

Contributing

See CONTRIBUTING.md.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

typewirepy-0.1.2.tar.gz (62.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

typewirepy-0.1.2-py3-none-any.whl (13.1 kB view details)

Uploaded Python 3

File details

Details for the file typewirepy-0.1.2.tar.gz.

File metadata

  • Download URL: typewirepy-0.1.2.tar.gz
  • Upload date:
  • Size: 62.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.10.10 {"installer":{"name":"uv","version":"0.10.10","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for typewirepy-0.1.2.tar.gz
Algorithm Hash digest
SHA256 128dcfa74e5cbeec930275e56b2cd735b1d09ff40b841b920e58d9e5da78c867
MD5 931bbd45c7390fc5c7d4a9c8622a9cf4
BLAKE2b-256 0ed5101c181ff90f49e0e8d70eb3a53055ae9d78fff40a3916365a98eec574fe

See more details on using hashes here.

File details

Details for the file typewirepy-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: typewirepy-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 13.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.10.10 {"installer":{"name":"uv","version":"0.10.10","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for typewirepy-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 bec0c848e567678b8a893ece1b409f39e61ea5b9a97f9bfd0e2649b39c4eb4f1
MD5 1a5e3c587d795f9da6ddfcd2c43372c5
BLAKE2b-256 e4568855adc74de3eddf7e6000c9aafa5949223013fd66ceb6ea1f142e0743af

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page