Injector API reference — Injector 0.21.0 documentation

# Injector API reference

Note

Unless specified otherwise, instance methods are not thread safe.

The following functions are thread safe:

- `Injector.get()`

- injection provided by `inject()` decorator (please note, however, that it doesn’t say anything about decorated function thread safety)

Injector - Python dependency injection framework, inspired by Guice

copyright:

  3. 2012 by Alec Thomas

license:

  BSD

class injector.Binder(injector: Injector, auto_bind: bool = True, parent: Binder | None = None)

  Bases: `object`

  Bind interfaces to implementations.

  Note

  This class is instantiated internally for you and there’s no need to instantiate it on your own.

  bind(interface: Type[T], to: None | T | Callable[[...], T] | Provider[T] = None, scope: None | Type[Scope] | ScopeDecorator = None) → None

    Bind an interface to an implementation.

    Binding T to an instance of T like

        binder.bind(A, to=A('some', 'thing'))

    is, for convenience, a shortcut for

        binder.bind(A, to=InstanceProvider(A('some', 'thing'))).

    Likewise, binding to a callable like

        binder.bind(A, to=some_callable)

    is a shortcut for

        binder.bind(A, to=CallableProvider(some_callable))

    and, as such, if some_callable there has any annotated parameters they’ll be provided automatically without having to use `inject()` or `Inject` with the callable.

    typing.List and typing.Dict instances are reserved for multibindings and trying to bind them here will result in an error (use `multibind()` instead):

        binder.bind(List[str], to=['hello', 'there'])  # Error

    Parameters:

      - interface – Type to bind.

      - to – Instance or class to bind to, or an instance of `Provider` subclass.

      - scope – Optional `Scope` in which to bind.

  install(module: Callable[[Binder], None] | Module | Type[Module]) → None

    Install a module into this binder.

    In this context the module is one of the following:

    - function taking the `Binder` as its only parameter

          def configure(binder):
              bind(str, to='s')

          binder.install(configure)

    - instance of `Module` (instance of its subclass counts)

          class MyModule(Module):
              def configure(self, binder):
                  binder.bind(str, to='s')

          binder.install(MyModule())

    - subclass of `Module` - the subclass needs to be instantiable so if it expects any parameters they need to be injected

          binder.install(MyModule)

  multibind(interface: Type[List[T]], to: List[T] | Callable[[...], List[T]] | Provider[List[T]], scope: Type[Scope] | ScopeDecorator | None = None) → None
  multibind(interface: Type[Dict[K, V]], to: Dict[K, V] | Callable[[...], Dict[K, V]] | Provider[Dict[K, V]], scope: Type[Scope] | ScopeDecorator | None = None) → None

    Creates or extends a multi-binding.

    A multi-binding contributes values to a list or to a dictionary. For example:

        binder.multibind(List[str], to=['some', 'strings'])
        binder.multibind(List[str], to=['other', 'strings'])
        injector.get(List[str])  # ['some', 'strings', 'other', 'strings']

        binder.multibind(Dict[str, int], to={'key': 11})
        binder.multibind(Dict[str, int], to={'other_key': 33})
        injector.get(Dict[str, int])  # {'key': 11, 'other_key': 33}

    Changed in version 0.17.0: Added support for using typing.Dict and typing.List instances as interfaces. Deprecated support for MappingKey, SequenceKey and single-item lists and dictionaries as interfaces.

    Parameters:

      - interface – typing.Dict or typing.List instance to bind to.

      - to – Instance, class to bind to, or an explicit `Provider` subclass. Must provide a list or a dictionary, depending on the interface.

      - scope – Optional Scope in which to bind.

class injector.BoundKey(interface: Type[T], **kwargs: Any)

  Bases: `tuple`

  A BoundKey provides a key to a type with pre-injected arguments.

      >>> class A:
      ...   def __init__(self, a, b):
      ...     self.a = a
      ...     self.b = b
      >>> InjectedA = BoundKey(A, a=InstanceProvider(1), b=InstanceProvider(2))
      >>> injector = Injector()
      >>> a = injector.get(InjectedA)
      >>> a.a, a.b
      (1, 2)

exception injector.CallError

  Bases: `Error`

  Call to callable object fails.

class injector.CallableProvider(callable: Callable[[...], T])

  Bases: `Provider`, `Generic`[`T`]

  Provides something using a callable.

  The callable is called every time new value is requested from the provider.

  There’s no need to explicitly use `inject()` or `Inject` with the callable as it’s assumed that, if the callable has annotated parameters, they’re meant to be provided automatically. It wouldn’t make sense any other way, as there’s no mechanism to provide parameters to the callable at a later time, so either they’ll be injected or there’ll be a CallError.

      >>> class MyClass:
      ...     def __init__(self, value: int) -> None:
      ...         self.value = value
      ...
      >>> def factory():
      ...     print('providing')
      ...     return MyClass(42)
      ...
      >>> def configure(binder):
      ...     binder.bind(MyClass, to=CallableProvider(factory))
      ...
      >>> injector = Injector(configure)
      >>> injector.get(MyClass) is injector.get(MyClass)
      providing
      providing
      False

exception injector.CircularDependency

  Bases: `Error`

  Circular dependency detected.

class injector.ClassProvider(cls: Type[T])

  Bases: `Provider`, `Generic`[`T`]

  Provides instances from a given class, created using an Injector.

exception injector.Error

  Bases: `Exception`

  Base exception.

injector.Inject

  An experimental way to declare injectable dependencies utilizing a PEP 593 implementation in Python 3.9 and backported to Python 3.7+ in typing_extensions.

  Those two declarations are equivalent:

      @inject
      def fun(t: SomeType) -> None:
          pass

      def fun(t: Inject[SomeType]) -> None:
          pass

  The advantage over using `inject()` is that if you have some noninjectable parameters it may be easier to spot what are they. Those two are equivalent:

      @inject
      @noninjectable('s')
      def fun(t: SomeType, s: SomeOtherType) -> None:
          pass

      def fun(t: Inject[SomeType], s: SomeOtherType) -> None:
          pass

  See also

  Function `get_bindings()`

    A way to inspect how various injection declarations interact with each other.

  New in version 0.18.0.

  Note

  Requires Python 3.7+.

  Note

  If you’re using mypy you need the version 0.750 or newer to fully type-check code using this construct.

  alias of `InjectT`[`InjectT`]

class injector.Injector(modules: Callable[[Binder], None] | Module | Type[Module] | Iterable[Callable[[Binder], None] | Module | Type[Module]] | None = None, auto_bind: bool = True, parent: Injector | None = None)

  Bases: `object`

  Parameters:

    - modules –

      Optional - a configuration module or iterable of configuration modules. Each module will be installed in current `Binder` using `Binder.install()`.

      Consult `Binder.install()` documentation for the details.

    - auto_bind – Whether to automatically bind missing types.

    - parent – Parent injector.

  New in version 0.7.5: `use_annotations` parameter

  Changed in version 0.13.0: `use_annotations` parameter is removed

  binder: Binder
  call_with_injection(callable: Callable[[...], T], self_: Any | None = None, args: Any = (), kwargs: Any = {}) → T

    Call a callable and provide its dependencies if needed.

    Dependencies are provided when the callable is decorated with `@inject` or some individual parameters are wrapped in `Inject` – otherwise `call_with_injection()` is equivalent to just calling the callable directly.

    If there is an overlap between arguments provided in `args` and `kwargs` and injectable dependencies the provided values take precedence and no dependency injection process will take place for the corresponding parameters.

    Parameters:

      - self – Instance of a class callable belongs to if it’s a method, None otherwise.

      - args (tuple of objects) – Arguments to pass to callable.

      - kwargs (dict of string -> object) – Keyword arguments to pass to callable.

    Returns:

      Value returned by callable.

  create_object(cls: Type[T], additional_kwargs: Any | None = None) → T

    Create a new instance, satisfying any dependencies on cls.

  get(interface: Type[T], scope: ScopeDecorator | Type[Scope] | None = None) → T

    Get an instance of the given interface.

    Note

    Although this method is part of `Injector`’s public interface it’s meant to be used in limited set of circumstances.

    For example, to create some kind of root object (application object) of your application (note that only one get call is needed, inside the Application class and any of its dependencies `inject()` can and should be used):

        class Application:

            @inject
            def __init__(self, dep1: Dep1, dep2: Dep2):
                self.dep1 = dep1
                self.dep2 = dep2

            def run(self):
                self.dep1.something()

        injector = Injector(configuration)
        application = injector.get(Application)
        application.run()

    Parameters:

      - interface – Interface whose implementation we want.

      - scope – Class of the Scope in which to resolve.

    Returns:

      An implementation of interface.

class injector.InstanceProvider(instance: T)

  Bases: `Provider`, `Generic`[`T`]

  Provide a specific instance.

      >>> class MyType:
      ...     def __init__(self):
      ...         self.contents = []
      >>> def configure(binder):
      ...     binder.bind(MyType, to=InstanceProvider(MyType()))
      ...
      >>> injector = Injector(configure)
      >>> injector.get(MyType) is injector.get(MyType)
      True
      >>> injector.get(MyType).contents.append('x')
      >>> injector.get(MyType).contents
      ['x']

class injector.Module

  Bases: `object`

  Configures injector and providers.

  configure(binder: Binder) → None

    Override to configure bindings.

injector.NoInject

  An experimental way to declare noninjectable dependencies utilizing a PEP 593 implementation in Python 3.9 and backported to Python 3.7+ in typing_extensions.

  Since `inject()` declares all function’s parameters to be injectable there needs to be a way to opt out of it. This has been provided by `noninjectable()` but noninjectable suffers from two issues:

  - You need to repeat the parameter name

  - The declaration may be relatively distance in space from the actual parameter declaration, thus hindering readability

  NoInject solves both of those concerns, for example (those two declarations are equivalent):

      @inject
      @noninjectable('b')
      def fun(a: TypeA, b: TypeB) -> None:
          pass

      @inject
      def fun(a: TypeA, b: NoInject[TypeB]) -> None:
          pass

  See also

  Function `get_bindings()`

    A way to inspect how various injection declarations interact with each other.

  New in version 0.18.0.

  Note

  Requires Python 3.7+.

  Note

  If you’re using mypy you need the version 0.750 or newer to fully type-check code using this construct.

  alias of `InjectT`[`InjectT`]

class injector.NoScope(injector: Injector)

  Bases: `Scope`

  An unscoped provider.

class injector.Provider

  Bases: `Generic`[`T`]

  Provides class instances.

class injector.ProviderOf(injector: Injector, interface: Type[T])

  Bases: `Generic`[`T`]

  Can be used to get a provider of an interface, for example:

      >>> def provide_int():
      ...     print('providing')
      ...     return 123
      >>>
      >>> def configure(binder):
      ...     binder.bind(int, to=provide_int)
      >>>
      >>> injector = Injector(configure)
      >>> provider = injector.get(ProviderOf[int])
      >>> value = provider.get()
      providing
      >>> value
      123

  get() → T

    Get an implementation for the specified interface.

class injector.Scope(injector: Injector)

  Bases: `object`

  A Scope looks up the Provider for a binding.

  By default (ie. `NoScope` ) this simply returns the default `Provider` .

  configure() → None

    Configure the scope.

  abstract get(key: Type[T], provider: Provider[T]) → Provider[T]

    Get a `Provider` for a key.

    Parameters:

      - key – The key to return a provider for.

      - provider – The default Provider associated with the key.

    Returns:

      A Provider instance that can provide an instance of key.

class injector.SingletonScope(injector: Injector)

  Bases: `Scope`

  A `Scope` that returns a per-Injector instance for a key.

  `singleton` can be used as a convenience class decorator.

      >>> class A: pass
      >>> injector = Injector()
      >>> provider = ClassProvider(A)
      >>> singleton = SingletonScope(injector)
      >>> a = singleton.get(A, provider)
      >>> b = singleton.get(A, provider)
      >>> a is b
      True

class injector.ThreadLocalScope(injector: Injector)

  Bases: `Scope`

  A `Scope` that returns a per-thread instance for a key.

exception injector.UnknownArgument

  Bases: `Error`

  Tried to mark an unknown argument as noninjectable.

exception injector.UnknownProvider

  Bases: `Error`

  Tried to bind to a type whose provider couldn’t be determined.

exception injector.UnsatisfiedRequirement(owner: object | None, interface: type)

  Bases: `Error`

  Requirement could not be satisfied.

injector.get_bindings(callable: Callable) → Dict[str, type]

  Get bindings of injectable parameters from a callable.

  If the callable is not decorated with `inject()` and does not have any of its parameters declared as injectable using `Inject` an empty dictionary will be returned. Otherwise the returned dictionary will contain a mapping between parameter names and their types with the exception of parameters excluded from dependency injection (either with `noninjectable()`, `NoInject` or only explicit injection with `Inject` being used). For example:

      >>> def function1(a: int) -> None:
      ...     pass
      ...
      >>> get_bindings(function1)
      {}

      >>> @inject
      ... def function2(a: int) -> None:
      ...     pass
      ...
      >>> get_bindings(function2)
      {'a': <class 'int'>}

      >>> @inject
      ... @noninjectable('b')
      ... def function3(a: int, b: str) -> None:
      ...     pass
      ...
      >>> get_bindings(function3)
      {'a': <class 'int'>}

      >>> # The simple case of no @inject but injection requested with Inject[...]
      >>> def function4(a: Inject[int], b: str) -> None:
      ...     pass
      ...
      >>> get_bindings(function4)
      {'a': <class 'int'>}

      >>> # Using @inject with Inject is redundant but it should not break anything
      >>> @inject
      ... def function5(a: Inject[int], b: str) -> None:
      ...     pass
      ...
      >>> get_bindings(function5)
      {'a': <class 'int'>, 'b': <class 'str'>}

      >>> # We need to be able to exclude a parameter from injection with NoInject
      >>> @inject
      ... def function6(a: int, b: NoInject[str]) -> None:
      ...     pass
      ...
      >>> get_bindings(function6)
      {'a': <class 'int'>}

      >>> # The presence of NoInject should not trigger anything on its own
      >>> def function7(a: int, b: NoInject[str]) -> None:
      ...     pass
      ...
      >>> get_bindings(function7)
      {}

  This function is used internally so by calling it you can learn what exactly Injector is going to try to provide to a callable.

injector.inject(constructor_or_class: CallableT) → CallableT
injector.inject(constructor_or_class: Type[T]) → Type[T]

  Decorator declaring parameters to be injected.

  eg.

      >>> class A:
      ...     @inject
      ...     def __init__(self, number: int, name: str):
      ...         print([number, name])
      ...
      >>> def configure(binder):
      ...     binder.bind(A)
      ...     binder.bind(int, to=123)
      ...     binder.bind(str, to='Bob')

  Use the Injector to get a new instance of A:

      >>> a = Injector(configure).get(A)
      [123, 'Bob']

  As a convenience one can decorate a class itself:

      @inject
      class B:
          def __init__(self, dependency: Dependency):
              self.dependency = dependency

  This is equivalent to decorating its constructor. In particular this provides integration with dataclasses (the order of decorator application is important here):

      @inject
      @dataclass
      class C:
          dependency: Dependency

  Note

  This decorator is to be used on class constructors (or, as a convenience, on classes). Using it on non-constructor methods worked in the past but it was an implementation detail rather than a design decision.

  Third party libraries may, however, provide support for injecting dependencies into non-constructor methods or free functions in one form or another.

  See also

  Generic type `Inject`

    A more explicit way to declare parameters as injectable.

  Function `get_bindings()`

    A way to inspect how various injection declarations interact with each other.

  Changed in version 0.16.2: (Re)added support for decorating classes with @inject.

injector.is_decorated_with_inject(function: Callable[[...], Any]) → bool

  See if given callable is declared to want some dependencies injected.

  Example use:

      >>> def fun(i: int) -> str:
      ...     return str(i)

      >>> is_decorated_with_inject(fun)
      False
      >>>
      >>> @inject
      ... def fun2(i: int) -> str:
      ...     return str(i)

      >>> is_decorated_with_inject(fun2)
      True

injector.multiprovider(function: CallableT) → CallableT

  Like `provider()`, but for multibindings. Example usage:

      class MyModule(Module):
          @multiprovider
          def provide_strs(self) -> List[str]:
              return ['str1']

      class OtherModule(Module):
          @multiprovider
          def provide_strs_also(self) -> List[str]:
              return ['str2']

      Injector([MyModule, OtherModule]).get(List[str])  # ['str1', 'str2']

  See also: `Binder.multibind()`.

injector.noninjectable(*args: str) → Callable[[CallableT], CallableT]

  Mark some parameters as not injectable.

  This serves as documentation for people reading the code and will prevent Injector from ever attempting to provide the parameters.

  For example:

      >>> class Service:
      ...    pass
      ...
      >>> class SomeClass:
      ...     @inject
      ...     @noninjectable('user_id')
      ...     def __init__(self, service: Service, user_id: int):
      ...         # ...
      ...         pass

  `noninjectable()` decorations can be stacked on top of each other and the order in which a function is decorated with `inject()` and `noninjectable()` doesn’t matter.

  See also

  Generic type `NoInject`

    A nicer way to declare parameters as noninjectable.

  Function `get_bindings()`

    A way to inspect how various injection declarations interact with each other.

injector.provider(function: CallableT) → CallableT

  Decorator for `Module` methods, registering a provider of a type.

      >>> class MyModule(Module):
      ...   @provider
      ...   def provide_name(self) -> str:
      ...       return 'Bob'

  @provider-decoration implies @inject so you can omit it and things will work just the same:

      >>> class MyModule2(Module):
      ...     def configure(self, binder):
      ...         binder.bind(int, to=654)
      ...
      ...     @provider
      ...     def provide_str(self, i: int) -> str:
      ...         return str(i)
      ...
      >>> injector = Injector(MyModule2)
      >>> injector.get(str)
      '654'