Reusable framework-agnostic DI container & @component auto-discovery
Project description
af-di-core
A small, framework-agnostic dependency-injection framework for Python. Decorate your classes with @component, point the auto-discovery scanner at your package, and get a wired global container — no manual registration boilerplate. Works in a FastAPI app, a plain script, an AWS Lambda, or anywhere else.
Built on dependency-injector for the underlying provider machinery.
Installation
pip install af-di-core
# or with Poetry:
poetry add af-di-core
Quickstart
Mark the classes you want managed with @component (pairs naturally with @attrs.define):
import attrs
from allfly.di.core import component
@component
@attrs.define
class GreetingRepository:
def greeting(self) -> str:
return "hello"
@component
@attrs.define
class GreetingService:
_repo: GreetingRepository
def greet(self) -> str:
return self._repo.greeting()
At startup, scan your package once and then resolve anything:
from allfly.di.core import auto_discover_components, di_provide
auto_discover_components(base_package="myapp")
service = di_provide(GreetingService) # GreetingRepository injected automatically
service.greet()
Constructor dependencies are resolved from their type hints. Registration is multi-pass, so the order in which components are discovered does not matter. Parameters with default values are treated as optional and skipped, and an Optional[T] / T | None dependency is skipped when no provider for T exists.
How resolution works
auto_discover_components(base_package, registrars=None) runs in three steps:
@settingsfunctions are registered first as singletons (see below).registrars— optional callbacks for manual singletons that need special construction (e.g. third-party clients) — are invoked.@componentclasses are registered with multi-pass dependency resolution.
from allfly.di.core import auto_discover_components, di_register_singleton
def register_external_clients() -> None:
di_register_singleton(SomeClient, api_key="...")
auto_discover_components(base_package="myapp", registrars=[register_external_clients])
Settings providers
Use @settings on a function whose return type is the type to register. Combine with functools.lru_cache for single instantiation:
from functools import lru_cache
from allfly.di.core import settings
@settings
@lru_cache
def get_db_settings() -> DatabaseSettings:
return DatabaseSettings()
The returned instance is registered as a singleton keyed by the return annotation, so any @component depending on DatabaseSettings receives it.
Core providers (app-supplied)
af-di-core ships no opinionated providers — it is deliberately decoupled from databases, sessions, and web frameworks. Your application supplies its own "core" providers (things that must exist before anything is resolved, e.g. a DB session factory) by registering one or more callables on the container. They run once, lazily, the first time anything is provided:
from allfly.di.core import register_core_provider, di_register, di_provide
def register_db_providers() -> None:
di_register(DatabaseSettings, providers.Object(get_db_settings()))
di_register_singleton(SessionFactory, settings=get_provider(DatabaseSettings))
register_core_provider(register_db_providers)
# First di_provide(...) anywhere triggers ensure_core_providers() internally.
di_provide(SessionFactory)
You can also drive this explicitly via ensure_core_providers(). Initialization is guarded by a lock and a one-time flag, so it is safe to call repeatedly. Register core providers at startup, before the first di_provide.
Public API
from allfly.di.core import (
component, # class decorator → register for auto-discovery
settings, # function decorator → register a singleton by return type
auto_discover_components, # scan a package and wire the container
di_provide, # resolve an instance by type
di_register, # register a raw dependency-injector provider
di_register_factory, # register a Factory provider
di_register_singleton, # register a Singleton provider
get_provider, # get the provider (not the instance) for chaining
provider_exists, # check whether a type is registered
register_core_provider, # add an app-supplied core provider callable
ensure_core_providers, # run core providers once (called lazily by di_provide)
get_global_container, # the GlobalDependencyContainer singleton
dependency_container, # alias for the same singleton
GlobalDependencyContainer, # the container type
get_component_registry, # introspection: everything @component/@settings collected
analyze_component_dependencies, # introspection: a class's required constructor deps
)
FastAPI
The framework intentionally does not import FastAPI. To expose a component to routes, write the small glue in your app:
from typing import Annotated
from fastapi import Depends, Request
from allfly.di.core import get_global_container
# Make the container available on app.state at startup:
# application.state.provide = get_global_container().provide
def build(request: Request) -> GreetingService:
return request.app.state.provide(GreetingService)
GreetingServiceDI = Annotated[GreetingService, Depends(build)]
Logging
The library logs through loguru (mostly at trace/debug). If your app does not configure loguru, these messages are simply silent by default at higher levels.
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file af_di_core-0.0.2.tar.gz.
File metadata
- Download URL: af_di_core-0.0.2.tar.gz
- Upload date:
- Size: 6.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
129a04fc25c98b657c81c0dc8a000f27e6df5d8aa02f3f3244be3639ebf81532
|
|
| MD5 |
98bfac1f7aa43066039eb061e88a8a41
|
|
| BLAKE2b-256 |
78d7af10e6c341fb0015e126f6d0ad31fa4d91ec397dfcf52db3618ed66017c1
|
Provenance
The following attestation bundles were made for af_di_core-0.0.2.tar.gz:
Publisher:
af-di-core-publish.yml on travelallfly/allfly-py-libs
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
af_di_core-0.0.2.tar.gz -
Subject digest:
129a04fc25c98b657c81c0dc8a000f27e6df5d8aa02f3f3244be3639ebf81532 - Sigstore transparency entry: 2017422733
- Sigstore integration time:
-
Permalink:
travelallfly/allfly-py-libs@75b7449073ad8c223c58d48e796cdc152ba6e786 -
Branch / Tag:
refs/heads/master - Owner: https://github.com/travelallfly
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
af-di-core-publish.yml@75b7449073ad8c223c58d48e796cdc152ba6e786 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file af_di_core-0.0.2-py3-none-any.whl.
File metadata
- Download URL: af_di_core-0.0.2-py3-none-any.whl
- Upload date:
- Size: 8.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5c7d8d04d540736d00ff71f11f90bd6fce5ccd40e2bc9cc008fc28697893548b
|
|
| MD5 |
9969984939600be449a4e4b1e05f9237
|
|
| BLAKE2b-256 |
43708d8aa304f92240fbfd2b89bbd5fbb1c6437e8b8a350fcc3cafd5d2278e23
|
Provenance
The following attestation bundles were made for af_di_core-0.0.2-py3-none-any.whl:
Publisher:
af-di-core-publish.yml on travelallfly/allfly-py-libs
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
af_di_core-0.0.2-py3-none-any.whl -
Subject digest:
5c7d8d04d540736d00ff71f11f90bd6fce5ccd40e2bc9cc008fc28697893548b - Sigstore transparency entry: 2017422855
- Sigstore integration time:
-
Permalink:
travelallfly/allfly-py-libs@75b7449073ad8c223c58d48e796cdc152ba6e786 -
Branch / Tag:
refs/heads/master - Owner: https://github.com/travelallfly
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
af-di-core-publish.yml@75b7449073ad8c223c58d48e796cdc152ba6e786 -
Trigger Event:
workflow_dispatch
-
Statement type: