Skip to main content

Spring-Boot-style autowiring (component scanning) for the Python injector library.

Project description

injector-autowired

Spring-Boot-style autowiring for the Python injector library. Zero manual wiring.

PyPI version Python versions License CI

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 no name= was given.

All inherit from AutowireError.

License

MIT © 2026 Tyler R. Suehr

Project details


Download files

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

Source Distribution

injector_autowired-1.0.0.tar.gz (18.4 kB view details)

Uploaded Source

Built Distribution

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

injector_autowired-1.0.0-py3-none-any.whl (14.3 kB view details)

Uploaded Python 3

File details

Details for the file injector_autowired-1.0.0.tar.gz.

File metadata

  • Download URL: injector_autowired-1.0.0.tar.gz
  • Upload date:
  • Size: 18.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for injector_autowired-1.0.0.tar.gz
Algorithm Hash digest
SHA256 63e6cb26ec495bf227e714b2c1ee007033465f76235aadf81733bd871fe075ce
MD5 e1b1adf5c07d3660d8b8958ff58f2222
BLAKE2b-256 a92d7eecb335ea8ae67f0579f967f2b7b3a95262eeff9509dc6a8282ced84da3

See more details on using hashes here.

Provenance

The following attestation bundles were made for injector_autowired-1.0.0.tar.gz:

Publisher: publish.yml on tylersuehr7/injector-autowired

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file injector_autowired-1.0.0-py3-none-any.whl.

File metadata

File hashes

Hashes for injector_autowired-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a5a3b668c98c4f2fd9469b4251bfb74c39ed966ce9c7a171c41f2191266c178b
MD5 9090b44586f443fff5833b3c7e63a67a
BLAKE2b-256 346c90e253127371caabe0aeb828fa55ab941bbe6876aaba5f77960333360bdf

See more details on using hashes here.

Provenance

The following attestation bundles were made for injector_autowired-1.0.0-py3-none-any.whl:

Publisher: publish.yml on tylersuehr7/injector-autowired

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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