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)

# Read-only access to the group's wires
group.wires  # [TypeWire(...), TypeWire(...), ...]

# Resolve all wires concurrently
instances = await group.get_all_instances(container)

Override wires for testing with with_extra_wires():

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

Combine multiple groups into one with combine_wire_groups():

from typewirepy import combine_wire_groups

infra = type_wire_group_of([config_wire, db_wire])
domain = type_wire_group_of([service_wire])
all_wires = combine_wire_groups([infra, domain])

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.5.tar.gz (64.2 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.5-py3-none-any.whl (14.2 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: typewirepy-0.1.5.tar.gz
  • Upload date:
  • Size: 64.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.2 {"installer":{"name":"uv","version":"0.11.2","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.5.tar.gz
Algorithm Hash digest
SHA256 a39d0b692ad0365d268956e2d0d4c009116397bf45cfa715b5b690e3e582aab9
MD5 cfa46acd519ce17b2138692d29dbde85
BLAKE2b-256 d9aaca34d1b29f162154eeb956ad2211a944255506df2105f4ccc559e046898b

See more details on using hashes here.

File details

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

File metadata

  • Download URL: typewirepy-0.1.5-py3-none-any.whl
  • Upload date:
  • Size: 14.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.2 {"installer":{"name":"uv","version":"0.11.2","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.5-py3-none-any.whl
Algorithm Hash digest
SHA256 09254cdaee9ec60dbce5cd28534af0ab0fd0f3148382a72b36f7d0215691f00c
MD5 b72b1bf957f22d0dfaa7452473ac43a2
BLAKE2b-256 5af9ef35c539569421f58e434c9f723e41ea8f3916c6832f84ed31f15a4c72c8

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