Skip to main content

Dependency injection package

Project description

depin — Small Dependency Injection container for Python

Simple, lightweight dependency injection for synchronous and asynchronous code, with request-scoped context useful for web frameworks such as FastAPI.

Features

  • Register classes and factories as SINGLETON, TRANSIENT or REQUEST scope.
  • Support for synchronous and asynchronous providers (functions / async functions).
  • Inject(...) helper for explicit provider parameters.
  • Container.inject decorator to auto-inject parameters into callables.
  • FastAPI integration: Container.Depends(...) and RequestScopeMiddleware.

Installation

Or install from a distribution package (if published):

# PIP
python -m pip install pydepin
# UV
uv add pydepin

Requirements: Python 3.9+.

Quickstart

Create a Container, register providers and resolve dependencies.

from depin import Container, Inject, Scope

DI = Container()

class Config:
    def __init__(self):
        self.value = 100


def config_provider():
    return Config()


def service(config: Config):
    return config.value * 2

# register providers
DI.bind(source=config_provider, scope=Scope.SINGLETON)
DI.bind(source=service, scope=Scope.SINGLETON)

result = DI.get(service)  # -> 200

Concepts

  • Container — main entry point. Use it to register providers and resolve values.
  • Scope — enumeration with SINGLETON, TRANSIENT and REQUEST.
    • SINGLETON: one instance shared during container lifetime.
    • TRANSIENT: a new instance produced on each resolution.
    • REQUEST: request-scoped lifecycle (requires RequestScopeService context).
  • Inject(provider) — used as a default value for function/constructor parameters to explicitly point to a provider.
  • Container.inject — decorator that wraps a function and automatically fills injectable parameters (by type-hint or Inject(...)). Works for sync and async.

Registration styles

You can register providers in a few different ways:

  • Using bind(...) with a class or function literal:
DI.bind(source=MyClass, scope=DI.Scope.SINGLETON)
DI.bind(source=my_factory_fn, scope=DI.Scope.SINGLETON)
  • Using the @register(...) decorator (convenient for modules):
@DI.register(DI.Scope.TRANSIENT)
def random_id():
    import uuid
    return uuid.uuid4().hex


@DI.register(DI.Scope.REQUEST)
class UserService:
    def __init__(self, repo: UserRepo, request: Request):
        ...

Explicit Inject parameter

Use Inject(provider) when you want to override the type hint and point to another provider directly:

from depin import Inject

def get_string():
    return 'from_provider'

class Service:
    def __init__(self, value: int = Inject(get_string)):
        self.value = value

DI.bind(source=get_string, scope=DI.Scope.SINGLETON)
DI.bind(source=Service, scope=DI.Scope.SINGLETON)

s = DI.get(Service)
assert s.value == 'from_provider'

Async providers and request-scoped resources

Request scope supports generator / async-generator providers which are entered when first resolved during a request and cleaned up when the request scope ends.

Example (from example/dependencies/database.py):

from contextlib import asynccontextmanager
from depin import Inject, Scope

@DI.register(Scope.SINGLETON)
async def db_engine():
    return Engine()


@DI.register(Scope.REQUEST)
async def db_session(engine: Engine = Inject(db_engine), session_id: str = Inject(random_id)):
    async with db_session_ctx(engine, session_id) as session:
        yield session

@asynccontextmanager
async def db_session_ctx(engine: Engine, session_id: str):
    session = Session(engine, session_id)
    try:
        yield session
    finally:
        # cleanup
        pass

The container will call __aenter__ / __enter__ for request-scoped generator providers and store the created resource in the current request store.

FastAPI integration

To use request scope with FastAPI, add the RequestScopeMiddleware from depin.extensions.fastapi before defining routes and use Container.Depends to get FastAPI-compatible dependencies that call into the container.

from fastapi import FastAPI
from depin import Container
from depin.extensions.fastapi import RequestScopeMiddleware

DI = Container()

app = FastAPI()
app.add_middleware(RequestScopeMiddleware)

@DI.register(DI.Scope.REQUEST)
class UserService:
    def __init__(self, request: Request):
        self.request = request

@app.get('/')
async def index(s: UserService = DI.Depends(UserService)):
    # `s` is resolved from the container using the request-scoped store
    return {'message': 'ok'}

Alternatively you can call RequestScopeService directly from providers to access the currently active Request instance.

Container helpers

  • Container.get(t) — synchronous resolution (raises when provider is async).
  • Container.get_async(t) — asynchronous resolution that awaits async providers.
  • Container.inject(func) — returns a wrapped callable that auto-injects dependencies by type hints and Inject(...) defaults.
  • Container.Depends(type_or_provider) — returns a FastAPI Depends wrapper.

Examples

  • See the example folder for a complete FastAPI example integrating request scope, async database sessions, and services.
  • See tests/ for unit tests showing usage patterns (provider functions, Inject param, decorators and scopes).

Running the example

From the repository root run:

python example/app.py

Open http://localhost:8001 to exercise the sample endpoints.

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

pydepin-0.1.7.tar.gz (12.3 kB view details)

Uploaded Source

Built Distribution

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

pydepin-0.1.7-py3-none-any.whl (11.6 kB view details)

Uploaded Python 3

File details

Details for the file pydepin-0.1.7.tar.gz.

File metadata

  • Download URL: pydepin-0.1.7.tar.gz
  • Upload date:
  • Size: 12.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.16 {"installer":{"name":"uv","version":"0.9.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for pydepin-0.1.7.tar.gz
Algorithm Hash digest
SHA256 cbe119ea5eb7a998ea908d64b7f7816d5ef2be7c62ddb9f2da2f41462c6abb07
MD5 e676a8b3161690c875f6f97c9bdf4d79
BLAKE2b-256 8132c25a261743b3c903bbf235702a3eeb271f2b7db975a083126e92aa703532

See more details on using hashes here.

File details

Details for the file pydepin-0.1.7-py3-none-any.whl.

File metadata

  • Download URL: pydepin-0.1.7-py3-none-any.whl
  • Upload date:
  • Size: 11.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.16 {"installer":{"name":"uv","version":"0.9.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for pydepin-0.1.7-py3-none-any.whl
Algorithm Hash digest
SHA256 ed5364334c921833800f8b6bb8041de5a311580f79e184586251f1ba769e4bf2
MD5 f7e177960d90e8af2f7d1c135f5a4105
BLAKE2b-256 6bb7050aebbd95f150a4b10117c04a66cddc82d9caf5b927826b637ae81e0ee2

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