voltwire-di-core
A small, dependency injection based framework for Python built on top of dependency-injector.
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.
Installation
pip install voltwire-di-core
# or with Poetry:
poetry add voltwire-di-core
Quickstart
Mark the classes you want managed with @component (pairs naturally with @attrs.define):
import attrs
from voltwire.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 voltwire.di.core import auto_discover_components, di
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.
Need exactly one shared instance instead of a new one per di.provide? Use @singleton instead of @component — it participates in the same auto-discovery and multi-pass dependency resolution, but is wired up with a providers.Singleton instead of a factory:
from voltwire.di.core import singleton
@singleton
class CacheService:
def __init__(self, settings: CacheSettings):
self._settings = settings
This is a drop-in alternative to manually calling di.register_singleton(...) after discovery — use whichever fits: @singleton for classes discovered by scanning your package, di.register_singleton for one-offs (e.g. third-party clients) inside a registrars callback.
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 voltwire.di.core import auto_discover_components, di
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 voltwire.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)
voltwire-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 voltwire.di.core import di
def register_db_providers() -> None:
di.register(DatabaseSettings, providers.Object(get_db_settings()))
di.register_singleton(SessionFactory, settings=di.get_provider(DatabaseSettings))
di.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 di.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 voltwire.di.core import (
component, # class decorator → register for auto-discovery (factory)
singleton, # class decorator → register for auto-discovery (singleton)
settings, # function decorator → register a singleton by return type
auto_discover_components, # scan a package and wire the container
di, # di.provide / di.register / di.register_factory / di.register_singleton /
# di.get_provider / di.provider_exists / di.register_core_provider /
# di.ensure_core_providers
dependency_container, # the DependencyContainer singleton
DependencyContainer, # the container type
get_component_registry, # introspection: everything @component/@singleton/@settings collected
analyze_component_dependencies, # introspection: a class's required constructor deps
is_singleton_component, # introspection: was this class marked @singleton?
providers, # re-export of dependency_injector.providers (Object/Factory/Singleton/Callable)
containers, # re-export of dependency_injector.containers
)
providers and containers are re-exported so consumers can build custom providers (e.g. providers.Object(instance)) without importing dependency-injector directly — this library is the single DI surface.
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 voltwire.di.core import dependency_container
# Make the container available on app.state at startup:
# application.state.provide = dependency_container().provide
def build(request: Request) -> GreetingService:
return request.app.state.provide(GreetingService)
GreetingServiceDI = Annotated[GreetingService, Depends(build)]
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 voltwire_di_core-0.0.2.tar.gz.
File metadata
- Download URL: voltwire_di_core-0.0.2.tar.gz
- Upload date:
- Size: 10.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.9.24 {"installer":{"name":"uv","version":"0.9.24","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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d19215db24bfabf050fb3ddd4a7eec7de659f5a748300018d2ecd116e6b8ca08
|
|
| MD5 |
275fb5423d5d1cf107443a8ca7123f6e
|
|
| BLAKE2b-256 |
b08a9efa2087416423e629c8363c4eb2fe1d2b2b9d63c46a515f51f897ea1fc8
|
File details
Details for the file voltwire_di_core-0.0.2-py3-none-any.whl.
File metadata
- Download URL: voltwire_di_core-0.0.2-py3-none-any.whl
- Upload date:
- Size: 9.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.9.24 {"installer":{"name":"uv","version":"0.9.24","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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9bcb7a12db408d3f250e6bb320d9edad78d09add54b088b888a193275088a965
|
|
| MD5 |
ec37f4472515ea366330112cfe9b8941
|
|
| BLAKE2b-256 |
50239c46270b4656f36dc304604696ea3065eb23b91b5d5c876e44222c0d8835
|