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-0.1.0.tar.gz (18.7 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-0.1.0-py3-none-any.whl (14.5 kB view details)

Uploaded Python 3

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

Hashes for injector_autowired-0.1.0.tar.gz
Algorithm Hash digest
SHA256 f94978afb58a660d6e92c9a7afcd6f84f3b4abaf243711d4088d7cd71ee9ae7c
MD5 cacdebfe697e16b4ca7c3ae6c26d1680
BLAKE2b-256 561333a4887419ab32bd5b4966e99c693bd0bf22fec40199fd94502a3f6315dd

See more details on using hashes here.

Provenance

The following attestation bundles were made for injector_autowired-0.1.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-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for injector_autowired-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 481c7b77a9b3020c287f1524459627e215eeaeb3f2eb74c577951d9a99ca9d62
MD5 91d69d74a543dccbcf05986b0169818e
BLAKE2b-256 7261b62aaf9d974ff5006e7601b678c6387c5651211d04bbf442b8039092a671

See more details on using hashes here.

Provenance

The following attestation bundles were made for injector_autowired-0.1.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