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():

  • Simple wire — uses creator (zero-arg callable, no dependencies)
  • 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)
svc_wire = type_wire_of(token="Svc", imports={"db": db_wire}, create_with=lambda *, db: Service(db))

# Named function
def create_svc(*, db: Database) -> Service: ...
svc_wire = 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.

Best Practices

For conventions on wire visibility, creator patterns, and file organization, see Best Practices.

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.6.tar.gz (71.8 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.6-py3-none-any.whl (14.9 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: typewirepy-0.1.6.tar.gz
  • Upload date:
  • Size: 71.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","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.6.tar.gz
Algorithm Hash digest
SHA256 4cf5238a4c4241205cccaeefa3dc6f0c8f2f566be26865a7e36e30dbacd26d3a
MD5 dca5526095c38dc30971cdba5bd611fb
BLAKE2b-256 9d0e2d571713f4b1222a7d539e2e4976561d24d038ece798bdb82bb6daa65d3a

See more details on using hashes here.

File details

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

File metadata

  • Download URL: typewirepy-0.1.6-py3-none-any.whl
  • Upload date:
  • Size: 14.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","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.6-py3-none-any.whl
Algorithm Hash digest
SHA256 63630cca533775468f6fef6fb140b2a4415b415f59219860b4586950462d8a07
MD5 1ae28a8376ffacb0ad15665c2f2d4946
BLAKE2b-256 56ca65fe9e519c3062c60765e7ab12f6a16202dfcabcf865229559c0322401fb

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