Skip to main content

Minimal dependency injection container

Project description

CI codecov Python >=3.10 PyPI version License: MIT

Downloads Dependencies PEP 561 async ready

Maintainability CodeFactor Scrutinizer Code Quality Reliability Rating Maintainability Rating

Quality gate status Bugs Code Smells Duplicated Lines (%) Technical Debt

mypy: strict linting: ruff Ruff uv Hatch

Hits-of-Code LoC GitHub issues GitHub pull requests

Minimal dependency injection container for Python. Provides immutable rule definitions, singleton/transient lifetimes, scoped caching, nested attribute resolution, cycle detection, and optional validation, logging, and ordering layers.

How to use it

Installation

pip install doppy-di

Requires Python 3.10 or later.

Basic usage

from container import ContainerBuilder

builder = ContainerBuilder()

# Register a singleton service
builder.service("answer", lambda: 42, lifetime="singleton")

# Register a transient service with dependencies
builder.service("greeting", lambda name: f"Hello, {name}!", deps=["name"])
builder.value("name", "World")

container = builder.build()

print(container.get("answer"))    # 42
print(container.get("greeting"))  # Hello, World!

Scoped caching

with container.scope("request") as scope:
    a = scope.get("greeting")
    b = scope.get("greeting")
    assert a is b  # cached within scope
# scope cache is cleared on exit

Override for testing

with container.override("answer", 99):
    print(container.get("answer"))  # 99
print(container.get("answer"))      # restored to 42

Use cases

Service registration with dependency injection

Register factories with explicit lifetime and dependency list. Container resolves the dependency graph on first access.

builder.service("db", lambda: Database("sqlite:///app.db"), lifetime="singleton")
builder.service("repo", lambda db: Repository(db), deps=["db"])
container = builder.build()
repo = container.get("repo")

Value objects and constants

Inject pre-computed values or configuration objects.

builder.value("config", {"debug": True, "port": 8080})
container.get("config")  # {"debug": True, "port": 8080}

Aliasing

Create an alias that delegates resolution to another key.

builder.service("real_service", lambda: Service(), lifetime="singleton")
builder.alias("service", "real_service")
assert container.get("service") is container.get("real_service")

Nested attribute resolution

Access nested attributes of resolved services using tuple keys.

builder.service("db", lambda: Database("prod"), lifetime="singleton")
# resolve db.connection directly
container.get(("db", "connection"))  # returns db.connection

Scoped request context

Use named scopes for per-request caching without polluting the global singleton cache.

def handle_request(request_id: str) -> dict:
    with container.scope(request_id) as scope:
        user = scope.get("current_user")
        data = scope.get("request_data")
        return process(user, data)

Validation at build time

Enable build-time validation to catch missing dependencies early.

builder.service("a", lambda b: A(b), deps=["b"])
try:
    container = builder.build(validate=True)
except ContainerBuildError as e:
    print(e.missing)  # [("a", "b")]

Duplicate key policy

Control behaviour on duplicate registration.

from container import DuplicateKeyPolicy

strict = ContainerBuilder(duplicate_policy=DuplicateKeyPolicy.FAIL)
strict.service("x", lambda: 1)
strict.service("x", lambda: 2)  # raises DuplicateKeyError

warning = ContainerBuilder(duplicate_policy=DuplicateKeyPolicy.WARN)
warning.service("x", lambda: 1)
warning.service("x", lambda: 2)  # logs warning, overwrites

Optional runtime layers

The devkit package provides optional extensions:

from devkit import LoggingContainer, ValidatingContainer

container = LoggingContainer(container)           # log all get operations
container = ValidatingContainer(container)         # validate before resolving
from devkit.nested import NestedRules, SameValuePolicy

nested = NestedRules()
nested.add_rule("parent", "child", SameValuePolicy())
from devkit import ChildrenFirstPolicy, ParentFirstPolicy
from devkit.policy import OrderPolicy

# control the order of nested field resolution
policy = ChildrenFirstPolicy()

How to contribute

  1. Fork the repository.
  2. Create a feature branch (git checkout -b feat/my-feature).
  3. Install development dependencies: uv sync --extra dev.
  4. Make changes. Format and lint with: uv run ruff format . && uv run ruff check --fix .
  5. Type-check: uv run mypy.
  6. Run tests: uv run pytest.
  7. Commit messages must follow Conventional Commits (enforced via commitlint).
  8. Open a pull request against main.

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

doppy_di-2.0.0.tar.gz (273.0 kB view details)

Uploaded Source

Built Distribution

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

doppy_di-2.0.0-py3-none-any.whl (14.2 kB view details)

Uploaded Python 3

File details

Details for the file doppy_di-2.0.0.tar.gz.

File metadata

  • Download URL: doppy_di-2.0.0.tar.gz
  • Upload date:
  • Size: 273.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.32 {"installer":{"name":"uv","version":"0.11.32","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 doppy_di-2.0.0.tar.gz
Algorithm Hash digest
SHA256 75e2ae15716db8b2a31f9e14409b499caaf739ba7bad441bfaa9d02a7f1221bf
MD5 93ee7ea6928b1e6725121330c18de4cd
BLAKE2b-256 1421cd7a7c6600695372c9e546b75afd5eb8ddbb1df37f479241ce9d8b82ea83

See more details on using hashes here.

File details

Details for the file doppy_di-2.0.0-py3-none-any.whl.

File metadata

  • Download URL: doppy_di-2.0.0-py3-none-any.whl
  • Upload date:
  • Size: 14.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.32 {"installer":{"name":"uv","version":"0.11.32","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 doppy_di-2.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 27879ff0768267ed7643126e2bb3a5344a4da52daa5a5f7fe0b9e564e5c6c898
MD5 0136e56816346d65b75b4f43692d67a9
BLAKE2b-256 35ea0f2a6fd03c8c7ac8207ebfcfa17df6389a317e96c66233ce63dde6780c92

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