Skip to main content

Dependency Injection library

Project description

AnyDI

Simple Dependency Injection library that uses Python type annotations.

CI Coverage Documentation CodSpeed


Documentation

http://anydi.readthedocs.io/


AnyDI is a simple Dependency Injection library for Python 3.10+. It works with sync and async applications and uses type annotations (PEP 484).

Main features:

  • Type-safe: Uses type hints for dependency resolution.
  • Async support: Works with both sync and async code.
  • Scopes: Provides singleton, transient, and request scopes. Supports custom scope definitions.
  • Simple: Minimal boilerplate with straightforward API.
  • Fast: Low overhead dependency resolution.
  • Named providers: Use Annotated[...] for multiple providers per type.
  • Resource management: Context manager protocol support for lifecycle management.
  • Modular: Container and module composition for large applications.
  • Auto-scan: Automatic discovery of injectable callables.
  • Generic support: Automatic TypeVar resolution for generic base classes.
  • Framework integrations: Extensions for popular frameworks.
  • Testing: Provider override mechanism for test isolation.

Installation

pip install anydi

Quick Example

Define a Service (app/services.py)

class GreetingService:
    def greet(self, name: str) -> str:
        return f"Hello, {name}!"

Create the Container and Providers (app/container.py)

from anydi import Container

from app.services import GreetingService


container = Container()


@container.provider(scope="singleton")
def service() -> GreetingService:
    return GreetingService()

Resolve Dependencies Directly

from app.container import container
from app.services import GreetingService


service = container.resolve(GreetingService)

if __name__ == "__main__":
    print(service.greet("World"))

Inject Into Functions (app/main.py)

from anydi import Provide

from app.container import container
from app.services import GreetingService


def greet(service: Provide[GreetingService]) -> str:
    return service.greet("World")


if __name__ == "__main__":
    print(container.run(greet))

Test with Overrides (tests/test_app.py)

from unittest import mock

from app.container import container
from app.services import GreetingService
from app.main import greet


def test_greet() -> None:
    service_mock = mock.Mock(spec=GreetingService)
    service_mock.greet.return_value = "Mocked"

    with container.override(GreetingService, service_mock):
        result = container.run(greet)

    assert result == "Mocked"

Integrate with FastAPI (app/api.py)

from typing import Annotated

import anydi.ext.fastapi
from fastapi import FastAPI

from anydi import Provide
from app.container import container
from app.services import GreetingService


app = FastAPI()


@app.get("/greeting")
async def greet(
    service: Provide[GreetingService]
) -> dict[str, str]:
    return {"greeting": service.greet("World")}


anydi.ext.fastapi.install(app, container)

Test the FastAPI Integration (test_api.py)

from unittest import mock

from fastapi.testclient import TestClient

from app.api import app
from app.container import container
from app.services import GreetingService


client = TestClient(app)


def test_api_greeting() -> None:
    service_mock = mock.Mock(spec=GreetingService)
    service_mock.greet.return_value = "Mocked"

    with container.override(GreetingService, service_mock):
        response = client.get("/greeting")

    assert response.json() == {"greeting": "Mocked"}

Integrate with Django Ninja

Install the Django integration extras:

pip install 'anydi-django[ninja]'

Expose the container factory (app/container.py):

from anydi import Container

from app.services import GreetingService


container = Container()


@container.provider(scope="singleton")
def service() -> GreetingService:
    return GreetingService()

Configure Django (settings.py):

INSTALLED_APPS = [
    ...,
    "anydi_django",
]

ANYDI = {
    "CONTAINER_FACTORY": "app.container.container",
    "PATCH_NINJA": True,
}

Wire Django Ninja (urls.py):

from typing import Annotated, Any

from anydi import Provide
from django.http import HttpRequest
from django.urls import path
from ninja import NinjaAPI

from app.services import GreetingService


api = NinjaAPI()


@api.get("/greeting")
def greet(request: HttpRequest, service: Provide[GreetingService]) -> Any:
    return {"greeting": service.greet("World")}


urlpatterns = [
    path("api/", api.urls),
]

Learn More

Want to know more? Here are helpful resources:

Core Documentation:

  • Core Concepts - Learn about containers, providers, scopes, and dependency injection
  • Providers - How to register providers and manage resources
  • Scopes - How to use built-in and custom scopes
  • Dependency Injection - Different ways to inject dependencies
  • Testing - How to test your code with provider overrides

Framework Integrations:

Full Documentation:

Project details


Release history Release notifications | RSS feed

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

anydi-0.75.0.tar.gz (36.7 kB view details)

Uploaded Source

Built Distribution

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

anydi-0.75.0-py3-none-any.whl (45.0 kB view details)

Uploaded Python 3

File details

Details for the file anydi-0.75.0.tar.gz.

File metadata

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

File hashes

Hashes for anydi-0.75.0.tar.gz
Algorithm Hash digest
SHA256 e834c2f8da608aa52da2d955ed7b5f9a804a0ac8dcee4a217df6281215e76ae6
MD5 b2b5235525188bd5b428a7d4291ff8b7
BLAKE2b-256 75c49cbc0730c160ea7123b8fa3ae4f4a3dd720ee037cd21e882a116b6adb1bd

See more details on using hashes here.

File details

Details for the file anydi-0.75.0-py3-none-any.whl.

File metadata

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

File hashes

Hashes for anydi-0.75.0-py3-none-any.whl
Algorithm Hash digest
SHA256 abae3eff8d4493f544496fa25d4a7d78b144e9bbe77f3d952eb948329b98a4b3
MD5 7ab4fa8f779017b26bc2f465292be027
BLAKE2b-256 ddd2158ffd4884eebb558a371d1784e6147ec1197b692496db243f60591bdb24

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