API Reference

World

world

get = <antidote._internal.world.WorldGet object>

Used to retrieve a dependency from Antidote. A type hint can be provided and the result will 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
>>> world.get(Dummy)
<Dummy ...>
>>> # But Mypy will only an object, not a `Dummy` instance. For this Mypy needs help:
>>> world.get[Dummy](Dummy)  # 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.get[Dummy]()
<Dummy ...>
lazy = <antidote._internal.world.WorldLazy object>

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()

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
Return type

None

debug(dependency, *, depth=- 1)

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 (Hashable) – Root of the dependency tree.

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

Returns:

Return type

str

provider(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 (~P) – Provider class

Return type

~P

Returns

The same class, unmodified.

world.scopes

new(*, name)

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 (str) – Friendly identifier used for debugging purposes. It must be unique.

Return type

Scope

reset(scope)

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) – Scope to reset.

Return type

None

world.test

Testing utilities with Antidote. Also used by Antidote itself.

clone(*, keep_singletons=False, keep_scopes=False)

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 (bool) – Whether current singletons should be kept in the new world. Beware that the same objects will be propagated. Defaults to False.

  • keep_scopes (bool) – 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.

Return type

Iterator[None]

Returns

context manager within you can make your tests.

empty()

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.

Return type

Iterator[None]

factory(dependency=None, *, singleton=None, scope=Scope(name='__sentinel__'))

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 (Optional[Hashable]) – Dependency to override.

  • singleton (Optional[bool]) – 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 (Optional[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().

Return type

Callable[[~F], ~F]

maybe_provide_from(provider, dependency)

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 (RawProvider) – provider from which the dependency should be retrieved.

  • dependency (Hashable) – dependency to retrieve.

Return type

Optional[DependencyValue]

Returns

dependency instance as returned by the provider.

new()

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'
Return type

Iterator[None]

singleton(dependency, value=<object object>, test_provider=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)
...     world.get[int]("test")
1
Parameters
  • dependency (Union[Dict[Hashable, object], Hashable]) – Singleton to declare, must be hashable. If a dict is provided, it’ll be treated as a dictionary of singletons to add.

  • value (object) – Associated value for the dependency.

Return type

None

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, value=<object object>)

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 (Union[Dict[Hashable, object], Hashable]) – Singleton to declare, must be hashable. If a dict is provided, it’ll be treated as a dictionary of singletons to add.

  • value (object) – Associated value for the dependency.

Return type

None

factory(dependency=None, *, singleton=None, scope=Scope(name='__sentinel__'))

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 (Optional[Hashable]) – Dependency to override.

  • singleton (Optional[bool]) – 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 (Optional[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.

Return type

Callable[[~F], ~F]

Returns

The decorated function, unchanged.

provider()

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().

Return type

Callable[[~P], ~P]

Returns

Function decorator.

Utilities

is_compiled()

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

Return type

bool

validate_injection(dependencies=None, auto_provide=None)

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

>>> from antidote.utils import validate_injection
>>> validate_injection(auto_provide=True)
>>> validate_injection(auto_provide=object())
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: auto_provide must be either a boolean or an iterable of classes ...
Parameters
Return type

None

validated_scope(scope=Scope(name='__sentinel__'), singleton=None, *, default)

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
Return type

Optional[Scope]

Returns

Scope defined by either singleton or scope.

Dependencies

Service

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

Defines the decorated class as a service. To be used when you cannot inherit Service

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

Like Service it’ll automatically wire the class.

>>> from antidote import Provide, world
>>> @service
... class Test:
...     def __init__(self, cis: Provide[CannotInheritService]):
...         self.cis = cis
>>> world.get[Test]().cis
<CannotInheritService object at ...>

Note

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

Parameters
  • klass (Optional[~C]) – Class to register as a dependency. It will be instantiated only when requested.

  • singleton (Optional[bool]) – Whether the service is a singleton or not. A singleton is instantiated only once. Mutually exclusive with scope. Defaults to True

  • scope (Optional[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().

  • wiring (Optional[Wiring]) – Wiring 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.

Return type

Union[~C, Callable[[~C], ~C]]

Returns

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

class Service

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()

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__().

Return type

object

Returns

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

class Conf(*, wiring=Wiring(methods=<Methods.ALL: 1>, auto_provide=False, dependencies=None, raise_on_double_injection=False), singleton=None, scope=Scope(name='__sentinel__'), parameters=None)

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

Parameters
  • wiring (Optional[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 (Optional[bool]) – Whether the service is a singleton or not. A singleton is instantiated only once. Mutually exclusive with scope. Defaults to True

  • scope (Optional[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=Copy.IDENTICAL, singleton=Copy.IDENTICAL, scope=Copy.IDENTICAL, parameters=Copy.IDENTICAL)

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

Return type

Conf

with_wiring(*, methods=Copy.IDENTICAL, dependencies=Copy.IDENTICAL, auto_provide=Copy.IDENTICAL, raise_on_double_injection=Copy.IDENTICAL)

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={}))
Return type

~W

class ABCService

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.F, *, singleton: bool = 'None', scope: Optional[antidote.core.container.Scope] = 'Scope.sentinel()') antidote.factory.FactoryProtocol[antidote.factory.F]
factory(*, singleton: bool = 'None', scope: Optional[antidote.core.container.Scope] = 'Scope.sentinel()') Callable[[antidote.factory.F], antidote.factory.FactoryProtocol[antidote.factory.F]]

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()

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 @ load_db)  # treated as `object` by Mypy
<Database ...>
>>> # With Mypy casting
... world.get[Database](Database @ load_db)
<Database ...>
>>> # Concise Mypy casting
... world.get[Database] @ load_db
<Database ...>
>>> @inject([Database @ load_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(load_db)]):
...     pass

The factory returns a singleton by default and is automatically injected, so you can use annotated type hints with it:

>>> # Singleton by default
... world.get[Database] @ load_db is world.get[Database] @ load_db
True
>>> class Session:
...     pass
>>> @factory(singleton=False)
... def session_gen(db: Annotated[Database, From(load_db)]) -> Session:
...     return Session()
>>> world.get[Session] @ session_gen is world.get[Session] @ session_gen
False

Note

If you need a stateful factory or want to implement a complex one prefer using Factory instead.

Parameters
  • f (Optional[~F]) – Callable which builds the dependency.

  • singleton (Optional[bool]) – 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 (Optional[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().

  • tags – Iterable of Tag applied to the provided dependency.

Return type

Union[FactoryProtocol[~F], Callable[[~F], FactoryProtocol[~F]]]

Returns

The factory or the function decorator.

class Factory

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()

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__().

Return type

PreBuild

Returns

Dependency to be retrieved from Antidote.

class Conf(*, wiring=Wiring(methods=<Methods.ALL: 1>, auto_provide=False, dependencies=None, raise_on_double_injection=False), singleton=None, scope=Scope(name='__sentinel__'), parameters=None)

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

Parameters
  • wiring (Optional[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 (Optional[bool]) – 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 (Optional[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=Copy.IDENTICAL, singleton=Copy.IDENTICAL, scope=Copy.IDENTICAL, parameters=Copy.IDENTICAL)

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

Return type

Conf

with_wiring(*, methods=Copy.IDENTICAL, dependencies=Copy.IDENTICAL, auto_provide=Copy.IDENTICAL, raise_on_double_injection=Copy.IDENTICAL)

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={}))
Return type

~W

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=True, wiring=Wiring(methods=<Methods.ALL: 1>, auto_provide=False, dependencies=None, raise_on_double_injection=False))

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

Parameters
  • wiring (Optional[Wiring]) – Wiring used on the class. Defaults to wire only __init__().

  • auto_cast (Union[Iterable[type], bool]) – 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=Copy.IDENTICAL, auto_cast=Copy.IDENTICAL)

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

Return type

Conf

provide_const(name, arg)

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 (str) – Name of the constant

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

Return type

object

Returns

Constant value.

Lazy

class LazyCall(func, *, singleton=None, 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 (Callable[…, object]) – Function to lazily call, any (keyword-)arguments given by the returned LazyCall itself will be propagated.

  • singleton (Optional[bool]) – 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 (Optional[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, *, singleton=None, 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 (Callable[…, object]) – Method name or the method itself that must be called.

  • singleton (Optional[bool]) – 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) – 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().

Implementation

implementation(interface, *, permanent=True)

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 (type) – Interface for which an implementation will be provided

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

Return type

Callable[[~F], ImplementationProtocol[~F]]

Returns

The decorated function, unmodified.

Core

Injection

Inject

class Arg(*args, **kwargs)

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

name

Name of the argument

type_hint

Type hint of the argument if any

type_hint_with_extras

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

inject(__arg: staticmethod, *, dependencies: Optional[Union[Mapping[str, Hashable], Sequence[Optional[Hashable]], Callable[[antidote.core.injection.Arg], Optional[Hashable]]]] = 'None', auto_provide: Optional[Union[bool, Iterable[type], Callable[[type], bool]]] = 'None', strict_validation: bool = 'True') staticmethod
inject(__arg: classmethod, *, dependencies: Optional[Union[Mapping[str, Hashable], Sequence[Optional[Hashable]], Callable[[antidote.core.injection.Arg], Optional[Hashable]]]] = 'None', auto_provide: Optional[Union[bool, Iterable[type], Callable[[type], bool]]] = 'None', strict_validation: bool = 'True') classmethod
inject(__arg: antidote.core.injection.F, *, dependencies: Optional[Union[Mapping[str, Hashable], Sequence[Optional[Hashable]], Callable[[antidote.core.injection.Arg], Optional[Hashable]]]] = 'None', auto_provide: Optional[Union[bool, Iterable[type], Callable[[type], bool]]] = 'None', strict_validation: bool = 'True') antidote.core.injection.F
inject(*, dependencies: Optional[Union[Mapping[str, Hashable], Sequence[Optional[Hashable]], Callable[[antidote.core.injection.Arg], Optional[Hashable]]]] = 'None', auto_provide: Optional[Union[bool, Iterable[type], Callable[[type], bool]]] = 'None', strict_validation: bool = 'True') Callable[[antidote.core.injection.F], antidote.core.injection.F]
inject(__arg: Sequence[Hashable], *, auto_provide: Optional[Union[bool, Iterable[type], Callable[[type], bool]]] = 'None', strict_validation: bool = 'True') Callable[[antidote.core.injection.F], antidote.core.injection.F]
inject(__arg: Mapping[str, Hashable], *, auto_provide: Optional[Union[bool, Iterable[type], Callable[[type], bool]]] = 'None', strict_validation: bool = 'True') 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. Annotated type hints (PEP-593)

  2. Dependencies declared explicitly if any declared with dependencies.

  3. Class type hints if specified with auto_provide.

>>> from antidote import inject, Service, Provide
>>> class A(Service):
...     pass
>>> class B(Service):
...     pass
>>> # PEP-593 annotation
... @inject
... def f(a: Provide[A]):
...     pass # a = world.get(A)
>>> # All possibilities for dependency argument.
>>> @inject(dependencies=dict(b='dependency'))
... def f(a, b):
...     pass  # a, b = <not injected>, world.get('dependency')
>>> @inject(dict(b='dependency'))  # shortcut
... def f(a, b):
...     pass
>>> @inject(dependencies=[None, 'dependency'])  # `a` is ignored
... def f(a, b):
...     pass  # a, b = <not injected>, world.get('dependency')
>>> @inject([None, 'dependency'])  # shortcut
... def f(a, b):
...     pass  # a, b = <not injected>, world.get('dependency')
>>> @inject(dependencies=lambda arg: 'dependency' if arg.name == 'b' else None)
... def f(a, b):
...     pass  # a, b = <not injected>, world.get('dependency')
>>> # All possibilities for auto_provide argument.
>>> @inject(auto_provide=True)
... def f(a: A):
...     pass  # a = world.get(A)
>>> @inject(auto_provide=[B])
... def f(a: A, b: B):
...     pass  # a, b = <not injected>, world.get(B)
>>> @inject(auto_provide=False)  # default
... def f(a: A):
...     pass  # a = <not injected>
Parameters
  • __arg (Union[Callable[…, Any], staticmethod, classmethod, Sequence[Hashable], Mapping[str, Hashable], None]) – 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 (Union[Mapping[str, Hashable], Sequence[Optional[Hashable]], Callable[[Arg], Optional[Hashable]], None]) –

    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.

    • Callable which receives Arg as arguments and should return the matching dependency. None should be used for arguments without dependency.

    • String which must have {arg_name} as format parameter

  • auto_provide (Union[bool, Iterable[type], Callable[[type], bool], None]) – 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 (bool) – 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.

Return type

Union[Callable[…, Any], staticmethod, classmethod]

Returns

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

Annotations

Provide

Annotation specifying that the type hint itself is the dependency:

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

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

class Get(_Get__dependency)

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)

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)

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=Methods.ALL, dependencies=None, auto_provide=False, raise_on_double_injection=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(). It accepts the same arguments as __init__() and overrides existing values.

>>> 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

Method names that must be injected.

copy(*, methods=Copy.IDENTICAL, dependencies=Copy.IDENTICAL, auto_provide=Copy.IDENTICAL, raise_on_double_injection=Copy.IDENTICAL)

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

Return type

Wiring

wire(_Wiring__klass)

Used to wire a class with specified configuration.

Parameters

__klass – Class to wired

Return type

~C

Returns

The same class object with specified methods injected.

wire(__klass: antidote.core.wiring.C, *, methods: Union[antidote.core.wiring.Methods, Iterable[str]] = 'Methods.ALL', dependencies: Optional[Union[Mapping[str, Hashable], Sequence[Optional[Hashable]], Callable[[antidote.core.injection.Arg], Optional[Hashable]]]] = 'None', auto_provide: Optional[Union[bool, Iterable[type], Callable[[type], bool]]] = 'False', raise_on_double_injection: bool = 'False') antidote.core.wiring.C
wire(*, methods: Union[antidote.core.wiring.Methods, Iterable[str]] = 'Methods.ALL', dependencies: Optional[Union[Mapping[str, Hashable], Sequence[Optional[Hashable]], Callable[[antidote.core.injection.Arg], Optional[Hashable]]]] = 'None', auto_provide: Optional[Union[bool, Iterable[type], Callable[[type], bool]]] = 'False', raise_on_double_injection: bool = 'False') 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().

Parameters
Return type

Union[~C, Callable[[~C], ~C]]

Returns

Wired class or a class decorator.

Provider

does_not_freeze(method)

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.

Return type

~M

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)

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

Used by the different test utilities.

Parameters

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

Return type

~P

Returns

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

exists(dependency)

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 (Hashable) – Dependency to check.

Returns

Whether dependency has been registered in this provider or not.

Return type

bool

provide(dependency, container)

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 (~T) – The dependency to be provided by the provider. It will have passed exists().

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

Return type

DependencyValue

Returns

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

debug(dependency)

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

Parameters

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

Return type

DependencyDebug

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, container)

Expert feature

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

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

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

Return type

Optional[DependencyValue]

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)

Expert feature

maybe_debug() MUST be consistent with exists().

Parameters

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

Return type

Optional[DependencyDebug]

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)

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

Used by the different test utilities.

Parameters

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

Return type

StatelessProvider[~T]

Returns

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

class Scope(*args, **kwargs)

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()

Using this scope or specifying singleton=True is equivalent.

Return type

Scope

Returns

Singleton scope (unique object).

static sentinel()

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.

Return type

Scope

Returns

Sentinel scope (unique object).

class DependencyValue(value, *, scope=None)

Simple wrapper of the dependency value given by a Provider.

Parameters
  • value (object) – Actual dependency value.

  • scope (Optional[Scope]) – Scope of the dependency.

unwrapped

Actual dependency value.

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)

Retrieves given dependency or raises a DependencyNotFoundError.

Parameters

dependency (Hashable) – Dependency to be retrieved.

Return type

object

Returns

The dependency instance.

provide(dependency)

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 (Hashable) – Dependency to be retrieved.

Return type

DependencyValue

Returns

wrapped dependency instance.

class DependencyDebug(_DependencyDebug__info, *, scope=None, wired=(), dependencies=())

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 (Optional[Scope]) – Scope of the dependency.

  • wired (Sequence[object]) – Every class or function that may have been wired for this dependency.

  • dependencies (Sequence[Hashable]) – Any transient dependency, so dependencies of this dependency.

Exceptions

exception AntidoteError

Base class of all errors of antidote.

exception DependencyCycleError(stack)

A dependency cycle is found.

exception DependencyInstantiationError(dependency, stack=None)

The dependency could not be instantiated.

exception DependencyNotFoundError(dependency)

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)

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