AnyDI
Documentation
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.
- 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.
- Lazy references: Module-level access to dependencies without injection.
- 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))
Or use a lazy reference (app/main.py)
from app.container import container
from app.services import GreetingService
service = container.ref(GreetingService)
def greet() -> str:
return service.greet("World")
if __name__ == "__main__":
print(greet())
The container owns the providers, scopes and lifespan either way. Injection and lazy references are two styles of reaching the same dependency.
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.test_mode(), 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.test_mode(), 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),
]
Documentation
Guides:
Framework integrations:
Everything else:
Metadata
Release files for anydi 0.81.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| anydi-0.81.0.tar.gz | 43.2 kB | Without provenance | Details for anydi-0.81.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| anydi-0.81.0-py3-none-any.whl | Python 3 | none | any | Details for anydi-0.81.0-py3-none-any.whl |
Total release size: 96.3 kB
Release files / anydi-0.81.0.tar.gz
| Download URL | anydi-0.81.0.tar.gz |
|---|---|
| Size | 43.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
2107554291fe90fe8477a58f01014e3573dc31e76a452f958ae86972d6998441
|
|
BLAKE2b-256 checksum How to use checksums |
72b9a75cb9331d0c871798029cb010c37777c41d7a00be4a56775c95f9264fa9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.23 {"installer":{"name":"uv","version":"0.12.23","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}
|
Release files / anydi-0.81.0-py3-none-any.whl
| Download URL | anydi-0.81.0-py3-none-any.whl |
|---|---|
| Size | 53.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
211b67eeedaa63ba54c06f255d610e4db7f05402d026914325883fa56f47d112
|
|
BLAKE2b-256 checksum How to use checksums |
3abf2c499ab52a1a695b95310ce7e205276b891625b691363c8101083dd5b5d7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.23 {"installer":{"name":"uv","version":"0.12.23","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}
|
Security for anydi 0.81.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.