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        # WireToken('UserService')
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.3.tar.gz (63.0 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.3-py3-none-any.whl (13.8 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: typewirepy-0.1.3.tar.gz
  • Upload date:
  • Size: 63.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.10.11 {"installer":{"name":"uv","version":"0.10.11","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.3.tar.gz
Algorithm Hash digest
SHA256 b5650bb666af95190cbd18a13ca3c38408d8caf69cf65aeb6b395a83611f8581
MD5 3eafb8c5ec06b6ca832dec7215bf7413
BLAKE2b-256 4aab687e2ca7aa90bf1793f88d636b3534f4520cb4ca3d018ad808663f471772

See more details on using hashes here.

File details

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

File metadata

  • Download URL: typewirepy-0.1.3-py3-none-any.whl
  • Upload date:
  • Size: 13.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.10.11 {"installer":{"name":"uv","version":"0.10.11","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.3-py3-none-any.whl
Algorithm Hash digest
SHA256 809eb8759f4b57a7b0308e5e9ef92f49945a6b0f7879094a3289a26d8f291721
MD5 5197ab730c0622a8595da3db89ed470d
BLAKE2b-256 d19c3a77bb34b15197836973790b7f8b8ca11b6389adcfaa6d08d2e4ae8d3778

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