2020-09-10 05:23:28 +03:00
|
|
|
"""Providers module."""
|
2017-03-25 23:38:48 +03:00
|
|
|
|
2020-08-27 05:24:20 +03:00
|
|
|
from __future__ import absolute_import
|
|
|
|
|
2017-03-25 23:38:48 +03:00
|
|
|
import copy
|
2021-01-24 06:37:50 +03:00
|
|
|
import errno
|
2021-01-11 03:26:15 +03:00
|
|
|
import functools
|
2020-10-25 03:56:32 +03:00
|
|
|
import inspect
|
2022-01-31 07:16:55 +03:00
|
|
|
import importlib
|
2020-06-25 19:50:42 +03:00
|
|
|
import os
|
2020-06-26 06:16:16 +03:00
|
|
|
import re
|
2017-03-25 23:38:48 +03:00
|
|
|
import sys
|
|
|
|
import types
|
|
|
|
import threading
|
2020-10-09 22:16:27 +03:00
|
|
|
import warnings
|
2017-03-25 23:38:48 +03:00
|
|
|
|
2021-04-19 04:37:55 +03:00
|
|
|
try:
|
|
|
|
import contextvars
|
|
|
|
except ImportError:
|
|
|
|
contextvars = None
|
|
|
|
|
2022-01-31 07:16:55 +03:00
|
|
|
try:
|
|
|
|
import builtins
|
|
|
|
except ImportError:
|
|
|
|
# Python 2.7
|
|
|
|
import __builtin__ as builtins
|
2021-04-19 04:37:55 +03:00
|
|
|
|
2018-10-18 19:39:19 +03:00
|
|
|
try:
|
|
|
|
import asyncio
|
|
|
|
except ImportError:
|
|
|
|
asyncio = None
|
|
|
|
_is_coroutine_marker = None
|
|
|
|
else:
|
2018-11-08 23:49:37 +03:00
|
|
|
if sys.version_info >= (3, 5, 3):
|
2018-10-18 19:39:19 +03:00
|
|
|
import asyncio.coroutines
|
|
|
|
_is_coroutine_marker = asyncio.coroutines._is_coroutine
|
2018-11-08 23:49:37 +03:00
|
|
|
else:
|
|
|
|
_is_coroutine_marker = True
|
2018-10-18 19:39:19 +03:00
|
|
|
|
2020-06-23 23:46:24 +03:00
|
|
|
try:
|
|
|
|
import ConfigParser as iniconfigparser
|
|
|
|
except ImportError:
|
|
|
|
import configparser as iniconfigparser
|
2018-10-18 19:39:19 +03:00
|
|
|
|
2020-06-25 04:04:30 +03:00
|
|
|
try:
|
|
|
|
import yaml
|
|
|
|
except ImportError:
|
|
|
|
yaml = None
|
2020-06-24 23:20:03 +03:00
|
|
|
|
2021-02-03 17:21:32 +03:00
|
|
|
try:
|
|
|
|
import pydantic
|
|
|
|
except ImportError:
|
|
|
|
pydantic = None
|
|
|
|
|
2017-10-04 01:28:31 +03:00
|
|
|
from .errors import (
|
|
|
|
Error,
|
|
|
|
NoSuchProviderError,
|
|
|
|
)
|
2017-03-25 23:38:48 +03:00
|
|
|
|
|
|
|
cimport cython
|
|
|
|
|
|
|
|
|
2017-03-29 00:08:38 +03:00
|
|
|
if sys.version_info[0] == 3: # pragma: no cover
|
|
|
|
CLASS_TYPES = (type,)
|
|
|
|
else: # pragma: no cover
|
|
|
|
CLASS_TYPES = (type, types.ClassType)
|
|
|
|
|
|
|
|
copy._deepcopy_dispatch[types.MethodType] = \
|
|
|
|
lambda obj, memo: type(obj)(obj.im_func,
|
|
|
|
copy.deepcopy(obj.im_self, memo),
|
|
|
|
obj.im_class)
|
|
|
|
|
2021-11-01 04:00:28 +03:00
|
|
|
if sys.version_info[:2] == (3, 5):
|
|
|
|
warnings.warn(
|
|
|
|
"Dependency Injector will drop support of Python 3.5 after Jan 1st of 2022. "
|
|
|
|
"This does not mean that there will be any immediate breaking changes, "
|
|
|
|
"but tests will no longer be executed on Python 3.5, and bugs will not be addressed.",
|
|
|
|
category=DeprecationWarning,
|
|
|
|
)
|
|
|
|
|
2021-06-13 22:07:30 +03:00
|
|
|
config_env_marker_pattern = re.compile(
|
2022-01-17 04:32:42 +03:00
|
|
|
r"\${(?P<name>[^}^{:]+)(?P<separator>:?)(?P<default>.*?)}",
|
2021-06-13 22:07:30 +03:00
|
|
|
)
|
|
|
|
|
2021-06-24 16:00:36 +03:00
|
|
|
def _resolve_config_env_markers(config_content, envs_required=False):
|
|
|
|
"""Replace environment variable markers with their values."""
|
|
|
|
findings = list(config_env_marker_pattern.finditer(config_content))
|
|
|
|
|
|
|
|
for match in reversed(findings):
|
2022-01-17 04:32:42 +03:00
|
|
|
env_name = match.group("name")
|
|
|
|
has_default = match.group("separator") == ":"
|
2021-06-13 22:07:30 +03:00
|
|
|
|
2021-06-24 16:00:36 +03:00
|
|
|
value = os.getenv(env_name)
|
2021-06-13 22:07:30 +03:00
|
|
|
if value is None:
|
2021-06-24 16:00:36 +03:00
|
|
|
if not has_default and envs_required:
|
2022-01-17 04:32:42 +03:00
|
|
|
raise ValueError(f"Missing required environment variable \"{env_name}\"")
|
|
|
|
value = match.group("default")
|
2021-06-13 22:07:30 +03:00
|
|
|
|
|
|
|
span_min, span_max = match.span()
|
2022-01-17 04:32:42 +03:00
|
|
|
config_content = f"{config_content[:span_min]}{value}{config_content[span_max:]}"
|
2021-06-24 16:00:36 +03:00
|
|
|
return config_content
|
2021-06-13 22:07:30 +03:00
|
|
|
|
2020-06-26 06:16:16 +03:00
|
|
|
|
|
|
|
if sys.version_info[0] == 3:
|
2021-06-24 16:00:36 +03:00
|
|
|
def _parse_ini_file(filepath, envs_required=False):
|
|
|
|
parser = iniconfigparser.ConfigParser()
|
2021-01-24 06:37:50 +03:00
|
|
|
with open(filepath) as config_file:
|
2021-06-24 16:00:36 +03:00
|
|
|
config_string = _resolve_config_env_markers(
|
|
|
|
config_file.read(),
|
|
|
|
envs_required=envs_required,
|
|
|
|
)
|
|
|
|
parser.read_string(config_string)
|
2020-06-26 06:37:01 +03:00
|
|
|
return parser
|
2020-06-26 06:16:16 +03:00
|
|
|
else:
|
2020-06-26 06:37:01 +03:00
|
|
|
import StringIO
|
|
|
|
|
2021-06-24 16:00:36 +03:00
|
|
|
def _parse_ini_file(filepath, envs_required=False):
|
2020-06-26 06:37:01 +03:00
|
|
|
parser = iniconfigparser.ConfigParser()
|
2021-01-24 06:37:50 +03:00
|
|
|
with open(filepath) as config_file:
|
2021-06-24 16:00:36 +03:00
|
|
|
config_string = _resolve_config_env_markers(
|
|
|
|
config_file.read(),
|
|
|
|
envs_required=envs_required,
|
|
|
|
)
|
2021-01-24 06:37:50 +03:00
|
|
|
parser.readfp(StringIO.StringIO(config_string))
|
|
|
|
return parser
|
2020-06-26 06:16:16 +03:00
|
|
|
|
2017-03-25 23:38:48 +03:00
|
|
|
|
2021-01-22 02:00:24 +03:00
|
|
|
if yaml:
|
|
|
|
class YamlLoader(yaml.SafeLoader):
|
2021-06-24 16:00:36 +03:00
|
|
|
"""YAML loader.
|
2021-01-22 02:00:24 +03:00
|
|
|
|
2021-06-24 16:00:36 +03:00
|
|
|
This loader mimics ``yaml.SafeLoader``.
|
2021-01-22 02:00:24 +03:00
|
|
|
"""
|
|
|
|
else:
|
|
|
|
class YamlLoader:
|
2021-06-24 16:00:36 +03:00
|
|
|
"""YAML loader.
|
2021-01-22 02:00:24 +03:00
|
|
|
|
2021-06-24 16:00:36 +03:00
|
|
|
This loader mimics ``yaml.SafeLoader``.
|
2021-01-22 02:00:24 +03:00
|
|
|
"""
|
|
|
|
|
|
|
|
|
2021-01-24 06:37:50 +03:00
|
|
|
UNDEFINED = object()
|
2021-01-22 02:00:24 +03:00
|
|
|
|
2021-01-11 03:26:15 +03:00
|
|
|
cdef int ASYNC_MODE_UNDEFINED = 0
|
|
|
|
cdef int ASYNC_MODE_ENABLED = 1
|
|
|
|
cdef int ASYNC_MODE_DISABLED = 2
|
|
|
|
|
|
|
|
|
2017-03-25 23:38:48 +03:00
|
|
|
cdef class Provider(object):
|
|
|
|
"""Base provider class.
|
|
|
|
|
|
|
|
:py:class:`Provider` is callable (implements ``__call__`` method). Every
|
|
|
|
call to provider object returns provided result, according to the providing
|
|
|
|
strategy of particular provider. This ``callable`` functionality is a
|
2022-01-17 04:32:42 +03:00
|
|
|
regular part of providers API and it should be the same for all provider
|
2017-03-25 23:38:48 +03:00
|
|
|
subclasses.
|
|
|
|
|
|
|
|
Implementation of particular providing strategy should be done in
|
|
|
|
:py:meth:`Provider._provide` of :py:class:`Provider` subclass. Current
|
|
|
|
method is called every time when not overridden provider is called.
|
|
|
|
|
|
|
|
:py:class:`Provider` implements provider overriding logic that should be
|
|
|
|
also common for all providers:
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
provider1 = Factory(SomeClass)
|
|
|
|
provider2 = Factory(ChildSomeClass)
|
|
|
|
|
|
|
|
provider1.override(provider2)
|
|
|
|
|
|
|
|
some_instance = provider1()
|
|
|
|
assert isinstance(some_instance, ChildSomeClass)
|
|
|
|
|
|
|
|
Also :py:class:`Provider` implements helper function for creating its
|
|
|
|
delegates:
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
provider = Factory(object)
|
|
|
|
delegate = provider.delegate()
|
|
|
|
|
|
|
|
delegated = delegate()
|
|
|
|
|
|
|
|
assert provider is delegated
|
|
|
|
|
|
|
|
All providers should extend this class.
|
|
|
|
|
|
|
|
.. py:attribute:: overridden
|
2020-06-17 05:22:06 +03:00
|
|
|
:noindex:
|
2017-03-25 23:38:48 +03:00
|
|
|
|
|
|
|
Tuple of overriding providers, if any.
|
|
|
|
|
|
|
|
:type: tuple[:py:class:`Provider`] | None
|
|
|
|
"""
|
|
|
|
|
|
|
|
__IS_PROVIDER__ = True
|
|
|
|
|
2017-04-18 23:30:29 +03:00
|
|
|
overriding_lock = threading.RLock()
|
2017-04-19 00:00:29 +03:00
|
|
|
"""Overriding reentrant lock.
|
2017-04-18 23:30:29 +03:00
|
|
|
|
|
|
|
:type: :py:class:`threading.RLock`
|
|
|
|
"""
|
|
|
|
|
2017-03-25 23:38:48 +03:00
|
|
|
def __init__(self):
|
|
|
|
"""Initializer."""
|
|
|
|
self.__overridden = tuple()
|
2017-04-06 12:34:04 +03:00
|
|
|
self.__last_overriding = None
|
2021-03-21 04:41:39 +03:00
|
|
|
self.__overrides = tuple()
|
2021-01-11 03:26:15 +03:00
|
|
|
self.__async_mode = ASYNC_MODE_UNDEFINED
|
2017-03-25 23:38:48 +03:00
|
|
|
super(Provider, self).__init__()
|
|
|
|
|
|
|
|
def __call__(self, *args, **kwargs):
|
|
|
|
"""Return provided object.
|
|
|
|
|
|
|
|
Callable interface implementation.
|
|
|
|
"""
|
2017-04-06 12:34:04 +03:00
|
|
|
if self.__last_overriding is not None:
|
2021-01-11 03:26:15 +03:00
|
|
|
result = self.__last_overriding(*args, **kwargs)
|
|
|
|
else:
|
|
|
|
result = self._provide(args, kwargs)
|
|
|
|
|
|
|
|
if self.is_async_mode_disabled():
|
|
|
|
return result
|
|
|
|
elif self.is_async_mode_enabled():
|
2021-02-17 17:56:39 +03:00
|
|
|
if __is_future_or_coroutine(result):
|
|
|
|
return result
|
|
|
|
return __future_result(result)
|
2021-01-11 03:26:15 +03:00
|
|
|
elif self.is_async_mode_undefined():
|
2021-02-17 17:56:39 +03:00
|
|
|
if __is_future_or_coroutine(result):
|
2021-01-11 03:26:15 +03:00
|
|
|
self.enable_async_mode()
|
|
|
|
else:
|
|
|
|
self.disable_async_mode()
|
|
|
|
return result
|
2017-03-25 23:38:48 +03:00
|
|
|
|
|
|
|
def __deepcopy__(self, memo):
|
|
|
|
"""Create and return full copy of provider."""
|
|
|
|
copied = memo.get(id(self))
|
|
|
|
if copied is not None:
|
|
|
|
return copied
|
|
|
|
|
2021-03-21 04:41:39 +03:00
|
|
|
copied = _memorized_duplicate(self, memo)
|
2018-07-24 23:09:56 +03:00
|
|
|
self._copy_overridings(copied, memo)
|
2017-03-25 23:38:48 +03:00
|
|
|
return copied
|
|
|
|
|
2020-09-14 03:32:21 +03:00
|
|
|
@classmethod
|
|
|
|
def __class_getitem__(cls, item):
|
|
|
|
return cls
|
|
|
|
|
2017-03-25 23:38:48 +03:00
|
|
|
def __str__(self):
|
|
|
|
"""Return string representation of provider.
|
|
|
|
|
|
|
|
:rtype: str
|
|
|
|
"""
|
|
|
|
return represent_provider(provider=self, provides=None)
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
"""Return string representation of provider.
|
|
|
|
|
|
|
|
:rtype: str
|
|
|
|
"""
|
|
|
|
return self.__str__()
|
|
|
|
|
|
|
|
@property
|
|
|
|
def overridden(self):
|
|
|
|
"""Return tuple of overriding providers."""
|
2018-07-26 10:22:43 +03:00
|
|
|
with self.overriding_lock:
|
2017-04-18 23:30:29 +03:00
|
|
|
return self.__overridden
|
2017-03-25 23:38:48 +03:00
|
|
|
|
2017-05-08 17:07:22 +03:00
|
|
|
@property
|
|
|
|
def last_overriding(self):
|
|
|
|
"""Return last overriding provider.
|
|
|
|
|
|
|
|
If provider is not overridden, then None is returned.
|
|
|
|
"""
|
|
|
|
return self.__last_overriding
|
|
|
|
|
2017-03-25 23:38:48 +03:00
|
|
|
def override(self, provider):
|
|
|
|
"""Override provider with another provider.
|
|
|
|
|
|
|
|
:param provider: Overriding provider.
|
|
|
|
:type provider: :py:class:`Provider`
|
|
|
|
|
|
|
|
:raise: :py:exc:`dependency_injector.errors.Error`
|
|
|
|
|
|
|
|
:return: Overriding context.
|
|
|
|
:rtype: :py:class:`OverridingContext`
|
|
|
|
"""
|
|
|
|
if provider is self:
|
2022-01-17 04:32:42 +03:00
|
|
|
raise Error("Provider {0} could not be overridden with itself".format(self))
|
2017-03-25 23:38:48 +03:00
|
|
|
|
|
|
|
if not is_provider(provider):
|
|
|
|
provider = Object(provider)
|
|
|
|
|
2018-07-26 10:22:43 +03:00
|
|
|
with self.overriding_lock:
|
2017-04-18 23:30:29 +03:00
|
|
|
self.__overridden += (provider,)
|
|
|
|
self.__last_overriding = provider
|
2021-03-21 04:41:39 +03:00
|
|
|
provider.register_overrides(self)
|
2017-03-25 23:38:48 +03:00
|
|
|
|
|
|
|
return OverridingContext(self, provider)
|
|
|
|
|
|
|
|
def reset_last_overriding(self):
|
|
|
|
"""Reset last overriding provider.
|
|
|
|
|
|
|
|
:raise: :py:exc:`dependency_injector.errors.Error` if provider is not
|
|
|
|
overridden.
|
|
|
|
|
|
|
|
:rtype: None
|
|
|
|
"""
|
2018-07-26 10:22:43 +03:00
|
|
|
with self.overriding_lock:
|
2017-04-18 23:30:29 +03:00
|
|
|
if len(self.__overridden) == 0:
|
2022-01-17 04:32:42 +03:00
|
|
|
raise Error("Provider {0} is not overridden".format(str(self)))
|
2017-03-25 23:38:48 +03:00
|
|
|
|
2021-03-21 04:41:39 +03:00
|
|
|
self.__last_overriding.unregister_overrides(self)
|
|
|
|
|
2017-04-18 23:30:29 +03:00
|
|
|
self.__overridden = self.__overridden[:-1]
|
|
|
|
try:
|
|
|
|
self.__last_overriding = self.__overridden[-1]
|
|
|
|
except IndexError:
|
|
|
|
self.__last_overriding = None
|
2017-03-25 23:38:48 +03:00
|
|
|
|
|
|
|
def reset_override(self):
|
|
|
|
"""Reset all overriding providers.
|
|
|
|
|
|
|
|
:rtype: None
|
|
|
|
"""
|
2018-07-26 10:22:43 +03:00
|
|
|
with self.overriding_lock:
|
2021-03-21 04:41:39 +03:00
|
|
|
for provider in self.__overridden:
|
|
|
|
provider.unregister_overrides(self)
|
2017-04-18 23:30:29 +03:00
|
|
|
self.__overridden = tuple()
|
|
|
|
self.__last_overriding = None
|
2017-03-25 23:38:48 +03:00
|
|
|
|
2021-03-21 04:41:39 +03:00
|
|
|
@property
|
|
|
|
def overrides(self):
|
|
|
|
"""Return providers that are overridden by the current provider."""
|
|
|
|
return self.__overrides
|
|
|
|
|
|
|
|
def register_overrides(self, provider):
|
|
|
|
"""Register provider that overrides current provider."""
|
|
|
|
self.__overrides = tuple(set(self.__overrides + (provider,)))
|
|
|
|
|
|
|
|
def unregister_overrides(self, provider):
|
|
|
|
"""Unregister provider that overrides current provider."""
|
|
|
|
overrides = set(self.__overrides)
|
|
|
|
if provider in overrides:
|
|
|
|
overrides.remove(provider)
|
|
|
|
self.__overrides = tuple(overrides)
|
|
|
|
|
2021-01-11 03:26:15 +03:00
|
|
|
def async_(self, *args, **kwargs):
|
|
|
|
"""Return provided object asynchronously.
|
|
|
|
|
|
|
|
This method is a synonym of __call__().
|
|
|
|
It provides typing stubs for correct type checking with
|
|
|
|
`await` expression:
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
database_provider: Provider[DatabaseConnection] = Resource(init_db_async)
|
|
|
|
|
|
|
|
async def main():
|
|
|
|
db: DatabaseConnection = await database_provider.async_()
|
|
|
|
...
|
|
|
|
"""
|
|
|
|
return self.__call__(*args, **kwargs)
|
|
|
|
|
2017-03-25 23:38:48 +03:00
|
|
|
def delegate(self):
|
2022-01-17 04:32:42 +03:00
|
|
|
"""Return provider delegate.
|
2017-03-25 23:38:48 +03:00
|
|
|
|
|
|
|
:rtype: :py:class:`Delegate`
|
|
|
|
"""
|
2020-10-09 22:16:27 +03:00
|
|
|
warnings.warn(
|
2022-01-17 04:32:42 +03:00
|
|
|
"Method \".delegate()\" is deprecated since version 4.0.0. "
|
|
|
|
"Use \".provider\" attribute instead.",
|
2020-10-09 22:16:27 +03:00
|
|
|
category=DeprecationWarning,
|
|
|
|
)
|
2017-03-25 23:38:48 +03:00
|
|
|
return Delegate(self)
|
|
|
|
|
2017-10-13 20:15:21 +03:00
|
|
|
@property
|
|
|
|
def provider(self):
|
2022-01-17 04:32:42 +03:00
|
|
|
"""Return provider"s delegate.
|
2017-10-13 20:15:21 +03:00
|
|
|
|
|
|
|
:rtype: :py:class:`Delegate`
|
|
|
|
"""
|
2020-10-09 22:16:27 +03:00
|
|
|
return Delegate(self)
|
2017-10-13 20:15:21 +03:00
|
|
|
|
2020-10-20 00:21:38 +03:00
|
|
|
@property
|
|
|
|
def provided(self):
|
|
|
|
"""Return :py:class:`ProvidedInstance` provider."""
|
|
|
|
return ProvidedInstance(self)
|
|
|
|
|
2021-01-11 03:26:15 +03:00
|
|
|
def enable_async_mode(self):
|
|
|
|
"""Enable async mode."""
|
|
|
|
self.__async_mode = ASYNC_MODE_ENABLED
|
|
|
|
|
|
|
|
def disable_async_mode(self):
|
|
|
|
"""Disable async mode."""
|
|
|
|
self.__async_mode = ASYNC_MODE_DISABLED
|
|
|
|
|
|
|
|
def reset_async_mode(self):
|
|
|
|
"""Reset async mode.
|
|
|
|
|
|
|
|
Provider will automatically set the mode on the next call.
|
|
|
|
"""
|
|
|
|
self.__async_mode = ASYNC_MODE_UNDEFINED
|
|
|
|
|
|
|
|
def is_async_mode_enabled(self):
|
|
|
|
"""Check if async mode is enabled."""
|
|
|
|
return self.__async_mode == ASYNC_MODE_ENABLED
|
|
|
|
|
|
|
|
def is_async_mode_disabled(self):
|
|
|
|
"""Check if async mode is disabled."""
|
|
|
|
return self.__async_mode == ASYNC_MODE_DISABLED
|
|
|
|
|
|
|
|
def is_async_mode_undefined(self):
|
|
|
|
"""Check if async mode is undefined."""
|
|
|
|
return self.__async_mode == ASYNC_MODE_UNDEFINED
|
|
|
|
|
2021-02-01 17:42:21 +03:00
|
|
|
@property
|
|
|
|
def related(self):
|
|
|
|
"""Return related providers generator."""
|
|
|
|
yield from self.overridden
|
|
|
|
|
|
|
|
def traverse(self, types=None):
|
|
|
|
"""Return providers traversal generator."""
|
|
|
|
return traverse(*self.related, types=types)
|
|
|
|
|
2017-03-25 23:38:48 +03:00
|
|
|
cpdef object _provide(self, tuple args, dict kwargs):
|
|
|
|
"""Providing strategy implementation.
|
|
|
|
|
|
|
|
Abstract protected method that implements providing strategy of
|
|
|
|
particular provider. Current method is called every time when not
|
|
|
|
overridden provider is called. Need to be overridden in subclasses.
|
|
|
|
"""
|
|
|
|
raise NotImplementedError()
|
|
|
|
|
2018-07-24 23:09:56 +03:00
|
|
|
cpdef void _copy_overridings(self, Provider copied, dict memo):
|
2018-01-22 00:55:32 +03:00
|
|
|
"""Copy provider overridings to a newly copied provider."""
|
2018-07-24 23:09:56 +03:00
|
|
|
copied.__overridden = deepcopy(self.__overridden, memo)
|
|
|
|
copied.__last_overriding = deepcopy(self.__last_overriding, memo)
|
2021-03-21 04:41:39 +03:00
|
|
|
copied.__overrides = deepcopy(self.__overrides, memo)
|
2018-01-22 00:55:32 +03:00
|
|
|
|
2017-03-25 23:38:48 +03:00
|
|
|
|
|
|
|
cdef class Object(Provider):
|
|
|
|
"""Object provider returns provided instance "as is".
|
|
|
|
|
|
|
|
.. py:attribute:: provides
|
|
|
|
|
|
|
|
Value that have to be provided.
|
|
|
|
|
|
|
|
:type: object
|
|
|
|
"""
|
|
|
|
|
2021-03-20 20:16:51 +03:00
|
|
|
def __init__(self, provides=None):
|
|
|
|
"""Initialize provider."""
|
|
|
|
self.__provides = None
|
|
|
|
self.set_provides(provides)
|
2017-03-25 23:38:48 +03:00
|
|
|
super(Object, self).__init__()
|
|
|
|
|
|
|
|
def __deepcopy__(self, memo):
|
|
|
|
"""Create and return full copy of provider."""
|
|
|
|
copied = memo.get(id(self))
|
|
|
|
if copied is not None:
|
|
|
|
return copied
|
|
|
|
|
2021-03-20 20:16:51 +03:00
|
|
|
copied = _memorized_duplicate(self, memo)
|
|
|
|
copied.set_provides(self.provides)
|
2017-03-25 23:38:48 +03:00
|
|
|
|
2018-07-24 23:09:56 +03:00
|
|
|
self._copy_overridings(copied, memo)
|
2017-03-25 23:38:48 +03:00
|
|
|
|
|
|
|
return copied
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
"""Return string representation of provider.
|
|
|
|
|
|
|
|
:rtype: str
|
|
|
|
"""
|
|
|
|
return represent_provider(provider=self, provides=self.__provides)
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
"""Return string representation of provider.
|
|
|
|
|
|
|
|
:rtype: str
|
|
|
|
"""
|
|
|
|
return self.__str__()
|
|
|
|
|
2021-03-20 20:16:51 +03:00
|
|
|
@property
|
|
|
|
def provides(self):
|
2022-01-17 04:32:42 +03:00
|
|
|
"""Return provider provides."""
|
2021-03-20 20:16:51 +03:00
|
|
|
return self.__provides
|
|
|
|
|
|
|
|
def set_provides(self, provides):
|
2022-01-17 04:32:42 +03:00
|
|
|
"""Set provider provides."""
|
2021-03-20 20:16:51 +03:00
|
|
|
self.__provides = provides
|
|
|
|
return self
|
|
|
|
|
2021-02-01 17:42:21 +03:00
|
|
|
@property
|
|
|
|
def related(self):
|
|
|
|
"""Return related providers generator."""
|
|
|
|
if isinstance(self.__provides, Provider):
|
|
|
|
yield self.__provides
|
|
|
|
yield from super().related
|
|
|
|
|
2017-03-25 23:38:48 +03:00
|
|
|
cpdef object _provide(self, tuple args, dict kwargs):
|
|
|
|
"""Return provided instance.
|
|
|
|
|
|
|
|
:param args: Tuple of context positional arguments.
|
|
|
|
:type args: tuple[object]
|
|
|
|
|
|
|
|
:param kwargs: Dictionary of context keyword arguments.
|
|
|
|
:type kwargs: dict[str, object]
|
|
|
|
|
|
|
|
:rtype: object
|
|
|
|
"""
|
|
|
|
return self.__provides
|
|
|
|
|
|
|
|
|
2021-02-07 22:13:23 +03:00
|
|
|
cdef class Self(Provider):
|
|
|
|
"""Self provider returns own container."""
|
|
|
|
|
|
|
|
def __init__(self, container=None):
|
|
|
|
"""Initialize provider."""
|
|
|
|
self.__container = container
|
|
|
|
self.__alt_names = tuple()
|
|
|
|
super().__init__()
|
|
|
|
|
|
|
|
def __deepcopy__(self, memo):
|
|
|
|
"""Create and return full copy of provider."""
|
|
|
|
copied = memo.get(id(self))
|
|
|
|
if copied is not None:
|
|
|
|
return copied
|
|
|
|
|
2021-03-21 04:41:39 +03:00
|
|
|
copied = _memorized_duplicate(self, memo)
|
2021-02-07 22:13:23 +03:00
|
|
|
copied.set_container(deepcopy(self.__container, memo))
|
|
|
|
copied.set_alt_names(self.__alt_names)
|
|
|
|
self._copy_overridings(copied, memo)
|
|
|
|
return copied
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
"""Return string representation of provider.
|
|
|
|
|
|
|
|
:rtype: str
|
|
|
|
"""
|
|
|
|
return represent_provider(provider=self, provides=self.__container)
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
"""Return string representation of provider.
|
|
|
|
|
|
|
|
:rtype: str
|
|
|
|
"""
|
|
|
|
return self.__str__()
|
|
|
|
|
|
|
|
def set_container(self, container):
|
|
|
|
self.__container = container
|
|
|
|
|
|
|
|
def set_alt_names(self, alt_names):
|
|
|
|
self.__alt_names = tuple(set(alt_names))
|
|
|
|
|
|
|
|
@property
|
|
|
|
def alt_names(self):
|
|
|
|
return self.__alt_names
|
|
|
|
|
|
|
|
cpdef object _provide(self, tuple args, dict kwargs):
|
|
|
|
return self.__container
|
|
|
|
|
|
|
|
|
2019-10-09 17:45:14 +03:00
|
|
|
cdef class Delegate(Provider):
|
2017-03-25 23:38:48 +03:00
|
|
|
"""Delegate provider returns provider "as is".
|
|
|
|
|
|
|
|
.. py:attribute:: provides
|
|
|
|
|
|
|
|
Value that have to be provided.
|
|
|
|
|
|
|
|
:type: object
|
|
|
|
"""
|
|
|
|
|
2021-03-06 01:24:48 +03:00
|
|
|
def __init__(self, provides=None):
|
2021-03-20 20:16:51 +03:00
|
|
|
"""Initialize provider."""
|
2021-03-06 01:24:48 +03:00
|
|
|
self.__provides = None
|
|
|
|
self.set_provides(provides)
|
2019-10-09 17:45:14 +03:00
|
|
|
super(Delegate, self).__init__()
|
|
|
|
|
|
|
|
def __deepcopy__(self, memo):
|
|
|
|
"""Create and return full copy of provider."""
|
|
|
|
copied = memo.get(id(self))
|
|
|
|
if copied is not None:
|
|
|
|
return copied
|
|
|
|
|
2021-03-20 20:16:51 +03:00
|
|
|
copied = _memorized_duplicate(self, memo)
|
2021-03-21 04:41:39 +03:00
|
|
|
copied.set_provides(_copy_if_provider(self.provides, memo))
|
2019-10-09 17:45:14 +03:00
|
|
|
self._copy_overridings(copied, memo)
|
|
|
|
|
|
|
|
return copied
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
"""Return string representation of provider.
|
|
|
|
|
|
|
|
:rtype: str
|
2017-03-25 23:38:48 +03:00
|
|
|
"""
|
2019-10-09 17:45:14 +03:00
|
|
|
return represent_provider(provider=self, provides=self.__provides)
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
"""Return string representation of provider.
|
|
|
|
|
|
|
|
:rtype: str
|
|
|
|
"""
|
|
|
|
return self.__str__()
|
|
|
|
|
2020-10-09 22:16:27 +03:00
|
|
|
@property
|
|
|
|
def provides(self):
|
2022-01-17 04:32:42 +03:00
|
|
|
"""Return provider provides."""
|
2020-10-09 22:16:27 +03:00
|
|
|
return self.__provides
|
|
|
|
|
2021-03-06 01:24:48 +03:00
|
|
|
def set_provides(self, provides):
|
2022-01-17 04:32:42 +03:00
|
|
|
"""Set provider provides."""
|
2021-03-06 01:24:48 +03:00
|
|
|
if provides:
|
|
|
|
provides = ensure_is_provider(provides)
|
|
|
|
self.__provides = provides
|
2021-03-20 20:16:51 +03:00
|
|
|
return self
|
2021-03-06 01:24:48 +03:00
|
|
|
|
2021-02-01 17:42:21 +03:00
|
|
|
@property
|
|
|
|
def related(self):
|
|
|
|
"""Return related providers generator."""
|
|
|
|
yield self.__provides
|
|
|
|
yield from super().related
|
|
|
|
|
2019-10-09 17:45:14 +03:00
|
|
|
cpdef object _provide(self, tuple args, dict kwargs):
|
|
|
|
"""Return provided instance.
|
|
|
|
|
|
|
|
:param args: Tuple of context positional arguments.
|
|
|
|
:type args: tuple[object]
|
|
|
|
|
|
|
|
:param kwargs: Dictionary of context keyword arguments.
|
|
|
|
:type kwargs: dict[str, object]
|
|
|
|
|
|
|
|
:rtype: object
|
|
|
|
"""
|
|
|
|
return self.__provides
|
2017-03-25 23:38:48 +03:00
|
|
|
|
|
|
|
|
2022-01-10 05:45:20 +03:00
|
|
|
cdef class Aggregate(Provider):
|
|
|
|
"""Providers aggregate.
|
|
|
|
|
|
|
|
:py:class:`Aggregate` is a delegated provider, meaning that it is
|
|
|
|
injected "as is".
|
|
|
|
|
|
|
|
All aggregated providers can be retrieved as a read-only
|
|
|
|
dictionary :py:attr:`Aggregate.providers` or as an attribute of
|
|
|
|
:py:class:`Aggregate`, e.g. ``aggregate.provider``.
|
|
|
|
"""
|
|
|
|
|
|
|
|
__IS_DELEGATED__ = True
|
|
|
|
|
|
|
|
def __init__(self, provider_dict=None, **provider_kwargs):
|
|
|
|
"""Initialize provider."""
|
|
|
|
self.__providers = {}
|
|
|
|
self.set_providers(provider_dict, **provider_kwargs)
|
|
|
|
super().__init__()
|
|
|
|
|
|
|
|
def __deepcopy__(self, memo):
|
|
|
|
"""Create and return full copy of provider."""
|
|
|
|
copied = memo.get(id(self))
|
|
|
|
if copied is not None:
|
|
|
|
return copied
|
|
|
|
|
|
|
|
copied = _memorized_duplicate(self, memo)
|
|
|
|
copied.set_providers(deepcopy(self.providers, memo))
|
|
|
|
|
|
|
|
self._copy_overridings(copied, memo)
|
|
|
|
|
|
|
|
return copied
|
|
|
|
|
|
|
|
def __getattr__(self, factory_name):
|
|
|
|
"""Return aggregated provider."""
|
|
|
|
return self.__get_provider(factory_name)
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
"""Return string representation of provider.
|
|
|
|
|
|
|
|
:rtype: str
|
|
|
|
"""
|
|
|
|
return represent_provider(provider=self, provides=self.providers)
|
|
|
|
|
|
|
|
@property
|
|
|
|
def providers(self):
|
|
|
|
"""Return dictionary of providers, read-only.
|
|
|
|
|
|
|
|
Alias for ``.factories`` attribute.
|
|
|
|
"""
|
|
|
|
return dict(self.__providers)
|
|
|
|
|
|
|
|
def set_providers(self, provider_dict=None, **provider_kwargs):
|
|
|
|
"""Set providers.
|
|
|
|
|
|
|
|
Alias for ``.set_factories()`` method.
|
|
|
|
"""
|
|
|
|
providers = {}
|
|
|
|
providers.update(provider_kwargs)
|
|
|
|
if provider_dict:
|
|
|
|
providers.update(provider_dict)
|
|
|
|
|
|
|
|
for provider in providers.values():
|
|
|
|
if not is_provider(provider):
|
|
|
|
raise Error(
|
2022-01-17 04:32:42 +03:00
|
|
|
"{0} can aggregate only instances of {1}, given - {2}".format(
|
2022-01-10 05:45:20 +03:00
|
|
|
self.__class__,
|
|
|
|
Provider,
|
|
|
|
provider,
|
|
|
|
),
|
|
|
|
)
|
|
|
|
|
|
|
|
self.__providers = providers
|
|
|
|
return self
|
|
|
|
|
|
|
|
def override(self, _):
|
|
|
|
"""Override provider with another provider.
|
|
|
|
|
|
|
|
:raise: :py:exc:`dependency_injector.errors.Error`
|
|
|
|
|
|
|
|
:return: Overriding context.
|
|
|
|
:rtype: :py:class:`OverridingContext`
|
|
|
|
"""
|
2022-01-17 04:32:42 +03:00
|
|
|
raise Error("{0} providers could not be overridden".format(self.__class__))
|
2022-01-10 05:45:20 +03:00
|
|
|
|
|
|
|
@property
|
|
|
|
def related(self):
|
|
|
|
"""Return related providers generator."""
|
|
|
|
yield from self.__providers.values()
|
|
|
|
yield from super().related
|
|
|
|
|
|
|
|
cpdef object _provide(self, tuple args, dict kwargs):
|
|
|
|
try:
|
|
|
|
provider_name = args[0]
|
|
|
|
except IndexError:
|
|
|
|
try:
|
|
|
|
provider_name = kwargs.pop("factory_name")
|
|
|
|
except KeyError:
|
|
|
|
raise TypeError("Missing 1st required positional argument: \"provider_name\"")
|
|
|
|
else:
|
|
|
|
args = args[1:]
|
|
|
|
|
|
|
|
return self.__get_provider(provider_name)(*args, **kwargs)
|
|
|
|
|
|
|
|
cdef Provider __get_provider(self, object provider_name):
|
|
|
|
if provider_name not in self.__providers:
|
|
|
|
raise NoSuchProviderError("{0} does not contain provider with name {1}".format(self, provider_name))
|
|
|
|
return <Provider> self.__providers[provider_name]
|
|
|
|
|
|
|
|
|
2017-12-21 23:47:31 +03:00
|
|
|
cdef class Dependency(Provider):
|
|
|
|
""":py:class:`Dependency` provider describes dependency interface.
|
2017-03-25 23:38:48 +03:00
|
|
|
|
|
|
|
This provider is used for description of dependency interface. That might
|
2022-01-17 04:32:42 +03:00
|
|
|
be useful when dependency could be provided in the client"s code only,
|
|
|
|
but its interface is known. Such situations could happen when required
|
2020-06-14 05:24:32 +03:00
|
|
|
dependency has non-deterministic list of dependencies itself.
|
2017-03-25 23:38:48 +03:00
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
2017-12-21 23:47:31 +03:00
|
|
|
database_provider = Dependency(sqlite3.dbapi2.Connection)
|
2022-01-17 04:32:42 +03:00
|
|
|
database_provider.override(Factory(sqlite3.connect, ":memory:"))
|
2017-03-25 23:38:48 +03:00
|
|
|
|
|
|
|
database = database_provider()
|
|
|
|
|
|
|
|
.. py:attribute:: instance_of
|
2020-06-17 05:22:06 +03:00
|
|
|
:noindex:
|
2017-03-25 23:38:48 +03:00
|
|
|
|
|
|
|
Class of required dependency.
|
|
|
|
|
|
|
|
:type: type
|
2017-12-21 23:47:31 +03:00
|
|
|
"""
|
2017-03-25 23:38:48 +03:00
|
|
|
|
2021-03-20 20:16:51 +03:00
|
|
|
def __init__(self, object instance_of=object, default=None):
|
|
|
|
"""Initialize provider."""
|
|
|
|
self.__instance_of = None
|
|
|
|
self.set_instance_of(instance_of)
|
2021-01-29 21:49:40 +03:00
|
|
|
|
2021-03-20 20:16:51 +03:00
|
|
|
self.__default = None
|
|
|
|
self.set_default(default)
|
2021-01-29 21:49:40 +03:00
|
|
|
|
2021-02-13 17:16:38 +03:00
|
|
|
self.__parent = None
|
|
|
|
|
2017-12-21 23:47:31 +03:00
|
|
|
super(Dependency, self).__init__()
|
2017-03-25 23:38:48 +03:00
|
|
|
|
|
|
|
def __deepcopy__(self, memo):
|
|
|
|
"""Create and return full copy of provider."""
|
|
|
|
copied = memo.get(id(self))
|
|
|
|
if copied is not None:
|
|
|
|
return copied
|
|
|
|
|
2021-03-20 20:16:51 +03:00
|
|
|
copied = _memorized_duplicate(self, memo)
|
|
|
|
copied.set_instance_of(self.instance_of)
|
|
|
|
copied.set_default(deepcopy(self.default, memo))
|
2017-03-25 23:38:48 +03:00
|
|
|
|
2021-02-13 17:16:38 +03:00
|
|
|
self._copy_parent(copied, memo)
|
2018-07-24 23:09:56 +03:00
|
|
|
self._copy_overridings(copied, memo)
|
2017-03-25 23:38:48 +03:00
|
|
|
|
|
|
|
return copied
|
|
|
|
|
|
|
|
def __call__(self, *args, **kwargs):
|
|
|
|
"""Return provided instance.
|
|
|
|
|
|
|
|
:raise: :py:exc:`dependency_injector.errors.Error`
|
|
|
|
|
|
|
|
:rtype: object
|
|
|
|
"""
|
2021-01-29 21:49:40 +03:00
|
|
|
if self.__last_overriding:
|
|
|
|
result = self.__last_overriding(*args, **kwargs)
|
2021-03-20 20:16:51 +03:00
|
|
|
elif self.__default:
|
2021-01-29 21:49:40 +03:00
|
|
|
result = self.__default(*args, **kwargs)
|
|
|
|
else:
|
2021-02-13 17:16:38 +03:00
|
|
|
self._raise_undefined_error()
|
2017-03-25 23:38:48 +03:00
|
|
|
|
2021-01-11 03:26:15 +03:00
|
|
|
if self.is_async_mode_disabled():
|
|
|
|
self._check_instance_type(result)
|
|
|
|
return result
|
|
|
|
elif self.is_async_mode_enabled():
|
2021-02-17 17:56:39 +03:00
|
|
|
if __is_future_or_coroutine(result):
|
2021-01-11 03:26:15 +03:00
|
|
|
future_result = asyncio.Future()
|
|
|
|
result = asyncio.ensure_future(result)
|
|
|
|
result.add_done_callback(functools.partial(self._async_provide, future_result))
|
|
|
|
return future_result
|
|
|
|
else:
|
|
|
|
self._check_instance_type(result)
|
2021-02-17 17:56:39 +03:00
|
|
|
return __future_result(result)
|
2021-01-11 03:26:15 +03:00
|
|
|
elif self.is_async_mode_undefined():
|
2021-02-17 17:56:39 +03:00
|
|
|
if __is_future_or_coroutine(result):
|
2021-01-11 03:26:15 +03:00
|
|
|
self.enable_async_mode()
|
|
|
|
|
|
|
|
future_result = asyncio.Future()
|
|
|
|
result = asyncio.ensure_future(result)
|
|
|
|
result.add_done_callback(functools.partial(self._async_provide, future_result))
|
|
|
|
return future_result
|
|
|
|
else:
|
|
|
|
self.disable_async_mode()
|
|
|
|
self._check_instance_type(result)
|
|
|
|
return result
|
2017-03-25 23:38:48 +03:00
|
|
|
|
2021-02-19 01:49:23 +03:00
|
|
|
def __getattr__(self, name):
|
|
|
|
if self.__last_overriding:
|
|
|
|
return getattr(self.__last_overriding, name)
|
2021-03-20 20:16:51 +03:00
|
|
|
elif self.__default:
|
2021-02-19 01:49:23 +03:00
|
|
|
return getattr(self.__default, name)
|
2022-01-17 04:32:42 +03:00
|
|
|
raise AttributeError(f"Provider \"{self.__class__.__name__}\" has no attribute \"{name}\"")
|
2021-02-19 01:49:23 +03:00
|
|
|
|
2017-03-25 23:38:48 +03:00
|
|
|
def __str__(self):
|
|
|
|
"""Return string representation of provider.
|
|
|
|
|
|
|
|
:rtype: str
|
|
|
|
"""
|
2022-01-17 04:32:42 +03:00
|
|
|
name = f"<{self.__class__.__module__}.{self.__class__.__name__}"
|
|
|
|
name += f"({repr(self.__instance_of)}) at {hex(id(self))}"
|
2021-02-15 03:09:08 +03:00
|
|
|
if self.parent_name:
|
2022-01-17 04:32:42 +03:00
|
|
|
name += f", container name: \"{self.parent_name}\""
|
|
|
|
name += f">"
|
2021-02-15 03:09:08 +03:00
|
|
|
return name
|
2017-03-25 23:38:48 +03:00
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
"""Return string representation of provider.
|
|
|
|
|
|
|
|
:rtype: str
|
|
|
|
"""
|
|
|
|
return self.__str__()
|
|
|
|
|
|
|
|
@property
|
|
|
|
def instance_of(self):
|
2021-03-20 20:16:51 +03:00
|
|
|
"""Return type."""
|
2017-03-25 23:38:48 +03:00
|
|
|
return self.__instance_of
|
|
|
|
|
2021-03-20 20:16:51 +03:00
|
|
|
def set_instance_of(self, instance_of):
|
|
|
|
"""Set type."""
|
|
|
|
if not isinstance(instance_of, CLASS_TYPES):
|
|
|
|
raise TypeError(
|
2022-01-17 04:32:42 +03:00
|
|
|
"\"instance_of\" has incorrect type (expected {0}, got {1}))".format(
|
2021-03-20 20:16:51 +03:00
|
|
|
CLASS_TYPES,
|
|
|
|
instance_of,
|
|
|
|
),
|
|
|
|
)
|
|
|
|
self.__instance_of = instance_of
|
|
|
|
return self
|
|
|
|
|
2021-01-29 21:49:40 +03:00
|
|
|
@property
|
|
|
|
def default(self):
|
|
|
|
"""Return default provider."""
|
|
|
|
return self.__default
|
|
|
|
|
2021-03-20 20:16:51 +03:00
|
|
|
def set_default(self, default):
|
|
|
|
"""Set type."""
|
2022-01-29 07:12:08 +03:00
|
|
|
if default is not None and not isinstance(default, Provider):
|
2021-03-20 20:16:51 +03:00
|
|
|
default = Object(default)
|
|
|
|
self.__default = default
|
|
|
|
return self
|
|
|
|
|
2021-02-15 02:47:15 +03:00
|
|
|
@property
|
|
|
|
def is_defined(self):
|
|
|
|
"""Return True if dependency is defined."""
|
2021-10-18 23:19:03 +03:00
|
|
|
return self.__last_overriding is not None or self.__default is not None
|
2021-02-15 02:47:15 +03:00
|
|
|
|
2017-03-25 23:38:48 +03:00
|
|
|
def provided_by(self, provider):
|
|
|
|
"""Set external dependency provider.
|
|
|
|
|
|
|
|
:param provider: Provider that provides required dependency.
|
|
|
|
:type provider: :py:class:`Provider`
|
|
|
|
|
|
|
|
:rtype: None
|
|
|
|
"""
|
|
|
|
return self.override(provider)
|
|
|
|
|
2021-02-13 17:16:38 +03:00
|
|
|
@property
|
|
|
|
def related(self):
|
|
|
|
"""Return related providers generator."""
|
2021-03-20 20:16:51 +03:00
|
|
|
if self.__default:
|
2021-02-13 17:16:38 +03:00
|
|
|
yield self.__default
|
|
|
|
yield from super().related
|
|
|
|
|
|
|
|
@property
|
|
|
|
def parent(self):
|
|
|
|
"""Return parent."""
|
|
|
|
return self.__parent
|
|
|
|
|
|
|
|
@property
|
|
|
|
def parent_name(self):
|
|
|
|
"""Return parent name."""
|
|
|
|
if not self.__parent:
|
|
|
|
return None
|
|
|
|
|
2022-01-17 04:32:42 +03:00
|
|
|
name = ""
|
2021-02-13 17:16:38 +03:00
|
|
|
if self.__parent.parent_name:
|
2022-01-17 04:32:42 +03:00
|
|
|
name += f"{self.__parent.parent_name}."
|
|
|
|
name += f"{self.__parent.resolve_provider_name(self)}"
|
2021-02-13 17:16:38 +03:00
|
|
|
|
|
|
|
return name
|
|
|
|
|
|
|
|
def assign_parent(self, parent):
|
|
|
|
"""Assign parent."""
|
|
|
|
self.__parent = parent
|
|
|
|
|
|
|
|
def _copy_parent(self, copied, memo):
|
|
|
|
_copy_parent(self, copied, memo)
|
|
|
|
|
2021-01-11 03:26:15 +03:00
|
|
|
def _async_provide(self, future_result, future):
|
|
|
|
try:
|
2021-03-01 17:01:51 +03:00
|
|
|
instance = future.result()
|
2021-01-11 03:26:15 +03:00
|
|
|
self._check_instance_type(instance)
|
2021-03-01 17:01:51 +03:00
|
|
|
except Exception as exception:
|
2021-01-11 03:26:15 +03:00
|
|
|
future_result.set_exception(exception)
|
|
|
|
else:
|
|
|
|
future_result.set_result(instance)
|
|
|
|
|
|
|
|
def _check_instance_type(self, instance):
|
|
|
|
if not isinstance(instance, self.instance_of):
|
2022-01-17 04:32:42 +03:00
|
|
|
raise Error("{0} is not an instance of {1}".format(instance, self.instance_of))
|
2021-01-11 03:26:15 +03:00
|
|
|
|
2021-02-13 17:16:38 +03:00
|
|
|
def _raise_undefined_error(self):
|
|
|
|
if self.parent_name:
|
2022-01-17 04:32:42 +03:00
|
|
|
raise Error(f"Dependency \"{self.parent_name}\" is not defined")
|
|
|
|
raise Error("Dependency is not defined")
|
2021-02-13 17:16:38 +03:00
|
|
|
|
2017-03-25 23:38:48 +03:00
|
|
|
|
2017-12-21 23:47:31 +03:00
|
|
|
cdef class ExternalDependency(Dependency):
|
|
|
|
""":py:class:`ExternalDependency` provider describes dependency interface.
|
|
|
|
|
|
|
|
This provider is used for description of dependency interface. That might
|
2022-01-17 04:32:42 +03:00
|
|
|
be useful when dependency could be provided in the client code only,
|
|
|
|
but its interface is known. Such situations could happen when required
|
2020-06-14 05:24:32 +03:00
|
|
|
dependency has non-deterministic list of dependencies itself.
|
2017-12-21 23:47:31 +03:00
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
database_provider = ExternalDependency(sqlite3.dbapi2.Connection)
|
2022-01-17 04:32:42 +03:00
|
|
|
database_provider.override(Factory(sqlite3.connect, ":memory:"))
|
2017-12-21 23:47:31 +03:00
|
|
|
|
|
|
|
database = database_provider()
|
|
|
|
|
|
|
|
.. deprecated:: 3.9
|
|
|
|
|
|
|
|
Use :py:class:`Dependency` instead.
|
|
|
|
|
|
|
|
.. py:attribute:: instance_of
|
2020-06-17 05:22:06 +03:00
|
|
|
:noindex:
|
2017-12-21 23:47:31 +03:00
|
|
|
|
|
|
|
Class of required dependency.
|
|
|
|
|
|
|
|
:type: type
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
2018-01-22 00:55:32 +03:00
|
|
|
cdef class DependenciesContainer(Object):
|
|
|
|
""":py:class:`DependenciesContainer` provider provides set of dependencies.
|
|
|
|
|
|
|
|
|
|
|
|
Dependencies container provider is used to implement late static binding
|
|
|
|
for a set of providers of a particular container.
|
|
|
|
|
|
|
|
Example code:
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
class Adapters(containers.DeclarativeContainer):
|
|
|
|
email_sender = providers.Singleton(SmtpEmailSender)
|
|
|
|
|
|
|
|
class TestAdapters(containers.DeclarativeContainer):
|
|
|
|
email_sender = providers.Singleton(EchoEmailSender)
|
|
|
|
|
|
|
|
class UseCases(containers.DeclarativeContainer):
|
|
|
|
adapters = providers.DependenciesContainer()
|
|
|
|
|
|
|
|
signup = providers.Factory(SignupUseCase,
|
|
|
|
email_sender=adapters.email_sender)
|
|
|
|
|
|
|
|
use_cases = UseCases(adapters=Adapters)
|
|
|
|
# or
|
|
|
|
use_cases = UseCases(adapters=TestAdapters)
|
|
|
|
|
|
|
|
# Another file
|
|
|
|
from .containers import use_cases
|
|
|
|
|
|
|
|
use_case = use_cases.signup()
|
|
|
|
use_case.execute()
|
|
|
|
"""
|
|
|
|
|
2020-06-24 21:39:07 +03:00
|
|
|
def __init__(self, **dependencies):
|
2018-01-22 00:55:32 +03:00
|
|
|
"""Initializer."""
|
2021-02-13 17:16:38 +03:00
|
|
|
for provider in dependencies.values():
|
|
|
|
if isinstance(provider, CHILD_PROVIDERS):
|
|
|
|
provider.assign_parent(self)
|
|
|
|
|
2018-01-22 00:55:32 +03:00
|
|
|
self.__providers = dependencies
|
2021-02-13 17:16:38 +03:00
|
|
|
self.__parent = None
|
|
|
|
|
2020-06-24 21:39:07 +03:00
|
|
|
super(DependenciesContainer, self).__init__(None)
|
2018-01-22 00:55:32 +03:00
|
|
|
|
|
|
|
def __deepcopy__(self, memo):
|
|
|
|
"""Create and return full copy of provider."""
|
|
|
|
cdef DependenciesContainer copied
|
|
|
|
|
|
|
|
copied = memo.get(id(self))
|
|
|
|
if copied is not None:
|
|
|
|
return copied
|
|
|
|
|
2021-03-21 04:41:39 +03:00
|
|
|
copied = <DependenciesContainer> _memorized_duplicate(self, memo)
|
2018-01-22 00:55:32 +03:00
|
|
|
copied.__provides = deepcopy(self.__provides, memo)
|
|
|
|
copied.__providers = deepcopy(self.__providers, memo)
|
2021-02-13 17:16:38 +03:00
|
|
|
self._copy_parent(copied, memo)
|
2018-07-24 23:09:56 +03:00
|
|
|
self._copy_overridings(copied, memo)
|
2018-01-22 00:55:32 +03:00
|
|
|
|
|
|
|
return copied
|
|
|
|
|
|
|
|
def __getattr__(self, name):
|
|
|
|
"""Return dependency provider."""
|
2022-01-17 04:32:42 +03:00
|
|
|
if name.startswith("__") and name.endswith("__"):
|
2018-01-22 00:55:32 +03:00
|
|
|
raise AttributeError(
|
2022-01-17 04:32:42 +03:00
|
|
|
"'{cls}' object has no attribute "
|
|
|
|
"'{attribute_name}'".format(cls=self.__class__.__name__, attribute_name=name)
|
|
|
|
)
|
2018-01-22 00:55:32 +03:00
|
|
|
|
|
|
|
provider = self.__providers.get(name)
|
|
|
|
if not provider:
|
|
|
|
provider = Dependency()
|
2021-02-13 17:16:38 +03:00
|
|
|
provider.assign_parent(self)
|
|
|
|
|
2018-01-22 00:55:32 +03:00
|
|
|
self.__providers[name] = provider
|
|
|
|
|
|
|
|
container = self.__call__()
|
|
|
|
if container:
|
|
|
|
dependency_provider = container.providers.get(name)
|
|
|
|
if dependency_provider:
|
|
|
|
provider.override(dependency_provider)
|
|
|
|
|
|
|
|
return provider
|
|
|
|
|
|
|
|
@property
|
|
|
|
def providers(self):
|
|
|
|
"""Read-only dictionary of dependency providers."""
|
|
|
|
return self.__providers
|
|
|
|
|
|
|
|
def override(self, provider):
|
|
|
|
"""Override provider with another provider.
|
|
|
|
|
|
|
|
:param provider: Overriding provider.
|
|
|
|
:type provider: :py:class:`Provider`
|
|
|
|
|
|
|
|
:raise: :py:exc:`dependency_injector.errors.Error`
|
|
|
|
|
|
|
|
:return: Overriding context.
|
|
|
|
:rtype: :py:class:`OverridingContext`
|
|
|
|
"""
|
|
|
|
self._override_providers(container=provider)
|
|
|
|
return super(DependenciesContainer, self).override(provider)
|
|
|
|
|
2018-01-24 21:11:36 +03:00
|
|
|
def reset_last_overriding(self):
|
|
|
|
"""Reset last overriding provider.
|
|
|
|
|
|
|
|
:raise: :py:exc:`dependency_injector.errors.Error` if provider is not
|
|
|
|
overridden.
|
|
|
|
|
|
|
|
:rtype: None
|
|
|
|
"""
|
|
|
|
for child in self.__providers.values():
|
|
|
|
try:
|
|
|
|
child.reset_last_overriding()
|
|
|
|
except Error:
|
|
|
|
pass
|
|
|
|
super(DependenciesContainer, self).reset_last_overriding()
|
|
|
|
|
|
|
|
def reset_override(self):
|
|
|
|
"""Reset all overriding providers.
|
|
|
|
|
|
|
|
:rtype: None
|
|
|
|
"""
|
|
|
|
for child in self.__providers.values():
|
|
|
|
child.reset_override()
|
|
|
|
super(DependenciesContainer, self).reset_override()
|
2018-01-22 00:55:32 +03:00
|
|
|
|
2021-02-01 17:42:21 +03:00
|
|
|
@property
|
|
|
|
def related(self):
|
|
|
|
"""Return related providers generator."""
|
|
|
|
yield from self.providers.values()
|
|
|
|
yield from super().related
|
|
|
|
|
2021-02-13 17:16:38 +03:00
|
|
|
def resolve_provider_name(self, provider):
|
|
|
|
"""Try to resolve provider name."""
|
|
|
|
for provider_name, container_provider in self.providers.items():
|
|
|
|
if container_provider is provider:
|
|
|
|
return provider_name
|
|
|
|
else:
|
2022-01-17 04:32:42 +03:00
|
|
|
raise Error(f"Can not resolve name for provider \"{provider}\"")
|
2021-02-13 17:16:38 +03:00
|
|
|
|
|
|
|
@property
|
|
|
|
def parent(self):
|
|
|
|
"""Return parent."""
|
|
|
|
return self.__parent
|
|
|
|
|
|
|
|
@property
|
|
|
|
def parent_name(self):
|
|
|
|
"""Return parent name."""
|
|
|
|
if not self.__parent:
|
|
|
|
return None
|
|
|
|
|
2022-01-17 04:32:42 +03:00
|
|
|
name = ""
|
2021-02-13 17:16:38 +03:00
|
|
|
if self.__parent.parent_name:
|
2022-01-17 04:32:42 +03:00
|
|
|
name += f"{self.__parent.parent_name}."
|
|
|
|
name += f"{self.__parent.resolve_provider_name(self)}"
|
2021-02-13 17:16:38 +03:00
|
|
|
|
|
|
|
return name
|
|
|
|
|
|
|
|
def assign_parent(self, parent):
|
|
|
|
"""Assign parent."""
|
|
|
|
self.__parent = parent
|
|
|
|
|
|
|
|
def _copy_parent(self, copied, memo):
|
|
|
|
_copy_parent(self, copied, memo)
|
|
|
|
|
2018-01-22 00:55:32 +03:00
|
|
|
cpdef object _override_providers(self, object container):
|
|
|
|
"""Override providers with providers from provided container."""
|
|
|
|
for name, dependency_provider in container.providers.items():
|
2021-01-14 01:07:41 +03:00
|
|
|
provider = getattr(self, name)
|
2018-01-22 00:55:32 +03:00
|
|
|
|
|
|
|
if provider.last_overriding is dependency_provider:
|
|
|
|
continue
|
|
|
|
|
|
|
|
provider.override(dependency_provider)
|
|
|
|
|
|
|
|
|
2017-03-25 23:38:48 +03:00
|
|
|
cdef class Callable(Provider):
|
|
|
|
r"""Callable provider calls wrapped callable on every call.
|
|
|
|
|
|
|
|
Callable supports positional and keyword argument injections:
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
some_function = Callable(some_function,
|
2022-01-17 04:32:42 +03:00
|
|
|
"positional_arg1", "positional_arg2",
|
2017-03-25 23:38:48 +03:00
|
|
|
keyword_argument1=3, keyword_argument=4)
|
|
|
|
|
|
|
|
# or
|
|
|
|
|
|
|
|
some_function = Callable(some_function) \
|
2022-01-17 04:32:42 +03:00
|
|
|
.add_args("positional_arg1", "positional_arg2") \
|
2017-03-25 23:38:48 +03:00
|
|
|
.add_kwargs(keyword_argument1=3, keyword_argument=4)
|
|
|
|
|
|
|
|
# or
|
|
|
|
|
|
|
|
some_function = Callable(some_function)
|
2022-01-17 04:32:42 +03:00
|
|
|
some_function.add_args("positional_arg1", "positional_arg2")
|
2017-03-25 23:38:48 +03:00
|
|
|
some_function.add_kwargs(keyword_argument1=3, keyword_argument=4)
|
|
|
|
"""
|
|
|
|
|
2021-03-20 20:16:51 +03:00
|
|
|
def __init__(self, provides=None, *args, **kwargs):
|
|
|
|
"""Initialize provider."""
|
|
|
|
self.__provides = None
|
|
|
|
self.set_provides(provides)
|
2017-03-25 23:38:48 +03:00
|
|
|
|
|
|
|
self.__args = tuple()
|
|
|
|
self.__args_len = 0
|
|
|
|
self.set_args(*args)
|
|
|
|
|
|
|
|
self.__kwargs = tuple()
|
|
|
|
self.__kwargs_len = 0
|
|
|
|
self.set_kwargs(**kwargs)
|
|
|
|
|
|
|
|
super(Callable, self).__init__()
|
|
|
|
|
|
|
|
def __deepcopy__(self, memo):
|
|
|
|
"""Create and return full copy of provider."""
|
|
|
|
copied = memo.get(id(self))
|
|
|
|
if copied is not None:
|
|
|
|
return copied
|
|
|
|
|
2021-03-20 20:16:51 +03:00
|
|
|
copied = _memorized_duplicate(self, memo)
|
2021-03-21 04:41:39 +03:00
|
|
|
copied.set_provides(_copy_if_provider(self.provides, memo))
|
2021-03-20 20:16:51 +03:00
|
|
|
copied.set_args(*deepcopy(self.args, memo))
|
|
|
|
copied.set_kwargs(**deepcopy(self.kwargs, memo))
|
2018-07-24 23:09:56 +03:00
|
|
|
self._copy_overridings(copied, memo)
|
2017-03-25 23:38:48 +03:00
|
|
|
return copied
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
"""Return string representation of provider.
|
|
|
|
|
|
|
|
:rtype: str
|
|
|
|
"""
|
|
|
|
return represent_provider(provider=self, provides=self.__provides)
|
|
|
|
|
|
|
|
@property
|
|
|
|
def provides(self):
|
2022-01-17 04:32:42 +03:00
|
|
|
"""Return provider provides."""
|
2017-03-25 23:38:48 +03:00
|
|
|
return self.__provides
|
|
|
|
|
2021-03-20 20:16:51 +03:00
|
|
|
def set_provides(self, provides):
|
2022-01-17 04:32:42 +03:00
|
|
|
"""Set provider provides."""
|
2022-01-31 07:19:09 +03:00
|
|
|
provides = _resolve_string_import(provides)
|
2021-03-20 20:16:51 +03:00
|
|
|
if provides and not callable(provides):
|
|
|
|
raise Error(
|
2022-01-17 04:32:42 +03:00
|
|
|
"Provider {0} expected to get callable, got {1} instead".format(
|
2021-03-20 20:16:51 +03:00
|
|
|
_class_qualname(self),
|
|
|
|
provides,
|
|
|
|
),
|
|
|
|
)
|
|
|
|
self.__provides = provides
|
|
|
|
return self
|
|
|
|
|
2017-03-25 23:38:48 +03:00
|
|
|
@property
|
|
|
|
def args(self):
|
|
|
|
"""Return positional argument injections."""
|
|
|
|
cdef int index
|
|
|
|
cdef PositionalInjection arg
|
|
|
|
cdef list args
|
|
|
|
|
|
|
|
args = list()
|
|
|
|
for index in range(self.__args_len):
|
|
|
|
arg = self.__args[index]
|
|
|
|
args.append(arg.__value)
|
|
|
|
return tuple(args)
|
|
|
|
|
|
|
|
def add_args(self, *args):
|
2018-10-19 12:56:41 +03:00
|
|
|
"""Add positional argument injections.
|
2017-03-25 23:38:48 +03:00
|
|
|
|
|
|
|
:return: Reference ``self``
|
|
|
|
"""
|
|
|
|
self.__args += parse_positional_injections(args)
|
|
|
|
self.__args_len = len(self.__args)
|
|
|
|
return self
|
|
|
|
|
|
|
|
def set_args(self, *args):
|
2020-06-14 05:24:32 +03:00
|
|
|
"""Set positional argument injections.
|
2017-03-25 23:38:48 +03:00
|
|
|
|
|
|
|
Existing positional argument injections are dropped.
|
|
|
|
|
|
|
|
:return: Reference ``self``
|
|
|
|
"""
|
|
|
|
self.__args = parse_positional_injections(args)
|
|
|
|
self.__args_len = len(self.__args)
|
|
|
|
return self
|
|
|
|
|
|
|
|
def clear_args(self):
|
2020-06-14 05:24:32 +03:00
|
|
|
"""Drop positional argument injections.
|
2017-03-25 23:38:48 +03:00
|
|
|
|
|
|
|
:return: Reference ``self``
|
|
|
|
"""
|
|
|
|
self.__args = tuple()
|
|
|
|
self.__args_len = len(self.__args)
|
|
|
|
return self
|
|
|
|
|
|
|
|
@property
|
|
|
|
def kwargs(self):
|
|
|
|
"""Return keyword argument injections."""
|
|
|
|
cdef int index
|
|
|
|
cdef NamedInjection kwarg
|
|
|
|
cdef dict kwargs
|
|
|
|
|
|
|
|
kwargs = dict()
|
|
|
|
for index in range(self.__kwargs_len):
|
|
|
|
kwarg = self.__kwargs[index]
|
|
|
|
kwargs[kwarg.__name] = kwarg.__value
|
|
|
|
return kwargs
|
|
|
|
|
|
|
|
def add_kwargs(self, **kwargs):
|
|
|
|
"""Add keyword argument injections.
|
|
|
|
|
|
|
|
:return: Reference ``self``
|
|
|
|
"""
|
|
|
|
self.__kwargs += parse_named_injections(kwargs)
|
|
|
|
self.__kwargs_len = len(self.__kwargs)
|
|
|
|
return self
|
|
|
|
|
|
|
|
def set_kwargs(self, **kwargs):
|
|
|
|
"""Set keyword argument injections.
|
|
|
|
|
|
|
|
Existing keyword argument injections are dropped.
|
|
|
|
|
|
|
|
:return: Reference ``self``
|
|
|
|
"""
|
|
|
|
self.__kwargs = parse_named_injections(kwargs)
|
|
|
|
self.__kwargs_len = len(self.__kwargs)
|
|
|
|
return self
|
|
|
|
|
|
|
|
def clear_kwargs(self):
|
|
|
|
"""Drop keyword argument injections.
|
|
|
|
|
|
|
|
:return: Reference ``self``
|
|
|
|
"""
|
|
|
|
self.__kwargs = tuple()
|
|
|
|
self.__kwargs_len = len(self.__kwargs)
|
|
|
|
return self
|
|
|
|
|
2021-02-01 17:42:21 +03:00
|
|
|
@property
|
|
|
|
def related(self):
|
|
|
|
"""Return related providers generator."""
|
|
|
|
yield from filter(is_provider, [self.provides])
|
|
|
|
yield from filter(is_provider, self.args)
|
|
|
|
yield from filter(is_provider, self.kwargs.values())
|
|
|
|
yield from super().related
|
|
|
|
|
2017-03-25 23:38:48 +03:00
|
|
|
cpdef object _provide(self, tuple args, dict kwargs):
|
2022-01-17 04:32:42 +03:00
|
|
|
"""Return result of provided callable call."""
|
2017-03-25 23:38:48 +03:00
|
|
|
return __callable_call(self, args, kwargs)
|
|
|
|
|
|
|
|
|
|
|
|
cdef class DelegatedCallable(Callable):
|
|
|
|
"""Callable that is injected "as is".
|
|
|
|
|
|
|
|
DelegatedCallable is a :py:class:`Callable`, that is injected "as is".
|
|
|
|
"""
|
|
|
|
|
|
|
|
__IS_DELEGATED__ = True
|
|
|
|
|
|
|
|
|
2017-04-06 18:17:06 +03:00
|
|
|
cdef class AbstractCallable(Callable):
|
|
|
|
"""Abstract callable provider.
|
|
|
|
|
|
|
|
:py:class:`AbstractCallable` is a :py:class:`Callable` provider that must
|
|
|
|
be explicitly overridden before calling.
|
|
|
|
|
|
|
|
Overriding of :py:class:`AbstractCallable` is possible only by another
|
|
|
|
:py:class:`Callable` provider.
|
|
|
|
"""
|
|
|
|
|
|
|
|
def __call__(self, *args, **kwargs):
|
|
|
|
"""Return provided object.
|
|
|
|
|
|
|
|
Callable interface implementation.
|
|
|
|
"""
|
|
|
|
if self.__last_overriding is None:
|
2022-01-17 04:32:42 +03:00
|
|
|
raise Error("{0} must be overridden before calling".format(self))
|
2021-01-11 03:26:15 +03:00
|
|
|
return super().__call__(*args, **kwargs)
|
2017-04-06 18:17:06 +03:00
|
|
|
|
|
|
|
def override(self, provider):
|
|
|
|
"""Override provider with another provider.
|
|
|
|
|
|
|
|
:param provider: Overriding provider.
|
|
|
|
:type provider: :py:class:`Provider`
|
|
|
|
|
|
|
|
:raise: :py:exc:`dependency_injector.errors.Error`
|
|
|
|
|
|
|
|
:return: Overriding context.
|
|
|
|
:rtype: :py:class:`OverridingContext`
|
|
|
|
"""
|
|
|
|
if not isinstance(provider, Callable):
|
2022-01-17 04:32:42 +03:00
|
|
|
raise Error("{0} must be overridden only by "
|
|
|
|
"{1} providers".format(self, Callable))
|
2017-04-06 18:17:06 +03:00
|
|
|
return super(AbstractCallable, self).override(provider)
|
|
|
|
|
|
|
|
cpdef object _provide(self, tuple args, dict kwargs):
|
2022-01-17 04:32:42 +03:00
|
|
|
"""Return result of provided callable"s call."""
|
|
|
|
raise NotImplementedError("Abstract provider forward providing logic "
|
|
|
|
"to overriding provider")
|
2017-04-06 18:17:06 +03:00
|
|
|
|
|
|
|
|
2017-07-09 23:29:33 +03:00
|
|
|
cdef class CallableDelegate(Delegate):
|
|
|
|
"""Callable delegate injects delegating callable "as is".
|
|
|
|
|
|
|
|
.. py:attribute:: provides
|
|
|
|
|
|
|
|
Value that have to be provided.
|
|
|
|
|
|
|
|
:type: object
|
|
|
|
"""
|
|
|
|
|
|
|
|
def __init__(self, callable):
|
|
|
|
"""Initializer.
|
|
|
|
|
2017-07-09 23:36:04 +03:00
|
|
|
:param callable: Value that have to be provided.
|
|
|
|
:type callable: object
|
2017-07-09 23:29:33 +03:00
|
|
|
"""
|
|
|
|
if isinstance(callable, Callable) is False:
|
2022-01-17 04:32:42 +03:00
|
|
|
raise Error("{0} can wrap only {1} providers".format(self.__class__, Callable))
|
2019-10-09 17:45:14 +03:00
|
|
|
super(CallableDelegate, self).__init__(callable)
|
2017-07-09 23:29:33 +03:00
|
|
|
|
|
|
|
|
2018-10-18 19:39:19 +03:00
|
|
|
cdef class Coroutine(Callable):
|
|
|
|
r"""Coroutine provider creates wrapped coroutine on every call.
|
|
|
|
|
|
|
|
Coroutine supports positional and keyword argument injections:
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
some_coroutine = Coroutine(some_coroutine,
|
2022-01-17 04:32:42 +03:00
|
|
|
"positional_arg1", "positional_arg2",
|
2018-10-18 19:39:19 +03:00
|
|
|
keyword_argument1=3, keyword_argument=4)
|
|
|
|
|
|
|
|
# or
|
|
|
|
|
|
|
|
some_coroutine = Coroutine(some_coroutine) \
|
2022-01-17 04:32:42 +03:00
|
|
|
.add_args("positional_arg1", "positional_arg2") \
|
2018-10-18 19:39:19 +03:00
|
|
|
.add_kwargs(keyword_argument1=3, keyword_argument=4)
|
|
|
|
|
|
|
|
# or
|
|
|
|
|
|
|
|
some_coroutine = Coroutine(some_coroutine)
|
2022-01-17 04:32:42 +03:00
|
|
|
some_coroutine.add_args("positional_arg1", "positional_arg2")
|
2018-10-18 19:39:19 +03:00
|
|
|
some_coroutine.add_kwargs(keyword_argument1=3, keyword_argument=4)
|
|
|
|
"""
|
|
|
|
|
|
|
|
_is_coroutine = _is_coroutine_marker
|
|
|
|
|
2021-03-20 20:16:51 +03:00
|
|
|
def set_provides(self, provides):
|
2022-01-31 07:16:55 +03:00
|
|
|
"""Set provider provides."""
|
2018-10-18 19:39:19 +03:00
|
|
|
if not asyncio:
|
2022-01-17 04:32:42 +03:00
|
|
|
raise Error("Package asyncio is not available")
|
2022-01-31 07:19:09 +03:00
|
|
|
provides = _resolve_string_import(provides)
|
2021-03-20 20:16:51 +03:00
|
|
|
if provides and not asyncio.iscoroutinefunction(provides):
|
2022-01-17 04:32:42 +03:00
|
|
|
raise Error(f"Provider {_class_qualname(self)} expected to get coroutine function, "
|
|
|
|
f"got {provides} instead")
|
2021-03-20 20:16:51 +03:00
|
|
|
return super().set_provides(provides)
|
2018-10-18 19:39:19 +03:00
|
|
|
|
|
|
|
|
|
|
|
cdef class DelegatedCoroutine(Coroutine):
|
|
|
|
"""Coroutine provider that is injected "as is".
|
|
|
|
|
|
|
|
DelegatedCoroutine is a :py:class:`Coroutine`, that is injected "as is".
|
|
|
|
"""
|
|
|
|
|
|
|
|
__IS_DELEGATED__ = True
|
|
|
|
|
|
|
|
|
|
|
|
cdef class AbstractCoroutine(Coroutine):
|
|
|
|
"""Abstract coroutine provider.
|
|
|
|
|
|
|
|
:py:class:`AbstractCoroutine` is a :py:class:`Coroutine` provider that must
|
|
|
|
be explicitly overridden before calling.
|
|
|
|
|
|
|
|
Overriding of :py:class:`AbstractCoroutine` is possible only by another
|
|
|
|
:py:class:`Coroutine` provider.
|
|
|
|
"""
|
|
|
|
|
|
|
|
def __call__(self, *args, **kwargs):
|
|
|
|
"""Return provided object.
|
|
|
|
|
|
|
|
Callable interface implementation.
|
|
|
|
"""
|
|
|
|
if self.__last_overriding is None:
|
2022-01-17 04:32:42 +03:00
|
|
|
raise Error("{0} must be overridden before calling".format(self))
|
2021-01-11 03:26:15 +03:00
|
|
|
return super().__call__(*args, **kwargs)
|
2018-10-18 19:39:19 +03:00
|
|
|
|
|
|
|
def override(self, provider):
|
|
|
|
"""Override provider with another provider.
|
|
|
|
|
|
|
|
:param provider: Overriding provider.
|
|
|
|
:type provider: :py:class:`Provider`
|
|
|
|
|
|
|
|
:raise: :py:exc:`dependency_injector.errors.Error`
|
|
|
|
|
|
|
|
:return: Overriding context.
|
|
|
|
:rtype: :py:class:`OverridingContext`
|
|
|
|
"""
|
|
|
|
if not isinstance(provider, Coroutine):
|
2022-01-17 04:32:42 +03:00
|
|
|
raise Error("{0} must be overridden only by "
|
|
|
|
"{1} providers".format(self, Coroutine))
|
2018-10-18 19:39:19 +03:00
|
|
|
return super(AbstractCoroutine, self).override(provider)
|
|
|
|
|
|
|
|
cpdef object _provide(self, tuple args, dict kwargs):
|
2022-01-17 04:32:42 +03:00
|
|
|
"""Return result of provided callable"s call."""
|
|
|
|
raise NotImplementedError("Abstract provider forward providing logic "
|
|
|
|
"to overriding provider")
|
2018-10-18 19:39:19 +03:00
|
|
|
|
|
|
|
|
|
|
|
cdef class CoroutineDelegate(Delegate):
|
|
|
|
"""Coroutine delegate injects delegating coroutine "as is".
|
|
|
|
|
|
|
|
.. py:attribute:: provides
|
|
|
|
|
|
|
|
Value that have to be provided.
|
|
|
|
|
|
|
|
:type: object
|
|
|
|
"""
|
|
|
|
|
|
|
|
def __init__(self, coroutine):
|
|
|
|
"""Initializer.
|
|
|
|
|
|
|
|
:param coroutine: Value that have to be provided.
|
|
|
|
:type coroutine: object
|
|
|
|
"""
|
|
|
|
if isinstance(coroutine, Coroutine) is False:
|
2022-01-17 04:32:42 +03:00
|
|
|
raise Error("{0} can wrap only {1} providers".format(self.__class__, Callable))
|
2018-10-18 19:39:19 +03:00
|
|
|
super(CoroutineDelegate, self).__init__(coroutine)
|
|
|
|
|
|
|
|
|
2020-08-04 01:01:20 +03:00
|
|
|
cdef class ConfigurationOption(Provider):
|
|
|
|
"""Child configuration option provider.
|
|
|
|
|
|
|
|
This provider should not be used directly. It is a part of the
|
|
|
|
:py:class:`Configuration` provider.
|
|
|
|
"""
|
|
|
|
|
2021-03-21 04:41:39 +03:00
|
|
|
def __init__(self, name=None, Configuration root=None, required=False):
|
2020-08-04 01:01:20 +03:00
|
|
|
self.__name = name
|
2021-01-28 16:40:43 +03:00
|
|
|
self.__root = root
|
2020-08-04 01:01:20 +03:00
|
|
|
self.__children = {}
|
2021-01-16 16:53:40 +03:00
|
|
|
self.__required = required
|
2021-01-24 06:37:50 +03:00
|
|
|
self.__cache = UNDEFINED
|
2020-08-04 01:01:20 +03:00
|
|
|
super().__init__()
|
|
|
|
|
|
|
|
def __deepcopy__(self, memo):
|
|
|
|
cdef ConfigurationOption copied
|
|
|
|
|
|
|
|
copied = memo.get(id(self))
|
|
|
|
if copied is not None:
|
|
|
|
return copied
|
|
|
|
|
2021-03-21 04:41:39 +03:00
|
|
|
copied = <ConfigurationOption> _memorized_duplicate(self, memo)
|
|
|
|
copied.__name = deepcopy(self.__name, memo)
|
|
|
|
copied.__root = deepcopy(self.__root, memo)
|
2020-08-04 01:01:20 +03:00
|
|
|
copied.__children = deepcopy(self.__children, memo)
|
2021-03-21 04:41:39 +03:00
|
|
|
copied.__required = self.__required
|
|
|
|
self._copy_overridings(copied, memo)
|
2020-08-04 01:01:20 +03:00
|
|
|
return copied
|
|
|
|
|
2021-02-15 17:11:39 +03:00
|
|
|
def __enter__(self):
|
|
|
|
return self
|
|
|
|
|
|
|
|
def __exit__(self, *exc_info):
|
|
|
|
pass
|
|
|
|
|
2020-08-04 01:01:20 +03:00
|
|
|
def __str__(self):
|
|
|
|
return represent_provider(provider=self, provides=self.get_name())
|
|
|
|
|
|
|
|
def __getattr__(self, item):
|
2022-01-17 04:32:42 +03:00
|
|
|
if item.startswith("__") and item.endswith("__"):
|
2020-08-04 01:01:20 +03:00
|
|
|
raise AttributeError(
|
2022-01-17 04:32:42 +03:00
|
|
|
"'{cls}' object has no attribute "
|
|
|
|
"'{attribute_name}'".format(cls=self.__class__.__name__, attribute_name=item)
|
|
|
|
)
|
2020-08-04 01:01:20 +03:00
|
|
|
|
|
|
|
child = self.__children.get(item)
|
|
|
|
if child is None:
|
|
|
|
child_name = self.__name + (item,)
|
2021-01-28 16:40:43 +03:00
|
|
|
child = ConfigurationOption(child_name, self.__root)
|
2020-08-04 01:01:20 +03:00
|
|
|
self.__children[item] = child
|
|
|
|
return child
|
|
|
|
|
|
|
|
def __getitem__(self, item):
|
|
|
|
child = self.__children.get(item)
|
|
|
|
if child is None:
|
|
|
|
child_name = self.__name + (item,)
|
2021-01-28 16:40:43 +03:00
|
|
|
child = ConfigurationOption(child_name, self.__root)
|
2020-08-04 01:01:20 +03:00
|
|
|
self.__children[item] = child
|
|
|
|
return child
|
|
|
|
|
|
|
|
cpdef object _provide(self, tuple args, dict kwargs):
|
|
|
|
"""Return new instance."""
|
2021-01-24 06:37:50 +03:00
|
|
|
if self.__cache is not UNDEFINED:
|
2020-08-04 01:01:20 +03:00
|
|
|
return self.__cache
|
|
|
|
|
2021-01-28 16:40:43 +03:00
|
|
|
value = self.__root.get(self._get_self_name(), self.__required)
|
2020-08-04 01:01:20 +03:00
|
|
|
self.__cache = value
|
|
|
|
return value
|
|
|
|
|
|
|
|
def _get_self_name(self):
|
2022-01-17 04:32:42 +03:00
|
|
|
return ".".join(
|
2020-08-04 01:01:20 +03:00
|
|
|
segment() if is_provider(segment) else segment for segment in self.__name
|
|
|
|
)
|
|
|
|
|
2020-10-09 22:16:27 +03:00
|
|
|
@property
|
|
|
|
def root(self):
|
2021-01-28 16:40:43 +03:00
|
|
|
return self.__root
|
2020-10-09 22:16:27 +03:00
|
|
|
|
2020-08-04 01:01:20 +03:00
|
|
|
def get_name(self):
|
2022-01-17 04:32:42 +03:00
|
|
|
return ".".join((self.__root.get_name(), self._get_self_name()))
|
2020-08-04 01:01:20 +03:00
|
|
|
|
2020-10-09 22:16:27 +03:00
|
|
|
def get_name_segments(self):
|
|
|
|
return self.__name
|
|
|
|
|
2020-08-24 20:34:47 +03:00
|
|
|
def as_int(self):
|
2020-10-09 22:16:27 +03:00
|
|
|
return TypedConfigurationOption(int, self)
|
2020-08-24 20:34:47 +03:00
|
|
|
|
|
|
|
def as_float(self):
|
2020-10-09 22:16:27 +03:00
|
|
|
return TypedConfigurationOption(float, self)
|
2020-08-24 20:34:47 +03:00
|
|
|
|
|
|
|
def as_(self, callback, *args, **kwargs):
|
2020-10-09 22:16:27 +03:00
|
|
|
return TypedConfigurationOption(callback, self, *args, **kwargs)
|
2020-08-24 20:34:47 +03:00
|
|
|
|
2021-01-16 16:53:40 +03:00
|
|
|
def required(self):
|
2021-01-28 16:40:43 +03:00
|
|
|
return self.__class__(self.__name, self.__root, required=True)
|
2021-01-16 16:53:40 +03:00
|
|
|
|
|
|
|
def is_required(self):
|
|
|
|
return self.__required
|
|
|
|
|
2020-08-04 01:01:20 +03:00
|
|
|
def override(self, value):
|
|
|
|
if isinstance(value, Provider):
|
2022-01-17 04:32:42 +03:00
|
|
|
raise Error("Configuration option can only be overridden by a value")
|
2021-01-28 16:40:43 +03:00
|
|
|
return self.__root.set(self._get_self_name(), value)
|
2020-08-04 01:01:20 +03:00
|
|
|
|
|
|
|
def reset_last_overriding(self):
|
2022-01-17 04:32:42 +03:00
|
|
|
raise Error("Configuration option does not support this method")
|
2020-08-04 01:01:20 +03:00
|
|
|
|
|
|
|
def reset_override(self):
|
2022-01-17 04:32:42 +03:00
|
|
|
raise Error("Configuration option does not support this method")
|
2020-08-04 01:01:20 +03:00
|
|
|
|
|
|
|
def reset_cache(self):
|
2021-01-24 06:37:50 +03:00
|
|
|
self.__cache = UNDEFINED
|
2021-03-21 04:41:39 +03:00
|
|
|
|
|
|
|
for provider in self.__children.values():
|
|
|
|
provider.reset_cache()
|
|
|
|
|
|
|
|
for provider in self.overrides:
|
|
|
|
if isinstance(provider, (Configuration, ConfigurationOption)):
|
|
|
|
provider.reset_cache()
|
2020-08-04 01:01:20 +03:00
|
|
|
|
|
|
|
def update(self, value):
|
|
|
|
"""Set configuration options.
|
|
|
|
|
|
|
|
.. deprecated:: 3.11
|
|
|
|
|
|
|
|
Use :py:meth:`Configuration.override` instead.
|
|
|
|
|
|
|
|
:param value: Value of configuration option.
|
|
|
|
:type value: object | dict
|
|
|
|
|
|
|
|
:rtype: None
|
|
|
|
"""
|
|
|
|
self.override(value)
|
|
|
|
|
2021-10-24 04:46:50 +03:00
|
|
|
def from_ini(self, filepath, required=UNDEFINED, envs_required=UNDEFINED):
|
2020-08-04 01:01:20 +03:00
|
|
|
"""Load configuration from the ini file.
|
|
|
|
|
|
|
|
Loaded configuration is merged recursively over existing configuration.
|
|
|
|
|
|
|
|
:param filepath: Path to the configuration file.
|
|
|
|
:type filepath: str
|
|
|
|
|
2021-01-24 18:27:45 +03:00
|
|
|
:param required: When required is True, raise an exception if file does not exist.
|
|
|
|
:type required: bool
|
|
|
|
|
2021-06-24 16:00:36 +03:00
|
|
|
:param envs_required: When True, raises an error on undefined environment variable.
|
|
|
|
:type envs_required: bool
|
|
|
|
|
2020-08-04 01:01:20 +03:00
|
|
|
:rtype: None
|
|
|
|
"""
|
2021-01-24 06:37:50 +03:00
|
|
|
try:
|
2021-06-24 16:00:36 +03:00
|
|
|
parser = _parse_ini_file(
|
|
|
|
filepath,
|
2021-10-24 04:46:50 +03:00
|
|
|
envs_required=envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(),
|
2021-06-24 16:00:36 +03:00
|
|
|
)
|
2021-01-24 06:37:50 +03:00
|
|
|
except IOError as exception:
|
2021-01-24 18:27:45 +03:00
|
|
|
if required is not False \
|
|
|
|
and (self._is_strict_mode_enabled() or required is True) \
|
|
|
|
and exception.errno in (errno.ENOENT, errno.EISDIR):
|
2022-01-17 04:32:42 +03:00
|
|
|
exception.strerror = "Unable to load configuration file {0}".format(exception.strerror)
|
2021-01-24 06:37:50 +03:00
|
|
|
raise
|
|
|
|
return
|
2020-08-04 01:01:20 +03:00
|
|
|
|
|
|
|
config = {}
|
|
|
|
for section in parser.sections():
|
|
|
|
config[section] = dict(parser.items(section))
|
|
|
|
|
|
|
|
current_config = self.__call__()
|
|
|
|
if not current_config:
|
|
|
|
current_config = {}
|
|
|
|
self.override(merge_dicts(current_config, config))
|
|
|
|
|
2021-10-24 04:46:50 +03:00
|
|
|
def from_yaml(self, filepath, required=UNDEFINED, loader=None, envs_required=UNDEFINED):
|
2020-08-04 01:01:20 +03:00
|
|
|
"""Load configuration from the yaml file.
|
|
|
|
|
|
|
|
Loaded configuration is merged recursively over existing configuration.
|
|
|
|
|
|
|
|
:param filepath: Path to the configuration file.
|
|
|
|
:type filepath: str
|
|
|
|
|
2021-01-24 18:27:45 +03:00
|
|
|
:param required: When required is True, raise an exception if file does not exist.
|
|
|
|
:type required: bool
|
|
|
|
|
2021-01-22 02:00:24 +03:00
|
|
|
:param loader: YAML loader, :py:class:`YamlLoader` is used if not specified.
|
|
|
|
:type loader: ``yaml.Loader``
|
|
|
|
|
2021-06-24 16:00:36 +03:00
|
|
|
:param envs_required: When True, raises an error on undefined environment variable.
|
|
|
|
:type envs_required: bool
|
|
|
|
|
2020-08-04 01:01:20 +03:00
|
|
|
:rtype: None
|
|
|
|
"""
|
|
|
|
if yaml is None:
|
|
|
|
raise Error(
|
2022-01-17 04:32:42 +03:00
|
|
|
"Unable to load yaml configuration - PyYAML is not installed. "
|
|
|
|
"Install PyYAML or install Dependency Injector with yaml extras: "
|
|
|
|
"\"pip install dependency-injector[yaml]\""
|
2020-08-04 01:01:20 +03:00
|
|
|
)
|
|
|
|
|
2021-01-22 02:00:24 +03:00
|
|
|
if loader is None:
|
|
|
|
loader = YamlLoader
|
|
|
|
|
2020-08-04 01:01:20 +03:00
|
|
|
try:
|
|
|
|
with open(filepath) as opened_file:
|
2021-06-24 16:00:36 +03:00
|
|
|
config_content = opened_file.read()
|
2021-01-24 06:37:50 +03:00
|
|
|
except IOError as exception:
|
2021-01-24 18:27:45 +03:00
|
|
|
if required is not False \
|
|
|
|
and (self._is_strict_mode_enabled() or required is True) \
|
|
|
|
and exception.errno in (errno.ENOENT, errno.EISDIR):
|
2022-01-17 04:32:42 +03:00
|
|
|
exception.strerror = "Unable to load configuration file {0}".format(exception.strerror)
|
2021-01-24 06:37:50 +03:00
|
|
|
raise
|
2020-08-04 01:01:20 +03:00
|
|
|
return
|
|
|
|
|
2021-06-24 16:00:36 +03:00
|
|
|
config_content = _resolve_config_env_markers(
|
|
|
|
config_content,
|
2021-10-24 04:46:50 +03:00
|
|
|
envs_required=envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(),
|
2021-06-24 16:00:36 +03:00
|
|
|
)
|
|
|
|
config = yaml.load(config_content, loader)
|
|
|
|
|
2020-08-04 01:01:20 +03:00
|
|
|
current_config = self.__call__()
|
|
|
|
if not current_config:
|
|
|
|
current_config = {}
|
|
|
|
self.override(merge_dicts(current_config, config))
|
|
|
|
|
2021-02-03 17:21:32 +03:00
|
|
|
def from_pydantic(self, settings, required=UNDEFINED, **kwargs):
|
|
|
|
"""Load configuration from pydantic settings.
|
|
|
|
|
|
|
|
Loaded configuration is merged recursively over existing configuration.
|
|
|
|
|
|
|
|
:param settings: Pydantic settings instances.
|
|
|
|
:type settings: :py:class:`pydantic.BaseSettings`
|
|
|
|
|
|
|
|
:param required: When required is True, raise an exception if settings dict is empty.
|
|
|
|
:type required: bool
|
|
|
|
|
|
|
|
:param kwargs: Keyword arguments forwarded to ``pydantic.BaseSettings.dict()`` call.
|
|
|
|
:type kwargs: Dict[Any, Any]
|
|
|
|
|
|
|
|
:rtype: None
|
|
|
|
"""
|
|
|
|
if pydantic is None:
|
|
|
|
raise Error(
|
2022-01-17 04:32:42 +03:00
|
|
|
"Unable to load pydantic configuration - pydantic is not installed. "
|
|
|
|
"Install pydantic or install Dependency Injector with pydantic extras: "
|
|
|
|
"\"pip install dependency-injector[pydantic]\""
|
2021-02-03 17:21:32 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
if isinstance(settings, CLASS_TYPES) and issubclass(settings, pydantic.BaseSettings):
|
|
|
|
raise Error(
|
2022-01-17 04:32:42 +03:00
|
|
|
"Got settings class, but expect instance: "
|
|
|
|
"instead \"{0}\" use \"{0}()\"".format(settings.__name__)
|
2021-02-03 17:21:32 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
if not isinstance(settings, pydantic.BaseSettings):
|
|
|
|
raise Error(
|
2022-01-17 04:32:42 +03:00
|
|
|
"Unable to recognize settings instance, expect \"pydantic.BaseSettings\", "
|
|
|
|
"got {0} instead".format(settings)
|
2021-02-03 17:21:32 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
self.from_dict(settings.dict(**kwargs), required=required)
|
|
|
|
|
2021-01-24 18:27:45 +03:00
|
|
|
def from_dict(self, options, required=UNDEFINED):
|
2020-08-04 01:01:20 +03:00
|
|
|
"""Load configuration from the dictionary.
|
|
|
|
|
|
|
|
Loaded configuration is merged recursively over existing configuration.
|
|
|
|
|
|
|
|
:param options: Configuration options.
|
|
|
|
:type options: dict
|
|
|
|
|
2021-01-24 18:27:45 +03:00
|
|
|
:param required: When required is True, raise an exception if dictionary is empty.
|
|
|
|
:type required: bool
|
|
|
|
|
2020-08-04 01:01:20 +03:00
|
|
|
:rtype: None
|
|
|
|
"""
|
2021-01-24 18:27:45 +03:00
|
|
|
if required is not False \
|
|
|
|
and (self._is_strict_mode_enabled() or required is True) \
|
|
|
|
and not options:
|
2022-01-17 04:32:42 +03:00
|
|
|
raise ValueError("Can not use empty dictionary")
|
2021-01-24 06:37:50 +03:00
|
|
|
|
2021-01-24 18:27:45 +03:00
|
|
|
try:
|
|
|
|
current_config = self.__call__()
|
|
|
|
except Error:
|
2020-08-04 01:01:20 +03:00
|
|
|
current_config = {}
|
2021-01-24 18:27:45 +03:00
|
|
|
else:
|
|
|
|
if not current_config:
|
|
|
|
current_config = {}
|
|
|
|
|
2020-08-04 01:01:20 +03:00
|
|
|
self.override(merge_dicts(current_config, options))
|
|
|
|
|
2021-12-21 01:46:51 +03:00
|
|
|
def from_env(self, name, default=UNDEFINED, required=UNDEFINED, as_=UNDEFINED):
|
2020-08-04 01:01:20 +03:00
|
|
|
"""Load configuration value from the environment variable.
|
|
|
|
|
|
|
|
:param name: Name of the environment variable.
|
|
|
|
:type name: str
|
|
|
|
|
|
|
|
:param default: Default value that is used if environment variable does not exist.
|
2021-01-24 06:37:50 +03:00
|
|
|
:type default: object
|
2020-08-04 01:01:20 +03:00
|
|
|
|
2021-01-24 18:27:45 +03:00
|
|
|
:param required: When required is True, raise an exception if environment variable is undefined.
|
|
|
|
:type required: bool
|
|
|
|
|
2021-12-21 01:46:51 +03:00
|
|
|
:param as_: Callable used for type casting (int, float, etc).
|
|
|
|
:type as_: object
|
|
|
|
|
2020-08-04 01:01:20 +03:00
|
|
|
:rtype: None
|
|
|
|
"""
|
2021-01-24 06:37:50 +03:00
|
|
|
value = os.environ.get(name, default)
|
|
|
|
|
|
|
|
if value is UNDEFINED:
|
2021-01-24 18:27:45 +03:00
|
|
|
if required is not False \
|
|
|
|
and (self._is_strict_mode_enabled() or required is True):
|
2022-01-17 04:32:42 +03:00
|
|
|
raise ValueError("Environment variable \"{0}\" is undefined".format(name))
|
2021-01-24 06:37:50 +03:00
|
|
|
value = None
|
|
|
|
|
2021-12-21 01:46:51 +03:00
|
|
|
if as_ is not UNDEFINED:
|
|
|
|
value = as_(value)
|
|
|
|
|
2020-08-04 01:01:20 +03:00
|
|
|
self.override(value)
|
|
|
|
|
2021-06-14 05:05:29 +03:00
|
|
|
def from_value(self, value):
|
|
|
|
"""Load configuration value.
|
|
|
|
|
|
|
|
:param value: Configuration value
|
|
|
|
:type value: object
|
|
|
|
|
|
|
|
:rtype: None
|
|
|
|
"""
|
|
|
|
self.override(value)
|
|
|
|
|
2021-02-01 17:42:21 +03:00
|
|
|
@property
|
|
|
|
def related(self):
|
|
|
|
"""Return related providers generator."""
|
|
|
|
yield from filter(is_provider, self.__name)
|
|
|
|
yield from self.__children.values()
|
|
|
|
yield from super().related
|
|
|
|
|
2021-01-24 06:37:50 +03:00
|
|
|
def _is_strict_mode_enabled(self):
|
2021-01-28 16:40:43 +03:00
|
|
|
return self.__root.__strict
|
2021-01-24 06:37:50 +03:00
|
|
|
|
2020-08-04 01:01:20 +03:00
|
|
|
|
2020-10-09 22:16:27 +03:00
|
|
|
cdef class TypedConfigurationOption(Callable):
|
|
|
|
|
|
|
|
@property
|
|
|
|
def option(self):
|
|
|
|
return self.args[0]
|
|
|
|
|
|
|
|
|
2018-01-24 20:58:03 +03:00
|
|
|
cdef class Configuration(Object):
|
2020-06-26 00:12:16 +03:00
|
|
|
"""Configuration provider provides configuration options to the other providers.
|
2017-03-25 23:38:48 +03:00
|
|
|
|
|
|
|
.. code-block:: python
|
2020-08-08 21:48:05 +03:00
|
|
|
|
2022-01-17 04:32:42 +03:00
|
|
|
config = Configuration("config")
|
2017-03-25 23:38:48 +03:00
|
|
|
print(config.section1.option1()) # None
|
|
|
|
print(config.section1.option2()) # None
|
2020-06-26 00:12:16 +03:00
|
|
|
config.from_dict(
|
|
|
|
{
|
2022-01-17 04:32:42 +03:00
|
|
|
"section1": {
|
|
|
|
"option1": 1,
|
|
|
|
"option2": 2,
|
2020-06-26 00:12:16 +03:00
|
|
|
},
|
|
|
|
},
|
|
|
|
)
|
2017-03-25 23:38:48 +03:00
|
|
|
print(config.section1.option1()) # 1
|
|
|
|
print(config.section1.option2()) # 2
|
|
|
|
"""
|
|
|
|
|
2022-01-17 04:32:42 +03:00
|
|
|
DEFAULT_NAME = "config"
|
2020-06-25 00:16:46 +03:00
|
|
|
|
2021-10-27 04:08:47 +03:00
|
|
|
def __init__(self, name=DEFAULT_NAME, default=None, strict=False, yaml_files=None, ini_files=None, pydantic_settings=None):
|
2020-08-04 01:01:20 +03:00
|
|
|
self.__name = name
|
2021-01-16 16:53:40 +03:00
|
|
|
self.__strict = strict
|
2020-08-04 01:01:20 +03:00
|
|
|
self.__children = {}
|
2021-10-24 04:46:50 +03:00
|
|
|
self.__yaml_files = []
|
2021-10-27 03:27:11 +03:00
|
|
|
self.__ini_files = []
|
2021-10-27 04:08:47 +03:00
|
|
|
self.__pydantic_settings = []
|
2020-06-25 00:16:46 +03:00
|
|
|
|
2021-03-20 20:16:51 +03:00
|
|
|
super().__init__(provides={})
|
|
|
|
self.set_default(default)
|
2017-03-25 23:38:48 +03:00
|
|
|
|
2021-10-24 04:46:50 +03:00
|
|
|
if yaml_files is None:
|
|
|
|
yaml_files = []
|
|
|
|
self.set_yaml_files(yaml_files)
|
|
|
|
|
2021-10-27 03:27:11 +03:00
|
|
|
if ini_files is None:
|
|
|
|
ini_files = []
|
|
|
|
self.set_ini_files(ini_files)
|
|
|
|
|
2021-10-27 04:08:47 +03:00
|
|
|
if pydantic_settings is None:
|
|
|
|
pydantic_settings = []
|
|
|
|
self.set_pydantic_settings(pydantic_settings)
|
|
|
|
|
2017-03-25 23:38:48 +03:00
|
|
|
def __deepcopy__(self, memo):
|
|
|
|
copied = memo.get(id(self))
|
|
|
|
if copied is not None:
|
|
|
|
return copied
|
|
|
|
|
2021-03-20 20:16:51 +03:00
|
|
|
copied = _memorized_duplicate(self, memo)
|
|
|
|
copied.set_name(self.get_name())
|
|
|
|
copied.set_default(self.get_default())
|
|
|
|
copied.set_strict(self.get_strict())
|
|
|
|
copied.set_children(deepcopy(self.get_children(), memo))
|
2021-10-24 04:46:50 +03:00
|
|
|
copied.set_yaml_files(self.get_yaml_files())
|
2021-10-27 03:27:11 +03:00
|
|
|
copied.set_ini_files(self.get_ini_files())
|
2021-10-27 04:08:47 +03:00
|
|
|
copied.set_pydantic_settings(self.get_pydantic_settings())
|
2017-03-25 23:38:48 +03:00
|
|
|
|
2018-07-24 23:09:56 +03:00
|
|
|
self._copy_overridings(copied, memo)
|
2017-03-25 23:38:48 +03:00
|
|
|
return copied
|
|
|
|
|
2021-02-15 17:11:39 +03:00
|
|
|
def __enter__(self):
|
|
|
|
return self
|
|
|
|
|
|
|
|
def __exit__(self, *exc_info):
|
|
|
|
pass
|
|
|
|
|
2017-03-25 23:38:48 +03:00
|
|
|
def __str__(self):
|
|
|
|
return represent_provider(provider=self, provides=self.__name)
|
|
|
|
|
2020-08-04 01:01:20 +03:00
|
|
|
def __getattr__(self, item):
|
2022-01-17 04:32:42 +03:00
|
|
|
if item.startswith("__") and item.endswith("__"):
|
2017-03-28 23:50:22 +03:00
|
|
|
raise AttributeError(
|
2022-01-17 04:32:42 +03:00
|
|
|
"'{cls}' object has no attribute "
|
|
|
|
"'{attribute_name}'".format(cls=self.__class__.__name__, attribute_name=item)
|
|
|
|
)
|
2017-03-28 23:50:22 +03:00
|
|
|
|
2020-08-04 01:01:20 +03:00
|
|
|
child = self.__children.get(item)
|
|
|
|
if child is None:
|
|
|
|
child = ConfigurationOption((item,), self)
|
|
|
|
self.__children[item] = child
|
|
|
|
return child
|
2017-03-25 23:38:48 +03:00
|
|
|
|
2020-08-04 01:01:20 +03:00
|
|
|
def __getitem__(self, item):
|
|
|
|
child = self.__children.get(item)
|
|
|
|
if child is None:
|
|
|
|
child = ConfigurationOption(item, self)
|
|
|
|
self.__children[item] = child
|
|
|
|
return child
|
2017-03-25 23:38:48 +03:00
|
|
|
|
2020-08-04 01:01:20 +03:00
|
|
|
def get_name(self):
|
2021-03-20 20:16:51 +03:00
|
|
|
"""Return name."""
|
2020-08-04 01:01:20 +03:00
|
|
|
return self.__name
|
2017-03-25 23:38:48 +03:00
|
|
|
|
2021-03-20 20:16:51 +03:00
|
|
|
def set_name(self, name):
|
|
|
|
"""Set name."""
|
|
|
|
self.__name = name
|
|
|
|
return self
|
|
|
|
|
|
|
|
def get_default(self):
|
|
|
|
"""Return default."""
|
|
|
|
return self.provides
|
|
|
|
|
|
|
|
def set_default(self, default):
|
|
|
|
"""Set default."""
|
|
|
|
if not default:
|
|
|
|
return self
|
|
|
|
|
|
|
|
assert isinstance(default, dict), default
|
|
|
|
self.set_provides(default.copy())
|
|
|
|
return self
|
|
|
|
|
|
|
|
def get_strict(self):
|
|
|
|
"""Return strict flag."""
|
|
|
|
return self.__strict
|
|
|
|
|
|
|
|
def set_strict(self, strict):
|
|
|
|
"""Set strict flag."""
|
|
|
|
self.__strict = strict
|
|
|
|
return self
|
|
|
|
|
|
|
|
def get_children(self):
|
|
|
|
"""Return children options."""
|
|
|
|
return self.__children
|
|
|
|
|
|
|
|
def set_children(self, children):
|
|
|
|
"""Set children options."""
|
|
|
|
self.__children = children
|
|
|
|
return self
|
|
|
|
|
2021-10-24 04:46:50 +03:00
|
|
|
def get_yaml_files(self):
|
|
|
|
"""Return list of YAML files."""
|
2021-10-27 04:08:47 +03:00
|
|
|
return list(self.__yaml_files)
|
2021-10-24 04:46:50 +03:00
|
|
|
|
|
|
|
def set_yaml_files(self, files):
|
|
|
|
"""Set list of YAML files."""
|
|
|
|
self.__yaml_files = list(files)
|
|
|
|
return self
|
|
|
|
|
2021-10-27 03:27:11 +03:00
|
|
|
def get_ini_files(self):
|
|
|
|
"""Return list of INI files."""
|
2021-10-27 04:08:47 +03:00
|
|
|
return list(self.__ini_files)
|
2021-10-27 03:27:11 +03:00
|
|
|
|
|
|
|
def set_ini_files(self, files):
|
|
|
|
"""Set list of INI files."""
|
|
|
|
self.__ini_files = list(files)
|
|
|
|
return self
|
|
|
|
|
2021-10-27 04:08:47 +03:00
|
|
|
def get_pydantic_settings(self):
|
|
|
|
"""Return list of Pydantic settings."""
|
|
|
|
return list(self.__pydantic_settings)
|
|
|
|
|
|
|
|
def set_pydantic_settings(self, settings):
|
|
|
|
"""Set list of Pydantic settings."""
|
|
|
|
self.__pydantic_settings = list(settings)
|
|
|
|
return self
|
|
|
|
|
2021-10-24 04:46:50 +03:00
|
|
|
def load(self, required=UNDEFINED, envs_required=UNDEFINED):
|
|
|
|
"""Load configuration.
|
|
|
|
|
|
|
|
This method loads configuration from configuration files or pydantic settings that
|
|
|
|
were set earlier with set_*() methods or provided to the __init__(), e.g.:
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
config = providers.Configuration(yaml_files=[file1, file2])
|
|
|
|
config.load()
|
|
|
|
|
|
|
|
:param required: When required is True, raise an exception if file does not exist.
|
|
|
|
:type required: bool
|
|
|
|
|
|
|
|
:param envs_required: When True, raises an error on undefined environment variable.
|
|
|
|
:type envs_required: bool
|
|
|
|
"""
|
|
|
|
for file in self.get_yaml_files():
|
|
|
|
self.from_yaml(file, required=required, envs_required=envs_required)
|
|
|
|
|
2021-10-27 03:27:11 +03:00
|
|
|
for file in self.get_ini_files():
|
|
|
|
self.from_ini(file, required=required, envs_required=envs_required)
|
|
|
|
|
2021-10-27 04:08:47 +03:00
|
|
|
for settings in self.get_pydantic_settings():
|
|
|
|
self.from_pydantic(settings, required=required)
|
|
|
|
|
2021-01-16 16:53:40 +03:00
|
|
|
def get(self, selector, required=False):
|
2020-08-04 01:01:20 +03:00
|
|
|
"""Return configuration option.
|
2017-03-25 23:38:48 +03:00
|
|
|
|
2020-08-04 01:01:20 +03:00
|
|
|
:param selector: Selector string, e.g. "option1.option2"
|
|
|
|
:type selector: str
|
2017-03-25 23:38:48 +03:00
|
|
|
|
2021-01-16 16:53:40 +03:00
|
|
|
:param required: Required flag, raise error if required option is missing
|
|
|
|
:type required: bool
|
|
|
|
|
2020-08-04 01:01:20 +03:00
|
|
|
:return: Option value.
|
|
|
|
:rtype: Any
|
|
|
|
"""
|
|
|
|
value = self.__call__()
|
2017-03-25 23:38:48 +03:00
|
|
|
|
2021-01-16 00:21:57 +03:00
|
|
|
if value is None:
|
2021-01-24 06:37:50 +03:00
|
|
|
if self._is_strict_mode_enabled() or required:
|
2022-01-17 04:32:42 +03:00
|
|
|
raise Error("Undefined configuration option \"{0}.{1}\"".format(self.__name, selector))
|
2021-01-16 00:21:57 +03:00
|
|
|
return None
|
|
|
|
|
2022-01-17 04:32:42 +03:00
|
|
|
keys = selector.split(".")
|
2020-08-04 01:01:20 +03:00
|
|
|
while len(keys) > 0:
|
|
|
|
key = keys.pop(0)
|
2021-01-24 06:37:50 +03:00
|
|
|
value = value.get(key, UNDEFINED)
|
2021-01-16 16:53:40 +03:00
|
|
|
|
2021-01-24 06:37:50 +03:00
|
|
|
if value is UNDEFINED:
|
|
|
|
if self._is_strict_mode_enabled() or required:
|
2022-01-17 04:32:42 +03:00
|
|
|
raise Error("Undefined configuration option \"{0}.{1}\"".format(self.__name, selector))
|
2021-01-16 16:53:40 +03:00
|
|
|
return None
|
2017-03-25 23:38:48 +03:00
|
|
|
|
2020-08-04 01:01:20 +03:00
|
|
|
return value
|
2017-03-25 23:38:48 +03:00
|
|
|
|
2020-08-04 01:01:20 +03:00
|
|
|
def set(self, selector, value):
|
|
|
|
"""Override configuration option.
|
|
|
|
|
|
|
|
:param selector: Selector string, e.g. "option1.option2"
|
|
|
|
:type selector: str
|
|
|
|
|
|
|
|
:param value: Overriding value
|
|
|
|
:type value: Any
|
2017-03-25 23:38:48 +03:00
|
|
|
|
2018-01-24 20:58:03 +03:00
|
|
|
:return: Overriding context.
|
|
|
|
:rtype: :py:class:`OverridingContext`
|
|
|
|
"""
|
2020-11-05 18:16:39 +03:00
|
|
|
original_value = current_value = deepcopy(self.__call__())
|
2017-03-25 23:38:48 +03:00
|
|
|
|
2022-01-17 04:32:42 +03:00
|
|
|
keys = selector.split(".")
|
2020-08-04 01:01:20 +03:00
|
|
|
while len(keys) > 0:
|
|
|
|
key = keys.pop(0)
|
|
|
|
if len(keys) == 0:
|
|
|
|
current_value[key] = value
|
|
|
|
break
|
|
|
|
temp_value = current_value.get(key, {})
|
|
|
|
current_value[key] = temp_value
|
|
|
|
current_value = temp_value
|
2020-06-23 04:52:00 +03:00
|
|
|
|
2020-08-04 01:01:20 +03:00
|
|
|
return self.override(original_value)
|
2020-06-23 04:52:00 +03:00
|
|
|
|
2020-08-04 01:01:20 +03:00
|
|
|
def override(self, provider):
|
|
|
|
"""Override provider with another provider.
|
2017-03-25 23:38:48 +03:00
|
|
|
|
2020-08-04 01:01:20 +03:00
|
|
|
:param provider: Overriding provider.
|
|
|
|
:type provider: :py:class:`Provider`
|
2017-03-25 23:38:48 +03:00
|
|
|
|
2020-08-04 01:01:20 +03:00
|
|
|
:raise: :py:exc:`dependency_injector.errors.Error`
|
|
|
|
|
|
|
|
:return: Overriding context.
|
|
|
|
:rtype: :py:class:`OverridingContext`
|
|
|
|
"""
|
|
|
|
context = super().override(provider)
|
|
|
|
self.reset_cache()
|
|
|
|
return context
|
2017-03-25 23:38:48 +03:00
|
|
|
|
2018-01-24 20:58:03 +03:00
|
|
|
def reset_last_overriding(self):
|
|
|
|
"""Reset last overriding provider.
|
|
|
|
|
|
|
|
:raise: :py:exc:`dependency_injector.errors.Error` if provider is not
|
|
|
|
overridden.
|
2017-03-25 23:38:48 +03:00
|
|
|
|
2018-01-24 20:58:03 +03:00
|
|
|
:rtype: None
|
|
|
|
"""
|
2020-08-04 01:01:20 +03:00
|
|
|
super().reset_last_overriding()
|
|
|
|
self.reset_cache()
|
2018-01-24 20:58:03 +03:00
|
|
|
|
|
|
|
def reset_override(self):
|
|
|
|
"""Reset all overriding providers.
|
|
|
|
|
|
|
|
:rtype: None
|
|
|
|
"""
|
2020-08-04 01:01:20 +03:00
|
|
|
super().reset_override()
|
|
|
|
self.reset_cache()
|
2018-01-24 20:58:03 +03:00
|
|
|
|
2020-08-04 01:01:20 +03:00
|
|
|
def reset_cache(self):
|
|
|
|
"""Reset children providers cache.
|
|
|
|
|
|
|
|
:rtype: None
|
|
|
|
"""
|
2021-03-21 04:41:39 +03:00
|
|
|
for provider in self.__children.values():
|
|
|
|
provider.reset_cache()
|
|
|
|
|
|
|
|
for provider in self.overrides:
|
|
|
|
if isinstance(provider, (Configuration, ConfigurationOption)):
|
|
|
|
provider.reset_cache()
|
2020-06-23 04:52:00 +03:00
|
|
|
|
2018-01-24 20:58:03 +03:00
|
|
|
def update(self, value):
|
|
|
|
"""Set configuration options.
|
|
|
|
|
|
|
|
.. deprecated:: 3.11
|
2017-03-25 23:38:48 +03:00
|
|
|
|
2018-01-24 20:58:03 +03:00
|
|
|
Use :py:meth:`Configuration.override` instead.
|
|
|
|
|
|
|
|
:param value: Value of configuration option.
|
|
|
|
:type value: object | dict
|
|
|
|
|
|
|
|
:rtype: None
|
|
|
|
"""
|
|
|
|
self.override(value)
|
|
|
|
|
2021-10-24 04:46:50 +03:00
|
|
|
def from_ini(self, filepath, required=UNDEFINED, envs_required=UNDEFINED):
|
2020-06-25 19:50:42 +03:00
|
|
|
"""Load configuration from the ini file.
|
2020-06-23 23:46:24 +03:00
|
|
|
|
2020-06-26 00:12:16 +03:00
|
|
|
Loaded configuration is merged recursively over existing configuration.
|
2020-06-24 01:09:20 +03:00
|
|
|
|
2020-06-23 23:46:24 +03:00
|
|
|
:param filepath: Path to the configuration file.
|
|
|
|
:type filepath: str
|
|
|
|
|
2021-01-24 18:27:45 +03:00
|
|
|
:param required: When required is True, raise an exception if file does not exist.
|
|
|
|
:type required: bool
|
|
|
|
|
2021-06-24 16:00:36 +03:00
|
|
|
:param envs_required: When True, raises an error on undefined environment variable.
|
|
|
|
:type envs_required: bool
|
|
|
|
|
2020-06-23 23:46:24 +03:00
|
|
|
:rtype: None
|
|
|
|
"""
|
2021-01-24 06:37:50 +03:00
|
|
|
try:
|
2021-06-24 16:00:36 +03:00
|
|
|
parser = _parse_ini_file(
|
|
|
|
filepath,
|
2021-10-24 04:46:50 +03:00
|
|
|
envs_required=envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(),
|
2021-06-24 16:00:36 +03:00
|
|
|
)
|
2021-01-24 06:37:50 +03:00
|
|
|
except IOError as exception:
|
2021-01-24 18:27:45 +03:00
|
|
|
if required is not False \
|
|
|
|
and (self._is_strict_mode_enabled() or required is True) \
|
|
|
|
and exception.errno in (errno.ENOENT, errno.EISDIR):
|
2022-01-17 04:32:42 +03:00
|
|
|
exception.strerror = "Unable to load configuration file {0}".format(exception.strerror)
|
2021-01-24 06:37:50 +03:00
|
|
|
raise
|
|
|
|
return
|
2020-06-23 23:46:24 +03:00
|
|
|
|
|
|
|
config = {}
|
|
|
|
for section in parser.sections():
|
2020-06-23 23:53:22 +03:00
|
|
|
config[section] = dict(parser.items(section))
|
2020-06-23 23:46:24 +03:00
|
|
|
|
2020-06-24 01:09:20 +03:00
|
|
|
current_config = self.__call__()
|
|
|
|
if not current_config:
|
|
|
|
current_config = {}
|
|
|
|
self.override(merge_dicts(current_config, config))
|
2020-06-23 23:46:24 +03:00
|
|
|
|
2021-10-24 04:46:50 +03:00
|
|
|
def from_yaml(self, filepath, required=UNDEFINED, loader=None, envs_required=UNDEFINED):
|
2020-06-25 19:50:42 +03:00
|
|
|
"""Load configuration from the yaml file.
|
2020-06-25 04:04:30 +03:00
|
|
|
|
2020-06-26 00:12:16 +03:00
|
|
|
Loaded configuration is merged recursively over existing configuration.
|
2020-06-25 04:04:30 +03:00
|
|
|
|
|
|
|
:param filepath: Path to the configuration file.
|
|
|
|
:type filepath: str
|
|
|
|
|
2021-01-24 18:27:45 +03:00
|
|
|
:param required: When required is True, raise an exception if file does not exist.
|
|
|
|
:type required: bool
|
|
|
|
|
2021-01-22 02:00:24 +03:00
|
|
|
:param loader: YAML loader, :py:class:`YamlLoader` is used if not specified.
|
|
|
|
:type loader: ``yaml.Loader``
|
|
|
|
|
2021-06-24 16:00:36 +03:00
|
|
|
:param envs_required: When True, raises an error on undefined environment variable.
|
|
|
|
:type envs_required: bool
|
|
|
|
|
2020-06-25 04:04:30 +03:00
|
|
|
:rtype: None
|
|
|
|
"""
|
|
|
|
if yaml is None:
|
|
|
|
raise Error(
|
2022-01-17 04:32:42 +03:00
|
|
|
"Unable to load yaml configuration - PyYAML is not installed. "
|
|
|
|
"Install PyYAML or install Dependency Injector with yaml extras: "
|
|
|
|
"\"pip install dependency-injector[yaml]\""
|
2020-06-25 04:04:30 +03:00
|
|
|
)
|
|
|
|
|
2021-01-22 02:00:24 +03:00
|
|
|
if loader is None:
|
|
|
|
loader = YamlLoader
|
|
|
|
|
2020-06-26 06:37:01 +03:00
|
|
|
try:
|
|
|
|
with open(filepath) as opened_file:
|
2021-06-24 16:00:36 +03:00
|
|
|
config_content = opened_file.read()
|
2021-01-24 06:37:50 +03:00
|
|
|
except IOError as exception:
|
2021-01-24 18:27:45 +03:00
|
|
|
if required is not False \
|
|
|
|
and (self._is_strict_mode_enabled() or required is True) \
|
|
|
|
and exception.errno in (errno.ENOENT, errno.EISDIR):
|
2022-01-17 04:32:42 +03:00
|
|
|
exception.strerror = "Unable to load configuration file {0}".format(exception.strerror)
|
2021-01-24 06:37:50 +03:00
|
|
|
raise
|
2020-06-26 06:37:01 +03:00
|
|
|
return
|
2020-06-25 04:04:30 +03:00
|
|
|
|
2021-06-24 16:00:36 +03:00
|
|
|
config_content = _resolve_config_env_markers(
|
|
|
|
config_content,
|
2021-10-24 04:46:50 +03:00
|
|
|
envs_required=envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(),
|
2021-06-24 16:00:36 +03:00
|
|
|
)
|
|
|
|
config = yaml.load(config_content, loader)
|
|
|
|
|
2020-06-25 04:04:30 +03:00
|
|
|
current_config = self.__call__()
|
|
|
|
if not current_config:
|
|
|
|
current_config = {}
|
|
|
|
self.override(merge_dicts(current_config, config))
|
2020-06-24 23:20:03 +03:00
|
|
|
|
2021-02-03 17:21:32 +03:00
|
|
|
def from_pydantic(self, settings, required=UNDEFINED, **kwargs):
|
|
|
|
"""Load configuration from pydantic settings.
|
|
|
|
|
|
|
|
Loaded configuration is merged recursively over existing configuration.
|
|
|
|
|
|
|
|
:param settings: Pydantic settings instances.
|
|
|
|
:type settings: :py:class:`pydantic.BaseSettings`
|
|
|
|
|
|
|
|
:param required: When required is True, raise an exception if settings dict is empty.
|
|
|
|
:type required: bool
|
|
|
|
|
|
|
|
:param kwargs: Keyword arguments forwarded to ``pydantic.BaseSettings.dict()`` call.
|
|
|
|
:type kwargs: Dict[Any, Any]
|
|
|
|
|
|
|
|
:rtype: None
|
|
|
|
"""
|
|
|
|
if pydantic is None:
|
|
|
|
raise Error(
|
2022-01-17 04:32:42 +03:00
|
|
|
"Unable to load pydantic configuration - pydantic is not installed. "
|
|
|
|
"Install pydantic or install Dependency Injector with pydantic extras: "
|
|
|
|
"\"pip install dependency-injector[pydantic]\""
|
2021-02-03 17:21:32 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
if isinstance(settings, CLASS_TYPES) and issubclass(settings, pydantic.BaseSettings):
|
|
|
|
raise Error(
|
2022-01-17 04:32:42 +03:00
|
|
|
"Got settings class, but expect instance: "
|
|
|
|
"instead \"{0}\" use \"{0}()\"".format(settings.__name__)
|
2021-02-03 17:21:32 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
if not isinstance(settings, pydantic.BaseSettings):
|
|
|
|
raise Error(
|
2022-01-17 04:32:42 +03:00
|
|
|
"Unable to recognize settings instance, expect \"pydantic.BaseSettings\", "
|
|
|
|
"got {0} instead".format(settings)
|
2021-02-03 17:21:32 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
self.from_dict(settings.dict(**kwargs), required=required)
|
|
|
|
|
2021-01-24 18:27:45 +03:00
|
|
|
def from_dict(self, options, required=UNDEFINED):
|
2020-06-25 19:50:42 +03:00
|
|
|
"""Load configuration from the dictionary.
|
|
|
|
|
2020-06-26 00:12:16 +03:00
|
|
|
Loaded configuration is merged recursively over existing configuration.
|
2020-06-25 19:50:42 +03:00
|
|
|
|
|
|
|
:param options: Configuration options.
|
|
|
|
:type options: dict
|
|
|
|
|
2021-01-24 18:27:45 +03:00
|
|
|
:param required: When required is True, raise an exception if dictionary is empty.
|
|
|
|
:type required: bool
|
|
|
|
|
2020-06-25 19:50:42 +03:00
|
|
|
:rtype: None
|
|
|
|
"""
|
2021-01-24 18:27:45 +03:00
|
|
|
if required is not False \
|
|
|
|
and (self._is_strict_mode_enabled() or required is True) \
|
|
|
|
and not options:
|
2022-01-17 04:32:42 +03:00
|
|
|
raise ValueError("Can not use empty dictionary")
|
2021-01-24 06:37:50 +03:00
|
|
|
|
2020-06-25 19:50:42 +03:00
|
|
|
current_config = self.__call__()
|
|
|
|
if not current_config:
|
|
|
|
current_config = {}
|
|
|
|
self.override(merge_dicts(current_config, options))
|
|
|
|
|
2021-12-21 01:46:51 +03:00
|
|
|
def from_env(self, name, default=UNDEFINED, required=UNDEFINED, as_=UNDEFINED):
|
2020-06-25 19:50:42 +03:00
|
|
|
"""Load configuration value from the environment variable.
|
|
|
|
|
|
|
|
:param name: Name of the environment variable.
|
|
|
|
:type name: str
|
|
|
|
|
|
|
|
:param default: Default value that is used if environment variable does not exist.
|
2021-01-24 06:37:50 +03:00
|
|
|
:type default: object
|
2020-06-25 19:50:42 +03:00
|
|
|
|
2021-01-24 18:27:45 +03:00
|
|
|
:param required: When required is True, raise an exception if environment variable is undefined.
|
|
|
|
:type required: bool
|
|
|
|
|
2021-12-21 01:46:51 +03:00
|
|
|
:param as_: Callable used for type casting (int, float, etc).
|
|
|
|
:type as_: object
|
|
|
|
|
2020-06-25 19:50:42 +03:00
|
|
|
:rtype: None
|
|
|
|
"""
|
2021-01-24 06:37:50 +03:00
|
|
|
value = os.environ.get(name, default)
|
|
|
|
|
|
|
|
if value is UNDEFINED:
|
2021-01-24 18:27:45 +03:00
|
|
|
if required is not False \
|
|
|
|
and (self._is_strict_mode_enabled() or required is True):
|
2022-01-17 04:32:42 +03:00
|
|
|
raise ValueError("Environment variable \"{0}\" is undefined".format(name))
|
2021-01-24 06:37:50 +03:00
|
|
|
value = None
|
|
|
|
|
2021-12-21 01:46:51 +03:00
|
|
|
if as_ is not UNDEFINED:
|
|
|
|
value = as_(value)
|
|
|
|
|
2020-06-25 19:50:42 +03:00
|
|
|
self.override(value)
|
|
|
|
|
2021-06-14 05:05:29 +03:00
|
|
|
def from_value(self, value):
|
|
|
|
"""Load configuration value.
|
|
|
|
|
|
|
|
:param value: Configuration value
|
|
|
|
:type value: object
|
|
|
|
|
|
|
|
:rtype: None
|
|
|
|
"""
|
|
|
|
self.override(value)
|
|
|
|
|
2021-02-01 17:42:21 +03:00
|
|
|
@property
|
|
|
|
def related(self):
|
|
|
|
"""Return related providers generator."""
|
|
|
|
yield from self.__children.values()
|
|
|
|
yield from super().related
|
|
|
|
|
2021-01-24 06:37:50 +03:00
|
|
|
def _is_strict_mode_enabled(self):
|
|
|
|
return self.__strict
|
|
|
|
|
2017-03-25 23:38:48 +03:00
|
|
|
|
|
|
|
cdef class Factory(Provider):
|
|
|
|
r"""Factory provider creates new instance on every call.
|
|
|
|
|
|
|
|
:py:class:`Factory` supports positional & keyword argument injections,
|
|
|
|
as well as attribute injections.
|
|
|
|
|
|
|
|
Positional and keyword argument injections could be defined like this:
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
factory = Factory(SomeClass,
|
2022-01-17 04:32:42 +03:00
|
|
|
"positional_arg1", "positional_arg2",
|
2017-03-25 23:38:48 +03:00
|
|
|
keyword_argument1=3, keyword_argument=4)
|
|
|
|
|
|
|
|
# or
|
|
|
|
|
|
|
|
factory = Factory(SomeClass) \
|
2022-01-17 04:32:42 +03:00
|
|
|
.add_args("positional_arg1", "positional_arg2") \
|
2017-03-25 23:38:48 +03:00
|
|
|
.add_kwargs(keyword_argument1=3, keyword_argument=4)
|
|
|
|
|
|
|
|
# or
|
|
|
|
|
|
|
|
factory = Factory(SomeClass)
|
2022-01-17 04:32:42 +03:00
|
|
|
factory.add_args("positional_arg1", "positional_arg2")
|
2017-03-25 23:38:48 +03:00
|
|
|
factory.add_kwargs(keyword_argument1=3, keyword_argument=4)
|
|
|
|
|
|
|
|
|
|
|
|
Attribute injections are defined by using
|
|
|
|
:py:meth:`Factory.add_attributes`:
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
factory = Factory(SomeClass) \
|
|
|
|
.add_attributes(attribute1=1, attribute2=2)
|
|
|
|
|
|
|
|
Retrieving of provided instance can be performed via calling
|
|
|
|
:py:class:`Factory` object:
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
factory = Factory(SomeClass)
|
|
|
|
some_object = factory()
|
|
|
|
|
|
|
|
.. py:attribute:: provided_type
|
|
|
|
|
|
|
|
If provided type is defined, provider checks that providing class is
|
|
|
|
its subclass.
|
|
|
|
|
|
|
|
:type: type | None
|
|
|
|
"""
|
|
|
|
|
|
|
|
provided_type = None
|
|
|
|
|
2021-03-20 20:16:51 +03:00
|
|
|
def __init__(self, provides=None, *args, **kwargs):
|
|
|
|
"""Initialize provider."""
|
|
|
|
self.__instantiator = Callable()
|
|
|
|
self.set_provides(provides)
|
|
|
|
self.set_args(*args)
|
|
|
|
self.set_kwargs(**kwargs)
|
2017-03-25 23:38:48 +03:00
|
|
|
|
|
|
|
self.__attributes = tuple()
|
|
|
|
self.__attributes_len = 0
|
|
|
|
|
|
|
|
super(Factory, self).__init__()
|
|
|
|
|
|
|
|
def __deepcopy__(self, memo):
|
|
|
|
"""Create and return full copy of provider."""
|
|
|
|
copied = memo.get(id(self))
|
|
|
|
if copied is not None:
|
|
|
|
return copied
|
|
|
|
|
2021-03-20 20:16:51 +03:00
|
|
|
copied = _memorized_duplicate(self, memo)
|
2021-03-21 04:41:39 +03:00
|
|
|
copied.set_provides(_copy_if_provider(self.provides, memo))
|
2021-03-20 20:16:51 +03:00
|
|
|
copied.set_args(*deepcopy(self.args, memo))
|
|
|
|
copied.set_kwargs(**deepcopy(self.kwargs, memo))
|
2017-03-25 23:38:48 +03:00
|
|
|
copied.set_attributes(**deepcopy(self.attributes, memo))
|
2018-07-24 23:09:56 +03:00
|
|
|
self._copy_overridings(copied, memo)
|
2017-03-25 23:38:48 +03:00
|
|
|
return copied
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
"""Return string representation of provider.
|
|
|
|
|
|
|
|
:rtype: str
|
|
|
|
"""
|
|
|
|
return represent_provider(provider=self,
|
|
|
|
provides=self.__instantiator.provides)
|
|
|
|
|
|
|
|
@property
|
|
|
|
def cls(self):
|
2020-07-11 19:15:00 +03:00
|
|
|
"""Return provided type."""
|
|
|
|
return self.provides
|
|
|
|
|
|
|
|
@property
|
|
|
|
def provides(self):
|
2022-01-17 04:32:42 +03:00
|
|
|
"""Return provider provides."""
|
2017-03-25 23:38:48 +03:00
|
|
|
return self.__instantiator.provides
|
|
|
|
|
2021-03-20 20:16:51 +03:00
|
|
|
def set_provides(self, provides):
|
2022-01-17 04:32:42 +03:00
|
|
|
"""Set provider provides."""
|
2022-01-31 07:19:09 +03:00
|
|
|
provides = _resolve_string_import(provides)
|
2021-03-20 20:16:51 +03:00
|
|
|
if (provides
|
|
|
|
and self.__class__.provided_type and
|
|
|
|
not issubclass(provides, self.__class__.provided_type)):
|
|
|
|
raise Error(
|
2022-01-17 04:32:42 +03:00
|
|
|
"{0} can provide only {1} instances".format(
|
2021-03-20 20:16:51 +03:00
|
|
|
_class_qualname(self),
|
|
|
|
self.__class__.provided_type,
|
|
|
|
),
|
|
|
|
)
|
|
|
|
self.__instantiator.set_provides(provides)
|
|
|
|
return self
|
|
|
|
|
2017-03-25 23:38:48 +03:00
|
|
|
@property
|
|
|
|
def args(self):
|
|
|
|
"""Return positional argument injections."""
|
|
|
|
return self.__instantiator.args
|
|
|
|
|
|
|
|
def add_args(self, *args):
|
2020-06-14 05:24:32 +03:00
|
|
|
"""Add __init__ positional argument injections.
|
2017-03-25 23:38:48 +03:00
|
|
|
|
|
|
|
:return: Reference ``self``
|
|
|
|
"""
|
|
|
|
self.__instantiator.add_args(*args)
|
|
|
|
return self
|
|
|
|
|
|
|
|
def set_args(self, *args):
|
2020-06-14 05:24:32 +03:00
|
|
|
"""Set __init__ positional argument injections.
|
2017-03-25 23:38:48 +03:00
|
|
|
|
|
|
|
Existing __init__ positional argument injections are dropped.
|
|
|
|
|
|
|
|
:return: Reference ``self``
|
|
|
|
"""
|
|
|
|
self.__instantiator.set_args(*args)
|
|
|
|
return self
|
|
|
|
|
|
|
|
def clear_args(self):
|
2020-06-14 05:24:32 +03:00
|
|
|
"""Drop __init__ positional argument injections.
|
2017-03-25 23:38:48 +03:00
|
|
|
|
|
|
|
:return: Reference ``self``
|
|
|
|
"""
|
|
|
|
self.__instantiator.clear_args()
|
|
|
|
return self
|
|
|
|
|
|
|
|
@property
|
|
|
|
def kwargs(self):
|
|
|
|
"""Return keyword argument injections."""
|
|
|
|
return self.__instantiator.kwargs
|
|
|
|
|
|
|
|
def add_kwargs(self, **kwargs):
|
|
|
|
"""Add __init__ keyword argument injections.
|
|
|
|
|
|
|
|
:return: Reference ``self``
|
|
|
|
"""
|
|
|
|
self.__instantiator.add_kwargs(**kwargs)
|
|
|
|
return self
|
|
|
|
|
|
|
|
def set_kwargs(self, **kwargs):
|
|
|
|
"""Set __init__ keyword argument injections.
|
|
|
|
|
|
|
|
Existing __init__ keyword argument injections are dropped.
|
|
|
|
|
|
|
|
:return: Reference ``self``
|
|
|
|
"""
|
|
|
|
self.__instantiator.set_kwargs(**kwargs)
|
|
|
|
return self
|
|
|
|
|
|
|
|
def clear_kwargs(self):
|
|
|
|
"""Drop __init__ keyword argument injections.
|
|
|
|
|
|
|
|
:return: Reference ``self``
|
|
|
|
"""
|
|
|
|
self.__instantiator.clear_kwargs()
|
|
|
|
return self
|
|
|
|
|
|
|
|
@property
|
|
|
|
def attributes(self):
|
|
|
|
"""Return attribute injections."""
|
|
|
|
cdef int index
|
|
|
|
cdef NamedInjection attribute
|
|
|
|
cdef dict attributes
|
|
|
|
|
|
|
|
attributes = dict()
|
|
|
|
for index in range(self.__attributes_len):
|
|
|
|
attribute = self.__attributes[index]
|
|
|
|
attributes[attribute.__name] = attribute.__value
|
|
|
|
return attributes
|
|
|
|
|
|
|
|
def add_attributes(self, **kwargs):
|
|
|
|
"""Add attribute injections.
|
|
|
|
|
|
|
|
:return: Reference ``self``
|
|
|
|
"""
|
|
|
|
self.__attributes += parse_named_injections(kwargs)
|
|
|
|
self.__attributes_len = len(self.__attributes)
|
|
|
|
return self
|
|
|
|
|
|
|
|
def set_attributes(self, **kwargs):
|
|
|
|
"""Set attribute injections.
|
|
|
|
|
|
|
|
Existing attribute injections are dropped.
|
|
|
|
|
|
|
|
:return: Reference ``self``
|
|
|
|
"""
|
|
|
|
self.__attributes = parse_named_injections(kwargs)
|
|
|
|
self.__attributes_len = len(self.__attributes)
|
|
|
|
return self
|
|
|
|
|
|
|
|
def clear_attributes(self):
|
|
|
|
"""Drop attribute injections.
|
|
|
|
|
|
|
|
:return: Reference ``self``
|
|
|
|
"""
|
|
|
|
self.__attributes = tuple()
|
|
|
|
self.__attributes_len = len(self.__attributes)
|
|
|
|
return self
|
|
|
|
|
2021-02-01 17:42:21 +03:00
|
|
|
@property
|
|
|
|
def related(self):
|
|
|
|
"""Return related providers generator."""
|
|
|
|
yield from filter(is_provider, [self.provides])
|
|
|
|
yield from filter(is_provider, self.args)
|
|
|
|
yield from filter(is_provider, self.kwargs.values())
|
|
|
|
yield from filter(is_provider, self.attributes.values())
|
|
|
|
yield from super().related
|
|
|
|
|
2017-03-25 23:38:48 +03:00
|
|
|
cpdef object _provide(self, tuple args, dict kwargs):
|
|
|
|
"""Return new instance."""
|
|
|
|
return __factory_call(self, args, kwargs)
|
|
|
|
|
|
|
|
|
|
|
|
cdef class DelegatedFactory(Factory):
|
|
|
|
"""Factory that is injected "as is".
|
|
|
|
|
|
|
|
.. py:attribute:: provided_type
|
|
|
|
|
|
|
|
If provided type is defined, provider checks that providing class is
|
|
|
|
its subclass.
|
|
|
|
|
|
|
|
:type: type | None
|
|
|
|
|
|
|
|
.. py:attribute:: cls
|
2020-06-17 05:22:06 +03:00
|
|
|
:noindex:
|
2017-03-25 23:38:48 +03:00
|
|
|
|
|
|
|
Class that provides object.
|
|
|
|
Alias for :py:attr:`provides`.
|
|
|
|
|
|
|
|
:type: type
|
|
|
|
"""
|
|
|
|
|
|
|
|
__IS_DELEGATED__ = True
|
|
|
|
|
|
|
|
|
2017-04-06 18:17:06 +03:00
|
|
|
cdef class AbstractFactory(Factory):
|
|
|
|
"""Abstract factory provider.
|
|
|
|
|
|
|
|
:py:class:`AbstractFactory` is a :py:class:`Factory` provider that must
|
|
|
|
be explicitly overridden before calling.
|
|
|
|
|
|
|
|
Overriding of :py:class:`AbstractFactory` is possible only by another
|
|
|
|
:py:class:`Factory` provider.
|
|
|
|
"""
|
|
|
|
|
|
|
|
def __call__(self, *args, **kwargs):
|
|
|
|
"""Return provided object.
|
|
|
|
|
|
|
|
Callable interface implementation.
|
|
|
|
"""
|
|
|
|
if self.__last_overriding is None:
|
2022-01-17 04:32:42 +03:00
|
|
|
raise Error("{0} must be overridden before calling".format(self))
|
2021-01-11 03:26:15 +03:00
|
|
|
return super().__call__(*args, **kwargs)
|
2017-04-06 18:17:06 +03:00
|
|
|
|
|
|
|
def override(self, provider):
|
|
|
|
"""Override provider with another provider.
|
|
|
|
|
|
|
|
:param provider: Overriding provider.
|
|
|
|
:type provider: :py:class:`Provider`
|
|
|
|
|
|
|
|
:raise: :py:exc:`dependency_injector.errors.Error`
|
|
|
|
|
|
|
|
:return: Overriding context.
|
|
|
|
:rtype: :py:class:`OverridingContext`
|
|
|
|
"""
|
|
|
|
if not isinstance(provider, Factory):
|
2022-01-17 04:32:42 +03:00
|
|
|
raise Error("{0} must be overridden only by "
|
|
|
|
"{1} providers".format(self, Factory))
|
2017-04-06 18:17:06 +03:00
|
|
|
return super(AbstractFactory, self).override(provider)
|
|
|
|
|
|
|
|
cpdef object _provide(self, tuple args, dict kwargs):
|
2022-01-17 04:32:42 +03:00
|
|
|
"""Return result of provided callable call."""
|
|
|
|
raise NotImplementedError("Abstract provider forward providing logic to overriding provider")
|
2017-04-06 18:17:06 +03:00
|
|
|
|
2017-07-09 23:13:10 +03:00
|
|
|
|
|
|
|
cdef class FactoryDelegate(Delegate):
|
|
|
|
"""Factory delegate injects delegating factory "as is".
|
|
|
|
|
|
|
|
.. py:attribute:: provides
|
|
|
|
|
|
|
|
Value that have to be provided.
|
|
|
|
|
|
|
|
:type: object
|
|
|
|
"""
|
|
|
|
|
|
|
|
def __init__(self, factory):
|
|
|
|
"""Initializer.
|
|
|
|
|
2017-07-09 23:38:01 +03:00
|
|
|
:param factory: Value that have to be provided.
|
|
|
|
:type factory: object
|
2017-07-09 23:13:10 +03:00
|
|
|
"""
|
|
|
|
if isinstance(factory, Factory) is False:
|
2022-01-17 04:32:42 +03:00
|
|
|
raise Error("{0} can wrap only {1} providers".format(self.__class__, Factory))
|
2019-10-09 17:45:14 +03:00
|
|
|
super(FactoryDelegate, self).__init__(factory)
|
2017-07-09 23:13:10 +03:00
|
|
|
|
|
|
|
|
2022-01-10 05:45:20 +03:00
|
|
|
cdef class FactoryAggregate(Aggregate):
|
2017-10-13 05:43:22 +03:00
|
|
|
"""Factory providers aggregate.
|
2017-10-04 01:28:31 +03:00
|
|
|
|
|
|
|
:py:class:`FactoryAggregate` is an aggregate of :py:class:`Factory`
|
|
|
|
providers.
|
|
|
|
|
|
|
|
:py:class:`FactoryAggregate` is a delegated provider, meaning that it is
|
|
|
|
injected "as is".
|
|
|
|
|
2021-11-26 19:50:19 +03:00
|
|
|
All aggregated providers can be retrieved as a read-only
|
|
|
|
dictionary :py:attr:`FactoryAggregate.providers` or as an attribute of
|
2017-10-13 05:43:22 +03:00
|
|
|
:py:class:`FactoryAggregate`.
|
2017-10-04 01:28:31 +03:00
|
|
|
"""
|
|
|
|
|
2021-11-26 19:50:19 +03:00
|
|
|
@property
|
|
|
|
def factories(self):
|
|
|
|
"""Return dictionary of factories, read-only.
|
|
|
|
|
|
|
|
Alias for ``.providers()`` attribute.
|
|
|
|
"""
|
|
|
|
return self.providers
|
|
|
|
|
|
|
|
def set_factories(self, factory_dict=None, **factory_kwargs):
|
|
|
|
"""Set factories.
|
|
|
|
|
|
|
|
Alias for ``.set_providers()`` method.
|
|
|
|
"""
|
|
|
|
return self.set_providers(factory_dict, **factory_kwargs)
|
|
|
|
|
2017-10-04 01:28:31 +03:00
|
|
|
|
2017-03-25 23:38:48 +03:00
|
|
|
cdef class BaseSingleton(Provider):
|
|
|
|
"""Base class of singleton providers."""
|
|
|
|
|
|
|
|
provided_type = None
|
|
|
|
|
2021-03-20 20:16:51 +03:00
|
|
|
def __init__(self, provides=None, *args, **kwargs):
|
|
|
|
"""Initialize provider."""
|
|
|
|
self.__instantiator = Factory()
|
|
|
|
self.set_provides(provides)
|
|
|
|
self.set_args(*args)
|
|
|
|
self.set_kwargs(**kwargs)
|
2017-03-25 23:38:48 +03:00
|
|
|
super(BaseSingleton, self).__init__()
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
"""Return string representation of provider.
|
|
|
|
|
|
|
|
:rtype: str
|
|
|
|
"""
|
|
|
|
return represent_provider(provider=self,
|
|
|
|
provides=self.__instantiator.cls)
|
|
|
|
|
|
|
|
def __deepcopy__(self, memo):
|
|
|
|
"""Create and return full copy of provider."""
|
|
|
|
copied = memo.get(id(self))
|
|
|
|
if copied is not None:
|
|
|
|
return copied
|
|
|
|
|
2021-03-20 20:16:51 +03:00
|
|
|
copied = _memorized_duplicate(self, memo)
|
2021-03-21 04:41:39 +03:00
|
|
|
copied.set_provides(_copy_if_provider(self.provides, memo))
|
2021-03-20 20:16:51 +03:00
|
|
|
copied.set_args(*deepcopy(self.args, memo))
|
|
|
|
copied.set_kwargs(**deepcopy(self.kwargs, memo))
|
2017-03-25 23:38:48 +03:00
|
|
|
copied.set_attributes(**deepcopy(self.attributes, memo))
|
2018-07-24 23:09:56 +03:00
|
|
|
self._copy_overridings(copied, memo)
|
2017-03-25 23:38:48 +03:00
|
|
|
return copied
|
|
|
|
|
|
|
|
@property
|
|
|
|
def cls(self):
|
|
|
|
"""Return provided type."""
|
2021-02-01 17:42:21 +03:00
|
|
|
return self.provides
|
|
|
|
|
|
|
|
@property
|
|
|
|
def provides(self):
|
2022-01-17 04:32:42 +03:00
|
|
|
"""Return provider provides."""
|
2021-02-01 17:42:21 +03:00
|
|
|
return self.__instantiator.provides
|
2017-03-25 23:38:48 +03:00
|
|
|
|
2021-03-20 20:16:51 +03:00
|
|
|
def set_provides(self, provides):
|
2022-01-17 04:32:42 +03:00
|
|
|
"""Set provider provides."""
|
2022-01-31 07:19:09 +03:00
|
|
|
provides = _resolve_string_import(provides)
|
2021-03-20 20:16:51 +03:00
|
|
|
if (provides
|
|
|
|
and self.__class__.provided_type and
|
|
|
|
not issubclass(provides, self.__class__.provided_type)):
|
|
|
|
raise Error(
|
2022-01-17 04:32:42 +03:00
|
|
|
"{0} can provide only {1} instances".format(
|
2021-03-20 20:16:51 +03:00
|
|
|
_class_qualname(self),
|
|
|
|
self.__class__.provided_type,
|
|
|
|
),
|
|
|
|
)
|
|
|
|
self.__instantiator.set_provides(provides)
|
|
|
|
return self
|
|
|
|
|
2017-03-25 23:38:48 +03:00
|
|
|
@property
|
|
|
|
def args(self):
|
|
|
|
"""Return positional argument injections."""
|
|
|
|
return self.__instantiator.args
|
|
|
|
|
|
|
|
def add_args(self, *args):
|
2018-10-19 12:56:41 +03:00
|
|
|
"""Add __init__ positional argument injections.
|
2017-03-25 23:38:48 +03:00
|
|
|
|
|
|
|
:return: Reference ``self``
|
|
|
|
"""
|
|
|
|
self.__instantiator.add_args(*args)
|
|
|
|
return self
|
|
|
|
|
|
|
|
def set_args(self, *args):
|
2018-10-19 12:56:41 +03:00
|
|
|
"""Set __init__ positional argument injections.
|
2017-03-25 23:38:48 +03:00
|
|
|
|
|
|
|
Existing __init__ positional argument injections are dropped.
|
|
|
|
|
|
|
|
:return: Reference ``self``
|
|
|
|
"""
|
|
|
|
self.__instantiator.set_args(*args)
|
|
|
|
return self
|
|
|
|
|
|
|
|
def clear_args(self):
|
2018-10-19 12:56:41 +03:00
|
|
|
"""Drop __init__ positional argument injections.
|
2017-03-25 23:38:48 +03:00
|
|
|
|
|
|
|
:return: Reference ``self``
|
|
|
|
"""
|
|
|
|
self.__instantiator.clear_args()
|
|
|
|
return self
|
|
|
|
|
|
|
|
@property
|
|
|
|
def kwargs(self):
|
|
|
|
"""Return keyword argument injections."""
|
|
|
|
return self.__instantiator.kwargs
|
|
|
|
|
|
|
|
def add_kwargs(self, **kwargs):
|
|
|
|
"""Add __init__ keyword argument injections.
|
|
|
|
|
|
|
|
:return: Reference ``self``
|
|
|
|
"""
|
|
|
|
self.__instantiator.add_kwargs(**kwargs)
|
|
|
|
return self
|
|
|
|
|
|
|
|
def set_kwargs(self, **kwargs):
|
|
|
|
"""Set __init__ keyword argument injections.
|
|
|
|
|
|
|
|
Existing __init__ keyword argument injections are dropped.
|
|
|
|
|
|
|
|
:return: Reference ``self``
|
|
|
|
"""
|
|
|
|
self.__instantiator.set_kwargs(**kwargs)
|
|
|
|
return self
|
|
|
|
|
|
|
|
def clear_kwargs(self):
|
|
|
|
"""Drop __init__ keyword argument injections.
|
|
|
|
|
|
|
|
:return: Reference ``self``
|
|
|
|
"""
|
|
|
|
self.__instantiator.clear_kwargs()
|
|
|
|
return self
|
|
|
|
|
|
|
|
@property
|
|
|
|
def attributes(self):
|
|
|
|
"""Return attribute injections."""
|
|
|
|
return self.__instantiator.attributes
|
|
|
|
|
|
|
|
def add_attributes(self, **kwargs):
|
|
|
|
"""Add attribute injections.
|
|
|
|
|
|
|
|
:return: Reference ``self``
|
|
|
|
"""
|
|
|
|
self.__instantiator.add_attributes(**kwargs)
|
|
|
|
return self
|
|
|
|
|
|
|
|
def set_attributes(self, **kwargs):
|
|
|
|
"""Set attribute injections.
|
|
|
|
|
|
|
|
Existing attribute injections are dropped.
|
|
|
|
|
|
|
|
:return: Reference ``self``
|
|
|
|
"""
|
|
|
|
self.__instantiator.set_attributes(**kwargs)
|
|
|
|
return self
|
|
|
|
|
|
|
|
def clear_attributes(self):
|
|
|
|
"""Drop attribute injections.
|
|
|
|
|
|
|
|
:return: Reference ``self``
|
|
|
|
"""
|
|
|
|
self.__instantiator.clear_attributes()
|
|
|
|
return self
|
|
|
|
|
|
|
|
def reset(self):
|
|
|
|
"""Reset cached instance, if any.
|
|
|
|
|
|
|
|
:rtype: None
|
|
|
|
"""
|
|
|
|
raise NotImplementedError()
|
|
|
|
|
2021-02-06 02:17:44 +03:00
|
|
|
def full_reset(self):
|
|
|
|
"""Reset cached instance in current and all underlying singletons, if any.
|
|
|
|
|
2021-03-03 16:28:10 +03:00
|
|
|
:rtype: :py:class:`SingletonFullResetContext`
|
2021-02-06 02:17:44 +03:00
|
|
|
"""
|
|
|
|
self.reset()
|
|
|
|
for provider in self.traverse(types=[BaseSingleton]):
|
|
|
|
provider.reset()
|
2021-03-03 16:28:10 +03:00
|
|
|
return SingletonFullResetContext(self)
|
2021-02-06 02:17:44 +03:00
|
|
|
|
2021-02-01 17:42:21 +03:00
|
|
|
@property
|
|
|
|
def related(self):
|
|
|
|
"""Return related providers generator."""
|
|
|
|
yield from filter(is_provider, [self.__instantiator.provides])
|
|
|
|
yield from filter(is_provider, self.args)
|
|
|
|
yield from filter(is_provider, self.kwargs.values())
|
|
|
|
yield from filter(is_provider, self.attributes.values())
|
|
|
|
yield from super().related
|
|
|
|
|
2021-01-11 03:26:15 +03:00
|
|
|
def _async_init_instance(self, future_result, result):
|
|
|
|
try:
|
|
|
|
instance = result.result()
|
|
|
|
except Exception as exception:
|
|
|
|
self.__storage = None
|
|
|
|
future_result.set_exception(exception)
|
|
|
|
else:
|
|
|
|
self.__storage = instance
|
|
|
|
future_result.set_result(instance)
|
|
|
|
|
2017-03-25 23:38:48 +03:00
|
|
|
|
|
|
|
cdef class Singleton(BaseSingleton):
|
|
|
|
"""Singleton provider returns same instance on every call.
|
|
|
|
|
|
|
|
:py:class:`Singleton` provider creates instance once and returns it on
|
|
|
|
every call. :py:class:`Singleton` extends :py:class:`Factory`, so, please
|
|
|
|
follow :py:class:`Factory` documentation for getting familiar with
|
|
|
|
injections syntax.
|
|
|
|
|
|
|
|
Retrieving of provided instance can be performed via calling
|
|
|
|
:py:class:`Singleton` object:
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
singleton = Singleton(SomeClass)
|
|
|
|
some_object = singleton()
|
|
|
|
|
|
|
|
.. py:attribute:: provided_type
|
|
|
|
|
|
|
|
If provided type is defined, provider checks that providing class is
|
|
|
|
its subclass.
|
|
|
|
|
|
|
|
:type: type | None
|
|
|
|
|
|
|
|
.. py:attribute:: cls
|
2020-06-17 05:22:06 +03:00
|
|
|
:noindex:
|
2017-03-25 23:38:48 +03:00
|
|
|
|
|
|
|
Class that provides object.
|
|
|
|
Alias for :py:attr:`provides`.
|
|
|
|
|
|
|
|
:type: type
|
|
|
|
"""
|
|
|
|
|
2021-03-20 20:16:51 +03:00
|
|
|
def __init__(self, provides=None, *args, **kwargs):
|
2017-03-25 23:38:48 +03:00
|
|
|
"""Initializer.
|
|
|
|
|
|
|
|
:param provides: Provided type.
|
|
|
|
:type provides: type
|
|
|
|
"""
|
|
|
|
self.__storage = None
|
|
|
|
super(Singleton, self).__init__(provides, *args, **kwargs)
|
|
|
|
|
|
|
|
def reset(self):
|
|
|
|
"""Reset cached instance, if any.
|
|
|
|
|
|
|
|
:rtype: None
|
|
|
|
"""
|
2021-02-17 17:56:39 +03:00
|
|
|
if __is_future_or_coroutine(self.__storage):
|
2021-01-11 03:26:15 +03:00
|
|
|
asyncio.ensure_future(self.__storage).cancel()
|
2017-03-25 23:38:48 +03:00
|
|
|
self.__storage = None
|
2021-03-03 16:28:10 +03:00
|
|
|
return SingletonResetContext(self)
|
2017-03-25 23:38:48 +03:00
|
|
|
|
|
|
|
cpdef object _provide(self, tuple args, dict kwargs):
|
|
|
|
"""Return single instance."""
|
|
|
|
if self.__storage is None:
|
2021-01-11 03:26:15 +03:00
|
|
|
instance = __factory_call(self.__instantiator, args, kwargs)
|
|
|
|
|
2021-02-17 17:56:39 +03:00
|
|
|
if __is_future_or_coroutine(instance):
|
2021-01-11 03:26:15 +03:00
|
|
|
future_result = asyncio.Future()
|
|
|
|
instance = asyncio.ensure_future(instance)
|
|
|
|
instance.add_done_callback(functools.partial(self._async_init_instance, future_result))
|
|
|
|
self.__storage = future_result
|
|
|
|
return future_result
|
|
|
|
|
|
|
|
self.__storage = instance
|
|
|
|
|
2017-03-25 23:38:48 +03:00
|
|
|
return self.__storage
|
|
|
|
|
|
|
|
|
|
|
|
cdef class DelegatedSingleton(Singleton):
|
|
|
|
"""Delegated singleton is injected "as is".
|
|
|
|
|
|
|
|
.. py:attribute:: provided_type
|
|
|
|
|
|
|
|
If provided type is defined, provider checks that providing class is
|
|
|
|
its subclass.
|
|
|
|
|
|
|
|
:type: type | None
|
|
|
|
|
|
|
|
.. py:attribute:: cls
|
2020-06-17 05:22:06 +03:00
|
|
|
:noindex:
|
2017-03-25 23:38:48 +03:00
|
|
|
|
|
|
|
Class that provides object.
|
|
|
|
Alias for :py:attr:`provides`.
|
|
|
|
|
|
|
|
:type: type
|
|
|
|
"""
|
|
|
|
|
|
|
|
__IS_DELEGATED__ = True
|
|
|
|
|
|
|
|
|
|
|
|
cdef class ThreadSafeSingleton(BaseSingleton):
|
|
|
|
"""Thread-safe singleton provider."""
|
|
|
|
|
2017-04-18 23:01:20 +03:00
|
|
|
storage_lock = threading.RLock()
|
|
|
|
"""Storage reentrant lock.
|
|
|
|
|
|
|
|
:type: :py:class:`threading.RLock`
|
|
|
|
"""
|
|
|
|
|
2021-03-20 20:16:51 +03:00
|
|
|
def __init__(self, provides=None, *args, **kwargs):
|
2017-03-25 23:38:48 +03:00
|
|
|
"""Initializer.
|
|
|
|
|
|
|
|
:param provides: Provided type.
|
|
|
|
:type provides: type
|
|
|
|
"""
|
|
|
|
self.__storage = None
|
2017-04-18 23:01:20 +03:00
|
|
|
self.__storage_lock = self.__class__.storage_lock
|
2017-03-25 23:38:48 +03:00
|
|
|
super(ThreadSafeSingleton, self).__init__(provides, *args, **kwargs)
|
|
|
|
|
|
|
|
def reset(self):
|
|
|
|
"""Reset cached instance, if any.
|
|
|
|
|
|
|
|
:rtype: None
|
|
|
|
"""
|
2020-11-10 22:57:10 +03:00
|
|
|
with self.__storage_lock:
|
2021-02-17 17:56:39 +03:00
|
|
|
if __is_future_or_coroutine(self.__storage):
|
2021-01-11 03:26:15 +03:00
|
|
|
asyncio.ensure_future(self.__storage).cancel()
|
2020-11-10 22:57:10 +03:00
|
|
|
self.__storage = None
|
2021-03-03 16:28:10 +03:00
|
|
|
return SingletonResetContext(self)
|
2021-01-11 03:26:15 +03:00
|
|
|
|
2017-03-25 23:38:48 +03:00
|
|
|
cpdef object _provide(self, tuple args, dict kwargs):
|
|
|
|
"""Return single instance."""
|
2021-01-11 03:26:15 +03:00
|
|
|
instance = self.__storage
|
|
|
|
|
|
|
|
if instance is None:
|
2020-11-10 22:57:10 +03:00
|
|
|
with self.__storage_lock:
|
|
|
|
if self.__storage is None:
|
2021-03-24 04:14:03 +03:00
|
|
|
result = __factory_call(self.__instantiator, args, kwargs)
|
|
|
|
if __is_future_or_coroutine(result):
|
2021-01-11 03:26:15 +03:00
|
|
|
future_result = asyncio.Future()
|
2021-03-24 04:14:03 +03:00
|
|
|
result = asyncio.ensure_future(result)
|
|
|
|
result.add_done_callback(functools.partial(self._async_init_instance, future_result))
|
|
|
|
result = future_result
|
|
|
|
self.__storage = result
|
|
|
|
instance = self.__storage
|
2021-01-11 03:26:15 +03:00
|
|
|
return instance
|
2017-03-25 23:38:48 +03:00
|
|
|
|
|
|
|
|
|
|
|
cdef class DelegatedThreadSafeSingleton(ThreadSafeSingleton):
|
|
|
|
"""Delegated thread-safe singleton is injected "as is".
|
|
|
|
|
|
|
|
.. py:attribute:: provided_type
|
|
|
|
|
|
|
|
If provided type is defined, provider checks that providing class is
|
|
|
|
its subclass.
|
|
|
|
|
|
|
|
:type: type | None
|
|
|
|
|
|
|
|
.. py:attribute:: cls
|
2020-06-17 05:22:06 +03:00
|
|
|
:noindex:
|
2017-03-25 23:38:48 +03:00
|
|
|
|
|
|
|
Class that provides object.
|
|
|
|
Alias for :py:attr:`provides`.
|
|
|
|
|
|
|
|
:type: type
|
|
|
|
"""
|
|
|
|
|
|
|
|
__IS_DELEGATED__ = True
|
|
|
|
|
|
|
|
|
|
|
|
cdef class ThreadLocalSingleton(BaseSingleton):
|
|
|
|
"""Thread-local singleton provides single objects in scope of thread.
|
|
|
|
|
|
|
|
.. py:attribute:: provided_type
|
|
|
|
|
|
|
|
If provided type is defined, provider checks that providing class is
|
|
|
|
its subclass.
|
|
|
|
|
|
|
|
:type: type | None
|
|
|
|
|
|
|
|
.. py:attribute:: cls
|
2020-06-17 05:22:06 +03:00
|
|
|
:noindex:
|
2017-03-25 23:38:48 +03:00
|
|
|
|
|
|
|
Class that provides object.
|
|
|
|
Alias for :py:attr:`provides`.
|
|
|
|
|
|
|
|
:type: type
|
|
|
|
"""
|
|
|
|
|
2021-03-20 20:16:51 +03:00
|
|
|
def __init__(self, provides=None, *args, **kwargs):
|
2017-03-25 23:38:48 +03:00
|
|
|
"""Initializer.
|
|
|
|
|
|
|
|
:param provides: Provided type.
|
|
|
|
:type provides: type
|
|
|
|
"""
|
|
|
|
self.__storage = threading.local()
|
|
|
|
super(ThreadLocalSingleton, self).__init__(provides, *args, **kwargs)
|
|
|
|
|
|
|
|
def reset(self):
|
|
|
|
"""Reset cached instance, if any.
|
|
|
|
|
|
|
|
:rtype: None
|
|
|
|
"""
|
2021-03-03 16:28:10 +03:00
|
|
|
try:
|
|
|
|
instance = self.__storage.instance
|
|
|
|
except AttributeError:
|
|
|
|
return SingletonResetContext(self)
|
|
|
|
|
|
|
|
if __is_future_or_coroutine(instance):
|
|
|
|
asyncio.ensure_future(instance).cancel()
|
|
|
|
|
2019-03-22 05:04:20 +03:00
|
|
|
del self.__storage.instance
|
2017-03-25 23:38:48 +03:00
|
|
|
|
2021-03-03 16:28:10 +03:00
|
|
|
return SingletonResetContext(self)
|
|
|
|
|
2017-03-25 23:38:48 +03:00
|
|
|
cpdef object _provide(self, tuple args, dict kwargs):
|
|
|
|
"""Return single instance."""
|
|
|
|
cdef object instance
|
|
|
|
|
|
|
|
try:
|
|
|
|
instance = self.__storage.instance
|
|
|
|
except AttributeError:
|
|
|
|
instance = __factory_call(self.__instantiator, args, kwargs)
|
2021-01-11 03:26:15 +03:00
|
|
|
|
2021-02-17 17:56:39 +03:00
|
|
|
if __is_future_or_coroutine(instance):
|
2021-01-11 03:26:15 +03:00
|
|
|
future_result = asyncio.Future()
|
|
|
|
instance = asyncio.ensure_future(instance)
|
|
|
|
instance.add_done_callback(functools.partial(self._async_init_instance, future_result))
|
|
|
|
self.__storage.instance = future_result
|
|
|
|
return future_result
|
|
|
|
|
2017-03-25 23:38:48 +03:00
|
|
|
self.__storage.instance = instance
|
|
|
|
finally:
|
|
|
|
return instance
|
|
|
|
|
2021-01-11 03:26:15 +03:00
|
|
|
def _async_init_instance(self, future_result, result):
|
|
|
|
try:
|
|
|
|
instance = result.result()
|
|
|
|
except Exception as exception:
|
|
|
|
del self.__storage.instance
|
|
|
|
future_result.set_exception(exception)
|
|
|
|
else:
|
|
|
|
self.__storage.instance = instance
|
|
|
|
future_result.set_result(instance)
|
|
|
|
|
2017-03-25 23:38:48 +03:00
|
|
|
|
2021-04-19 04:37:55 +03:00
|
|
|
cdef class ContextLocalSingleton(BaseSingleton):
|
|
|
|
"""Context-local singleton provides single objects in scope of a context.
|
|
|
|
|
|
|
|
.. py:attribute:: provided_type
|
|
|
|
|
|
|
|
If provided type is defined, provider checks that providing class is
|
|
|
|
its subclass.
|
|
|
|
|
|
|
|
:type: type | None
|
|
|
|
|
|
|
|
.. py:attribute:: cls
|
|
|
|
:noindex:
|
|
|
|
|
|
|
|
Class that provides object.
|
|
|
|
Alias for :py:attr:`provides`.
|
|
|
|
|
|
|
|
:type: type
|
|
|
|
"""
|
|
|
|
_none = object()
|
|
|
|
|
|
|
|
def __init__(self, provides=None, *args, **kwargs):
|
|
|
|
"""Initializer.
|
|
|
|
|
|
|
|
:param provides: Provided type.
|
|
|
|
:type provides: type
|
|
|
|
"""
|
|
|
|
if not contextvars:
|
|
|
|
raise RuntimeError(
|
2022-01-17 04:32:42 +03:00
|
|
|
"Contextvars library not found. This provider "
|
|
|
|
"requires Python 3.7 or a backport of contextvars. "
|
|
|
|
"To install a backport run \"pip install contextvars\"."
|
2021-04-19 04:37:55 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
super(ContextLocalSingleton, self).__init__(provides, *args, **kwargs)
|
2022-01-17 04:32:42 +03:00
|
|
|
self.__storage = contextvars.ContextVar("__storage", default=self._none)
|
2021-04-19 04:37:55 +03:00
|
|
|
|
|
|
|
def reset(self):
|
|
|
|
"""Reset cached instance, if any.
|
|
|
|
|
|
|
|
:rtype: None
|
|
|
|
"""
|
|
|
|
instance = self.__storage.get()
|
|
|
|
if instance is self._none:
|
|
|
|
return SingletonResetContext(self)
|
|
|
|
|
|
|
|
if __is_future_or_coroutine(instance):
|
|
|
|
asyncio.ensure_future(instance).cancel()
|
|
|
|
|
|
|
|
self.__storage.set(self._none)
|
|
|
|
|
|
|
|
return SingletonResetContext(self)
|
|
|
|
|
|
|
|
cpdef object _provide(self, tuple args, dict kwargs):
|
|
|
|
"""Return single instance."""
|
|
|
|
cdef object instance
|
|
|
|
|
|
|
|
instance = self.__storage.get()
|
|
|
|
|
|
|
|
if instance is self._none:
|
|
|
|
instance = __factory_call(self.__instantiator, args, kwargs)
|
|
|
|
|
|
|
|
if __is_future_or_coroutine(instance):
|
|
|
|
future_result = asyncio.Future()
|
|
|
|
instance = asyncio.ensure_future(instance)
|
|
|
|
instance.add_done_callback(functools.partial(self._async_init_instance, future_result))
|
|
|
|
self.__storage.set(future_result)
|
|
|
|
return future_result
|
|
|
|
|
|
|
|
self.__storage.set(instance)
|
|
|
|
|
|
|
|
return instance
|
|
|
|
|
|
|
|
def _async_init_instance(self, future_result, result):
|
|
|
|
try:
|
|
|
|
instance = result.result()
|
|
|
|
except Exception as exception:
|
|
|
|
self.__storage.set(self._none)
|
|
|
|
future_result.set_exception(exception)
|
|
|
|
else:
|
|
|
|
self.__storage.set(instance)
|
|
|
|
future_result.set_result(instance)
|
|
|
|
|
|
|
|
|
2017-03-25 23:38:48 +03:00
|
|
|
cdef class DelegatedThreadLocalSingleton(ThreadLocalSingleton):
|
|
|
|
"""Delegated thread-local singleton is injected "as is".
|
|
|
|
|
|
|
|
.. py:attribute:: provided_type
|
|
|
|
|
|
|
|
If provided type is defined, provider checks that providing class is
|
|
|
|
its subclass.
|
|
|
|
|
|
|
|
:type: type | None
|
|
|
|
|
|
|
|
.. py:attribute:: cls
|
2020-06-17 05:22:06 +03:00
|
|
|
:noindex:
|
2017-03-25 23:38:48 +03:00
|
|
|
|
|
|
|
Class that provides object.
|
|
|
|
Alias for :py:attr:`provides`.
|
|
|
|
|
|
|
|
:type: type
|
|
|
|
"""
|
|
|
|
|
|
|
|
__IS_DELEGATED__ = True
|
|
|
|
|
|
|
|
|
2017-04-06 18:17:06 +03:00
|
|
|
cdef class AbstractSingleton(BaseSingleton):
|
|
|
|
"""Abstract singleton provider.
|
|
|
|
|
|
|
|
:py:class:`AbstractSingleton` is a :py:class:`Singleton` provider that must
|
|
|
|
be explicitly overridden before calling.
|
|
|
|
|
|
|
|
Overriding of :py:class:`AbstractSingleton` is possible only by another
|
|
|
|
:py:class:`BaseSingleton` provider.
|
|
|
|
"""
|
|
|
|
|
|
|
|
def __call__(self, *args, **kwargs):
|
|
|
|
"""Return provided object.
|
|
|
|
|
|
|
|
Callable interface implementation.
|
|
|
|
"""
|
|
|
|
if self.__last_overriding is None:
|
2022-01-17 04:32:42 +03:00
|
|
|
raise Error("{0} must be overridden before calling".format(self))
|
2021-01-11 03:26:15 +03:00
|
|
|
return super().__call__(*args, **kwargs)
|
2017-04-06 18:17:06 +03:00
|
|
|
|
|
|
|
def override(self, provider):
|
|
|
|
"""Override provider with another provider.
|
|
|
|
|
|
|
|
:param provider: Overriding provider.
|
|
|
|
:type provider: :py:class:`Provider`
|
|
|
|
|
|
|
|
:raise: :py:exc:`dependency_injector.errors.Error`
|
|
|
|
|
|
|
|
:return: Overriding context.
|
|
|
|
:rtype: :py:class:`OverridingContext`
|
|
|
|
"""
|
|
|
|
if not isinstance(provider, BaseSingleton):
|
2022-01-17 04:32:42 +03:00
|
|
|
raise Error("{0} must be overridden only by "
|
|
|
|
"{1} providers".format(self, BaseSingleton))
|
2017-04-06 18:17:06 +03:00
|
|
|
return super(AbstractSingleton, self).override(provider)
|
|
|
|
|
|
|
|
def reset(self):
|
|
|
|
"""Reset cached instance, if any.
|
|
|
|
|
|
|
|
:rtype: None
|
|
|
|
"""
|
|
|
|
if self.__last_overriding is None:
|
2022-01-17 04:32:42 +03:00
|
|
|
raise Error("{0} must be overridden before calling".format(self))
|
2017-04-06 18:17:06 +03:00
|
|
|
return self.__last_overriding.reset()
|
|
|
|
|
|
|
|
|
2017-07-09 23:34:14 +03:00
|
|
|
cdef class SingletonDelegate(Delegate):
|
|
|
|
"""Singleton delegate injects delegating singleton "as is".
|
|
|
|
|
|
|
|
.. py:attribute:: provides
|
|
|
|
|
|
|
|
Value that have to be provided.
|
|
|
|
|
|
|
|
:type: object
|
|
|
|
"""
|
|
|
|
|
|
|
|
def __init__(self, singleton):
|
|
|
|
"""Initializer.
|
|
|
|
|
|
|
|
:param singleton: Value that have to be provided.
|
|
|
|
:type singleton: py:class:`BaseSingleton`
|
|
|
|
"""
|
|
|
|
if isinstance(singleton, BaseSingleton) is False:
|
2022-01-17 04:32:42 +03:00
|
|
|
raise Error("{0} can wrap only {1} providers".format(
|
2017-07-09 23:34:14 +03:00
|
|
|
self.__class__, BaseSingleton))
|
2019-10-09 17:45:14 +03:00
|
|
|
super(SingletonDelegate, self).__init__(singleton)
|
2017-07-09 23:34:14 +03:00
|
|
|
|
|
|
|
|
2020-06-15 00:32:12 +03:00
|
|
|
cdef class List(Provider):
|
|
|
|
"""List provider provides a list of values.
|
|
|
|
|
|
|
|
:py:class:`List` provider is needed for injecting a list of dependencies. It handles
|
|
|
|
positional argument injections the same way as :py:class:`Factory` provider.
|
|
|
|
|
|
|
|
Keyword argument injections are not supported.
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
dispatcher_factory = Factory(
|
|
|
|
Dispatcher,
|
|
|
|
modules=List(
|
|
|
|
Factory(ModuleA, dependency_a),
|
|
|
|
Factory(ModuleB, dependency_b),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
|
|
|
|
dispatcher = dispatcher_factory()
|
|
|
|
|
|
|
|
# is equivalent to:
|
|
|
|
|
|
|
|
dispatcher = Dispatcher(
|
|
|
|
modules=[
|
|
|
|
ModuleA(dependency_a),
|
|
|
|
ModuleB(dependency_b),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
"""
|
|
|
|
|
|
|
|
def __init__(self, *args):
|
|
|
|
"""Initializer."""
|
|
|
|
self.__args = tuple()
|
|
|
|
self.__args_len = 0
|
|
|
|
self.set_args(*args)
|
|
|
|
super(List, self).__init__()
|
|
|
|
|
|
|
|
def __deepcopy__(self, memo):
|
|
|
|
"""Create and return full copy of provider."""
|
|
|
|
copied = memo.get(id(self))
|
|
|
|
if copied is not None:
|
|
|
|
return copied
|
|
|
|
|
2021-03-21 04:41:39 +03:00
|
|
|
copied = _memorized_duplicate(self, memo)
|
|
|
|
copied.set_args(*deepcopy(self.args, memo))
|
2020-06-15 00:32:12 +03:00
|
|
|
self._copy_overridings(copied, memo)
|
|
|
|
return copied
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
"""Return string representation of provider.
|
|
|
|
|
|
|
|
:rtype: str
|
|
|
|
"""
|
|
|
|
return represent_provider(provider=self, provides=list(self.args))
|
|
|
|
|
|
|
|
@property
|
|
|
|
def args(self):
|
|
|
|
"""Return positional argument injections."""
|
|
|
|
cdef int index
|
|
|
|
cdef PositionalInjection arg
|
|
|
|
cdef list args
|
|
|
|
|
|
|
|
args = list()
|
|
|
|
for index in range(self.__args_len):
|
|
|
|
arg = self.__args[index]
|
|
|
|
args.append(arg.__value)
|
|
|
|
return tuple(args)
|
|
|
|
|
|
|
|
def add_args(self, *args):
|
|
|
|
"""Add positional argument injections.
|
|
|
|
|
|
|
|
:return: Reference ``self``
|
|
|
|
"""
|
|
|
|
self.__args += parse_positional_injections(args)
|
|
|
|
self.__args_len = len(self.__args)
|
|
|
|
return self
|
|
|
|
|
|
|
|
def set_args(self, *args):
|
|
|
|
"""Set positional argument injections.
|
|
|
|
|
|
|
|
Existing positional argument injections are dropped.
|
|
|
|
|
|
|
|
:return: Reference ``self``
|
|
|
|
"""
|
|
|
|
self.__args = parse_positional_injections(args)
|
|
|
|
self.__args_len = len(self.__args)
|
|
|
|
return self
|
|
|
|
|
|
|
|
def clear_args(self):
|
|
|
|
"""Drop positional argument injections.
|
|
|
|
|
|
|
|
:return: Reference ``self``
|
|
|
|
"""
|
|
|
|
self.__args = tuple()
|
|
|
|
self.__args_len = len(self.__args)
|
|
|
|
return self
|
|
|
|
|
2021-02-01 17:42:21 +03:00
|
|
|
@property
|
|
|
|
def related(self):
|
|
|
|
"""Return related providers generator."""
|
|
|
|
yield from filter(is_provider, self.args)
|
|
|
|
yield from super().related
|
|
|
|
|
2020-06-15 00:32:12 +03:00
|
|
|
cpdef object _provide(self, tuple args, dict kwargs):
|
2022-01-17 04:32:42 +03:00
|
|
|
"""Return result of provided callable call."""
|
2021-04-25 20:45:34 +03:00
|
|
|
return __provide_positional_args(args, self.__args, self.__args_len)
|
2020-06-15 00:32:12 +03:00
|
|
|
|
|
|
|
|
2020-10-22 21:49:39 +03:00
|
|
|
cdef class Dict(Provider):
|
|
|
|
"""Dict provider provides a dictionary of values.
|
|
|
|
|
|
|
|
:py:class:`Dict` provider is needed for injecting a dictionary of dependencies. It handles
|
|
|
|
keyword argument injections the same way as :py:class:`Factory` provider.
|
|
|
|
|
|
|
|
Positional argument injections are not supported.
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
dispatcher_factory = Factory(
|
|
|
|
Dispatcher,
|
|
|
|
modules=Dict(
|
|
|
|
module1=Factory(ModuleA, dependency_a),
|
|
|
|
module2=Factory(ModuleB, dependency_b),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
|
|
|
|
dispatcher = dispatcher_factory()
|
|
|
|
|
|
|
|
# is equivalent to:
|
|
|
|
|
|
|
|
dispatcher = Dispatcher(
|
|
|
|
modules={
|
2022-01-17 04:32:42 +03:00
|
|
|
"module1": ModuleA(dependency_a),
|
|
|
|
"module2": ModuleB(dependency_b),
|
2020-10-22 21:49:39 +03:00
|
|
|
},
|
|
|
|
)
|
|
|
|
"""
|
|
|
|
|
2020-11-21 01:30:42 +03:00
|
|
|
def __init__(self, dict_=None, **kwargs):
|
2020-10-22 21:49:39 +03:00
|
|
|
"""Initializer."""
|
|
|
|
self.__kwargs = tuple()
|
|
|
|
self.__kwargs_len = 0
|
2020-11-21 01:30:42 +03:00
|
|
|
self.add_kwargs(dict_, **kwargs)
|
2020-10-22 21:49:39 +03:00
|
|
|
super(Dict, self).__init__()
|
|
|
|
|
|
|
|
def __deepcopy__(self, memo):
|
|
|
|
"""Create and return full copy of provider."""
|
|
|
|
copied = memo.get(id(self))
|
|
|
|
if copied is not None:
|
|
|
|
return copied
|
|
|
|
|
2021-03-21 04:41:39 +03:00
|
|
|
copied = _memorized_duplicate(self, memo)
|
2021-03-30 17:25:45 +03:00
|
|
|
self._copy_kwargs(copied, memo)
|
2020-10-22 21:49:39 +03:00
|
|
|
self._copy_overridings(copied, memo)
|
|
|
|
return copied
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
"""Return string representation of provider.
|
|
|
|
|
|
|
|
:rtype: str
|
|
|
|
"""
|
2020-10-25 03:56:32 +03:00
|
|
|
return represent_provider(provider=self, provides=self.kwargs)
|
2020-10-22 21:49:39 +03:00
|
|
|
|
|
|
|
@property
|
|
|
|
def kwargs(self):
|
|
|
|
"""Return keyword argument injections."""
|
|
|
|
cdef int index
|
|
|
|
cdef NamedInjection kwarg
|
|
|
|
cdef dict kwargs
|
|
|
|
|
|
|
|
kwargs = dict()
|
|
|
|
for index in range(self.__kwargs_len):
|
|
|
|
kwarg = self.__kwargs[index]
|
|
|
|
kwargs[kwarg.__name] = kwarg.__value
|
|
|
|
return kwargs
|
|
|
|
|
2020-11-21 01:30:42 +03:00
|
|
|
def add_kwargs(self, dict_=None, **kwargs):
|
2020-10-22 21:49:39 +03:00
|
|
|
"""Add keyword argument injections.
|
|
|
|
|
|
|
|
:return: Reference ``self``
|
|
|
|
"""
|
2020-11-21 01:30:42 +03:00
|
|
|
if dict_ is None:
|
|
|
|
dict_ = {}
|
|
|
|
|
|
|
|
self.__kwargs += parse_named_injections(dict_)
|
2020-10-22 21:49:39 +03:00
|
|
|
self.__kwargs += parse_named_injections(kwargs)
|
|
|
|
self.__kwargs_len = len(self.__kwargs)
|
2020-11-21 01:30:42 +03:00
|
|
|
|
2020-10-22 21:49:39 +03:00
|
|
|
return self
|
|
|
|
|
2020-11-21 01:30:42 +03:00
|
|
|
def set_kwargs(self, dict_=None, **kwargs):
|
2020-10-22 21:49:39 +03:00
|
|
|
"""Set keyword argument injections.
|
|
|
|
|
|
|
|
Existing keyword argument injections are dropped.
|
|
|
|
|
|
|
|
:return: Reference ``self``
|
|
|
|
"""
|
2020-11-21 01:30:42 +03:00
|
|
|
if dict_ is None:
|
|
|
|
dict_ = {}
|
|
|
|
|
|
|
|
self.__kwargs = parse_named_injections(dict_)
|
|
|
|
self.__kwargs += parse_named_injections(kwargs)
|
2020-10-22 21:49:39 +03:00
|
|
|
self.__kwargs_len = len(self.__kwargs)
|
2020-11-21 01:30:42 +03:00
|
|
|
|
2020-10-22 21:49:39 +03:00
|
|
|
return self
|
|
|
|
|
|
|
|
def clear_kwargs(self):
|
|
|
|
"""Drop keyword argument injections.
|
|
|
|
|
|
|
|
:return: Reference ``self``
|
|
|
|
"""
|
|
|
|
self.__kwargs = tuple()
|
|
|
|
self.__kwargs_len = len(self.__kwargs)
|
|
|
|
return self
|
|
|
|
|
2021-02-01 17:42:21 +03:00
|
|
|
@property
|
|
|
|
def related(self):
|
|
|
|
"""Return related providers generator."""
|
|
|
|
yield from filter(is_provider, self.kwargs.values())
|
|
|
|
yield from super().related
|
|
|
|
|
2021-03-30 17:25:45 +03:00
|
|
|
def _copy_kwargs(self, copied, memo):
|
|
|
|
"""Return copy of kwargs."""
|
|
|
|
copied_kwargs = {
|
|
|
|
_copy_if_provider(name, memo): _copy_if_provider(value, memo)
|
|
|
|
for name, value in self.kwargs.items()
|
|
|
|
}
|
|
|
|
copied.set_kwargs(copied_kwargs)
|
|
|
|
|
2020-10-22 21:49:39 +03:00
|
|
|
cpdef object _provide(self, tuple args, dict kwargs):
|
2022-01-17 04:32:42 +03:00
|
|
|
"""Return result of provided callable call."""
|
2020-10-22 21:49:39 +03:00
|
|
|
return __provide_keyword_args(kwargs, self.__kwargs, self.__kwargs_len)
|
|
|
|
|
|
|
|
|
2021-03-30 17:25:45 +03:00
|
|
|
|
2020-10-25 03:56:32 +03:00
|
|
|
cdef class Resource(Provider):
|
|
|
|
"""Resource provider provides a component with initialization and shutdown."""
|
|
|
|
|
2021-03-20 20:16:51 +03:00
|
|
|
def __init__(self, provides=None, *args, **kwargs):
|
|
|
|
self.__provides = None
|
|
|
|
self.set_provides(provides)
|
|
|
|
|
2020-10-25 03:56:32 +03:00
|
|
|
self.__initialized = False
|
|
|
|
self.__resource = None
|
|
|
|
self.__shutdowner = None
|
|
|
|
|
|
|
|
self.__args = tuple()
|
|
|
|
self.__args_len = 0
|
|
|
|
self.set_args(*args)
|
|
|
|
|
|
|
|
self.__kwargs = tuple()
|
|
|
|
self.__kwargs_len = 0
|
|
|
|
self.set_kwargs(**kwargs)
|
|
|
|
|
|
|
|
super().__init__()
|
|
|
|
|
|
|
|
def __deepcopy__(self, memo):
|
|
|
|
"""Create and return full copy of provider."""
|
|
|
|
copied = memo.get(id(self))
|
|
|
|
if copied is not None:
|
|
|
|
return copied
|
|
|
|
|
|
|
|
if self.__initialized:
|
2022-01-17 04:32:42 +03:00
|
|
|
raise Error("Can not copy initialized resource")
|
2020-10-25 03:56:32 +03:00
|
|
|
|
2021-03-20 20:16:51 +03:00
|
|
|
copied = _memorized_duplicate(self, memo)
|
|
|
|
copied.set_provides(_copy_if_provider(self.provides, memo))
|
|
|
|
copied.set_args(*deepcopy(self.args, memo))
|
|
|
|
copied.set_kwargs(**deepcopy(self.kwargs, memo))
|
|
|
|
|
2020-10-25 03:56:32 +03:00
|
|
|
self._copy_overridings(copied, memo)
|
|
|
|
|
|
|
|
return copied
|
|
|
|
|
2021-02-01 17:42:21 +03:00
|
|
|
def __str__(self):
|
|
|
|
"""Return string representation of provider.
|
|
|
|
|
|
|
|
:rtype: str
|
|
|
|
"""
|
2021-03-20 20:16:51 +03:00
|
|
|
return represent_provider(provider=self, provides=self.provides)
|
2021-02-01 17:42:21 +03:00
|
|
|
|
|
|
|
@property
|
2021-03-20 20:16:51 +03:00
|
|
|
def provides(self):
|
2022-01-17 04:32:42 +03:00
|
|
|
"""Return provider provides."""
|
2021-03-20 20:16:51 +03:00
|
|
|
return self.__provides
|
|
|
|
|
|
|
|
def set_provides(self, provides):
|
2022-01-17 04:32:42 +03:00
|
|
|
"""Set provider provides."""
|
2022-01-31 07:19:09 +03:00
|
|
|
provides = _resolve_string_import(provides)
|
2021-03-20 20:16:51 +03:00
|
|
|
self.__provides = provides
|
|
|
|
return self
|
2020-10-25 03:56:32 +03:00
|
|
|
|
|
|
|
@property
|
|
|
|
def args(self):
|
|
|
|
"""Return positional argument injections."""
|
|
|
|
cdef int index
|
|
|
|
cdef PositionalInjection arg
|
|
|
|
cdef list args
|
|
|
|
|
|
|
|
args = list()
|
|
|
|
for index in range(self.__args_len):
|
|
|
|
arg = self.__args[index]
|
|
|
|
args.append(arg.__value)
|
|
|
|
return tuple(args)
|
|
|
|
|
|
|
|
def add_args(self, *args):
|
|
|
|
"""Add positional argument injections.
|
|
|
|
|
|
|
|
:return: Reference ``self``
|
|
|
|
"""
|
|
|
|
self.__args += parse_positional_injections(args)
|
|
|
|
self.__args_len = len(self.__args)
|
|
|
|
return self
|
|
|
|
|
|
|
|
def set_args(self, *args):
|
|
|
|
"""Set positional argument injections.
|
|
|
|
|
|
|
|
Existing positional argument injections are dropped.
|
|
|
|
|
|
|
|
:return: Reference ``self``
|
|
|
|
"""
|
|
|
|
self.__args = parse_positional_injections(args)
|
|
|
|
self.__args_len = len(self.__args)
|
|
|
|
return self
|
|
|
|
|
|
|
|
def clear_args(self):
|
|
|
|
"""Drop positional argument injections.
|
|
|
|
|
|
|
|
:return: Reference ``self``
|
|
|
|
"""
|
|
|
|
self.__args = tuple()
|
|
|
|
self.__args_len = len(self.__args)
|
|
|
|
return self
|
|
|
|
|
|
|
|
@property
|
|
|
|
def kwargs(self):
|
|
|
|
"""Return keyword argument injections."""
|
|
|
|
cdef int index
|
|
|
|
cdef NamedInjection kwarg
|
|
|
|
cdef dict kwargs
|
|
|
|
|
|
|
|
kwargs = dict()
|
|
|
|
for index in range(self.__kwargs_len):
|
|
|
|
kwarg = self.__kwargs[index]
|
|
|
|
kwargs[kwarg.__name] = kwarg.__value
|
|
|
|
return kwargs
|
|
|
|
|
|
|
|
def add_kwargs(self, **kwargs):
|
|
|
|
"""Add keyword argument injections.
|
|
|
|
|
|
|
|
:return: Reference ``self``
|
|
|
|
"""
|
|
|
|
self.__kwargs += parse_named_injections(kwargs)
|
|
|
|
self.__kwargs_len = len(self.__kwargs)
|
|
|
|
return self
|
|
|
|
|
|
|
|
def set_kwargs(self, **kwargs):
|
|
|
|
"""Set keyword argument injections.
|
|
|
|
|
|
|
|
Existing keyword argument injections are dropped.
|
|
|
|
|
|
|
|
:return: Reference ``self``
|
|
|
|
"""
|
|
|
|
self.__kwargs = parse_named_injections(kwargs)
|
|
|
|
self.__kwargs_len = len(self.__kwargs)
|
|
|
|
return self
|
|
|
|
|
|
|
|
def clear_kwargs(self):
|
|
|
|
"""Drop keyword argument injections.
|
|
|
|
|
|
|
|
:return: Reference ``self``
|
|
|
|
"""
|
|
|
|
self.__kwargs = tuple()
|
|
|
|
self.__kwargs_len = len(self.__kwargs)
|
|
|
|
return self
|
|
|
|
|
|
|
|
@property
|
|
|
|
def initialized(self):
|
|
|
|
"""Check if resource is initialized."""
|
|
|
|
return self.__initialized
|
|
|
|
|
|
|
|
def init(self):
|
|
|
|
"""Initialize resource."""
|
|
|
|
return self.__call__()
|
|
|
|
|
|
|
|
def shutdown(self):
|
|
|
|
"""Shutdown resource."""
|
|
|
|
if not self.__initialized:
|
2021-01-11 03:26:15 +03:00
|
|
|
if self.is_async_mode_enabled():
|
|
|
|
result = asyncio.Future()
|
|
|
|
result.set_result(None)
|
|
|
|
return result
|
2020-10-25 03:56:32 +03:00
|
|
|
return
|
|
|
|
|
|
|
|
if self.__shutdowner:
|
|
|
|
try:
|
2021-01-11 03:26:15 +03:00
|
|
|
shutdown = self.__shutdowner(self.__resource)
|
2020-10-25 03:56:32 +03:00
|
|
|
except StopIteration:
|
|
|
|
pass
|
2021-01-11 03:26:15 +03:00
|
|
|
else:
|
|
|
|
if inspect.isawaitable(shutdown):
|
|
|
|
return self._create_shutdown_future(shutdown)
|
2020-10-25 03:56:32 +03:00
|
|
|
|
|
|
|
self.__resource = None
|
|
|
|
self.__initialized = False
|
|
|
|
self.__shutdowner = None
|
|
|
|
|
2021-01-11 03:26:15 +03:00
|
|
|
if self.is_async_mode_enabled():
|
|
|
|
result = asyncio.Future()
|
|
|
|
result.set_result(None)
|
|
|
|
return result
|
|
|
|
|
2021-02-01 17:42:21 +03:00
|
|
|
@property
|
|
|
|
def related(self):
|
|
|
|
"""Return related providers generator."""
|
2021-03-20 20:16:51 +03:00
|
|
|
yield from filter(is_provider, [self.provides])
|
2021-02-01 17:42:21 +03:00
|
|
|
yield from filter(is_provider, self.args)
|
|
|
|
yield from filter(is_provider, self.kwargs.values())
|
|
|
|
yield from super().related
|
|
|
|
|
2020-10-25 03:56:32 +03:00
|
|
|
cpdef object _provide(self, tuple args, dict kwargs):
|
|
|
|
if self.__initialized:
|
|
|
|
return self.__resource
|
|
|
|
|
2021-03-20 20:16:51 +03:00
|
|
|
if self._is_resource_subclass(self.__provides):
|
|
|
|
initializer = self.__provides()
|
2020-10-25 03:56:32 +03:00
|
|
|
self.__resource = __call(
|
|
|
|
initializer.init,
|
|
|
|
args,
|
|
|
|
self.__args,
|
|
|
|
self.__args_len,
|
|
|
|
kwargs,
|
|
|
|
self.__kwargs,
|
|
|
|
self.__kwargs_len,
|
|
|
|
)
|
|
|
|
self.__shutdowner = initializer.shutdown
|
2021-03-20 20:16:51 +03:00
|
|
|
elif self._is_async_resource_subclass(self.__provides):
|
|
|
|
initializer = self.__provides()
|
2021-01-11 03:26:15 +03:00
|
|
|
async_init = __call(
|
|
|
|
initializer.init,
|
|
|
|
args,
|
|
|
|
self.__args,
|
|
|
|
self.__args_len,
|
|
|
|
kwargs,
|
|
|
|
self.__kwargs,
|
|
|
|
self.__kwargs_len,
|
|
|
|
)
|
|
|
|
self.__initialized = True
|
|
|
|
return self._create_init_future(async_init, initializer.shutdown)
|
2021-03-20 20:16:51 +03:00
|
|
|
elif inspect.isgeneratorfunction(self.__provides):
|
2020-10-25 03:56:32 +03:00
|
|
|
initializer = __call(
|
2021-03-20 20:16:51 +03:00
|
|
|
self.__provides,
|
2020-10-25 03:56:32 +03:00
|
|
|
args,
|
|
|
|
self.__args,
|
|
|
|
self.__args_len,
|
|
|
|
kwargs,
|
|
|
|
self.__kwargs,
|
|
|
|
self.__kwargs_len,
|
|
|
|
)
|
|
|
|
self.__resource = next(initializer)
|
|
|
|
self.__shutdowner = initializer.send
|
2021-03-20 20:16:51 +03:00
|
|
|
elif iscoroutinefunction(self.__provides):
|
2021-01-11 03:26:15 +03:00
|
|
|
initializer = __call(
|
2021-03-20 20:16:51 +03:00
|
|
|
self.__provides,
|
2021-01-11 03:26:15 +03:00
|
|
|
args,
|
|
|
|
self.__args,
|
|
|
|
self.__args_len,
|
|
|
|
kwargs,
|
|
|
|
self.__kwargs,
|
|
|
|
self.__kwargs_len,
|
|
|
|
)
|
|
|
|
self.__initialized = True
|
|
|
|
return self._create_init_future(initializer)
|
2021-03-20 20:16:51 +03:00
|
|
|
elif isasyncgenfunction(self.__provides):
|
2021-01-11 03:26:15 +03:00
|
|
|
initializer = __call(
|
2021-03-20 20:16:51 +03:00
|
|
|
self.__provides,
|
2021-01-11 03:26:15 +03:00
|
|
|
args,
|
|
|
|
self.__args,
|
|
|
|
self.__args_len,
|
|
|
|
kwargs,
|
|
|
|
self.__kwargs,
|
|
|
|
self.__kwargs_len,
|
|
|
|
)
|
|
|
|
self.__initialized = True
|
2021-01-20 01:47:25 +03:00
|
|
|
return self._create_async_gen_init_future(initializer)
|
2021-03-20 20:16:51 +03:00
|
|
|
elif callable(self.__provides):
|
2020-10-25 03:56:32 +03:00
|
|
|
self.__resource = __call(
|
2021-03-20 20:16:51 +03:00
|
|
|
self.__provides,
|
2020-10-25 03:56:32 +03:00
|
|
|
args,
|
|
|
|
self.__args,
|
|
|
|
self.__args_len,
|
|
|
|
kwargs,
|
|
|
|
self.__kwargs,
|
|
|
|
self.__kwargs_len,
|
|
|
|
)
|
|
|
|
else:
|
2022-01-17 04:32:42 +03:00
|
|
|
raise Error("Unknown type of resource initializer")
|
2020-10-25 03:56:32 +03:00
|
|
|
|
|
|
|
self.__initialized = True
|
|
|
|
return self.__resource
|
|
|
|
|
2021-01-11 03:26:15 +03:00
|
|
|
def _create_init_future(self, future, shutdowner=None):
|
|
|
|
callback = self._async_init_callback
|
|
|
|
if shutdowner:
|
|
|
|
callback = functools.partial(callback, shutdowner=shutdowner)
|
|
|
|
|
|
|
|
future = asyncio.ensure_future(future)
|
|
|
|
future.add_done_callback(callback)
|
|
|
|
self.__resource = future
|
|
|
|
|
|
|
|
return future
|
|
|
|
|
2021-01-20 01:47:25 +03:00
|
|
|
def _create_async_gen_init_future(self, initializer):
|
|
|
|
if inspect.isasyncgen(initializer):
|
|
|
|
return self._create_init_future(initializer.__anext__(), initializer.asend)
|
|
|
|
|
|
|
|
future = asyncio.Future()
|
|
|
|
|
|
|
|
create_initializer = asyncio.ensure_future(initializer)
|
|
|
|
create_initializer.add_done_callback(functools.partial(self._async_create_gen_callback, future))
|
|
|
|
self.__resource = future
|
|
|
|
|
|
|
|
return future
|
|
|
|
|
2021-01-11 03:26:15 +03:00
|
|
|
def _async_init_callback(self, initializer, shutdowner=None):
|
|
|
|
try:
|
|
|
|
resource = initializer.result()
|
|
|
|
except Exception:
|
|
|
|
self.__initialized = False
|
|
|
|
else:
|
|
|
|
self.__resource = resource
|
|
|
|
self.__shutdowner = shutdowner
|
|
|
|
|
2021-01-20 01:47:25 +03:00
|
|
|
def _async_create_gen_callback(self, future, initializer_future):
|
|
|
|
initializer = initializer_future.result()
|
|
|
|
init_future = self._create_init_future(initializer.__anext__(), initializer.asend)
|
|
|
|
init_future.add_done_callback(functools.partial(self._async_trigger_result, future))
|
|
|
|
|
|
|
|
def _async_trigger_result(self, future, future_result):
|
|
|
|
future.set_result(future_result.result())
|
|
|
|
|
2021-01-11 03:26:15 +03:00
|
|
|
def _create_shutdown_future(self, shutdown_future):
|
|
|
|
future = asyncio.Future()
|
|
|
|
shutdown_future = asyncio.ensure_future(shutdown_future)
|
|
|
|
shutdown_future.add_done_callback(functools.partial(self._async_shutdown_callback, future))
|
|
|
|
return future
|
|
|
|
|
|
|
|
def _async_shutdown_callback(self, future_result, shutdowner):
|
|
|
|
try:
|
|
|
|
shutdowner.result()
|
|
|
|
except StopAsyncIteration:
|
|
|
|
pass
|
|
|
|
|
|
|
|
self.__resource = None
|
|
|
|
self.__initialized = False
|
|
|
|
self.__shutdowner = None
|
|
|
|
|
|
|
|
future_result.set_result(None)
|
|
|
|
|
2020-10-25 03:56:32 +03:00
|
|
|
@staticmethod
|
|
|
|
def _is_resource_subclass(instance):
|
|
|
|
if sys.version_info < (3, 5):
|
|
|
|
return False
|
|
|
|
if not isinstance(instance, CLASS_TYPES):
|
|
|
|
return
|
|
|
|
from . import resources
|
|
|
|
return issubclass(instance, resources.Resource)
|
|
|
|
|
2021-01-11 03:26:15 +03:00
|
|
|
@staticmethod
|
|
|
|
def _is_async_resource_subclass(instance):
|
|
|
|
if sys.version_info < (3, 5):
|
|
|
|
return False
|
|
|
|
if not isinstance(instance, CLASS_TYPES):
|
|
|
|
return
|
|
|
|
from . import resources
|
|
|
|
return issubclass(instance, resources.AsyncResource)
|
|
|
|
|
2020-10-25 03:56:32 +03:00
|
|
|
|
2020-06-23 05:45:16 +03:00
|
|
|
cdef class Container(Provider):
|
|
|
|
"""Container provider provides an instance of declarative container.
|
|
|
|
|
|
|
|
.. warning::
|
|
|
|
Provider is experimental. Its interface may change.
|
|
|
|
"""
|
|
|
|
|
2021-03-21 04:41:39 +03:00
|
|
|
def __init__(self, container_cls=None, container=None, **overriding_providers):
|
2020-06-23 05:45:16 +03:00
|
|
|
"""Initialize provider."""
|
2020-10-09 22:16:27 +03:00
|
|
|
self.__container_cls = container_cls
|
|
|
|
self.__overriding_providers = overriding_providers
|
2020-06-23 05:45:16 +03:00
|
|
|
|
2021-03-21 04:41:39 +03:00
|
|
|
if container is None and container_cls:
|
2020-06-23 05:45:16 +03:00
|
|
|
container = container_cls()
|
2021-02-13 17:16:38 +03:00
|
|
|
container.assign_parent(self)
|
2020-10-09 22:16:27 +03:00
|
|
|
self.__container = container
|
2020-06-23 05:45:16 +03:00
|
|
|
|
2021-02-13 17:16:38 +03:00
|
|
|
if self.__container and self.__overriding_providers:
|
|
|
|
self.apply_overridings()
|
|
|
|
|
|
|
|
self.__parent = None
|
2021-01-27 22:01:33 +03:00
|
|
|
|
2020-06-23 05:45:16 +03:00
|
|
|
super(Container, self).__init__()
|
|
|
|
|
|
|
|
def __deepcopy__(self, memo):
|
|
|
|
"""Create and return full copy of provider."""
|
2021-02-13 17:16:38 +03:00
|
|
|
cdef Container copied
|
|
|
|
|
2020-06-23 05:45:16 +03:00
|
|
|
copied = memo.get(id(self))
|
|
|
|
if copied is not None:
|
|
|
|
return copied
|
|
|
|
|
2021-03-21 04:41:39 +03:00
|
|
|
copied = <Container> _memorized_duplicate(self, memo)
|
|
|
|
copied.__container_cls = self.__container_cls
|
2021-02-13 17:16:38 +03:00
|
|
|
copied.__container = deepcopy(self.__container, memo)
|
|
|
|
copied.__overriding_providers = deepcopy(self.__overriding_providers, memo)
|
|
|
|
self._copy_parent(copied, memo)
|
2021-03-21 04:41:39 +03:00
|
|
|
self._copy_overridings(copied, memo)
|
2020-06-23 05:45:16 +03:00
|
|
|
return copied
|
|
|
|
|
|
|
|
def __getattr__(self, name):
|
|
|
|
"""Return dependency provider."""
|
2022-01-17 04:32:42 +03:00
|
|
|
if name.startswith("__") and name.endswith("__"):
|
2020-06-23 05:45:16 +03:00
|
|
|
raise AttributeError(
|
2022-01-17 04:32:42 +03:00
|
|
|
"'{cls}' object has no attribute "
|
|
|
|
"'{attribute_name}'".format(cls=self.__class__.__name__, attribute_name=name))
|
2020-10-09 22:16:27 +03:00
|
|
|
return getattr(self.__container, name)
|
|
|
|
|
2021-01-12 16:41:59 +03:00
|
|
|
@property
|
|
|
|
def providers(self):
|
|
|
|
return self.__container.providers
|
|
|
|
|
2020-10-09 22:16:27 +03:00
|
|
|
@property
|
|
|
|
def container(self):
|
|
|
|
return self.__container
|
2020-06-23 05:45:16 +03:00
|
|
|
|
|
|
|
def override(self, provider):
|
|
|
|
"""Override provider with another provider."""
|
2022-01-17 04:32:42 +03:00
|
|
|
if not hasattr(provider, "providers"):
|
|
|
|
raise Error("Container provider {0} can be overridden only by providers container".format(self))
|
2021-01-12 16:41:59 +03:00
|
|
|
|
2021-01-13 17:11:24 +03:00
|
|
|
self.__container.override_providers(**provider.providers)
|
2021-03-03 17:05:15 +03:00
|
|
|
return super().override(provider)
|
|
|
|
|
|
|
|
def reset_last_overriding(self):
|
|
|
|
"""Reset last overriding provider.
|
|
|
|
|
|
|
|
:raise: :py:exc:`dependency_injector.errors.Error` if provider is not
|
|
|
|
overridden.
|
|
|
|
|
|
|
|
:rtype: None
|
|
|
|
"""
|
|
|
|
super().reset_last_overriding()
|
|
|
|
for provider in self.__container.providers.values():
|
|
|
|
if not provider.overridden:
|
|
|
|
continue
|
|
|
|
provider.reset_last_overriding()
|
|
|
|
|
|
|
|
def reset_override(self):
|
|
|
|
"""Reset all overriding providers.
|
|
|
|
|
|
|
|
:rtype: None
|
|
|
|
"""
|
|
|
|
super().reset_override()
|
|
|
|
for provider in self.__container.providers.values():
|
|
|
|
if not provider.overridden:
|
|
|
|
continue
|
|
|
|
provider.reset_override()
|
2020-06-23 05:45:16 +03:00
|
|
|
|
2021-01-14 01:07:41 +03:00
|
|
|
def apply_overridings(self):
|
|
|
|
"""Apply container overriding.
|
|
|
|
|
|
|
|
This method should not be called directly. It is called on
|
|
|
|
declarative container initialization."""
|
|
|
|
self.__container.override_providers(**self.__overriding_providers)
|
|
|
|
|
2021-02-01 17:42:21 +03:00
|
|
|
@property
|
|
|
|
def related(self):
|
|
|
|
"""Return related providers generator."""
|
|
|
|
yield from self.providers.values()
|
|
|
|
yield from super().related
|
|
|
|
|
2021-02-13 17:16:38 +03:00
|
|
|
def resolve_provider_name(self, provider):
|
|
|
|
"""Try to resolve provider name."""
|
|
|
|
for provider_name, container_provider in self.providers.items():
|
|
|
|
if container_provider is provider:
|
|
|
|
return provider_name
|
|
|
|
else:
|
2022-01-17 04:32:42 +03:00
|
|
|
raise Error(f"Can not resolve name for provider \"{provider}\"")
|
2021-02-13 17:16:38 +03:00
|
|
|
|
|
|
|
@property
|
|
|
|
def parent(self):
|
|
|
|
"""Return parent."""
|
|
|
|
return self.__parent
|
|
|
|
|
|
|
|
@property
|
|
|
|
def parent_name(self):
|
|
|
|
"""Return parent name."""
|
|
|
|
if not self.__parent:
|
|
|
|
return None
|
|
|
|
|
2022-01-17 04:32:42 +03:00
|
|
|
name = ""
|
2021-02-13 17:16:38 +03:00
|
|
|
if self.__parent.parent_name:
|
2022-01-17 04:32:42 +03:00
|
|
|
name += f"{self.__parent.parent_name}."
|
|
|
|
name += f"{self.__parent.resolve_provider_name(self)}"
|
2021-02-13 17:16:38 +03:00
|
|
|
|
|
|
|
return name
|
|
|
|
|
|
|
|
def assign_parent(self, parent):
|
|
|
|
"""Assign parent."""
|
|
|
|
self.__parent = parent
|
|
|
|
|
|
|
|
def _copy_parent(self, copied, memo):
|
|
|
|
_copy_parent(self, copied, memo)
|
|
|
|
|
2020-06-23 05:45:16 +03:00
|
|
|
cpdef object _provide(self, tuple args, dict kwargs):
|
|
|
|
"""Return single instance."""
|
2020-10-09 22:16:27 +03:00
|
|
|
return self.__container
|
2020-06-23 05:45:16 +03:00
|
|
|
|
|
|
|
|
2020-06-29 23:32:12 +03:00
|
|
|
cdef class Selector(Provider):
|
|
|
|
"""Selector provider selects provider based on the configuration value or other callable.
|
|
|
|
|
|
|
|
:py:class:`Selector` provider has a callable called ``selector`` and a dictionary of providers.
|
|
|
|
|
|
|
|
The ``selector`` callable is provided as a first positional argument. It can be
|
|
|
|
:py:class:`Configuration` provider or any other callable. It has to return a string value.
|
|
|
|
That value is used as a key for selecting the provider from the dictionary of providers.
|
|
|
|
|
|
|
|
The providers are provided as keyword arguments. Argument name is used as a key for
|
|
|
|
selecting the provider.
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
config = Configuration()
|
|
|
|
|
|
|
|
selector = Selector(
|
|
|
|
config.one_or_another,
|
|
|
|
one=providers.Factory(SomeClass),
|
|
|
|
another=providers.Factory(SomeOtherClass),
|
|
|
|
)
|
|
|
|
|
2022-01-17 04:32:42 +03:00
|
|
|
config.override({"one_or_another": "one"})
|
2020-06-29 23:32:12 +03:00
|
|
|
instance_1 = selector()
|
|
|
|
assert isinstance(instance_1, SomeClass)
|
|
|
|
|
2022-01-17 04:32:42 +03:00
|
|
|
config.override({"one_or_another": "another"})
|
2020-06-29 23:32:12 +03:00
|
|
|
instance_2 = selector()
|
|
|
|
assert isinstance(instance_2, SomeOtherClass)
|
|
|
|
"""
|
|
|
|
|
2021-03-20 20:16:51 +03:00
|
|
|
def __init__(self, selector=None, **providers):
|
2020-06-29 23:32:12 +03:00
|
|
|
"""Initialize provider."""
|
2021-03-20 20:16:51 +03:00
|
|
|
self.__selector = None
|
|
|
|
self.set_selector(selector)
|
|
|
|
|
|
|
|
self.__providers = {}
|
|
|
|
self.set_providers(**providers)
|
|
|
|
|
2020-06-29 23:32:12 +03:00
|
|
|
super(Selector, self).__init__()
|
|
|
|
|
|
|
|
def __deepcopy__(self, memo):
|
|
|
|
"""Create and return full copy of provider."""
|
|
|
|
copied = memo.get(id(self))
|
|
|
|
if copied is not None:
|
|
|
|
return copied
|
|
|
|
|
2021-03-20 20:16:51 +03:00
|
|
|
copied = _memorized_duplicate(self, memo)
|
|
|
|
copied.set_selector(deepcopy(self.__selector, memo))
|
|
|
|
copied.set_providers(**deepcopy(self.__providers, memo))
|
|
|
|
|
2020-06-29 23:32:12 +03:00
|
|
|
self._copy_overridings(copied, memo)
|
|
|
|
|
|
|
|
return copied
|
|
|
|
|
|
|
|
def __getattr__(self, name):
|
|
|
|
"""Return provider."""
|
2022-01-17 04:32:42 +03:00
|
|
|
if name.startswith("__") and name.endswith("__"):
|
2020-06-29 23:32:12 +03:00
|
|
|
raise AttributeError(
|
2022-01-17 04:32:42 +03:00
|
|
|
"'{cls}' object has no attribute "
|
|
|
|
"'{attribute_name}'".format(cls=self.__class__.__name__, attribute_name=name))
|
2020-06-29 23:32:12 +03:00
|
|
|
if name not in self.__providers:
|
2022-01-17 04:32:42 +03:00
|
|
|
raise AttributeError("Selector has no \"{0}\" provider".format(name))
|
2020-06-29 23:32:12 +03:00
|
|
|
|
|
|
|
return self.__providers[name]
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
"""Return string representation of provider.
|
|
|
|
|
|
|
|
:rtype: str
|
|
|
|
"""
|
|
|
|
|
2022-01-17 04:32:42 +03:00
|
|
|
return "<{provider}({selector}, {providers}) at {address}>".format(
|
|
|
|
provider=".".join(( self.__class__.__module__, self.__class__.__name__)),
|
2020-06-29 23:32:12 +03:00
|
|
|
selector=self.__selector,
|
2022-01-17 04:32:42 +03:00
|
|
|
providers=", ".join((
|
|
|
|
"{0}={1}".format(name, provider)
|
2020-06-29 23:32:12 +03:00
|
|
|
for name, provider in self.__providers.items()
|
|
|
|
)),
|
|
|
|
address=hex(id(self)),
|
|
|
|
)
|
|
|
|
|
2021-03-20 20:16:51 +03:00
|
|
|
@property
|
|
|
|
def selector(self):
|
|
|
|
"""Return selector."""
|
|
|
|
return self.__selector
|
|
|
|
|
|
|
|
def set_selector(self, selector):
|
|
|
|
"""Set selector."""
|
|
|
|
self.__selector = selector
|
|
|
|
return self
|
|
|
|
|
2020-06-29 23:32:12 +03:00
|
|
|
@property
|
|
|
|
def providers(self):
|
|
|
|
"""Return providers."""
|
|
|
|
return dict(self.__providers)
|
|
|
|
|
2021-03-20 20:16:51 +03:00
|
|
|
def set_providers(self, **providers: Provider):
|
|
|
|
"""Set providers."""
|
|
|
|
self.__providers = providers
|
|
|
|
return self
|
|
|
|
|
2021-02-01 17:42:21 +03:00
|
|
|
@property
|
|
|
|
def related(self):
|
|
|
|
"""Return related providers generator."""
|
|
|
|
yield from filter(is_provider, [self.__selector])
|
|
|
|
yield from self.providers.values()
|
|
|
|
yield from super().related
|
|
|
|
|
2020-06-29 23:32:12 +03:00
|
|
|
cpdef object _provide(self, tuple args, dict kwargs):
|
|
|
|
"""Return single instance."""
|
|
|
|
selector_value = self.__selector()
|
|
|
|
|
|
|
|
if selector_value is None:
|
2022-01-17 04:32:42 +03:00
|
|
|
raise Error("Selector value is undefined")
|
2020-06-29 23:32:12 +03:00
|
|
|
|
|
|
|
if selector_value not in self.__providers:
|
2022-01-17 04:32:42 +03:00
|
|
|
raise Error("Selector has no \"{0}\" provider".format(selector_value))
|
2020-06-29 23:32:12 +03:00
|
|
|
|
|
|
|
return self.__providers[selector_value](*args, **kwargs)
|
|
|
|
|
|
|
|
|
2020-08-21 04:52:12 +03:00
|
|
|
cdef class ProvidedInstance(Provider):
|
|
|
|
"""Provider that helps to inject attributes and items of the injected instance.
|
|
|
|
|
|
|
|
You can use it like that:
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
service = providers.Singleton(Service)
|
|
|
|
|
|
|
|
client_factory = providers.Factory(
|
|
|
|
Client,
|
|
|
|
value1=service.provided[0],
|
|
|
|
value2=service.provided.value,
|
|
|
|
value3=service.provided.values[0],
|
|
|
|
value4=service.provided.get_value.call(),
|
|
|
|
)
|
|
|
|
|
|
|
|
You should not create this provider directly. Get it from the ``.provided`` attribute of the
|
|
|
|
injected provider. This attribute returns the :py:class:`ProvidedInstance` for that provider.
|
|
|
|
|
|
|
|
Providers that have ``.provided`` attribute:
|
|
|
|
|
|
|
|
- :py:class:`Callable` and its subclasses
|
|
|
|
- :py:class:`Factory` and its subclasses
|
|
|
|
- :py:class:`Singleton` and its subclasses
|
|
|
|
- :py:class:`Object`
|
|
|
|
- :py:class:`List`
|
|
|
|
- :py:class:`Selector`
|
|
|
|
- :py:class:`Dependency`
|
|
|
|
"""
|
|
|
|
|
2021-03-20 20:16:51 +03:00
|
|
|
def __init__(self, provides=None):
|
|
|
|
self.__provides = None
|
|
|
|
self.set_provides(provides)
|
2020-08-21 04:52:12 +03:00
|
|
|
super().__init__()
|
|
|
|
|
|
|
|
def __repr__(self):
|
2022-01-17 04:32:42 +03:00
|
|
|
return f"{self.__class__.__name__}(\"{self.__provides}\")"
|
2020-08-21 04:52:12 +03:00
|
|
|
|
2021-03-20 20:16:51 +03:00
|
|
|
def __deepcopy__(self, memo):
|
2020-08-21 04:52:12 +03:00
|
|
|
copied = memo.get(id(self))
|
|
|
|
if copied is not None:
|
|
|
|
return copied
|
|
|
|
|
2021-03-20 20:16:51 +03:00
|
|
|
copied = _memorized_duplicate(self, memo)
|
|
|
|
copied.set_provides(_copy_if_provider(self.provides, memo))
|
|
|
|
return copied
|
2020-08-21 04:52:12 +03:00
|
|
|
|
|
|
|
def __getattr__(self, item):
|
|
|
|
return AttributeGetter(self, item)
|
|
|
|
|
|
|
|
def __getitem__(self, item):
|
|
|
|
return ItemGetter(self, item)
|
|
|
|
|
2020-10-09 22:16:27 +03:00
|
|
|
@property
|
|
|
|
def provides(self):
|
2022-01-17 04:32:42 +03:00
|
|
|
"""Return provider provides."""
|
2021-03-20 20:16:51 +03:00
|
|
|
return self.__provides
|
|
|
|
|
|
|
|
def set_provides(self, provides):
|
2022-01-17 04:32:42 +03:00
|
|
|
"""Set provider provides."""
|
2021-03-20 20:16:51 +03:00
|
|
|
self.__provides = provides
|
|
|
|
return self
|
2020-10-09 22:16:27 +03:00
|
|
|
|
2020-08-21 04:52:12 +03:00
|
|
|
def call(self, *args, **kwargs):
|
|
|
|
return MethodCaller(self, *args, **kwargs)
|
|
|
|
|
2021-02-01 17:42:21 +03:00
|
|
|
@property
|
|
|
|
def related(self):
|
|
|
|
"""Return related providers generator."""
|
2021-03-20 20:16:51 +03:00
|
|
|
if is_provider(self.provides):
|
|
|
|
yield self.provides
|
2021-02-01 17:42:21 +03:00
|
|
|
yield from super().related
|
|
|
|
|
2020-08-21 04:52:12 +03:00
|
|
|
cpdef object _provide(self, tuple args, dict kwargs):
|
2021-03-20 20:16:51 +03:00
|
|
|
return self.__provides(*args, **kwargs)
|
2020-08-21 04:52:12 +03:00
|
|
|
|
|
|
|
|
|
|
|
cdef class AttributeGetter(Provider):
|
|
|
|
"""Provider that returns the attribute of the injected instance.
|
|
|
|
|
|
|
|
You should not create this provider directly. See :py:class:`ProvidedInstance` instead.
|
|
|
|
"""
|
|
|
|
|
2021-03-20 20:16:51 +03:00
|
|
|
def __init__(self, provides=None, name=None):
|
|
|
|
self.__provides = None
|
|
|
|
self.set_provides(provides)
|
|
|
|
|
|
|
|
self.__name = None
|
|
|
|
self.set_name(name)
|
2020-08-21 04:52:12 +03:00
|
|
|
super().__init__()
|
|
|
|
|
|
|
|
def __repr__(self):
|
2022-01-17 04:32:42 +03:00
|
|
|
return f"{self.__class__.__name__}(\"{self.name}\")"
|
2020-08-21 04:52:12 +03:00
|
|
|
|
2021-03-20 20:16:51 +03:00
|
|
|
def __deepcopy__(self, memo):
|
2020-08-21 04:52:12 +03:00
|
|
|
copied = memo.get(id(self))
|
|
|
|
if copied is not None:
|
|
|
|
return copied
|
|
|
|
|
2021-03-20 20:16:51 +03:00
|
|
|
copied = _memorized_duplicate(self, memo)
|
|
|
|
copied.set_provides(_copy_if_provider(self.provides, memo))
|
|
|
|
copied.set_name(self.name)
|
|
|
|
return copied
|
2020-08-21 04:52:12 +03:00
|
|
|
|
|
|
|
def __getattr__(self, item):
|
|
|
|
return AttributeGetter(self, item)
|
|
|
|
|
|
|
|
def __getitem__(self, item):
|
|
|
|
return ItemGetter(self, item)
|
|
|
|
|
2020-10-09 22:16:27 +03:00
|
|
|
@property
|
|
|
|
def provides(self):
|
2022-01-17 04:32:42 +03:00
|
|
|
"""Return provider provides."""
|
2021-03-20 20:16:51 +03:00
|
|
|
return self.__provides
|
|
|
|
|
|
|
|
def set_provides(self, provides):
|
2022-01-17 04:32:42 +03:00
|
|
|
"""Set provider provides."""
|
2021-03-20 20:16:51 +03:00
|
|
|
self.__provides = provides
|
|
|
|
return self
|
2020-10-09 22:16:27 +03:00
|
|
|
|
|
|
|
@property
|
|
|
|
def name(self):
|
|
|
|
"""Return name of the attribute."""
|
2021-03-20 20:16:51 +03:00
|
|
|
return self.__name
|
|
|
|
|
|
|
|
def set_name(self, name):
|
|
|
|
"""Set name of the attribute."""
|
|
|
|
self.__name = name
|
|
|
|
return self
|
2020-10-09 22:16:27 +03:00
|
|
|
|
2020-08-21 04:52:12 +03:00
|
|
|
def call(self, *args, **kwargs):
|
|
|
|
return MethodCaller(self, *args, **kwargs)
|
|
|
|
|
2021-02-01 17:42:21 +03:00
|
|
|
@property
|
|
|
|
def related(self):
|
|
|
|
"""Return related providers generator."""
|
2021-03-20 20:16:51 +03:00
|
|
|
if is_provider(self.provides):
|
|
|
|
yield self.provides
|
2021-02-01 17:42:21 +03:00
|
|
|
yield from super().related
|
|
|
|
|
2020-08-21 04:52:12 +03:00
|
|
|
cpdef object _provide(self, tuple args, dict kwargs):
|
2021-03-20 20:16:51 +03:00
|
|
|
provided = self.provides(*args, **kwargs)
|
2021-02-17 17:56:39 +03:00
|
|
|
if __is_future_or_coroutine(provided):
|
2021-01-11 03:26:15 +03:00
|
|
|
future_result = asyncio.Future()
|
|
|
|
provided = asyncio.ensure_future(provided)
|
|
|
|
provided.add_done_callback(functools.partial(self._async_provide, future_result))
|
|
|
|
return future_result
|
2021-03-20 20:16:51 +03:00
|
|
|
return getattr(provided, self.name)
|
2020-08-21 04:52:12 +03:00
|
|
|
|
2021-01-11 03:26:15 +03:00
|
|
|
def _async_provide(self, future_result, future):
|
2021-02-17 17:56:39 +03:00
|
|
|
try:
|
|
|
|
provided = future.result()
|
2021-03-20 20:16:51 +03:00
|
|
|
result = getattr(provided, self.name)
|
|
|
|
except Exception as exception:
|
|
|
|
future_result.set_exception(exception)
|
2021-02-17 17:56:39 +03:00
|
|
|
else:
|
|
|
|
future_result.set_result(result)
|
2021-01-11 03:26:15 +03:00
|
|
|
|
2020-08-21 04:52:12 +03:00
|
|
|
|
|
|
|
cdef class ItemGetter(Provider):
|
|
|
|
"""Provider that returns the item of the injected instance.
|
|
|
|
|
|
|
|
You should not create this provider directly. See :py:class:`ProvidedInstance` instead.
|
|
|
|
"""
|
|
|
|
|
2021-03-20 20:16:51 +03:00
|
|
|
def __init__(self, provides=None, name=None):
|
|
|
|
self.__provides = None
|
|
|
|
self.set_provides(provides)
|
|
|
|
|
|
|
|
self.__name = None
|
|
|
|
self.set_name(name)
|
2020-08-21 04:52:12 +03:00
|
|
|
super().__init__()
|
|
|
|
|
|
|
|
def __repr__(self):
|
2022-01-17 04:32:42 +03:00
|
|
|
return f"{self.__class__.__name__}(\"{self.name}\")"
|
2020-08-21 04:52:12 +03:00
|
|
|
|
2021-03-20 20:16:51 +03:00
|
|
|
def __deepcopy__(self, memo):
|
2020-08-21 04:52:12 +03:00
|
|
|
copied = memo.get(id(self))
|
|
|
|
if copied is not None:
|
|
|
|
return copied
|
|
|
|
|
2021-03-20 20:16:51 +03:00
|
|
|
copied = _memorized_duplicate(self, memo)
|
|
|
|
copied.set_provides(_copy_if_provider(self.provides, memo))
|
|
|
|
copied.set_name(self.name)
|
|
|
|
return copied
|
2020-08-21 04:52:12 +03:00
|
|
|
|
|
|
|
def __getattr__(self, item):
|
|
|
|
return AttributeGetter(self, item)
|
|
|
|
|
|
|
|
def __getitem__(self, item):
|
|
|
|
return ItemGetter(self, item)
|
|
|
|
|
2020-10-09 22:16:27 +03:00
|
|
|
@property
|
|
|
|
def provides(self):
|
2022-01-17 04:32:42 +03:00
|
|
|
"""Return provider"s provides."""
|
2021-03-20 20:16:51 +03:00
|
|
|
return self.__provides
|
|
|
|
|
|
|
|
def set_provides(self, provides):
|
2022-01-17 04:32:42 +03:00
|
|
|
"""Set provider"s provides."""
|
2021-03-20 20:16:51 +03:00
|
|
|
self.__provides = provides
|
|
|
|
return self
|
2020-10-09 22:16:27 +03:00
|
|
|
|
|
|
|
@property
|
|
|
|
def name(self):
|
|
|
|
"""Return name of the item."""
|
2021-03-20 20:16:51 +03:00
|
|
|
return self.__name
|
|
|
|
|
|
|
|
def set_name(self, name):
|
|
|
|
"""Set name of the item."""
|
|
|
|
self.__name = name
|
|
|
|
return self
|
2020-10-09 22:16:27 +03:00
|
|
|
|
2020-08-21 04:52:12 +03:00
|
|
|
def call(self, *args, **kwargs):
|
|
|
|
return MethodCaller(self, *args, **kwargs)
|
|
|
|
|
2021-02-01 17:42:21 +03:00
|
|
|
@property
|
|
|
|
def related(self):
|
|
|
|
"""Return related providers generator."""
|
2021-03-20 20:16:51 +03:00
|
|
|
if is_provider(self.provides):
|
|
|
|
yield self.provides
|
2021-02-01 17:42:21 +03:00
|
|
|
yield from super().related
|
|
|
|
|
2020-08-21 04:52:12 +03:00
|
|
|
cpdef object _provide(self, tuple args, dict kwargs):
|
2021-03-20 20:16:51 +03:00
|
|
|
provided = self.provides(*args, **kwargs)
|
2021-02-17 17:56:39 +03:00
|
|
|
if __is_future_or_coroutine(provided):
|
2021-01-11 03:26:15 +03:00
|
|
|
future_result = asyncio.Future()
|
|
|
|
provided = asyncio.ensure_future(provided)
|
|
|
|
provided.add_done_callback(functools.partial(self._async_provide, future_result))
|
|
|
|
return future_result
|
2021-03-20 20:16:51 +03:00
|
|
|
return provided[self.name]
|
2020-08-21 04:52:12 +03:00
|
|
|
|
2021-01-11 03:26:15 +03:00
|
|
|
def _async_provide(self, future_result, future):
|
2021-03-20 20:16:51 +03:00
|
|
|
try:
|
|
|
|
provided = future.result()
|
|
|
|
result = provided[self.name]
|
|
|
|
except Exception as exception:
|
|
|
|
future_result.set_exception(exception)
|
|
|
|
else:
|
|
|
|
future_result.set_result(result)
|
2021-01-11 03:26:15 +03:00
|
|
|
|
2020-08-21 04:52:12 +03:00
|
|
|
|
|
|
|
cdef class MethodCaller(Provider):
|
|
|
|
"""Provider that calls the method of the injected instance.
|
|
|
|
|
|
|
|
You should not create this provider directly. See :py:class:`ProvidedInstance` instead.
|
|
|
|
"""
|
|
|
|
|
2021-03-20 20:16:51 +03:00
|
|
|
def __init__(self, provides=None, *args, **kwargs):
|
|
|
|
self.__provides = None
|
|
|
|
self.set_provides(provides)
|
2020-08-21 04:52:12 +03:00
|
|
|
|
2021-03-20 20:16:51 +03:00
|
|
|
self.__args = tuple()
|
|
|
|
self.__args_len = 0
|
|
|
|
self.set_args(*args)
|
2020-08-21 04:52:12 +03:00
|
|
|
|
2021-03-20 20:16:51 +03:00
|
|
|
self.__kwargs = tuple()
|
|
|
|
self.__kwargs_len = 0
|
|
|
|
self.set_kwargs(**kwargs)
|
2020-08-21 04:52:12 +03:00
|
|
|
|
|
|
|
super().__init__()
|
|
|
|
|
|
|
|
def __repr__(self):
|
2022-01-17 04:32:42 +03:00
|
|
|
return f"{self.__class__.__name__}({self.provides})"
|
2020-08-21 04:52:12 +03:00
|
|
|
|
2021-03-20 20:16:51 +03:00
|
|
|
def __deepcopy__(self, memo):
|
2020-08-21 04:52:12 +03:00
|
|
|
copied = memo.get(id(self))
|
|
|
|
if copied is not None:
|
|
|
|
return copied
|
|
|
|
|
2021-03-20 20:16:51 +03:00
|
|
|
copied = _memorized_duplicate(self, memo)
|
|
|
|
copied.set_provides(_copy_if_provider(self.provides, memo))
|
|
|
|
copied.set_args(*deepcopy(self.args, memo))
|
|
|
|
copied.set_kwargs(**deepcopy(self.kwargs, memo))
|
2020-08-21 04:52:12 +03:00
|
|
|
self._copy_overridings(copied, memo)
|
|
|
|
return copied
|
|
|
|
|
|
|
|
def __getattr__(self, item):
|
|
|
|
return AttributeGetter(self, item)
|
|
|
|
|
|
|
|
def __getitem__(self, item):
|
|
|
|
return ItemGetter(self, item)
|
|
|
|
|
2021-03-20 20:16:51 +03:00
|
|
|
def call(self, *args, **kwargs):
|
|
|
|
return MethodCaller(self, *args, **kwargs)
|
|
|
|
|
2020-10-09 22:16:27 +03:00
|
|
|
@property
|
|
|
|
def provides(self):
|
2022-01-17 04:32:42 +03:00
|
|
|
"""Return provider provides."""
|
2021-03-20 20:16:51 +03:00
|
|
|
return self.__provides
|
|
|
|
|
|
|
|
def set_provides(self, provides):
|
2022-01-17 04:32:42 +03:00
|
|
|
"""Set provider provides."""
|
2021-03-20 20:16:51 +03:00
|
|
|
self.__provides = provides
|
|
|
|
return self
|
2020-10-09 22:16:27 +03:00
|
|
|
|
|
|
|
@property
|
|
|
|
def args(self):
|
|
|
|
"""Return positional argument injections."""
|
|
|
|
cdef int index
|
|
|
|
cdef PositionalInjection arg
|
|
|
|
cdef list args
|
|
|
|
|
|
|
|
args = list()
|
|
|
|
for index in range(self.__args_len):
|
|
|
|
arg = self.__args[index]
|
|
|
|
args.append(arg.__value)
|
|
|
|
return tuple(args)
|
|
|
|
|
2021-03-20 20:16:51 +03:00
|
|
|
def set_args(self, *args):
|
|
|
|
"""Set positional argument injections.
|
|
|
|
|
|
|
|
Existing positional argument injections are dropped.
|
|
|
|
|
|
|
|
:return: Reference ``self``
|
|
|
|
"""
|
|
|
|
self.__args = parse_positional_injections(args)
|
|
|
|
self.__args_len = len(self.__args)
|
|
|
|
return self
|
|
|
|
|
2020-10-09 22:16:27 +03:00
|
|
|
@property
|
|
|
|
def kwargs(self):
|
|
|
|
"""Return keyword argument injections."""
|
|
|
|
cdef int index
|
|
|
|
cdef NamedInjection kwarg
|
|
|
|
cdef dict kwargs
|
|
|
|
|
|
|
|
kwargs = dict()
|
|
|
|
for index in range(self.__kwargs_len):
|
|
|
|
kwarg = self.__kwargs[index]
|
|
|
|
kwargs[kwarg.__name] = kwarg.__value
|
|
|
|
return kwargs
|
|
|
|
|
2021-03-20 20:16:51 +03:00
|
|
|
def set_kwargs(self, **kwargs):
|
|
|
|
"""Set keyword argument injections.
|
|
|
|
|
|
|
|
Existing keyword argument injections are dropped.
|
|
|
|
|
|
|
|
:return: Reference ``self``
|
|
|
|
"""
|
|
|
|
self.__kwargs = parse_named_injections(kwargs)
|
|
|
|
self.__kwargs_len = len(self.__kwargs)
|
|
|
|
return self
|
2020-08-21 04:52:12 +03:00
|
|
|
|
2021-02-01 17:42:21 +03:00
|
|
|
@property
|
|
|
|
def related(self):
|
|
|
|
"""Return related providers generator."""
|
2021-03-20 20:16:51 +03:00
|
|
|
if is_provider(self.provides):
|
|
|
|
yield self.provides
|
2021-02-01 17:42:21 +03:00
|
|
|
yield from filter(is_provider, self.args)
|
|
|
|
yield from filter(is_provider, self.kwargs.values())
|
|
|
|
yield from super().related
|
|
|
|
|
2020-08-21 04:52:12 +03:00
|
|
|
cpdef object _provide(self, tuple args, dict kwargs):
|
2021-03-20 20:16:51 +03:00
|
|
|
call = self.provides()
|
2021-02-17 17:56:39 +03:00
|
|
|
if __is_future_or_coroutine(call):
|
2021-01-11 03:26:15 +03:00
|
|
|
future_result = asyncio.Future()
|
|
|
|
call = asyncio.ensure_future(call)
|
|
|
|
call.add_done_callback(functools.partial(self._async_provide, future_result, args, kwargs))
|
|
|
|
return future_result
|
2020-08-21 04:52:12 +03:00
|
|
|
return __call(
|
|
|
|
call,
|
|
|
|
args,
|
|
|
|
self.__args,
|
|
|
|
self.__args_len,
|
|
|
|
kwargs,
|
|
|
|
self.__kwargs,
|
|
|
|
self.__kwargs_len,
|
|
|
|
)
|
|
|
|
|
2021-01-11 03:26:15 +03:00
|
|
|
def _async_provide(self, future_result, args, kwargs, future):
|
2021-03-20 20:16:51 +03:00
|
|
|
try:
|
|
|
|
call = future.result()
|
|
|
|
result = __call(
|
|
|
|
call,
|
|
|
|
args,
|
|
|
|
self.__args,
|
|
|
|
self.__args_len,
|
|
|
|
kwargs,
|
|
|
|
self.__kwargs,
|
|
|
|
self.__kwargs_len,
|
|
|
|
)
|
|
|
|
except Exception as exception:
|
|
|
|
future_result.set_exception(exception)
|
|
|
|
else:
|
|
|
|
future_result.set_result(result)
|
2021-01-11 03:26:15 +03:00
|
|
|
|
2020-08-21 04:52:12 +03:00
|
|
|
|
2017-03-25 23:38:48 +03:00
|
|
|
cdef class Injection(object):
|
|
|
|
"""Abstract injection class."""
|
|
|
|
|
|
|
|
|
|
|
|
cdef class PositionalInjection(Injection):
|
|
|
|
"""Positional injection class."""
|
|
|
|
|
2021-03-21 04:41:39 +03:00
|
|
|
def __init__(self, value=None):
|
2017-03-25 23:38:48 +03:00
|
|
|
"""Initializer."""
|
2021-03-21 04:41:39 +03:00
|
|
|
self.__value = None
|
|
|
|
self.__is_provider = 0
|
|
|
|
self.__is_delegated = 0
|
|
|
|
self.__call = 0
|
|
|
|
self.set(value)
|
2017-03-25 23:38:48 +03:00
|
|
|
super(PositionalInjection, self).__init__()
|
|
|
|
|
|
|
|
def __deepcopy__(self, memo):
|
|
|
|
"""Create and return full copy of provider."""
|
|
|
|
copied = memo.get(id(self))
|
|
|
|
if copied is not None:
|
|
|
|
return copied
|
2021-03-21 04:41:39 +03:00
|
|
|
copied = _memorized_duplicate(self, memo)
|
|
|
|
copied.set(_copy_if_provider(self.__value, memo))
|
|
|
|
return copied
|
2017-03-25 23:38:48 +03:00
|
|
|
|
|
|
|
def get_value(self):
|
|
|
|
"""Return injection value."""
|
|
|
|
return __get_value(self)
|
|
|
|
|
|
|
|
def get_original_value(self):
|
|
|
|
"""Return original value."""
|
|
|
|
return self.__value
|
|
|
|
|
2021-03-21 04:41:39 +03:00
|
|
|
def set(self, value):
|
|
|
|
"""Set injection."""
|
|
|
|
self.__value = value
|
|
|
|
self.__is_provider = <int>is_provider(value)
|
|
|
|
self.__is_delegated = <int>is_delegated(value)
|
|
|
|
self.__call = <int>(self.__is_provider == 1 and self.__is_delegated == 0)
|
|
|
|
|
2017-03-25 23:38:48 +03:00
|
|
|
|
|
|
|
cdef class NamedInjection(Injection):
|
|
|
|
"""Keyword injection class."""
|
|
|
|
|
2021-03-21 04:41:39 +03:00
|
|
|
def __init__(self, name=None, value=None):
|
2017-03-25 23:38:48 +03:00
|
|
|
"""Initializer."""
|
|
|
|
self.__name = name
|
2021-03-21 04:41:39 +03:00
|
|
|
self.set_name(name)
|
|
|
|
|
|
|
|
self.__value = None
|
|
|
|
self.__is_provider = 0
|
|
|
|
self.__is_delegated = 0
|
|
|
|
self.__call = 0
|
|
|
|
self.set(value)
|
|
|
|
|
2017-03-25 23:38:48 +03:00
|
|
|
super(NamedInjection, self).__init__()
|
|
|
|
|
|
|
|
def __deepcopy__(self, memo):
|
|
|
|
"""Create and return full copy of provider."""
|
|
|
|
copied = memo.get(id(self))
|
|
|
|
if copied is not None:
|
|
|
|
return copied
|
2021-03-21 04:41:39 +03:00
|
|
|
copied = _memorized_duplicate(self, memo)
|
|
|
|
copied.set_name(self.get_name())
|
|
|
|
copied.set(_copy_if_provider(self.__value, memo))
|
|
|
|
return copied
|
2017-03-25 23:38:48 +03:00
|
|
|
|
|
|
|
def get_name(self):
|
2021-03-21 04:41:39 +03:00
|
|
|
"""Return injection name."""
|
2017-03-25 23:38:48 +03:00
|
|
|
return __get_name(self)
|
|
|
|
|
2021-03-21 04:41:39 +03:00
|
|
|
def set_name(self, name):
|
|
|
|
"""Set injection name."""
|
|
|
|
self.__name = name
|
|
|
|
|
2017-03-25 23:38:48 +03:00
|
|
|
def get_value(self):
|
|
|
|
"""Return injection value."""
|
|
|
|
return __get_value(self)
|
|
|
|
|
|
|
|
def get_original_value(self):
|
|
|
|
"""Return original value."""
|
|
|
|
return self.__value
|
|
|
|
|
2021-03-21 04:41:39 +03:00
|
|
|
def set(self, value):
|
|
|
|
"""Set injection."""
|
|
|
|
self.__value = value
|
|
|
|
self.__is_provider = <int>is_provider(value)
|
|
|
|
self.__is_delegated = <int>is_delegated(value)
|
|
|
|
self.__call = <int>(self.__is_provider == 1 and self.__is_delegated == 0)
|
|
|
|
|
2017-03-25 23:38:48 +03:00
|
|
|
|
|
|
|
@cython.boundscheck(False)
|
|
|
|
@cython.wraparound(False)
|
|
|
|
cpdef tuple parse_positional_injections(tuple args):
|
|
|
|
"""Parse positional injections."""
|
|
|
|
cdef list injections = list()
|
|
|
|
cdef int args_len = len(args)
|
|
|
|
|
|
|
|
cdef int index
|
|
|
|
cdef object arg
|
|
|
|
cdef PositionalInjection injection
|
|
|
|
|
|
|
|
for index in range(args_len):
|
|
|
|
arg = args[index]
|
|
|
|
injection = PositionalInjection(arg)
|
|
|
|
injections.append(injection)
|
|
|
|
|
|
|
|
return tuple(injections)
|
|
|
|
|
|
|
|
|
|
|
|
@cython.boundscheck(False)
|
|
|
|
@cython.wraparound(False)
|
|
|
|
cpdef tuple parse_named_injections(dict kwargs):
|
|
|
|
"""Parse named injections."""
|
|
|
|
cdef list injections = list()
|
|
|
|
|
|
|
|
cdef object name
|
|
|
|
cdef object arg
|
|
|
|
cdef NamedInjection injection
|
|
|
|
|
|
|
|
for name, arg in kwargs.items():
|
|
|
|
injection = NamedInjection(name, arg)
|
|
|
|
injections.append(injection)
|
|
|
|
|
|
|
|
return tuple(injections)
|
|
|
|
|
|
|
|
|
2021-03-03 16:28:10 +03:00
|
|
|
cdef class OverridingContext(object):
|
|
|
|
"""Provider overriding context.
|
|
|
|
|
|
|
|
:py:class:`OverridingContext` is used by :py:meth:`Provider.override` for
|
|
|
|
implementing ``with`` contexts. When :py:class:`OverridingContext` is
|
|
|
|
closed, overriding that was created in this context is dropped also.
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
with provider.override(another_provider):
|
|
|
|
assert provider.overridden
|
|
|
|
assert not provider.overridden
|
|
|
|
"""
|
|
|
|
|
|
|
|
def __init__(self, Provider overridden, Provider overriding):
|
|
|
|
"""Initializer.
|
|
|
|
|
|
|
|
:param overridden: Overridden provider.
|
|
|
|
:type overridden: :py:class:`Provider`
|
|
|
|
|
|
|
|
:param overriding: Overriding provider.
|
|
|
|
:type overriding: :py:class:`Provider`
|
|
|
|
"""
|
|
|
|
self.__overridden = overridden
|
|
|
|
self.__overriding = overriding
|
|
|
|
super(OverridingContext, self).__init__()
|
|
|
|
|
|
|
|
def __enter__(self):
|
|
|
|
"""Do nothing."""
|
|
|
|
return self.__overriding
|
|
|
|
|
|
|
|
def __exit__(self, *_):
|
|
|
|
"""Exit overriding context."""
|
|
|
|
self.__overridden.reset_last_overriding()
|
|
|
|
|
|
|
|
|
|
|
|
cdef class BaseSingletonResetContext(object):
|
|
|
|
|
|
|
|
def __init__(self, Provider provider):
|
|
|
|
self.__singleton = provider
|
|
|
|
super().__init__()
|
|
|
|
|
|
|
|
def __enter__(self):
|
|
|
|
return self.__singleton
|
|
|
|
|
|
|
|
def __exit__(self, *_):
|
|
|
|
raise NotImplementedError()
|
|
|
|
|
|
|
|
|
|
|
|
cdef class SingletonResetContext(BaseSingletonResetContext):
|
|
|
|
|
|
|
|
def __exit__(self, *_):
|
|
|
|
return self.__singleton.reset()
|
|
|
|
|
|
|
|
|
|
|
|
cdef class SingletonFullResetContext(BaseSingletonResetContext):
|
|
|
|
|
|
|
|
def __exit__(self, *_):
|
|
|
|
return self.__singleton.full_reset()
|
|
|
|
|
|
|
|
|
2021-02-13 17:16:38 +03:00
|
|
|
CHILD_PROVIDERS = (Dependency, DependenciesContainer, Container)
|
|
|
|
|
|
|
|
|
2017-03-25 23:38:48 +03:00
|
|
|
cpdef bint is_provider(object instance):
|
|
|
|
"""Check if instance is provider instance.
|
|
|
|
|
|
|
|
:param instance: Instance to be checked.
|
|
|
|
:type instance: object
|
|
|
|
|
|
|
|
:rtype: bool
|
|
|
|
"""
|
|
|
|
return (not isinstance(instance, CLASS_TYPES) and
|
2022-01-17 04:32:42 +03:00
|
|
|
getattr(instance, "__IS_PROVIDER__", False) is True)
|
2017-03-25 23:38:48 +03:00
|
|
|
|
|
|
|
|
|
|
|
cpdef object ensure_is_provider(object instance):
|
|
|
|
"""Check if instance is provider instance and return it.
|
|
|
|
|
|
|
|
:param instance: Instance to be checked.
|
|
|
|
:type instance: object
|
|
|
|
|
|
|
|
:raise: :py:exc:`dependency_injector.errors.Error` if provided instance is
|
|
|
|
not provider.
|
|
|
|
|
|
|
|
:rtype: :py:class:`dependency_injector.providers.Provider`
|
|
|
|
"""
|
|
|
|
if not is_provider(instance):
|
2022-01-17 04:32:42 +03:00
|
|
|
raise Error("Expected provider instance, got {0}".format(str(instance)))
|
2017-03-25 23:38:48 +03:00
|
|
|
return instance
|
|
|
|
|
|
|
|
|
|
|
|
cpdef bint is_delegated(object instance):
|
|
|
|
"""Check if instance is delegated provider.
|
|
|
|
|
|
|
|
:param instance: Instance to be checked.
|
|
|
|
:type instance: object
|
|
|
|
|
|
|
|
:rtype: bool
|
|
|
|
"""
|
|
|
|
return (not isinstance(instance, CLASS_TYPES) and
|
2022-01-17 04:32:42 +03:00
|
|
|
getattr(instance, "__IS_DELEGATED__", False) is True)
|
2017-03-25 23:38:48 +03:00
|
|
|
|
|
|
|
|
|
|
|
cpdef str represent_provider(object provider, object provides):
|
|
|
|
"""Return string representation of provider.
|
|
|
|
|
|
|
|
:param provider: Provider object
|
|
|
|
:type provider: :py:class:`dependency_injector.providers.Provider`
|
|
|
|
|
|
|
|
:param provides: Object that provider provides
|
|
|
|
:type provider: object
|
|
|
|
|
|
|
|
:return: String representation of provider
|
|
|
|
:rtype: str
|
|
|
|
"""
|
2022-01-17 04:32:42 +03:00
|
|
|
return "<{provider}({provides}) at {address}>".format(
|
|
|
|
provider=".".join((provider.__class__.__module__,
|
2017-03-25 23:38:48 +03:00
|
|
|
provider.__class__.__name__)),
|
2022-01-17 04:32:42 +03:00
|
|
|
provides=repr(provides) if provides is not None else "",
|
2017-03-25 23:38:48 +03:00
|
|
|
address=hex(id(provider)))
|
|
|
|
|
|
|
|
|
2021-02-13 17:16:38 +03:00
|
|
|
cpdef bint is_container_instance(object instance):
|
|
|
|
"""Check if instance is container instance.
|
|
|
|
|
|
|
|
:param instance: Instance to be checked.
|
|
|
|
:type instance: object
|
|
|
|
|
|
|
|
:rtype: bool
|
|
|
|
"""
|
|
|
|
return (not isinstance(instance, CLASS_TYPES) and
|
2022-01-17 04:32:42 +03:00
|
|
|
getattr(instance, "__IS_CONTAINER__", False) is True)
|
2021-02-13 17:16:38 +03:00
|
|
|
|
|
|
|
|
|
|
|
cpdef bint is_container_class(object instance):
|
|
|
|
"""Check if instance is container class.
|
|
|
|
|
|
|
|
:param instance: Instance to be checked.
|
|
|
|
:type instance: object
|
|
|
|
|
|
|
|
:rtype: bool
|
|
|
|
"""
|
|
|
|
return (isinstance(instance, CLASS_TYPES) and
|
2022-01-17 04:32:42 +03:00
|
|
|
getattr(instance, "__IS_CONTAINER__", False) is True)
|
2021-02-13 17:16:38 +03:00
|
|
|
|
|
|
|
|
2017-03-25 23:38:48 +03:00
|
|
|
cpdef object deepcopy(object instance, dict memo=None):
|
|
|
|
"""Return full copy of provider or container with providers."""
|
2018-12-22 21:37:53 +03:00
|
|
|
if memo is None:
|
|
|
|
memo = dict()
|
|
|
|
|
|
|
|
__add_sys_streams(memo)
|
|
|
|
|
2017-03-25 23:38:48 +03:00
|
|
|
return copy.deepcopy(instance, memo)
|
2018-12-22 21:37:53 +03:00
|
|
|
|
2021-02-13 17:16:38 +03:00
|
|
|
|
2018-12-22 21:37:53 +03:00
|
|
|
def __add_sys_streams(memo):
|
|
|
|
"""Add system streams to memo dictionary.
|
|
|
|
|
|
|
|
This helps to avoid copying of system streams while making a deepcopy of
|
|
|
|
objects graph.
|
|
|
|
"""
|
|
|
|
memo[id(sys.stdin)] = sys.stdin
|
|
|
|
memo[id(sys.stdout)] = sys.stdout
|
|
|
|
memo[id(sys.stderr)] = sys.stderr
|
2020-06-24 01:09:20 +03:00
|
|
|
|
|
|
|
|
|
|
|
def merge_dicts(dict1, dict2):
|
|
|
|
"""Merge dictionaries recursively.
|
|
|
|
|
|
|
|
:param dict1: Dictionary 1
|
|
|
|
:type dict1: dict
|
|
|
|
|
|
|
|
:param dict2: Dictionary 2
|
|
|
|
:type dict2: dict
|
|
|
|
|
|
|
|
:return: New resulting dictionary
|
|
|
|
:rtype: dict
|
|
|
|
"""
|
|
|
|
for key, value in dict1.items():
|
|
|
|
if key in dict2:
|
2020-06-25 04:04:30 +03:00
|
|
|
if isinstance(value, dict) and isinstance(dict2[key], dict):
|
2020-06-24 01:09:20 +03:00
|
|
|
dict2[key] = merge_dicts(value, dict2[key])
|
|
|
|
result = dict1.copy()
|
|
|
|
result.update(dict2)
|
|
|
|
return result
|
2021-01-11 03:26:15 +03:00
|
|
|
|
|
|
|
|
2021-02-01 17:42:21 +03:00
|
|
|
def traverse(*providers, types=None):
|
|
|
|
"""Return providers traversal generator."""
|
|
|
|
visited = set()
|
|
|
|
to_visit = set(providers)
|
|
|
|
|
|
|
|
if types:
|
|
|
|
types = tuple(types)
|
|
|
|
|
|
|
|
while len(to_visit) > 0:
|
|
|
|
visiting = to_visit.pop()
|
|
|
|
visited.add(visiting)
|
|
|
|
|
|
|
|
for child in visiting.related:
|
|
|
|
if child in visited:
|
|
|
|
continue
|
|
|
|
to_visit.add(child)
|
|
|
|
|
|
|
|
if types and not isinstance(visiting, types):
|
|
|
|
continue
|
|
|
|
|
|
|
|
yield visiting
|
|
|
|
|
|
|
|
|
2021-01-11 03:26:15 +03:00
|
|
|
def isawaitable(obj):
|
2021-10-12 19:16:49 +03:00
|
|
|
"""Check if object is a coroutine function."""
|
2021-01-11 03:26:15 +03:00
|
|
|
try:
|
|
|
|
return inspect.isawaitable(obj)
|
|
|
|
except AttributeError:
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
def iscoroutinefunction(obj):
|
2021-10-12 19:16:49 +03:00
|
|
|
"""Check if object is a coroutine function."""
|
2021-01-11 03:26:15 +03:00
|
|
|
try:
|
|
|
|
return inspect.iscoroutinefunction(obj)
|
|
|
|
except AttributeError:
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
def isasyncgenfunction(obj):
|
2021-10-12 19:16:49 +03:00
|
|
|
"""Check if object is an asynchronous generator function."""
|
2021-01-11 03:26:15 +03:00
|
|
|
try:
|
|
|
|
return inspect.isasyncgenfunction(obj)
|
|
|
|
except AttributeError:
|
|
|
|
return False
|
2021-02-13 17:16:38 +03:00
|
|
|
|
|
|
|
|
2022-01-31 07:19:09 +03:00
|
|
|
def _resolve_string_import(provides):
|
2022-01-31 07:16:55 +03:00
|
|
|
if provides is None:
|
|
|
|
return provides
|
|
|
|
|
|
|
|
if not isinstance(provides, str):
|
|
|
|
return provides
|
|
|
|
|
|
|
|
segments = provides.split(".")
|
|
|
|
member_name = segments[-1]
|
|
|
|
|
|
|
|
if len(segments) == 1:
|
|
|
|
if member_name in dir(builtins):
|
|
|
|
module = builtins
|
|
|
|
else:
|
|
|
|
module = _resolve_calling_module()
|
|
|
|
return getattr(module, member_name)
|
|
|
|
|
|
|
|
module_name = ".".join(segments[:-1])
|
|
|
|
|
|
|
|
package_name = _resolve_calling_package_name()
|
|
|
|
if module_name.startswith(".") and package_name is None:
|
|
|
|
raise ImportError("Attempted relative import with no known parent package")
|
|
|
|
|
|
|
|
module = importlib.import_module(module_name, package=package_name)
|
|
|
|
return getattr(module, member_name)
|
|
|
|
|
|
|
|
|
|
|
|
def _resolve_calling_module():
|
|
|
|
stack = inspect.stack()
|
|
|
|
pre_last_frame = stack[0]
|
|
|
|
return inspect.getmodule(pre_last_frame[0])
|
|
|
|
|
|
|
|
|
|
|
|
def _resolve_calling_package_name():
|
|
|
|
module = _resolve_calling_module()
|
|
|
|
return module.__package__
|
|
|
|
|
|
|
|
|
2021-02-13 17:16:38 +03:00
|
|
|
cpdef _copy_parent(object from_, object to, dict memo):
|
|
|
|
"""Copy and assign provider parent."""
|
|
|
|
copied_parent = (
|
|
|
|
deepcopy(from_.parent, memo)
|
|
|
|
if is_provider(from_.parent) or is_container_instance(from_.parent)
|
|
|
|
else from_.parent
|
|
|
|
)
|
|
|
|
to.assign_parent(copied_parent)
|
2021-03-20 20:16:51 +03:00
|
|
|
|
|
|
|
|
|
|
|
cpdef object _memorized_duplicate(object instance, dict memo):
|
|
|
|
copied = instance.__class__()
|
|
|
|
memo[id(instance)] = copied
|
|
|
|
return copied
|
|
|
|
|
|
|
|
|
|
|
|
cpdef object _copy_if_provider(object instance, dict memo):
|
|
|
|
if not is_provider(instance):
|
|
|
|
return instance
|
|
|
|
return deepcopy(instance, memo)
|
|
|
|
|
|
|
|
|
|
|
|
cpdef str _class_qualname(object instance):
|
2022-01-17 04:32:42 +03:00
|
|
|
name = getattr(instance.__class__, "__qualname__", None)
|
2021-03-20 20:16:51 +03:00
|
|
|
if not name:
|
2022-01-17 04:32:42 +03:00
|
|
|
name = ".".join((instance.__class__.__module__, instance.__class__.__name__))
|
2021-04-19 04:37:55 +03:00
|
|
|
return name
|