API Reference

World

world

get: antidote.core.getter.Getter

Used to retrieve a dependency from Antidote. A type hint can also be provided. The resulting dependency will be type checked if possible. Typically Protocol without runtime_checkable() will not be enforced.

>>> from antidote import world, injectable
>>> @injectable
... class Dummy:
...     pass
>>> world.get(Dummy)
<Dummy ...>
>>> # You can also provide a type hint which will be enforced if possible
>>> world.get[object](Dummy)  # Treated by Mypy as an object
<Dummy ...>
lazy = <antidote._internal.world.WorldLazy object>

Deprecated since version 1.1: If you need lazy behavior, wrap world.get() yourself. This feature barely brings anything.

Used to retrieves lazily a dependency. A type hint can be provided and the retrieved instance will be cast to match it. It follows the same philosophy as mypy and will NOT enforce it.

>>> from antidote import world, Service
>>> class Dummy(Service):
...     pass
>>> dep = world.lazy(Dummy)
>>> dep.get()
<Dummy ...>
>>> # But Mypy will only an object, not a `Dummy` instance. For this Mypy needs help:
>>> world.lazy[Dummy](Dummy).get()  # Treated by Mypy as a Dummy
<Dummy ...>
>>> # To avoid repetition, if a type hint is provided but no argument, it
... # will retrieve the type hint itself.
... world.lazy[Dummy]().get()
<Dummy ...>
freeze() None

Freezes Antidote. No additional dependencies or scope can be defined.

Its primary purpose is to state explicitly in your code when all dependencies have been defined and to offer a bit more control on Antidote’s state.

>>> from antidote import world, Service
>>> world.freeze()
>>> class Dummy(Service):
...     pass
Traceback (most recent call last):
...
FrozenWorldError
debug(dependency: object, *, depth: int = - 1) str

To help you debug issues you may encounter with your injections, this will provide a tree representing all the dependencies that will be retrieved by Antidote when instantiating the specified dependency. It will also show whether those are singletons or if there are any cyclic dependencies.

>>> from antidote import world, Service, Provide
>>> class Dummy(Service):
...     pass
>>> class Root(Service):
...     def __init__(self, dummy: Provide[Dummy]):
...         pass
>>> print(world.debug(Root))
Root
└── Dummy

Singletons have no scope markers.
<∅> = no scope (new instance each time)
<name> = custom scope

Note

The output is not part of the public API. Any variation in it will not be considered as a breaking change. So only rely on it to actively debug issues.

Warning

With implementation(), it’ll execute the function to know which dependency to follow.

Parameters
  • dependency – Root of the dependency tree.

  • depth – Maximum depth of the result tree. Defaults to -1 which implies not limit.

Returns:

provider(p: antidote.world._methods.P) antidote.world._methods.P

Class decorator used to register a new Provider.

>>> from antidote import world
>>> from antidote.core import Provider
>>> @world.provider
... class MyProvider(Provider):
...     '''Implementation missing'''
Parameters

p – Provider class

Returns

The same class, unmodified.

world.scopes

new(*, name: str) antidote.core.container.Scope

Creates a new scope. See Scope for more information on scopes.

>>> from antidote import world, Service
>>> REQUEST_SCOPE = world.scopes.new(name='request')
>>> class Dummy(Service):
...     __antidote__ = Service.Conf(scope=REQUEST_SCOPE)
Parameters

name – Friendly identifier used for debugging purposes. It must be unique.

reset(scope: antidote.core.container.Scope) None

All dependencies values of the specified scope will be discarded, so invalidating the scope. See Scope for more information on scopes.

>>> from antidote import world
>>> REQUEST_SCOPE = world.scopes.new(name='request')
>>> # All cached dependencies value with scope=REQUEST_SCOPE will be discarded.
... world.scopes.reset(REQUEST_SCOPE)
Parameters

scope – Scope to reset.

world.test

Testing utilities with Antidote. Also used by Antidote itself.

clone(*, keep_singletons: bool = False, keep_scopes: bool = False, frozen: bool = True) Iterator[None]

Clone the current container, namely scopes and providers. Existing singletons and scopes depdency values can also be propagated. Its primary use is to test existing dependencies. Hence the world will already be frozen but overridding dependencies will be possible.

>>> from antidote import world, Service
>>> class MyService(Service):
...     pass
>>> # Declared dependencies are available
... with world.test.clone():
...     world.get[MyService]()
<MyService object ...>
>>> s = world.get[MyService]()
>>> # But by default singletons are NOT kept
... with world.test.clone():
...     world.get[MyService]() is s
False
>>> # which is of course configurable
... with world.test.clone(keep_singletons=True):
...     world.get[MyService]() is s
True
>>> # You'll keep the same objects though, so be careful !
... with world.test.clone(keep_singletons=True):
...     world.get[MyService]().new_attr = "hello"
>>> world.get[MyService]().new_attr
'hello'
>>> # You cannot define new dependencies
... with world.test.clone():
...     class NewService(Service):
...         pass
Traceback (most recent call last):
...
FrozenWorldError
>>> # but they can be overridden
... with world.test.clone():
...     # dependencies can also be overridden as many times as necessary
...     world.test.override.singleton(MyService, "fake service")
...     world.get(MyService)
'fake service'
Parameters
  • keep_singletons – Whether current singletons should be kept in the new world. Beware that the same objects will be propagated. Defaults to False.

  • keep_scopes – Whether current dependency values within the existing scopes should be kept in the new world. Like keep_singletons, the same objects will be propagated. Defaults to False.

Returns

context manager within you can make your tests.

empty() Iterator[None]

Only used to test providers.

Creates an empty container. No initial providers are set up. This is useful to test one or several providers in isolation. If you’re not testing provider, consider use new() instead.

factory(dependency: Optional[Hashable] = None, *, singleton: Optional[bool] = None, scope: Optional[antidote.core.container.Scope] = Scope(name='__sentinel__')) Callable[[antidote.world.test._methods.F], antidote.world.test._methods.F]

Declare one or multiple singleton dependencies with its associated value.

>>> from antidote import world
>>> class Dummy:
...     pass
>>> with world.test.new():  # or empty()
...     @world.test.factory()
...     def build_dummy() -> Dummy:
...         return Dummy()
...     world.get[Dummy]()
<Dummy ...>
>>> with world.test.new():
...     # You don't need to rely on type hints
...     @world.test.factory(Dummy)
...     def build_dummy():
...         return Dummy()
...     world.get[Dummy]()
<Dummy ...>
Parameters
  • dependency – Dependency to override.

  • singleton – Whether the returned dependency is a singleton or not. If yes, the factory will be called at most once and the result re-used. Mutually exclusive with scope. Defaults to True.

  • scope – Scope of the returned dependency. Mutually exclusive with singleton. The scope defines if and how long the returned dependency will be cached. See Scope. Defaults to singleton().

maybe_provide_from(provider: antidote.core.container.RawProvider, dependency: Hashable) Optional[antidote.core.container.DependencyValue]

Only used to test providers.

Utility function to test _providers that have not been registered in world. The current :py:class:~.core.container.DependencyContainer` will be given as if it were registered. This allows to test exactly what has been returned by the provider.

Parameters
  • provider – provider from which the dependency should be retrieved.

  • dependency – dependency to retrieve.

Returns

dependency instance as returned by the provider.

new() Iterator[None]

Creates a new container with the same kind of providers and scopes. So it doesn’t have any dependencies defined, but any scope or provider added will be present.

>>> from antidote import world
>>> with world.test.new():
...     # Current world is empty but has the same providers and scope as the
...     # original one.
...     # You can define dependencies locally with world.test.singleton or factory
...     world.test.singleton("test", 1)
...     world.get[int]("test")
...
1
>>> # Anything done within the context manager stays there.
... world.get[int]("test")
Traceback (most recent call last):
...
DependencyNotFoundError: test
singleton(dependency: typing.Union[typing.Dict[object, object], object], value: object = <object object>, test_provider: typing.Optional[antidote._providers.world_test.WorldTestProvider] = <antidote.core.marker.InjectClassMarker object>) None

Declare one or multiple singleton dependencies with its associated value.

>>> from antidote import world
>>> with world.test.new():  # or empty()
...     world.test.singleton("test", 1)
...     # or
...     world.test.singleton({"dummy": 1})
...     world.get[int]("test")
1
Parameters
  • test_provider

  • dependency – Singleton to declare, must be hashable. If a dict is provided, it’ll be treated as a dictionary of singletons to add.

  • value – Associated value for the dependency.

world.test.override

Utilities to overrides dependencies in Antidote. Those can only be used in a test world created by world.test.clone() with overridable=True. Dependencies can be overridden multiple times, it won’t raise any error. Overridden dependencies also won’t be taken into account when Antidote checks for duplicates. Hence you may do the following:

>>> from antidote import world
>>> with world.test.clone():
...     world.test.override.singleton('test', 1)
...     # Override a second time a singleton
...     world.test.override.singleton('test', 2)
...     world.get[int]("test")
2
singleton(dependency: typing.Union[typing.Dict[typing.Hashable, object], typing.Hashable], value: object = <object object>) None

Override one or multiple dependencies with one/multiple singletons.

>>> from antidote import world, Service
>>> class MyService(Service):
...     pass
>>> world.get(MyService)
<MyService ...>
>>> with world.test.clone():
...     # dependencies can also be overridden as many times as necessary
...     world.test.override.singleton(MyService, "fake service")
...     # Within the cloned world, we changed the dependency value of MyService
...     world.get(MyService)
'fake service'
Parameters
  • dependency – Singleton to declare, must be hashable. If a dict is provided, it’ll be treated as a dictionary of singletons to add.

  • value – Associated value for the dependency.

factory(dependency: Optional[Hashable] = None, *, singleton: Optional[bool] = None, scope: Optional[antidote.core.container.Scope] = Scope(name='__sentinel__')) Callable[[antidote.world.test._override.F], antidote.world.test._override.F]

Override a dependency with the result of a factory. To be used as a function decorator. The result of the underlying function, the factory, will be used as the associated value of the dependency.

>>> from antidote import world, Service
>>> class MyService(Service):
...     pass
>>> world.get(MyService)
<MyService ...>
>>> with world.test.clone():
...     @world.test.override.factory(MyService)
...     def test():
...         return "fake service"
...     # Within the cloned world, we changed the dependency value of MyService
...     world.get(MyService)
'fake service'
>>> with world.test.clone():
...     # If not specified explicitly, the return type hint will be used
...     # as the dependency.
...     @world.test.override.factory()
...     def test() -> MyService:
...         return "fake service"
...     world.get(MyService)
'fake service'
Parameters
  • dependency – Dependency to override.

  • singleton – Whether the returned dependency is a singleton or not. If yes, the factory will be called at most once and the result re-used. Mutually exclusive with scope. Defaults to True.

  • scope – Scope of the returned dependency. Mutually exclusive with singleton. The scope defines if and how long the returned dependency will be cached. See Scope. Defaults to singleton().

Note

As all override work as a single layer on top of the usual dependencies, if the dependency is already overridden as a singleton, the factory won’t be called. Overrides done with a provider also have a higher priority. See world.test.override.

Returns

The decorated function, unchanged.

provider() Callable[[antidote.world.test._override.P], antidote.world.test._override.P]

Function decorator used to declare a simplified provider to override some dependencies. The function will be called for each dependency and should return core.DependencyValue if it can be provided or None if not.

>>> from antidote import world, Service
>>> from antidote.core import DependencyValue, Scope
>>> class MyService(Service):
...     pass
>>> world.get(MyService)
<MyService ...>
>>> with world.test.clone():
...     @world.test.override.provider()
...     def test(dependency):
...         if dependency is MyService:
...             return DependencyValue('fake service', scope=Scope.singleton())
...     # Within the cloned world, we changed the dependency value of MyService
...     world.get(MyService)
'fake service'

Note

As all override work as a single layer on top of the usual dependencies, singleton overrides won’t pass through the provider. See world.test.override.

Warning

Beware of provider(), it can conflict with factory() and singleton(). Dependencies declared with singleton() will hide provider(). And provider() will hide factory().

Moreover it won’t appear in world.debug().

Returns

Function decorator.

Utilities

config: antidote._config.Config

Singleton instance of Config

class Config

Global configuration used by Antidote to (de-)activate features.

property auto_detect_type_hints_locals: bool

New in version 1.3.

Whether inject(), injectable(), implements and wire() should rely on inspection to determine automatically the locals for their argument type_hints_locals used for typing.get_type_hints(). Deactivated by default. If activated, inspection is only used when type_hints_locals is not specified explitely.

It’s mostly interesting during tests. The following example wouldn’t work with string annotations:

>>> from __future__ import annotations
>>> from antidote import config, injectable, inject, world
>>> config.auto_detect_type_hints_locals = True
>>> def dummy_test():
...     with world.test.new():
...         @injectable
...         class Dummy:
...             pass
...
...         @inject
...         def f(dummy: Dummy = inject.me()) -> Dummy:
...             return dummy
...
...         return f() is world.get(Dummy)
>>> dummy_test()
True
is_compiled() bool

Whether current Antidote implementations is the compiled (Cython) version or not

validate_injection(dependencies: Union[None, Mapping[str, Hashable], Sequence[Optional[Hashable]], Callable[[antidote.core.injection.Arg], Optional[Hashable]]] = None, auto_provide: Optional[Optional[Union[bool, Iterable[type], Callable[[type], bool]]][Optional[Union[bool, Iterable[type], Callable[[type], bool]]]]] = None) None

Validates that injection parameters are valid. If not, an error is raised.

>>> from antidote.utils import validate_injection
>>> validate_injection(dependencies={'name': object()})
>>> validate_injection(dependencies=object())
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: ...
Parameters
  • dependencies – As defined by inject()

  • auto_provide

    Deprecated since version 1.1.

    As defined by inject()

validated_scope(scope: Optional[antidote.core.container.Scope] = Scope(name='__sentinel__'), singleton: Optional[bool] = None, *, default: Optional[antidote.core.container.Scope]) Optional[antidote.core.container.Scope]

Validates given arguments and ensures consistency between singleton and scope.

Top-level APIs exposes both singleton and scope arguments. Users can choose either, but not both. This function does all validation necessary in those APIs.

>>> from antidote import Scope, world
>>> from antidote.utils import validated_scope
>>> custom_scope = world.scopes.new(name='custom')
>>> validated_scope(singleton=None, default=custom_scope)
Scope(name='custom')
>>> validated_scope(singleton=True, default=None)
Scope(name='singleton')
>>> validated_scope(scope=Scope.singleton(), default=None)
Scope(name='singleton')
Parameters
  • scope – Scope argument to validate. Neutral value is Scope.sentinel().

  • singleton – Singleton argument to validate. Neutral value is None.

  • default – Default value to use if both scope and singleton have neutral values.

Returns

Scope defined by either singleton or scope.

Lib

Injectable

injectable(klass: typing.Optional[antidote.lib.injectable.injectable.C] = None, *, singleton: typing.Optional[bool] = None, scope: typing.Optional[antidote.core.container.Scope] = Scope(name='__sentinel__'), wiring: typing.Optional[antidote.core.wiring.Wiring] = Wiring(methods=<Methods.ALL: 1>, dependencies=None, ignore_type_hints=False, auto_provide=False, raise_on_double_injection=False), factory_method: typing.Optional[str] = None, type_hints_locals: typing.Optional[typing.Union[typing.Dict[str, object], typing.Literal['auto'], antidote._internal.utils.Default]] = Default.sentinel) Union[antidote.lib.injectable.injectable.C, Callable[[antidote.lib.injectable.injectable.C], antidote.lib.injectable.injectable.C]]

New in version 1.3.

Defines the decorated class as an injectable.

>>> from antidote import injectable
>>> @injectable
... class Dummy:
...     pass

All methods of the classe are automatically injected by default:

>>> from antidote import world, inject
>>> @injectable
... class MyService:
...     def __init__(self, dummy: Dummy = inject.me()):
...         self.dummy = dummy
>>> world.get(MyService).dummy
<Dummy object at ...>

By default all injectables are declared as singleton, meaning only one instances will be used during the application lifetime. But you can configure it however you need it:

>>> world.get(MyService) is world.get(MyService)
True
>>> @injectable(singleton=False)
... class ThrowAwayService:
...     pass
>>> world.get(ThrowAwayService) is world.get(ThrowAwayService)
False

One can also specify a factory_method instead of relying only on __init__.

>>> @injectable(factory_method='build')
... class ComplexService:
...     def __init__(self, name: str, dummy: Dummy) -> None:
...         self.name = name
...         self.dummy = dummy
...
...     @classmethod
...     def build(cls, dummy: Dummy = inject.me()) -> 'ComplexService':
...         return ComplexService('Greetings from build!', dummy)
>>> world.get(ComplexService).name
'Greetings from build!'

Note

If your wish to declare to register an external class to Antidote, prefer using a factory with factory().

Parameters
  • klass – Class to register as a dependency. It will be instantiated only when requested.

  • singleton – Whether the service is a singleton or not. A singleton is instantiated only once. Mutually exclusive with scope. Defaults to True

  • scope – Scope of the service. Mutually exclusive with singleton. The scope defines if and how long the service will be cached. See Scope. Defaults to singleton().

  • wiringWiring to be used on the class. By defaults will apply a simple inject() on all methods, so only annotated type hints are taken into account. Can be deactivated by specifying None.

  • factory_method

    Class or static method to use to build the class. Defaults to None.

    New in version 1.3.

  • type_hints_locals

    Local variables to use for typing.get_type_hints(). They can be explicitly defined by passing a dictionary or automatically detected with inspect and frame manipulation by specifying 'auto'. Specifying None will deactivate the use of locals. When ignore_type_hints is True, this features cannot be used. The default behavior depends on the config value of auto_detect_type_hints_locals. If True the default value is equivalent to specifying 'auto', otherwise to None.

    New in version 1.3.

Returns

The decorated class, unmodified, if specified or the class decorator.

service(klass: antidote.service.C, *, singleton: Optional[bool] = 'None', scope: Optional[antidote.core.container.Scope] = 'Scope.sentinel()', wiring: Optional[antidote.core.wiring.Wiring] = 'Wiring()') antidote.service.C
service(*, singleton: Optional[bool] = 'None', scope: Optional[antidote.core.container.Scope] = 'Scope.sentinel()', wiring: Optional[antidote.core.wiring.Wiring] = 'Wiring()') Callable[[antidote.service.C], antidote.service.C]

Deprecated since version 1.3: Use injectable() instead.

Defines the decorated class as a service.

>>> from antidote import service
>>> @service
... class MyService:
...     pass

Like Service it’ll automatically wire the class.

>>> from antidote import world, inject
>>> @service
... class SecondService:
...     def __init__(self, cis: MyService = inject.me()):
...         self.cis = cis
>>> world.get[SecondService]().cis
<MyService object at ...>

Note

If your wish to declare to register an external class to Antidote, prefer using a factory with factory().

Parameters
  • klass – Class to register as a dependency. It will be instantiated only when requested.

  • singleton – Whether the service is a singleton or not. A singleton is instantiated only once. Mutually exclusive with scope. Defaults to True

  • scope – Scope of the service. Mutually exclusive with singleton. The scope defines if and how long the service will be cached. See Scope. Defaults to singleton().

  • wiringWiring to be used on the class. By defaults will apply a simple inject() on all methods, so only annotated type hints are taken into account. Can be deactivated by specifying None.

Returns

The decorated class, unmodified, if specified or the class decorator.

class Service

Deprecated since version 1.1: Use injectable() instead.

Note

If you encounter conflicts with Service metaclass, consider using the class decorator service() instead. You may also use ABCService if you only need to be compatible with the abc.ABC class.

Defines subclasses as services:

>>> from antidote import Service, world, inject
>>> class Database(Service):
...     pass

The service can then be retrieved by its class:

>>> world.get(Database)  # treated as `object` by Mypy
<Database ...>
>>> # With Mypy casting
... world.get[Database]()
<Database ...>
>>> @inject([Database])
... def f(db: Database):
...     pass

Or with annotated type hints:

>>> from antidote import Provide
>>> @inject
... def f(db: Provide[Database]):
...     pass

All methods are injected by default and the service itself is a singleton. All of this can be configured with __antidote__:

>>> # Singleton by default
... world.get[Database]() is world.get[Database]()
True
>>> class Session(Service):
...     __antidote__ = Service.Conf(singleton=False)
...
...     def __init__(self, db: Provide[Database]):
...         self.db = db
...
>>> world.get[Session]() is world.get[Session]()
False

You may also parameterize the service. Parameters will be passed on to __init__():

>>> class Database(Service):
...     __antidote__ = Service.Conf(parameters=['host'])
...
...     def __init__(self, host: str):
...         self.host = host
...
...     @classmethod
...     def hosted(cls, host: str) -> object:
...         return cls.parameterized(host=host)
...
>>> test_db = world.get[Database](Database.hosted('test'))
>>> test_db.host
'test'
>>> # The factory returns a singleton so our test_session will also be one
... world.get[Database](Database.hosted('test')) is test_db
True
classmethod parameterized(**kwargs: object) object

Deprecated since version 1.1: parameterized() is a complex behavior with poor type-safety. Use-cases that really benefit from this behavior are few and would be better implemeneted explicitly in your own code.

Creates a new dependency based on the service with the given arguments. The new dependency will have the same scope as the original one.

The recommended usage is to provide a classmethod exposing only parameters that may be changed:

>>> from antidote import Service, world
>>> class Database(Service):
...     __antidote__ = Service.Conf(parameters=['host'])
...
...     def __init__(self, host: str):
...         self.host = host
...
...     @classmethod
...     def with_host(cls, host: str) -> object:
...         return cls.parameterized(host=host)
>>> db = world.get(Database.with_host(host='remote'))
>>> db.host
'remote'
>>> # As Database is defined as a singleton, the same is applied:
... world.get(Database.with_host(host='remote')) is db
True
Parameters

**kwargs – Arguments passed on to __init__().

Returns

Dependency to be retrieved from Antidote. You cannot use it directly.

class Conf(*, wiring: typing.Optional[antidote.core.wiring.Wiring] = Wiring(methods=<Methods.ALL: 1>, dependencies=None, ignore_type_hints=False, auto_provide=False, raise_on_double_injection=False), singleton: typing.Optional[bool] = None, scope: typing.Optional[antidote.core.container.Scope] = Scope(name='__sentinel__'), parameters: typing.Optional[typing.Iterable[str]] = None)

Deprecated since version 1.1.

Immutable service configuration. To change parameters on a existing instance, use either method copy() or core.wiring.WithWiringMixin.with_wiring().

Parameters
  • wiring – Wiring to be applied on the service. By default only __init__() will be wired. Unless factory is class method name, in which case only the latter would be wired. To deactivate any wiring at all use None.

  • singleton – Whether the service is a singleton or not. A singleton is instantiated only once. Mutually exclusive with scope. Defaults to True

  • scope – Scope of the service. Mutually exclusive with singleton. The scope defines if and how long the service will be cached. See Scope. Defaults to singleton().

copy(*, wiring: Union[antidote.core.wiring.Wiring, None, antidote._internal.utils.Copy] = Copy.IDENTICAL, singleton: Union[bool, None, antidote._internal.utils.Copy] = Copy.IDENTICAL, scope: Union[antidote.core.container.Scope, None, antidote._internal.utils.Copy] = Copy.IDENTICAL, parameters: Union[Iterable[str], None, antidote._internal.utils.Copy] = Copy.IDENTICAL) antidote.service.Service.Conf

Deprecated since version 1.1.

Copies current configuration and overrides only specified arguments. Accepts the same arguments as __init__()

with_wiring(*, methods: Union[antidote.core.wiring.Methods, Iterable[str], antidote._internal.utils.Copy] = Copy.IDENTICAL, dependencies: Union[None, Mapping[str, Hashable], Sequence[Optional[Hashable]], Callable[[antidote.core.injection.Arg], Optional[Hashable]], antidote._internal.utils.Copy] = Copy.IDENTICAL, auto_provide: Union[bool, Iterable[type], Callable[[type], bool], None, antidote._internal.utils.Copy][Union[bool, Iterable[type], Callable[[type], bool], None, antidote._internal.utils.Copy]] = Copy.IDENTICAL, raise_on_double_injection: Union[bool, antidote._internal.utils.Copy] = Copy.IDENTICAL, ignore_type_hints: Union[bool, antidote._internal.utils.Copy] = Copy.IDENTICAL) antidote.core.wiring.W

Accepts the same arguments as Wiring. Its only purpose is to provide a easier way to change the wiring:

conf.with_wiring(dependencies={})
# instead of
conf.copy(wiring=conf.wiring.copy(dependencies={}))
class ABCService

Deprecated since version 1.1: Use injectable() instead.

This class only purpose is to facilitate the use of a abstract parent class, relying on abc.ABC, with Service.

>>> from abc import ABC, abstractmethod
>>> from antidote import ABCService, world
>>> class AbstractClass(ABC):
...     @abstractmethod
...     def hello(self) -> str:
...         pass
>>> class MyService(AbstractClass, ABCService):
...     def hello(self) -> str:
...         return "world"
>>> world.get[MyService]().hello()
'world'

Factory

factory(f: antidote.factory.T, *, singleton: Optional[bool] = 'None', scope: Optional[antidote.core.container.Scope] = 'Scope.sentinel()', wiring: Optional[antidote.core.wiring.Wiring] = 'Wiring()') antidote.factory.T
factory(*, singleton: Optional[bool] = 'None', scope: Optional[antidote.core.container.Scope] = 'Scope.sentinel()', wiring: Optional[antidote.core.wiring.Wiring] = 'Wiring()') Callable[[antidote.factory.T], antidote.factory.T]

Registers a factory which provides as single dependency, defined through the return type annotation.

>>> from antidote import factory
>>> class Database:
...     pass
>>> @factory
... def load_db() -> Database:
...     return Database()

Now to retrieve the dependency:

>>> from antidote import inject, world
>>> @inject
... def f(db: Database = inject.me(source=load_db)) -> Database:
...     return db
>>> f() is world.get(Database, source=load_db)
True

inject() supports two other alternatives:

>>> from typing import Annotated
>>> from antidote import From, Get
>>> @inject
... def f(db: Annotated[Database, From(load_db)]) -> Database:
...     return db
>>> @inject({'db': Get(Database, source=load_db)})
... def f(db: Database) -> Database:
...     return db

It’s also possible to have a stateful factory using a class. The class will be instantiated only once.

>>> @factory
... class DatabaseFactory:
...     def __call__(self) -> Database:
...         return Database()
Parameters
  • f – Factory function or class which builds the dependency.

  • singleton – Whether the returned dependency is a singleton or not. If so, the factory will be called at most once and the result re-used. Mutually exclusive with scope. Defaults to True.

  • scope – Scope of the returned dependency. Mutually exclusive with singleton. The scope defines if and how long the returned dependency will be cached. See Scope. Defaults to singleton().

  • wiringWiring to be used on the class. By defaults will apply a simple inject() on all methods, so only annotated type hints are taken into account. Can be deactivated by specifying None. If the factory is a function, it’ll only be injected if not None.

Returns

The factory or the decorator.

class Factory

Deprecated since version 1.1: Use factory() instead.

Defines sublcass as a factory to Antidote. The provided dependency is defined through the type annotation of __call__:

>>> from antidote import Factory
>>> class Database:
...     pass
...
>>> class DatabaseLoader(Factory):
...     def __call__(self) -> Database:
...         return Database()

To retrieve the dependency from Antidote you need to use a specific syntax dependency @ factory as presented in the following examples. The goal of it is twofold:

  • Ensure that the factory is loaded whenever you require the dependency.

  • Better maintainability as you know where the dependency comes from.

>>> from antidote import world, inject
>>> world.get(Database @ DatabaseLoader)  # treated as `object` by Mypy
<Database ...>
>>> # With Mypy casting
... world.get[Database](Database @ DatabaseLoader)
<Database ...>
>>> # Concise Mypy casting
... world.get[Database] @ DatabaseLoader
<Database ...>
>>> @inject([Database @ DatabaseLoader])
... def f(db: Database):
...     pass

Or with annotated type hints:

>>> from typing import Annotated
... # from typing_extensions import Annotated # Python < 3.9
>>> from antidote import From
>>> @inject
... def f(db: Annotated[Database, From(DatabaseLoader)]):
...     pass

Note

If you only need a simple function, consider using factory() instead. It behaves the same way as above

All methods are injected by default and the factory returns a singleton by default. All of this can be configured with __antidote__:

>>> # Singleton by default
... world.get[Database] @ DatabaseLoader is world.get[Database] @ DatabaseLoader
True
>>> class Session:
...     pass
>>> # The factory will be called anew each time a `Session` is needed. But the
... # factory itself, `SessionFactory`, will only be created once.
... class SessionFactory(Factory):
...     __antidote__ = Factory.Conf(singleton=False)
...
...     def __init__(self, db: Annotated[Database, From(DatabaseLoader)]):
...         self.db = db
...
...     def __call__(self) -> Session:
...         return Session()
...
>>> world.get[Session] @ SessionFactory is world.get[Session] @ SessionFactory
False

You may also parameterize the service. Parameters will be passed on to __call__():

>>> class Database:
...     def __init__(self, host: str):
...         self.host = host
...
>>> class DatabaseFactory(Factory):
...     __antidote__ = Factory.Conf(parameters=['host'])
...
...     def __call__(self, host: str) -> Database:
...         return Database(host)
...
...     @classmethod
...     def hosted(cls, host: str) -> object:
...          return cls.parameterized(host=host)
...
>>> test_db = world.get[Database] @ DatabaseFactory.hosted('test')
>>> test_db.host
'test'
>>> # The factory returns a singleton so our test_session will also be one
... world.get[Database] @ DatabaseFactory.hosted('test') is test_db
True
classmethod parameterized(**kwargs: object) antidote._factory.PreBuild

Deprecated since version 1.1: parameterized() is a complex behavior with poor type-safety. Use-cases that really benefit from this behavior are few and would be better implemeneted explicitly in your own code.

Creates a new dependency based on the factory with the given arguments. The new dependency will have the same scope as the original one.

The recommended usage is to provide a classmethod exposing only parameters that may be changed:

>>> from antidote import Factory, world
>>> class Database:
...     def __init__(self, host: str):
...         self.host = host
>>> class DatabaseFactory(Factory):
...     __antidote__ = Factory.Conf(parameters=['host'])
...
...     def __call__(self, host: str) -> Database:
...         return Database(host)
...
...     @classmethod
...     def with_host(cls, host: str) -> object:
...         return cls.parameterized(host=host)
>>> db = world.get(Database @ DatabaseFactory.with_host(host='remote'))
>>> # or with Mypy type hint
... db = world.get[Database] @ DatabaseFactory.with_host(host='remote')
>>> db.host
'remote'
>>> # As DatabaseFactory is defined to return a singleton,
... # the same is applied:
... world.get(Database @ DatabaseFactory.with_host(host='remote')) is db
True
Parameters

**kwargs – Arguments passed on to __call__().

Returns

Dependency to be retrieved from Antidote.

class Conf(*, wiring: typing.Optional[antidote.core.wiring.Wiring] = Wiring(methods=<Methods.ALL: 1>, dependencies=None, ignore_type_hints=False, auto_provide=False, raise_on_double_injection=False), singleton: typing.Optional[bool] = None, scope: typing.Optional[antidote.core.container.Scope] = Scope(name='__sentinel__'), parameters: typing.Optional[typing.Iterable[str]] = None)

Deprecated since version 1.1.

Immutable factory configuration. To change parameters on a existing instance, use either method copy() or core.wiring.WithWiringMixin.with_wiring().

Parameters
  • wiring – Wiring to be applied on the factory. By default only __init__() and __call__() will be wired. To deactivate any wiring at all use None.

  • singleton – Whether the returned dependency is a singleton or not. If yes, the factory will be called at most once and the result re-used. Mutually exclusive with scope. Defaults to True

  • scope – Scope of the returned dependency. Mutually exclusive with singleton. The scope defines if and how long the returned dependency will be cached. See Scope. Defaults to singleton().

copy(*, wiring: Union[antidote.core.wiring.Wiring, None, antidote._internal.utils.Copy] = Copy.IDENTICAL, singleton: Union[bool, antidote._internal.utils.Copy] = Copy.IDENTICAL, scope: Union[antidote.core.container.Scope, None, antidote._internal.utils.Copy] = Copy.IDENTICAL, parameters: Union[Iterable[str], None, antidote._internal.utils.Copy] = Copy.IDENTICAL) antidote.factory.Factory.Conf

Deprecated since version 1.1.

Copies current configuration and overrides only specified arguments. Accepts the same arguments as __init__()

with_wiring(*, methods: Union[antidote.core.wiring.Methods, Iterable[str], antidote._internal.utils.Copy] = Copy.IDENTICAL, dependencies: Union[None, Mapping[str, Hashable], Sequence[Optional[Hashable]], Callable[[antidote.core.injection.Arg], Optional[Hashable]], antidote._internal.utils.Copy] = Copy.IDENTICAL, auto_provide: Union[bool, Iterable[type], Callable[[type], bool], None, antidote._internal.utils.Copy][Union[bool, Iterable[type], Callable[[type], bool], None, antidote._internal.utils.Copy]] = Copy.IDENTICAL, raise_on_double_injection: Union[bool, antidote._internal.utils.Copy] = Copy.IDENTICAL, ignore_type_hints: Union[bool, antidote._internal.utils.Copy] = Copy.IDENTICAL) antidote.core.wiring.W

Accepts the same arguments as Wiring. Its only purpose is to provide a easier way to change the wiring:

conf.with_wiring(dependencies={})
# instead of
conf.copy(wiring=conf.wiring.copy(dependencies={}))

Constants

class Constants

Used to declare constants, typically configuration which may be retrieved from the environment, a file, etc…

Constants are simply defined with const():

>>> from antidote import Constants, const, world
>>> class Config(Constants):
...     PORT = const(80)
...     HOST = const('localhost')
>>> world.get[int](Config.PORT)
80
>>> world.get[str](Config.HOST)
'localhost'
>>> # Or directly from an instance, for testing typically:
... Config().PORT
80

Constants really shines when you need to change how you retrieve the constants. For example, let’s suppose we need to load our configuration from a file:

>>> class Config(Constants):
...     PORT = const[int]('port')
...     WRONG_TYPE = const[Constants]('port')
...     HOST = const[str]('host')
...
...     def __init__(self):
...         self._data = {'host': 'localhost', 'port': '80'}
...
...     def provide_const(self, name: str, arg: str):
...         return self._data[arg]
...
>>> # the actual constant will be auto casted to the type hint if present and
... # is one of {str, float, int}. In all cases, the type of the constant value
... # is always checked at runtime.
... world.get(Config.PORT)
80
>>> Config().WRONG_TYPE
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError
>>> # The type hint is also used as... a type hint !
... Config().PORT  # will be treated as an int by Mypy
80
>>> world.get(Config.PORT)  # will also be treated as an int by Mypy
80
>>> world.get(Config.HOST)
'localhost'

In the previous example we’re also using the auto_cast feature. It provides a type hint for Mypy and will be used to cast the result of Constants.provide_const() if one of str, int or float. It can be configured to be either deactivated or extended to support enums for example.

Another useful feature is default which simply defines the default value to be used if a LookUpError is raised in Constants.provide_const(). It must already have the correct type, auto_cast will not be applied on it.

>>> class Config(Constants):
...     PORT = const[int]('port', default=80)
...     HOST = const[str]('host')
...
...     def __init__(self):
...         self._data = {'host': 'localhost'}
...
...     def provide_const(self, name: str, arg: str):
...         return self._data[arg]
...
>>> world.get(Config.PORT)
80
>>> world.get(Config.HOST)
'localhost'
class Conf(*, auto_cast: typing.Union[typing.Iterable[type], bool] = True, wiring: typing.Optional[antidote.core.wiring.Wiring] = Wiring(methods=<Methods.ALL: 1>, dependencies=None, ignore_type_hints=False, auto_provide=False, raise_on_double_injection=False))

Immutable Constants configuration. To change parameters on a existing instance, use either method copy() or with_wiring().

Parameters
  • wiringWiring used on the class. Defaults to wire only __init__().

  • auto_cast – When the type of the constant is specified with const(), the value of the dependency will be cast to its type if it’s one of str, float or int by default. You can disable this by specifying code:auto_cast=False or change the types for which it’s done by specifying explicitly those types.

copy(*, wiring: Union[antidote.core.wiring.Wiring, None, antidote._internal.utils.Copy] = Copy.IDENTICAL, auto_cast: Union[Sequence[type], bool, antidote._internal.utils.Copy] = Copy.IDENTICAL) antidote.constants.Constants.Conf

Copies current configuration and overrides only specified arguments. Accepts the same arguments as __init__()

provide_const(*, name: str, arg: Optional[Any]) object

Used to retrieve the value of the constant defined with const(). If a LookUpError is raised, the default value defined with const() will be used if any.

Parameters
  • name – Name of the constant

  • arg – Argument given to const() when creating the constant.

Returns

Constant value.

Lazy

class LazyCall(func: Callable[[...], object], *, singleton: Optional[bool] = None, scope: Optional[antidote.core.container.Scope] = Scope(name='__sentinel__'))

Declares the result of a function call as a depdency.

>>> from antidote import LazyCall, world
>>> def f(x, y):
...     return x + y
>>> Computation = LazyCall(f)(2, y=3)
>>> world.get(Computation)
5
>>> user = 'John'
>>> def hello():
...     return f"Hello {user}"
>>> HelloUser = LazyCall(hello, singleton=False)
>>> world.get(HelloUser)
'Hello John'
>>> user = "Adam"
>>> world.get(HelloUser)
'Hello Adam'
Parameters
  • func – Function to lazily call, any (keyword-)arguments given by the returned LazyCall itself will be propagated.

  • singleton – Whether the lazy dependency is a singleton or not. If yes, the function will be called at most once and the result re-used. Mutually exclusive with scope. Defaults to True.

  • scope – Scope of the dependency. Mutually exclusive with singleton. The scope defines if and how long the returned dependency will be cached. See Scope. Defaults to singleton().

class LazyMethodCall(method: Callable[[...], object], *, singleton: Optional[bool] = None, scope: antidote.core.container.Scope = Scope(name='__sentinel__'))

Similar to LazyCall but adapted to methods within a class definition. It can only be used with a Service subclass.

LazyMethodCall behaves in a similar way as constants in Constants. When accessed as class attributes, the dependency is returned. But on an instance, the actual method call result is returned.

>>> from antidote import LazyMethodCall, Service, world
>>> class Api(Service):
...     def __init__(self, host: str = 'localhost'):
...         self.host = host
...
...     def query(self, url: str = '/status'):
...         return f"requested {self.host}{url}"
...
...     status = LazyMethodCall(query, singleton=False)
...     conf = LazyMethodCall(query)('/conf')
>>> world.get(Api.conf)
'requested localhost/conf'
>>> world.get(Api.status)
'requested localhost/status'
>>> # For ease of use, accessing the dependency through a instance will simply
... # call the method without passing through Antidote. Useful for tests typically
... Api(host='example.com').status
'requested example.com/status'

Note

Consider using Constants if you only declare constants.

Parameters
  • method – Method name or the method itself that must be called.

  • singleton – Whether the lazy dependency is a singleton or not. If yes, the function will be called at most once and the result re-used. Mutually exclusive with scope. Defaults to True.

  • scope – Scope of the dependency. Mutually exclusive with singleton. The scope defines if and how long the returned dependency will be cached. See Scope. Defaults to singleton().

Interface

interface(klass: antidote.lib.interface.interface.C) antidote.lib.interface.interface.C

New in version 1.2.

Declares a class as an interface. One or multiple implementations can then be declared for it:

>>> from antidote import interface, implements, inject, world
>>> @interface
... class Service:
...     pass
>>> @implements(Service)
... class ServiceImpl(Service):
...     pass
>>> @inject
... def f(service: Service = inject.me()) -> Service:
...     return service
>>> f()
<ServiceImpl ...>
>>> world.get[Service].single()
<ServiceImpl ...>
>>> world.get(Service)  # equivalent to previous line
<ServiceImpl ...>

Alternative implementations can be declared and one can retrieve all of them at once:

>>> @implements(Service)
... class ServiceImplV2(Service):
...     pass
>>> @inject
... def f(services: list[Service] = inject.me()) -> list[Service]:
...     return services
>>> f()
[<ServiceImplV2 ...>, <ServiceImpl ...>]
>>> world.get[Service].all()
[<ServiceImplV2 ...>, <ServiceImpl ...>]

However, as defined Antidote is not able to provide a single implementation anymore.

>>> world.get[Service].single()
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
DependencyInstantiationError: ...

To enable Antidote to select one implementation among many, you need to use Predicate and eventually PredicateConstraint. Antidote currently only provides one kind out of the box QualifiedBy.

Qualifiers can be used to narrow the candidate implementations for an interface. Qualifiers are matched by their id() and not their equality.

>>> from enum import Enum, auto
>>> from antidote import interface, implements, inject, world
>>> class System(Enum):
...     LINUX = auto()
...     WINDOWS = auto()
>>> V1 = object()
>>> def _(x):  # Necessary for Python <3.9 (PEP 614)
...     return x
>>> @interface
... class Command:
...     def execute(self) -> str:
...         raise NotImplementedError()
>>> @_(implements(Command).when(qualified_by=[System.LINUX, V1]))
... class LinuxCommand(Command):
...     def execute(self) -> str:
...         return "Linux"
>>> @_(implements(Command).when(qualified_by=[System.WINDOWS, V1]))
... class WindowsCommand(Command):
...     def execute(self) -> str:
...         return "Windows"
>>> @inject
... def run_command(command: Command = inject.me(qualified_by=System.LINUX)) -> str:
...     return command.execute()
>>> run_command()
'Linux'
>>> world.get[Command].all(qualified_by=V1)
[<WindowsCommand ...>, <LinuxCommand ...>]

Antidote also provides more complex ways to select specific qualifiers such as:

>>> world.get[Command].all(qualified_by_one_of=[System.WINDOWS, System.LINUX])
[<WindowsCommand ...>, <LinuxCommand ...>]
Parameters

klass – Interface class which implementations should implement. Implementations should be a subclass of it. The interface can also be a Protocol in which case type checking will only be enforced if runtime_checkable() is used.

Returns

decorated interface class.

class implements(_implements__interface: antidote.lib.interface.interface.Itf, *, type_hints_locals: Optional[Union[Dict[str, object], Literal['auto'], antidote._internal.utils.Default]] = Default.sentinel)

New in version 1.2.

Declares the decorated class to be a candidate implementation for the specified interface. The interface can also be a Protocol. If the interface is a regular class or a runtime_checkable() Protocol, the implementation will be type checked.

>>> from antidote import interface, implements, inject, world
>>> @interface
... class Service:
...     pass
>>> @implements(Service)
... class ServiceImpl(Service):
...     pass
>>> @implements(Service)
... class BadImpl:
...     pass
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: ...
Parameters
  • __interface – Interface class.

  • type_hints_locals

    Local variables to use for typing.get_type_hints(). They can be explicitly defined by passing a dictionary or automatically detected with inspect and frame manipulation by specifying 'auto'. Specifying None will deactivate the use of locals. When ignore_type_hints is True, this features cannot be used. The default behavior depends on the config value of auto_detect_type_hints_locals. If True the default value is equivalent to specifying 'auto', otherwise to None.

    New in version 1.3.

when(*_predicates: Predicate[Weight] | Predicate[NeutralWeight], qualified_by: Optional[object | list[object]] = None) Callable[[C], C]

Associate Predicate with the decorated implementation. The implementation will only be used if all the predicates return a weight. In case multiple implementations are valid candidates, their ordering is determined by the total weight of all predicates.

Note

See interface() for examples.

Parameters
  • *_predicates – Objects implementing the Predicate protocol.

  • qualified_by – An object, or a list of it, by which the implementation is qualified. Those qualifiers can then be used at runtime to narrow the possible implementations for an interface. Beware, qualifiers rely on the id() of the objects, not their equality.

Returns

class decorator

class ImplementationsOf(interface: typing.Type[antidote.lib.interface.interface.T], *, provider: antidote.lib.interface._provider.InterfaceProvider = Get(dependency=<class 'antidote.lib.interface._provider.InterfaceProvider'>, default=<Default.sentinel: 1>))

New in version 1.2.

Used to construct the actual dependency for the provider handling the interfaces.

>>> from antidote import interface, implements, inject, world, ImplementationsOf
>>> @interface
... class Service:
...     pass
>>> @implements(Service)
... class ServiceImpl(Service):
...     pass
>>> world.get[Service].single()
<ServiceImpl ...>
>>> # the former is equivalent to:
... world.get(ImplementationsOf(Service).single())
<ServiceImpl ...>
Parameters

interface – Interface for which implementations should be retrieved. It must have been decorated with interface().

all(*constraints: PredicateConstraint[Any], qualified_by: Optional[object | list[object]] = None, qualified_by_one_of: Optional[list[object]] = None) Dependency[list[T]]

Construct the dependency to retrieve all implementations matching given constraints for the specified interface. Having no implementation matching the constraints will not raise an error, but instead an empty list will be retrieved.

Parameters
  • *constraintsPredicateConstraint to evaluate for each implementation.

  • qualified_by – All specified qualifiers must qualify the implementation.

  • qualified_by_one_of – At least one of the specified qualifiers must qualify the implementation.

Returns

A dependency for the list of implementations matching the constraints.

single(*constraints: PredicateConstraint[Any], qualified_by: Optional[object | list[object]] = None, qualified_by_one_of: Optional[list[object]] = None) Dependency[T]

Construct the dependency to retrieve a single implementation matching given constraints for the specified interface. If multiple or no implementation is found, an error will be raised upon retrieval.

Parameters
  • *constraintsPredicateConstraint to evaluate for each implementation.

  • qualified_by – All specified qualifiers must qualify the implementation.

  • qualified_by_one_of – At least one of the specified qualifiers must qualify the implementation.

Returns

A dependency for the list of implementations matching the constraints.

class QualifiedBy(*qualifiers: object)

Qualifiers for interface() / implements. Implementations can be qualified with one or multiple objects. One can then impose some constraints on those qualifiers to retrieve only a selection of implementations.

Qualifiers are identified by their id() and not their equality. Moreover builtins types, such as int or str, cannot be used. This ensures that usage of a specific qualifiers is easy to track.

>>> from antidote import QualifiedBy, interface, implements, world, inject
>>> V1, V2, V3 = object(), object(), object()
>>> @interface
... class Alert:
...     pass
>>> @implements(Alert).when(qualified_by=V1)
... class AlertV1(Alert):
...     pass
>>> @implements(Alert).when(qualified_by=[V2, V3])
... class AlertV2andV3(Alert):
...     pass
>>> world.get[Alert].single(qualified_by=V1)
<AlertV1 object at ...>
>>> @inject
... def v1alert(alert: Alert = inject.me(qualified_by=V1)) -> Alert:
...     return alert
>>> v1alert()
<AlertV1 object at ...>

QualifiedBy can also be used directly:

>>> @implements(Alert).when(QualifiedBy(V3))
... class AlertV3(Alert):
...     pass
>>> world.get[Alert].single(QualifiedBy(V1))
<AlertV1 object at ...>

Multiple constraints can be used to query the exact implementation one needs, qualified_by enforces that the specified qualifiers are present:

>>> world.get[Alert].single(qualified_by=[V2, V3])
<AlertV2andV3 object at ...>
>>> world.get[Alert].all(qualified_by=V3)
[<AlertV3 object at ...>, <AlertV2andV3 object at ...>]

One can also require that at least one qualifier of a list must be present with QualifiedBy.one_of() or qualified_by_one_of.

All of those constraints can be used together or multiple times without any issues.

Parameters

*qualifiers – Qualifiers to use for an implementation.

classmethod one_of(*qualifiers: object) antidote.lib.interface.predicate.PredicateConstraint[antidote.lib.interface.qualifier.QualifiedBy]

Constraints enforcing the presence of a least one qualifier on a implementation.

>>> from antidote import QualifiedBy, interface, implements, world
>>> V1, V2 = object(), object()
>>> @interface
... class Alert:
...     pass
>>> @implements(Alert).when(qualified_by=V1)
... class AlertV1(Alert):
...     pass
>>> @implements(Alert).when(qualified_by=V2)
... class AlertV2(Alert):
...     pass
>>> world.get[Alert].all(qualified_by_one_of=[V1, V2])
[<AlertV2 object at ...>, <AlertV1 object at ...>]
>>> world.get[Alert].all(QualifiedBy.one_of(V1, V2))
[<AlertV2 object at ...>, <AlertV1 object at ...>]
Parameters

*qualifiers – All potential qualifiers.

Predicate (experimental)

class NeutralWeight

Simple PredicateWeight implementation where the weight always stays the same, neutral. All implementations are treated equally.

class Predicate(*args, **kwargs)

A predicate can be used to define in which conditions an implementations should be used. A single method must be implemented weight() which should return an optional PredicateWeight. It is called immediately at import time. The weight is used to determine the ordering of the implementations. If None is returned, the implementation will not be used at all, which allows one to customize which implementations are available at import time.

Antidote only provides a single weight system out of the box, NeutralWeight which as its name implies does not provide any ordering. All implementations are treated equally. You’re free to use your own weight though, see PredicateWeight for more details.

>>> from typing import Optional
>>> import os
>>> from antidote import Constants, const, inject, interface, implements, world
>>> from antidote.lib.interface import NeutralWeight
>>> class Conf(Constants):
...     CLOUD = const[str]('aws')
>>> class InCloud:
...     def __init__(self, cloud: str) -> None:
...         self.cloud = cloud
...
...     @inject
...     def weight(self, cloud: str = Conf.CLOUD) -> Optional[NeutralWeight]:
...         if cloud == self.cloud:
...             return NeutralWeight()
>>> @interface
... class ObjectStorage:
...     def put(self, name: str, data: bytes) -> None:
...         pass
>>> @implements(ObjectStorage).when(InCloud('aws'))
... class AwsStorage(ObjectStorage):
...     pass
>>> @implements(ObjectStorage).when(InCloud('gcp'))
... class GCPStorage(ObjectStorage):
...     pass
>>> world.get[ObjectStorage].all()
[<AwsStorage ...>]

Predicates can share mother classes, but only one predicate of a specific type can be applied on a implementation:

>>> @implements(ObjectStorage).when(InCloud('gcp'), InCloud('gcp'))
... class GCPStorage(ObjectStorage):
...     pass
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
RuntimeError: Cannot have multiple predicates ...

To provide some flexibility Predicate can be merged if they implement MergeablePredicate.

class PredicateWeight(*args, **kwargs)

The weight defines the ordering of the implementations. When requesting all implementations, their ordering is the one defined by their weight. For a single implementation, it’s the one with the highest weight. If multiple implementations have the highest weight, an exception will be raised when requesting a single implementation.

A weight must define the operator < for the ordering, + to sum the weights of multiple predicates and the method of_neutral to handle predicates with a neutral NeutralWeight.

Mixing predicates and/or implementations with NeutralWeight and your custom weight is supported as long as your method of_neutral can provide a weight. However, multiple custom weights are not.

All methods are only called at import time, when declaring dependencies.

>>> from typing import Optional, Any
>>> from antidote.lib.interface import Predicate, QualifiedBy
>>> class Weight:
...     def __init__(self, value: int) -> None:
...         self.value = value
...
...     @classmethod
...     def of_neutral(cls, predicate: Optional[Predicate[Any]]) -> 'Weight':
...         if isinstance(predicate, QualifiedBy):
...             return Weight(len(predicate.qualifiers))
...         return Weight(0)
...
...     def __lt__(self, other: 'Weight') -> bool:
...         return self.value < other.value
...
...     def __add__(self, other: 'Weight') -> 'Weight':
...         return Weight(self.value + other.value)
classmethod of_neutral(predicate: Optional[antidote.lib.interface.predicate.Predicate[Any]]) antidote.lib.interface.predicate.SelfWeight

Called when a predicate has a NeutralWeight or when an implementation has no weight at all. In which case None is given as argument.

Parameters

predicate – Neutral weighted predicate or None for an implementation without predicates.

Returns

Weight of the predicate or implementation.

class PredicateConstraint(*args, **kwargs)

A constraint can be used to define at runtime whether a given predicate matches a specific criteria or not. Antidote will evaluate the constraint on all predicate that matches the argument type hint. If not predicate of this type is present on an implementation, it is evaluated with None instead.

>>> from typing import Optional, Protocol
>>> from antidote import QualifiedBy, interface, implements, world
>>> class NotQualified:
...     def evaluate(self, predicate: Optional[QualifiedBy]) -> bool:
...         return predicate is None
>>> class AtLeastTwoQualifiers:
...     def evaluate(self, predicate: Optional[QualifiedBy]) -> bool:
...         return predicate is not None and len(predicate.qualifiers) >= 2
>>> @interface
... class Dummy(Protocol):
...     pass
>>> @implements(Dummy)
... class NoQualifiers:
...     pass
>>> @implements(Dummy).when(qualified_by=object())
... class OneQualifier:
...     pass
>>> @implements(Dummy).when(qualified_by=[object(), object()])
... class TwoQualifiers:
...     pass
>>> world.get[Dummy].single(NotQualified())
<NoQualifiers ...>
>>> world.get[Dummy].single(AtLeastTwoQualifiers())
<TwoQualifiers ...>

Contrary to Predicate you can use multiple instances of a single constraint class. But, you can still implement MergeablePredicateConstraint which allows for runtime optimization by merging them.

class MergeablePredicateConstraint(*args, **kwargs)

A PredicateConstraint implementing merge allows for runtime optimization by merging all constraints into one.

>>> from typing import Optional
>>> from antidote.lib.interface import NeutralWeight, QualifiedBy
>>> class NotQualified:
...     @classmethod
...     def merge(cls, a: 'NotQualified', b: 'NotQualified') -> 'NotQualified':
...         return a
...
...     def evaluate(self, predicate: Optional[QualifiedBy]) -> bool:
...         return predicate is None
class MergeablePredicate(*args, **kwargs)

A Predicate implementing merge can be specified multiple times. All instances will be merged at import time, ensuring only one instance exists for a given implementation.

>>> from typing import Optional
>>> from antidote.lib.interface import NeutralWeight
>>> class UseMe:
...     @classmethod
...     def merge(cls, a: 'UseMe', b: 'UseMe') -> 'UseMe':
...         return UseMe(a.condition and b.condition)
...
...     def __init__(self, condition: bool) -> None:
...         self.condition = condition
...
...     def weight(self) -> Optional[NeutralWeight]:
...         return NeutralWeight() if self.condition else None

Implementation

implementation(interface: type, *, permanent: bool = True) Callable[[Callable[[antidote.implementation.P], antidote.implementation.T]], antidote.implementation.ImplementationProtocol[antidote.implementation.P, antidote.implementation.T]]

Deprecated since version 1.2: Use interface() instead. While you cannot use @factory with it anymore, it’s a lot more flexible for service discovery.

Defines which dependency should be retrieved when interface is requested. Suppose we have to support two different databases and the configuration defines which one should be chosen:

>>> from antidote import Constants, const
>>> class Config(Constants):
...     DB = const('postgres')
>>> # The interface
... class Database:
...     pass
>>> from antidote import Service, factory
>>> class PostgreSQL(Database, Service):
...     pass
>>> class MySQL(Database):
...     pass
>>> @factory
... def build_mysql() -> MySQL:
...     return MySQL()

The decorated function must return the dependency that should be used for Database. It’ll be automatically injected, so you can use annotated type hints on it. Furthermore, the chosen implementation is by default permanent. Meaning that the decorated function will only be called once, at most.

>>> from antidote import implementation, Get
>>> from typing import Annotated
... # from typing_extensions import Annotated # Python < 3.9
>>> @implementation(Database)
... def local_db(choice: Annotated[str, Get(Config.DB)]) -> object:
...     if choice == 'postgres':
...         return PostgreSQL
...     else:
...         return MySQL @ build_mysql

To retrieve the actual implementation from Antidote you need to use a specific syntax dependency @ implementation as presented in the following examples. The goal of it is twofold:

  • Ensure that the implementation is loaded whenever you require the interface.

  • Better maintainability as you know where the dependenciy comes from.

>>> from antidote import world, inject
>>> world.get(Database @ local_db)  # treated as `object` by Mypy
<PostgreSQL ...>
>>> # With Mypy casting
... world.get[Database](Database @ local_db)
<PostgreSQL ...>
>>> # Concise Mypy casting
... world.get[Database] @ local_db
<PostgreSQL ...>
>>> @inject([Database @ local_db])
... def f(db: Database):
...     pass

Or with annotated type hints:

>>> from typing import Annotated
... # from typing_extensions import Annotated # Python < 3.9
>>> from antidote import From
>>> @inject
... def f(db: Annotated[Database, From(local_db)]):
...     pass
Parameters
  • interface – Interface for which an implementation will be provided

  • permanent – Whether the function should be called each time the interface is needed or not. Defaults to True.

Returns

The decorated function, unmodified.

Core

Injection

Inject

inject()

Singleton instance of Injector

class Arg(name: str, type_hint: Any, type_hint_with_extras: Any)

Deprecated since version 1.1: Deprecated as specifying a function to inject() is deprecated

Represents an argument (name and type hint) if you need a very custom injection logic.

name: str

Name of the argument

type_hint: Any

Type hint of the argument if any

type_hint_with_extras: Any

Type hint of the argument if any with include_extras=True, so with annotations.

class Injector

Use inject directly, this class is not meant to instantiated or subclassed.

__call__(__arg: staticmethod[F], *, dependencies: Union[None, Mapping[str, Hashable], Sequence[Optional[Hashable]], Callable[[antidote.core.injection.Arg], Optional[Hashable]]] = 'None', auto_provide: Optional[Union[bool, Iterable[type], Callable[[type], bool]]][Optional[Union[bool, Iterable[type], Callable[[type], bool]]]] = 'None', strict_validation: bool = 'True', ignore_type_hints: bool = 'False', type_hints_locals: Optional[Union[Dict[str, object], Literal['auto'], antidote._internal.utils.Default]] = 'Default.sentinel') staticmethod[F]
__call__(__arg: classmethod[F], *, dependencies: Union[None, Mapping[str, Hashable], Sequence[Optional[Hashable]], Callable[[antidote.core.injection.Arg], Optional[Hashable]]] = 'None', auto_provide: Optional[Union[bool, Iterable[type], Callable[[type], bool]]][Optional[Union[bool, Iterable[type], Callable[[type], bool]]]] = 'None', strict_validation: bool = 'True', ignore_type_hints: bool = 'False', type_hints_locals: Optional[Union[Dict[str, object], Literal['auto'], antidote._internal.utils.Default]] = 'Default.sentinel') classmethod[F]
__call__(__arg: antidote.core.injection.F, *, dependencies: Union[None, Mapping[str, Hashable], Sequence[Optional[Hashable]], Callable[[antidote.core.injection.Arg], Optional[Hashable]]] = 'None', auto_provide: Optional[Union[bool, Iterable[type], Callable[[type], bool]]][Optional[Union[bool, Iterable[type], Callable[[type], bool]]]] = 'None', strict_validation: bool = 'True', ignore_type_hints: bool = 'False', type_hints_locals: Optional[Union[Dict[str, object], Literal['auto'], antidote._internal.utils.Default]] = 'Default.sentinel') antidote.core.injection.F
__call__(*, dependencies: Union[None, Mapping[str, Hashable], Sequence[Optional[Hashable]], Callable[[antidote.core.injection.Arg], Optional[Hashable]]] = 'None', auto_provide: Optional[Union[bool, Iterable[type], Callable[[type], bool]]][Optional[Union[bool, Iterable[type], Callable[[type], bool]]]] = 'None', strict_validation: bool = 'True', ignore_type_hints: bool = 'False', type_hints_locals: Optional[Union[Dict[str, object], Literal['auto'], antidote._internal.utils.Default]] = 'Default.sentinel') Callable[[antidote.core.injection.F], antidote.core.injection.F]
__call__(__arg: Sequence[object], *, auto_provide: Optional[Union[bool, Iterable[type], Callable[[type], bool]]][Optional[Union[bool, Iterable[type], Callable[[type], bool]]]] = 'None', strict_validation: bool = 'True', ignore_type_hints: bool = 'False', type_hints_locals: Optional[Union[Dict[str, object], Literal['auto'], antidote._internal.utils.Default]] = 'Default.sentinel') Callable[[antidote.core.injection.F], antidote.core.injection.F]
__call__(__arg: Mapping[str, object], *, auto_provide: Optional[Union[bool, Iterable[type], Callable[[type], bool]]][Optional[Union[bool, Iterable[type], Callable[[type], bool]]]] = 'None', strict_validation: bool = 'True', ignore_type_hints: bool = 'False', type_hints_locals: Optional[Union[Dict[str, object], Literal['auto'], antidote._internal.utils.Default]] = 'Default.sentinel') Callable[[antidote.core.injection.F], antidote.core.injection.F]

Inject the dependencies into the function lazily, they are only retrieved upon execution. As several options can apply to the same argument, the priority is defined as:

  1. Markers inject.me… / Annotated type hints (PEP-593)

  2. Dependencies declared explicitly if any declared with dependencies.

  3. Deprecated: Class type hints if specified with auto_provide.

>>> from antidote import inject, injectable, Inject
>>> @injectable
... class A:
...     pass
>>> @injectable
... class B:
...     pass
>>> @inject
... def f(a: A = inject.me()):
...     pass # a = world.get(A)
>>> # PEP-593 annotation
... @inject
... def f(a: Inject[A]):
...     pass # a = world.get(A)
>>> # All possibilities for dependency argument.
>>> @inject(dict(b='dependency'))  # shortcut
... def f(a, b):
...     pass
>>> @inject([None, 'dependency'])  # shortcut
... def f(a, b):
...     pass  # a, b = <not injected>, world.get('dependency')
Parameters
  • __arg – Callable to be wrapped. Can also be used on static methods or class methods. May also be sequence of dependencies or mapping from argument name to dependencies.

  • dependencies

    Explicit definition of the dependencies which overrides auto_provide. Defaults to None. Can be one of:

    • Mapping from argument name to its dependency

    • Sequence of dependencies which will be mapped with the position of the arguments. None can be used as a placeholder.

    Deprecated since version 1.1: Specifying a callable is deprecated. The Callable receives Arg as arguments and should return the matching dependency. None should be used for arguments without dependency.

  • auto_provide

    Deprecated since version 1.1.

    Whether or not class type hints should be used as the arguments dependency. Only classes are taken into account and it works with Optional. An iterable of classes may also be supplied to activate this feature only for those. A function may also be provided, in which case it’ll be called to determine whether the class type hint ca be provided or not. Defaults to False.

  • strict_validation – Whether arguments should be strictly validated given the decorated function’s argumnet. For example, a key in the dependencies dict that does not match any argument would raise error. Defaults to True.

  • ignore_type_hints – If True, type hints will not be used at all and type_hints_locals will have no impact.

  • type_hints_locals

    Local variables to use for typing.get_type_hints(). They can be explicitly defined by passing a dictionary or automatically detected with inspect and frame manipulation by specifying 'auto'. Specifying None will deactivate the use of locals. When ignore_type_hints is True, this features cannot be used. The default behavior depends on the config value of auto_detect_type_hints_locals. If True the default value is equivalent to specifying 'auto', otherwise to None.

    New in version 1.3.

Returns

The decorator to be applied or the injected function if the argument func was supplied.

me() Any
me(*, source: Union[Source[Any], Callable[..., Any], Type[CallableClass[Any]]]) Any
me(*constraints: PredicateConstraint[Any], qualified_by: Optional[object | list[object]] = 'None', qualified_by_one_of: Optional[list[object]] = 'None') Any

Injection Marker specifying that the current type hint should be used as dependency.

>>> from antidote import inject, injectable
>>> @injectable
... class MyService:
...     pass
>>> @inject
... def f(s: MyService = inject.me()) -> MyService:
...     return s
>>> f()
<MyService object at ...>

It also works with dependency sources such as factory() or implementation():

>>> from antidote import inject, factory
>>> class Dummy:
...     pass
>>> @factory
... def current_dummy() -> Dummy:
...     return Dummy()
>>> @inject
... def f(d: Dummy = inject.me(source=current_dummy)) -> Dummy:
...     return d
>>> f()
<Dummy object at ...>

When the type hint is Optional inject() won’t raise DependencyNotFoundError but will provide None instead:

>>> from typing import Optional
>>> from antidote import inject
>>> class MyService:
...     pass
>>> @inject
... def f(s: Optional[MyService] = inject.me()) -> MyService:
...     return s
>>> f() is None
True

interface() are also supported:

>>> from antidote import inject, interface, implements
>>> @interface
... class Alert:
...     pass
>>> @implements(Alert)
... class DefaultAlert(Alert):
...     pass
>>> @inject
... def get_single(alert: Alert = inject.me()) -> Alert:
...     return alert
>>> get_single()
<DefaultAlert object at ...>
>>> @inject
... def get_all(alerts: list[Alert] = inject.me()) -> list[Alert]:
...     return alerts
>>> get_all()
[<DefaultAlert object at ...>]
Parameters
  • source – Source to use for the dependency. Mutually exclusive with all other arguments.

  • *constraintsPredicateConstraint to evaluate for each implementation.

  • qualified_by – All specified qualifiers must qualify the implementation.

  • qualified_by_one_of – At least one of the specified qualifiers must qualify the implementation.

get: antidote.core.getter.DependencyGetter

DependencyGetter to explicit state the dependencies to be retrieved. It follows the same API as world.get.

class DependencyGetter(_DependencyGetter__enforce_type: 'bool', _DependencyGetter__load: 'DependencyLoader')
__call__(__dependency: antidote.core.typing.Dependency[antidote.core.getter.T], *, default: Union[antidote.core.getter.T, antidote._internal.utils.Default] = 'Default.sentinel') antidote.core.getter.T
__call__(__dependency: Type[antidote.core.getter.T], *, default: Union[antidote.core.getter.T, antidote._internal.utils.Default] = 'Default.sentinel') antidote.core.getter.T
__call__(__dependency: Type[antidote.core.getter.T], *, source: Union[antidote.core.typing.Source[antidote.core.getter.T], Callable[[...], antidote.core.getter.T], Type[antidote.core.typing.CallableClass[antidote.core.getter.T]]], default: Union[antidote.core.getter.T, antidote._internal.utils.Default] = 'Default.sentinel') antidote.core.getter.T

Retrieve the specified dependency. The interface is the same for both inject and world:

>>> from antidote import world, injectable, inject
>>> @injectable
... class Dummy:
...     pass
>>> world.get(Dummy)
<Dummy object at ...>
>>> @inject
... def f(dummy = inject.get(Dummy)) -> Dummy:
...     return dummy
>>> f()
<Dummy object at ...>
Parameters
  • __dependency – dependency to retrieve.

  • default – Default value to use if the dependency could not be retrieved. By default an error is raised.

  • source – Source of the dependency if any, typically a factory.

Returns:

__getitem__(tpe: Type[antidote.core.getter.T]) antidote.core.getter.TypedDependencyGetter[antidote.core.getter.T]
Parameters

tpe – Type to use as reference

Returns:

class TypedDependencyGetter(*args, **kwds)
__call__(*, default: Union[antidote.core.getter.T, antidote._internal.utils.Default] = 'Default.sentinel') antidote.core.getter.T
__call__(*, default: Union[antidote.core.getter.T, antidote._internal.utils.Default] = 'Default.sentinel', source: Union[antidote.core.typing.Source[antidote.core.getter.T], Callable[[...], antidote.core.getter.T], Type[antidote.core.typing.CallableClass[antidote.core.getter.T]]]) antidote.core.getter.T
__call__(__dependency: Any, *, default: Union[antidote.core.getter.T, antidote._internal.utils.Default] = 'Default.sentinel') antidote.core.getter.T
__call__(__dependency: Type[antidote.core.getter.R], *, default: Union[antidote.core.getter.T, antidote._internal.utils.Default] = 'Default.sentinel', source: Union[antidote.core.typing.Source[antidote.core.getter.R], Callable[[...], antidote.core.getter.R], Type[antidote.core.typing.CallableClass[antidote.core.getter.R]]]) antidote.core.getter.T

Call self as a function.

single(*constraints: PredicateConstraint[Any], qualified_by: Optional[object | list[object]] = None, qualified_by_one_of: Optional[list[object]] = None) T

New in version 1.2.

Retrieve a single implementation matching given constraints. If multiple or no implementation is found, an error will be raised upon retrieval.

Parameters
  • *constraintsPredicateConstraint to evaluate for each implementation.

  • qualified_by – All specified qualifiers must qualify the implementation.

  • qualified_by_one_of – At least one of the specified qualifiers must qualify the implementation.

all(*constraints: PredicateConstraint[Any], qualified_by: Optional[object | list[object]] = None, qualified_by_one_of: Optional[list[object]] = None) list[T]

New in version 1.2.

Retrieve all implementations matching given constraints.

Parameters
  • *constraintsPredicateConstraint to evaluate for each implementation.

  • qualified_by – All specified qualifiers must qualify the implementation.

  • qualified_by_one_of – At least one of the specified qualifiers must qualify the implementation.

Annotations

Provide

Annotation specifying that the type hint itself is the dependency:

>>> from antidote import world, inject, Inject, injectable
>>> from typing import Annotated
... # from typing_extensions import Annotated # Python < 3.9
>>> @injectable
... class Database:
...     pass
>>> @inject
... def load_db(db: Inject[Database]):
...     return db
>>> load_db()
<Database ...>

alias of antidote.core.annotations.T[antidote.core.annotations.T]

class Get(__dependency: object, *, default: object = 'Default.sentinel')
class Get(__dependency: Type[antidote.core.annotations.T], *, source: Union[antidote.core.typing.Source[antidote.core.annotations.T], Callable[[...], antidote.core.annotations.T], Type[antidote.core.typing.CallableClass[antidote.core.annotations.T]]], default: object = 'Default.sentinel')

Annotation specifying explicitly which dependency to inject.

>>> from typing import Annotated
>>> from antidote import world, inject, Get, Constants, const
>>> class Config(Constants):
...     DB_HOST = const('localhost')
>>> @inject
... def f(host: Annotated[str, Get(Config.DB_HOST)]):
...     return host
>>> f() == world.get(Config.DB_HOST)
True
class From(_From__source: Union[antidote.core.annotations.SupportsRMatmul, antidote.core.typing.Source[Any], Callable[[...], Any], Type[antidote.core.typing.CallableClass[Any]]])

Annotation specifying from where a dependency must be provided. To be used with factory(), Factory and implementation() typically.

>>> from typing import Annotated
>>> from antidote import factory, world, inject, From
>>> class Database:
...     def __init__(self, host: str):
...         self.host = host
>>> @factory
... def build_db(host: str = 'localhost:5432') -> Database:
...     return Database(host=host)
>>> @inject
... def f(db: Annotated[Database, From(build_db)]) -> Database:
...     return db
>>> f().host
'localhost:5432'
class FromArg(_FromArg__function: Callable[[Arg], Optional[Hashable]])

Deprecated since version 1.1: Specifying a callable to inject() is deprecated, so is this annotation. If you rely on this behavior, you’ll need to wrap @inject and do the annotation parsing yourself.

Annotation specifying which dependency should be provided based on the argument. The function should accept a single argument of type Arg and return either a dependency or None.

>>> from typing import Annotated, TypeVar
>>> from antidote import world, inject, FromArg, Constants, const, Arg
>>> class Config(Constants):
...     PORT = const(5432)
...
...     @classmethod
...     def from_arg(cls, arg: Arg):
...         return getattr(cls, arg.name.upper())
...
>>> T = TypeVar('T')
>>> ProvideFromConfig = Annotated[T, FromArg(Config.from_arg)]
>>> @inject
... def f(port: ProvideFromConfig[int]) -> int:
...     return port
>>> f()
5432

Wiring

class Wiring(*, methods: Union[antidote.core.wiring.Methods, Iterable[str]] = Methods.ALL, dependencies: Union[None, Mapping[str, Hashable], Sequence[Optional[Hashable]], Callable[[antidote.core.injection.Arg], Optional[Hashable]]] = None, auto_provide: Optional[Optional[Union[bool, Iterable[type], Callable[[type], bool]]][Optional[Union[bool, Iterable[type], Callable[[type], bool]]]]] = None, raise_on_double_injection: bool = False, ignore_type_hints: bool = False)

Defines how a class should be wired, meaning if/how/which methods are injected. This class is intended to be used by configuration objects. If you just want to wire a single class, consider using the class decorator wire() instead. There are two purposes:

  • providing a default injection which can be overridden either by changing the wiring or using @inject when using attempt_methods.

  • wiring of multiple methods with similar dependencies.

Instances are immutable. If you want to change some parameters, typically defaults defined by Antidote, you’ll need to rely on copy().

>>> from antidote import Wiring, Service, Provide
>>> wiring = Wiring(methods=['my_method'])
>>> class Database(Service):
...     pass
>>> @wiring.wire
... class Dummy:
...     def my_method(self, db: Provide[Database]) -> Database:
...         return db
>>> Dummy().my_method()
<Database ...>
Parameters
  • methods – Names of methods to be injected. If any of them is already injected, an error will be raised. Consider using attempt_methods otherwise.

  • dependencies – Propagated for every method to inject()

  • auto_provide

    Deprecated since version 1.1.

    Propagated for every method to inject()

  • ignore_type_hints

    If True, type hints will not be used at all and type_hints_locals, when calling wire(), will have no impact.

    New in version 1.3.

methods: Union[antidote.core.wiring.Methods, FrozenSet[str]]

Method names that must be injected.

copy(*, methods: Union[antidote.core.wiring.Methods, Iterable[str], antidote._internal.utils.Copy] = Copy.IDENTICAL, dependencies: Union[None, Mapping[str, Hashable], Sequence[Optional[Hashable]], Callable[[antidote.core.injection.Arg], Optional[Hashable]], antidote._internal.utils.Copy] = Copy.IDENTICAL, auto_provide: Union[bool, Iterable[type], Callable[[type], bool], None, antidote._internal.utils.Copy][Union[bool, Iterable[type], Callable[[type], bool], None, antidote._internal.utils.Copy]] = Copy.IDENTICAL, raise_on_double_injection: Union[bool, antidote._internal.utils.Copy] = Copy.IDENTICAL, ignore_type_hints: Union[bool, antidote._internal.utils.Copy] = Copy.IDENTICAL) antidote.core.wiring.Wiring

Copies current wiring and overrides only specified arguments. Accepts the same arguments as __init__()

wire(__klass: antidote.core.wiring.C) antidote.core.wiring.C
wire(*, klass: type, type_hints_locals: Optional[dict[str, object]] = 'None', class_in_localns: bool = 'True') None

Used to wire a class with specified configuration.

Parameters
  • __klass – Class to wire. Deprecated, use klass instead.

  • klass – Class to wire.

  • type_hints_locals

    local variables to use for typing.get_type_hints().

    New in version 1.3.

  • class_in_localns

    Whether to add the current class as a local variable. This is typically helpful when the class uses itself as a type hint as during the wiring, the class has not yet been defined in the globals/locals. The default depends on the value of ignore_type_hints. If ignored, the class will not be added to the type_hints_locals. Specifying type_hints_locals=None does not prevent the class to be added.

    New in version 1.3.

Returns

The same class object with specified methods injected. It doesn’t return anything with the next API.

Return type

Deprecated

wire(__klass: antidote.core.wiring.C, *, methods: Union[antidote.core.wiring.Methods, Iterable[str]] = 'Methods.ALL', dependencies: Union[None, Mapping[str, Hashable], Sequence[Optional[Hashable]], Callable[[antidote.core.injection.Arg], Optional[Hashable]]] = 'None', auto_provide: Optional[Union[bool, Iterable[type], Callable[[type], bool]]][Optional[Union[bool, Iterable[type], Callable[[type], bool]]]] = 'None', raise_on_double_injection: bool = 'False', ignore_type_hints: bool = 'False', type_hints_locals: Optional[Union[Dict[str, object], Literal['auto'], antidote._internal.utils.Default]] = 'Default.sentinel') antidote.core.wiring.C
wire(*, methods: Union[antidote.core.wiring.Methods, Iterable[str]] = 'Methods.ALL', dependencies: Union[None, Mapping[str, Hashable], Sequence[Optional[Hashable]], Callable[[antidote.core.injection.Arg], Optional[Hashable]]] = 'None', auto_provide: Optional[Union[bool, Iterable[type], Callable[[type], bool]]][Optional[Union[bool, Iterable[type], Callable[[type], bool]]]] = 'None', raise_on_double_injection: bool = 'False', ignore_type_hints: bool = 'False', type_hints_locals: Optional[Union[Dict[str, object], Literal['auto'], antidote._internal.utils.Default]] = 'Default.sentinel') Callable[[antidote.core.wiring.C], antidote.core.wiring.C]

Wire a class by injecting specified methods. This avoids repetition if similar dependencies need to be injected in different methods. Methods will only be wrapped if and only if Antidote may inject a dependency in it, like inject().

>>> from antidote import wire, injectable, inject
>>> @injectable
... class MyService:
...     pass
>>> @wire
... class Dummy:
...     def method(self, service: MyService = inject.me()) -> MyService:
...         return service
>>> Dummy().method()
<MyService object at ...>
Parameters
  • raise_on_double_injection – Whether an error should be raised if method is already injected. Defaults to False.

  • __klass – Class to wire.

  • methods – Names of methods that must be injected. Defaults to all method

  • dependencies – Propagated for every method to inject().

  • auto_provide

    Deprecated since version 1.1.

    Propagated for every method to inject().

  • ignore_type_hints – If True, type hints will not be used at all and type_hints_locals will have no impact.

  • type_hints_locals

    Local variables to use for typing.get_type_hints(). They can be explicitly defined by passing a dictionary or automatically detected with inspect and frame manipulation by specifying 'auto'. Specifying None will deactivate the use of locals. When ignore_type_hints is True, this features cannot be used. The default behavior depends on the config value of auto_detect_type_hints_locals. If True the default value is equivalent to specifying 'auto', otherwise to None.

    New in version 1.3.

Returns

Wired class or a class decorator.

Provider

does_not_freeze(method: antidote.core.provider.M) antidote.core.provider.M

Decorated method won’t freeze when freeze() is called. All others will, with the exceptions of base methods defined in :py:class`.Provider`.

Beware that is must be the last decorator if you use multiple ones.

class Provider

Abstract Base class for a provider.

Consider using StatelessProvider if you do not have any state to keep.

>>> from typing import Hashable, Optional
>>> from antidote import world, Scope
>>> from antidote.core import Provider, Container, \
...     DependencyValue, does_not_freeze
>>> @world.provider
... class SquareProvider(Provider[int]):
...     def __init__(self, registered: set = None):
...         super().__init__()
...         self._registered = registered or set()
...
...     def register_numbers(self, number: int):
...         self._assert_not_duplicate(number)
...         self._registered |= {number}
...
...     def exists(self, dependency: Hashable):
...         return isinstance(dependency, int) and dependency in self._registered
...
...     def provide(self, dependency: int, container: Container
...                 ) -> DependencyValue:
...         return DependencyValue(self._square(dependency),
...                                   scope=Scope.singleton())
...
...     # we don't want this method to fail when world freezes
...     # and it does not modify any state, so we're safe !
...     @does_not_freeze
...     def _square(self, n: int) -> int:
...         return n ** 2
...
...     def clone(self, keep_singletons_cache: bool) -> 'SquareProvider':
...         return SquareProvider(self._registered.copy())
...
>>> # To modify your provider, retrieve it from world
... world.get[SquareProvider]().register_numbers(9)
>>> world.get[SquareProvider]().register_numbers(7)
>>> world.get[int](9)
81
>>> # If you freeze world, you cannot register any dependencies
... world.freeze()
>>> from antidote.exceptions import FrozenWorldError
>>> try:
...     world.get[SquareProvider]().register_numbers(10)
... except FrozenWorldError:
...     print("Frozen world !")
Frozen world !
>>> # But you can still retrieve previously defined ones
... world.get[int](9)
81
>>> # even if never called before
... world.get[int](7)
49

Warning

This is most advanced feature of Antidote and allows you to extend Antidote’s behavior. So be careful with it, it will impact the whole library. There are several rules to respect:

  1. Different providers MUST provide strictly different dependencies.

  2. You MUST NOT use world. If you need a dependency, rely on the provided Container.

  3. Methods will automatically freeze except those marked with the decorator does_not_freeze(). Methods changing dependencies definitions MUST NOT use does_not_freeze(), others may.

  4. You may cache singletons by yourself, but you need to clean your cache when clone() is called with keep_singletons_cache=False.

clone(keep_singletons_cache: bool) antidote.core.provider.P

If you have no internal state, consider implementing StatelessProvider instead.

Used by the different test utilities.

Parameters

keep_singletons_cache – Whether cached singletons, if any, should be kept or discarded.

Returns

A deep copy of the current provider. It should always be a new instance.

exists(dependency: object) bool

Check whether dependency exists in the current provider. It is recommended to be relatively fast as this function will often be called for all _providers. Among others it is used to check for duplicates and ensure that it is providable.

Parameters

dependency – Dependency to check.

Returns

Whether dependency has been registered in this provider or not.

Return type

bool

provide(dependency: antidote.core.provider.T, container: antidote.core.container.Container) antidote.core.container.DependencyValue

Method called by the Container when searching for a dependency. Be sure that the dependency space of your _providers don’t intersect ! This function will only be called if exists() succeeded on the dependency.

If you need to access other dependencies, you MUST use the provided container argument and NEVER world.

It is always called within a thread-safe (locked) environment, so you don’t need to worry about it. You also don’t need to worry about handling singletons, the Container will handle it for you.

Be careful to be consistent with exists().

Parameters
  • dependency – The dependency to be provided by the provider. It will have passed exists().

  • container – current container which may use to retrieve other dependencies.

Returns

The requested instance wrapped in a DependencyValue. If the dependency is a singleton, you MUST specify it with singleton=True.

debug(dependency: antidote.core.provider.T) antidote.core.utils.DependencyDebug

Optional support for world.debug. If not implemented, debug information will not be provided for dependencies.

Parameters

dependency – The dependency for which debug information should be provided. It will have passed exists().

Returns

A short information text on the dependency, whether it’s a singleton and everything that has been wired for the dependency and its respective dependencies if any.

maybe_provide(dependency: object, container: antidote.core.container.Container) Optional[antidote.core.container.DependencyValue]

Expert feature

maybe_provide() MUST be consistent with exists(). It should return

Parameters
  • dependency – The dependency to be provided by the provider.

  • container – current container which may use to retrieve other dependencies.

Returns

The requested instance wrapped in a DependencyValue if available or None. If the dependency is a singleton, you MUST specify it with singleton=True.

maybe_debug(dependency: object) Optional[antidote.core.utils.DependencyDebug]

Expert feature

maybe_debug() MUST be consistent with exists().

Parameters

dependency – The dependency for which debug information should be provided. It will have passed exists().

Returns

A short information text on the dependency, whether it’s a singleton and everything that has been wired for the dependency and its respective dependencies if any.

class StatelessProvider

Abstract stateless Provider which implements clone().

>>> from typing import Hashable, Optional
>>> from antidote import world, Scope
>>> from antidote.core import StatelessProvider, Container, DependencyValue
>>> @world.provider
... class SquareProvider(StatelessProvider[int]):
...     def exists(self, dependency: Hashable) -> bool:
...         return isinstance(dependency, int)
...
...     def provide(self, dependency: int, container: Container
...                 ) -> Optional[DependencyValue]:
...         return DependencyValue(dependency ** 2, scope=Scope.singleton())
>>> world.get(9)
81
clone(keep_singletons_cache: bool) antidote.core.provider.StatelessProvider[antidote.core.provider.T]

If you have no internal state, consider implementing StatelessProvider instead.

Used by the different test utilities.

Parameters

keep_singletons_cache – Whether cached singletons, if any, should be kept or discarded.

Returns

A deep copy of the current provider. It should always be a new instance.

class Scope(*args: object, **kwargs: object)

Used to identify a specific scope for dependencies. The scope of a dependency defines when the dependency value is valid or not. Or said differently, for how long it is valid. A singleton is valid forever, and no scope at all means that a new dependency value needs to be retrieved every time.

Scopes can be create through world.scopes.new(). The name is only used to have a friendly identifier when debugging.

>>> from antidote import world
>>> REQUEST_SCOPE = world.scopes.new(name='request')

To use the newly created scope, use scope parameters:

>>> from antidote import Service
>>> class Dummy(Service):
...     __antidote__ = Service.Conf(scope=REQUEST_SCOPE)

As Dummy has been defined with a custom scope, the dependency value will be kep as long as REQUEST_SCOPE stays valid. That is to say, until you reset it with world.scopes.reset():

>>> dummy = world.get[Dummy]()
>>> dummy is world.get(Dummy)
True
>>> world.scopes.reset(REQUEST_SCOPE)
>>> dummy is world.get(Dummy)
False

Note

You probably noticed that dependencies supporting scopes always offer both singleton and scope arguments. Those are mutually exclusive. The reason behind this is simply due to the nature of scopes. Scopes are hard to get right ! For example, what should happen with a service in a scope A which required a dependency in scope B when B resets ? What if the service kept the dependency as an attribute ? There are no good answers for this, hence scopes are a pretty advanced feature which can cause inconsistencies. Moreover they do have a performance impact. But at the same time, scopes aren’t uncommon. In a webapp you’ll typically need soon enough a request scope. So it shouldn’t be hard to use it.

So to have easy to use scopes while keeping them under the radar until you actually need them, Antidote exposes both singleton and scope arguments.

static singleton() antidote.core.container.Scope

Using this scope or specifying singleton=True is equivalent.

Returns

Singleton scope (unique object).

static sentinel() antidote.core.container.Scope

For functions having both singleton and scope argument, validation of those is done with validated_scope(). To correctly identify if the scope was actually set by the user, we’re using this sentinel scope as the default value.

Returns

Sentinel scope (unique object).

class DependencyValue(value: object, *, scope: Optional[antidote.core.container.Scope] = None)

Simple wrapper of the dependency value given by a Provider.

Parameters
  • value – Actual dependency value.

  • scope – Scope of the dependency.

unwrapped: object

Actual dependency value.

scope: Optional[antidote.core.container.Scope]

Scope of the dependency.

class Container

Public interface of the container used by Antidote to handles all dependencies. Used in a Provider to access other dependencies.

get(dependency: Hashable, default: object = Default.sentinel) object

Retrieves given dependency or raises a DependencyNotFoundError.

Parameters
  • dependency – Dependency to be retrieved.

  • default – Returned if specified and no dependency can be retrieved.

Returns

The dependency instance or the default if specified.

provide(dependency: Hashable) antidote.core.container.DependencyValue

Similar to get() it will retrieve a dependency. However it will be wrapped in a DependencyValue if found. If not None will be returned. This allows to get additional information such as whether the dependency is a singleton or not.

Parameters

dependency – Dependency to be retrieved.

Returns

wrapped dependency instance.

class DebugInfoPrefix(prefix: str, dependency: object)
class DependencyDebug(_DependencyDebug__info: str, *, scope: Optional[antidote.core.container.Scope] = None, wired: Sequence[object] = (), dependencies: Sequence[object] = ())

Debug information on a dependency. Used by world.debug to provide runtime information for debugging.

Parameters
  • __info – Short and concise information on the dependency, just enough to identify clearly which one it is.

  • scope – Scope of the dependency.

  • wired – Every class or function that may have been wired for this dependency.

  • dependencies – Dependencies of the dependency itself. Ordering is kept.

Exceptions

exception AntidoteError

Base class of all errors of antidote.

exception DependencyCycleError(stack: List[Hashable])

A dependency cycle is found.

exception DependencyInstantiationError(dependency: Hashable, stack: Optional[List[Hashable]] = None)

The dependency could not be instantiated.

exception DependencyNotFoundError(dependency: Hashable)

The dependency could not be found.

exception DuplicateDependencyError

A dependency already exists with the same id. May be raised by _providers.

exception FrozenWorldError

An action failed because the world is frozen. Typically happens when trying to register a dependency after having called freeze() on the world.

exception DoubleInjectionError(func: object)

Raised when injecting a function/method that already has been injected.