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
- 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
Returns:
- Return type
- 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)
- 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)
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 toFalse
.keep_scopes (
bool
) – Whether current dependency values within the existing scopes should be kept in the new world. Likekeep_singletons
, the same objects will be propagated. Defaults toFalse
.
- Return type
- 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.
- 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
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 withscope
. Defaults toTrue
.scope (
Optional
[Scope
]) – Scope of the returned dependency. Mutually exclusive withsingleton
. The scope defines if and how long the returned dependency will be cached. SeeScope
. Defaults tosingleton()
.
- 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
- 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'
- 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
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'
- 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
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 withscope
. Defaults toTrue
.scope (
Optional
[Scope
]) – Scope of the returned dependency. Mutually exclusive withsingleton
. The scope defines if and how long the returned dependency will be cached. SeeScope
. Defaults tosingleton()
.
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 orNone
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 withfactory()
andsingleton()
. Dependencies declared withsingleton()
will hideprovider()
. Andprovider()
will hidefactory()
.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
- 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 ...
- 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
- Returns
Scope defined by either
singleton
orscope
.
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
orfactory()
.- 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 withscope
. Defaults toTrue
scope (
Optional
[Scope
]) – Scope of the service. Mutually exclusive withsingleton
. The scope defines if and how long the service will be cached. SeeScope
. Defaults tosingleton()
.wiring (
Optional
[Wiring
]) –Wiring
to be used on the class. By defaults will apply a simpleinject()
on all methods, so only annotated type hints are taken into account. Can be deactivated by specifyingNone
.
- Return type
- 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 decoratorservice()
instead. You may also useABCService
if you only need to be compatible with theabc.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
- 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()
orcore.wiring.WithWiringMixin.with_wiring()
.- Parameters
wiring (
Optional
[Wiring
]) – Wiring to be applied on the service. By default only__init__()
will be wired. Unlessfactory
is class method name, in which case only the latter would be wired. To deactivate any wiring at all useNone
.singleton (
Optional
[bool
]) – Whether the service is a singleton or not. A singleton is instantiated only once. Mutually exclusive withscope
. Defaults toTrue
scope (
Optional
[Scope
]) – Scope of the service. Mutually exclusive withsingleton
. The scope defines if and how long the service will be cached. SeeScope
. Defaults tosingleton()
.
- 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
- 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
, withService
.>>> 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 withscope
. Defaults toTrue
.scope (
Optional
[Scope
]) – Scope of the returned dependency. Mutually exclusive withsingleton
. The scope defines if and how long the returned dependency will be cached. SeeScope
. Defaults tosingleton()
.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 aboveAll 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()
orcore.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 useNone
.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 withscope
. Defaults toTrue
scope (
Optional
[Scope
]) – Scope of the returned dependency. Mutually exclusive withsingleton
. The scope defines if and how long the returned dependency will be cached. SeeScope
. Defaults tosingleton()
.
- 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
- 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 ofConstants.provide_const()
if one ofstr
,int
orfloat
. 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 aLookUpError
is raised inConstants.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()
orwith_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 withconst()
, the value of the dependency will be cast to its type if it’s one ofstr
,float
orint
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.
- provide_const(name, arg)
Used to retrieve the value of the constant defined with
const()
. If aLookUpError
is raised, thedefault
value defined withconst()
will be used if any.
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 returnedLazyCall
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 withscope
. Defaults toTrue
.scope (
Optional
[Scope
]) – Scope of the dependency. Mutually exclusive withsingleton
. The scope defines if and how long the returned dependency will be cached. SeeScope
. Defaults tosingleton()
.
- 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 aService
subclass.LazyMethodCall
behaves in a similar way as constants inConstants
. 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 withscope
. Defaults toTrue
.scope (
Scope
) – Scope of the dependency. Mutually exclusive withsingleton
. The scope defines if and how long the returned dependency will be cached. SeeScope
. Defaults tosingleton()
.
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
- 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:
Annotated type hints (PEP-593)
Dependencies declared explicitly if any declared with
dependencies
.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 toNone
. 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 withOptional
. 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 toFalse
.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 toTrue
.
- Return type
- 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
andimplementation()
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 orNone
.>>> 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 (
Union
[Methods
,Iterable
[str
]]) – Names of methods to be injected. If any of them is already injected, an error will be raised. Consider usingattempt_methods
otherwise.dependencies (
Union
[Mapping
[str
,Hashable
],Sequence
[Optional
[Hashable
]],Callable
[[Arg
],Optional
[Hashable
]],None
]) – Propagated for every method toinject()
auto_provide (
Union
[bool
,Iterable
[type
],Callable
[[type
],bool
],None
]) – Propagated for every method toinject()
- 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
- 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
raise_on_double_injection (
bool
) – Whether an error should be raised if method is already injected. Defaults toFalse
.__klass (
Optional
[~C]) – Class to wire.methods (
Union
[Methods
,Iterable
[str
]]) – Names of methods that must be injected. Defaults to all methoddependencies (
Union
[Mapping
[str
,Hashable
],Sequence
[Optional
[Hashable
]],Callable
[[Arg
],Optional
[Hashable
]],None
]) – Propagated for every method toinject()
.auto_provide (
Union
[bool
,Iterable
[type
],Callable
[[type
],bool
],None
]) – Propagated for every method toinject()
.
- Return type
- 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:
Different providers MUST provide strictly different dependencies.
You MUST NOT use
world
. If you need a dependency, rely on the providedContainer
.Methods will automatically freeze except those marked with the decorator
does_not_freeze()
. Methods changing dependencies definitions MUST NOT usedoes_not_freeze()
, others may.You may cache singletons by yourself, but you need to clean your cache when
clone()
is called withkeep_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.
- 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 ifexists()
succeeded on thedependency
.If you need to access other dependencies, you MUST use the provided
container
argument and NEVERworld
.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
- Return type
- Returns
The requested instance wrapped in a
DependencyValue
. If the dependency is a singleton, you MUST specify it withsingleton=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
- 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 withexists()
. It should return- Parameters
- Return type
- Returns
The requested instance wrapped in a
DependencyValue
if available orNone
. If the dependency is a singleton, you MUST specify it withsingleton=True
.
- maybe_debug(dependency)
Expert feature
maybe_debug()
MUST be consistent withexists()
.- Parameters
dependency (
Hashable
) – The dependency for which debug information should be provided. It will have passedexists()
.- Return type
- 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 implementsclone()
.>>> 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
- 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 asREQUEST_SCOPE
stays valid. That is to say, until you reset it withworld.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
andscope
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
andscope
arguments.- static singleton()
Using this scope or specifying
singleton=True
is equivalent.- Return type
- Returns
Singleton scope (unique object).
- static sentinel()
For functions having both
singleton
andscope
argument, validation of those is done withvalidated_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
- Returns
Sentinel scope (unique object).
- class DependencyValue(value, *, scope=None)
Simple wrapper of the dependency value given by a
Provider
.- Parameters
- 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
.
- provide(dependency)
Similar to
get()
it will retrieve a dependency. However it will be wrapped in aDependencyValue
if found. If notNone
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
- 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.
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.