Spring-Boot-style autowiring (component scanning) for the Python injector library.
Project description
injector-autowired
Spring-Boot-style autowiring for the Python
injectorlibrary. Zero manual wiring.
Decorate your classes and factories, point a single scan() call at your
package, and get a fully wired container back. No hand-written Module
subclasses, no binder.bind(...) calls.
from injector_autowired import service, provider, scan, inject
class Clock:
def now(self) -> str: ...
@service(bind=Clock)
class SystemClock(Clock):
def now(self) -> str:
return "2026-07-12T00:00:00Z"
@service
class InvoiceService:
def __init__(self, clock: Clock): # autowired — no @inject needed
self.clock = clock
@provider(profiles=["prod"])
def make_database(config: Config) -> Database:
return PostgresDatabase(config.url) # conditional construction
# once, at startup:
container = scan("billing", profiles=["prod"])
invoices = container.get(InvoiceService)
Install
pip install injector-autowired # depends on `injector`
The one entry point: scan
container = scan("myapp", profiles=["prod"])
scan(packages, *, profiles=(), source=None, set_global=True) imports every
module under each package (recursively) so the decorators run, filters
registrations by active profile, and returns a Container. It also stores the
result as a process-wide container, so resolve(SomeType) works anywhere.
Any framework can call it once its app is ready — for example, Django from
AppConfig.ready:
class BillingConfig(AppConfig):
def ready(self):
from injector_autowired import scan
scan("billing", profiles=settings.ACTIVE_PROFILES)
Resolve things with container.get(T), or globally with resolve(T).
Decorators
| Decorator | Marks | Notes |
|---|---|---|
@component |
a class | Base marker. @service, @repository, @controller, @adapter are identical aliases that read better at call sites. |
@provider |
a factory function | Builds the instance itself; use for conditional or logic-heavy construction. The interface comes from the return annotation (or provides=). |
Both wire dependencies automatically: a plain type-annotated __init__ (or
factory signature) has its parameters injected for you.
Binding an interface
@service(bind=Clock)
class SystemClock(Clock): ...
bind= makes the class resolvable as the interface, so anything depending on
Clock gets SystemClock.
Scopes
Scopes are a Scope enum; they control instance lifetime:
| Member | Lifetime |
|---|---|
Scope.SINGLETON (default) |
One instance per container. |
Scope.TRANSIENT |
A fresh instance on every resolve. |
Scope.THREAD |
One instance per thread. |
from injector_autowired import Scope, component
@component(scope=Scope.TRANSIENT)
class RequestContext: ...
Scope is a StrEnum, so the plain strings ("transient", …) still work, and
a raw injector.ScopeDecorator is accepted for custom scopes.
Profiles
Gate a registration to certain environments. A registration with no profiles is
always active; otherwise it is active when any of its profiles matches.
Prefix with ! to mean "active unless this profile is on".
@service(bind=Notifier, profiles=["prod"])
class EmailNotifier(Notifier): ...
@service(bind=Notifier, profiles=["!prod"])
class ConsoleNotifier(Notifier): ...
scan("myapp", profiles=["prod"]).get(Notifier) # -> EmailNotifier
Custom providers
When construction is conditional or needs logic a constructor can't express, use a factory. Its parameters are injected like any component's:
@provider(scope=Scope.SINGLETON, profiles=["prod"])
def make_cache(settings: Settings) -> Cache:
return RedisCache(settings.redis_url)
Multiple implementations (qualifiers)
Register several implementations of one interface with name=, then resolve by
name — or fetch them all:
@adapter(bind=BookingProvider, name="fareharbor")
class FareHarborProvider(BookingProvider): ...
@adapter(bind=BookingProvider, name="peek")
class PeekProvider(BookingProvider): ...
container.get(BookingProvider, name="peek") # one of them
container.get_all(BookingProvider) # {"fareharbor": ..., "peek": ...}
Two unqualified components claiming the same interface is an error
(DuplicateBindingError) — give one a name= or gate them behind profiles.
Errors
DuplicateBindingError— two active, unqualified registrations claim one interface.ComponentNotFoundError— nothing (active) provides the requested interface/name.AmbiguousComponentError— only named implementations exist and noname=was given.
All inherit from AutowireError.
License
MIT © 2026 Tyler R. Suehr
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 injector_autowired-0.1.0.tar.gz.
File metadata
- Download URL: injector_autowired-0.1.0.tar.gz
- Upload date:
- Size: 18.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f94978afb58a660d6e92c9a7afcd6f84f3b4abaf243711d4088d7cd71ee9ae7c
|
|
| MD5 |
cacdebfe697e16b4ca7c3ae6c26d1680
|
|
| BLAKE2b-256 |
561333a4887419ab32bd5b4966e99c693bd0bf22fec40199fd94502a3f6315dd
|
Provenance
The following attestation bundles were made for injector_autowired-0.1.0.tar.gz:
Publisher:
publish.yml on tylersuehr7/injector-autowired
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
injector_autowired-0.1.0.tar.gz -
Subject digest:
f94978afb58a660d6e92c9a7afcd6f84f3b4abaf243711d4088d7cd71ee9ae7c - Sigstore transparency entry: 2158361993
- Sigstore integration time:
-
Permalink:
tylersuehr7/injector-autowired@f96910eda9b071724df3e74dd0c641e827d25f8c -
Branch / Tag:
refs/heads/main - Owner: https://github.com/tylersuehr7
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@f96910eda9b071724df3e74dd0c641e827d25f8c -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file injector_autowired-0.1.0-py3-none-any.whl.
File metadata
- Download URL: injector_autowired-0.1.0-py3-none-any.whl
- Upload date:
- Size: 14.5 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 |
481c7b77a9b3020c287f1524459627e215eeaeb3f2eb74c577951d9a99ca9d62
|
|
| MD5 |
91d69d74a543dccbcf05986b0169818e
|
|
| BLAKE2b-256 |
7261b62aaf9d974ff5006e7601b678c6387c5651211d04bbf442b8039092a671
|
Provenance
The following attestation bundles were made for injector_autowired-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on tylersuehr7/injector-autowired
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
injector_autowired-0.1.0-py3-none-any.whl -
Subject digest:
481c7b77a9b3020c287f1524459627e215eeaeb3f2eb74c577951d9a99ca9d62 - Sigstore transparency entry: 2158362078
- Sigstore integration time:
-
Permalink:
tylersuehr7/injector-autowired@f96910eda9b071724df3e74dd0c641e827d25f8c -
Branch / Tag:
refs/heads/main - Owner: https://github.com/tylersuehr7
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@f96910eda9b071724df3e74dd0c641e827d25f8c -
Trigger Event:
workflow_dispatch
-
Statement type: