mirror of
https://github.com/ets-labs/python-dependency-injector.git
synced 2024-11-22 09:36:48 +03:00
Update project structure
This commit is contained in:
parent
7bcb882425
commit
5695c781c9
13
setup.py
13
setup.py
|
@ -40,22 +40,15 @@ setup(name='dependency-injector',
|
||||||
install_requires=requirements,
|
install_requires=requirements,
|
||||||
packages=[
|
packages=[
|
||||||
'dependency_injector',
|
'dependency_injector',
|
||||||
|
'dependency_injector.containers',
|
||||||
'dependency_injector.providers',
|
'dependency_injector.providers',
|
||||||
],
|
],
|
||||||
package_dir={
|
package_dir={
|
||||||
'': 'src',
|
'': 'src',
|
||||||
},
|
},
|
||||||
ext_modules=[
|
ext_modules=[
|
||||||
Extension('dependency_injector.injections',
|
Extension('dependency_injector.providers.injections',
|
||||||
['src/dependency_injector/injections.c'],
|
['src/dependency_injector/providers/injections.c'],
|
||||||
define_macros=defined_macros,
|
|
||||||
extra_compile_args=['-O2']),
|
|
||||||
Extension('dependency_injector.utils',
|
|
||||||
['src/dependency_injector/utils.c'],
|
|
||||||
define_macros=defined_macros,
|
|
||||||
extra_compile_args=['-O2']),
|
|
||||||
Extension('dependency_injector.errors',
|
|
||||||
['src/dependency_injector/errors.c'],
|
|
||||||
define_macros=defined_macros,
|
define_macros=defined_macros,
|
||||||
extra_compile_args=['-O2']),
|
extra_compile_args=['-O2']),
|
||||||
],
|
],
|
||||||
|
|
|
@ -1,363 +0,0 @@
|
||||||
"""Dependency injector IoC containers module."""
|
|
||||||
|
|
||||||
import six
|
|
||||||
|
|
||||||
from dependency_injector import (
|
|
||||||
providers,
|
|
||||||
utils,
|
|
||||||
errors,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class DynamicContainer(object):
|
|
||||||
"""Dynamic inversion of control container.
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
services = DynamicContainer()
|
|
||||||
services.auth = providers.Factory(AuthService)
|
|
||||||
services.users = providers.Factory(UsersService,
|
|
||||||
auth_service=services.auth)
|
|
||||||
|
|
||||||
.. py:attribute:: providers
|
|
||||||
|
|
||||||
Read-only dictionary of all providers.
|
|
||||||
|
|
||||||
:type: dict[str, :py:class:`dependency_injector.providers.Provider`]
|
|
||||||
|
|
||||||
.. py:attribute:: overridden
|
|
||||||
|
|
||||||
Tuple of overriding containers.
|
|
||||||
|
|
||||||
:type: tuple[:py:class:`DynamicContainer`]
|
|
||||||
|
|
||||||
.. py:attribute:: provider_type
|
|
||||||
|
|
||||||
Type of providers that could be placed in container.
|
|
||||||
|
|
||||||
:type: type
|
|
||||||
"""
|
|
||||||
|
|
||||||
__IS_CONTAINER__ = True
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
"""Initializer.
|
|
||||||
|
|
||||||
:rtype: None
|
|
||||||
"""
|
|
||||||
self.provider_type = providers.Provider
|
|
||||||
self.providers = dict()
|
|
||||||
self.overridden = tuple()
|
|
||||||
super(DynamicContainer, self).__init__()
|
|
||||||
|
|
||||||
def __setattr__(self, name, value):
|
|
||||||
"""Set instance attribute.
|
|
||||||
|
|
||||||
If value of attribute is provider, it will be added into providers
|
|
||||||
dictionary.
|
|
||||||
|
|
||||||
:param name: Attribute's name
|
|
||||||
:type name: str
|
|
||||||
|
|
||||||
:param value: Attribute's value
|
|
||||||
:type value: object
|
|
||||||
|
|
||||||
:rtype: None
|
|
||||||
"""
|
|
||||||
if utils.is_provider(value):
|
|
||||||
_check_provider_type(self, value)
|
|
||||||
self.providers[name] = value
|
|
||||||
super(DynamicContainer, self).__setattr__(name, value)
|
|
||||||
|
|
||||||
def __delattr__(self, name):
|
|
||||||
"""Delete instance attribute.
|
|
||||||
|
|
||||||
If value of attribute is provider, it will be deleted from providers
|
|
||||||
dictionary.
|
|
||||||
|
|
||||||
:param name: Attribute's name
|
|
||||||
:type name: str
|
|
||||||
|
|
||||||
:rtype: None
|
|
||||||
"""
|
|
||||||
if name in self.providers:
|
|
||||||
del self.providers[name]
|
|
||||||
super(DynamicContainer, self).__delattr__(name)
|
|
||||||
|
|
||||||
def override(self, overriding):
|
|
||||||
"""Override current container by overriding container.
|
|
||||||
|
|
||||||
:param overriding: Overriding container.
|
|
||||||
:type overriding: :py:class:`DynamicContainer`
|
|
||||||
|
|
||||||
:raise: :py:exc:`dependency_injector.errors.Error` if trying to
|
|
||||||
override container by itself
|
|
||||||
|
|
||||||
:rtype: None
|
|
||||||
"""
|
|
||||||
if overriding is self:
|
|
||||||
raise errors.Error('Container {0} could not be overridden '
|
|
||||||
'with itself'.format(self))
|
|
||||||
|
|
||||||
self.overridden += (overriding,)
|
|
||||||
|
|
||||||
for name, provider in six.iteritems(overriding.providers):
|
|
||||||
try:
|
|
||||||
getattr(self, name).override(provider)
|
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def reset_last_overriding(self):
|
|
||||||
"""Reset last overriding provider for each container providers.
|
|
||||||
|
|
||||||
:rtype: None
|
|
||||||
"""
|
|
||||||
if not self.overridden:
|
|
||||||
raise errors.Error('Container {0} is not overridden'.format(self))
|
|
||||||
|
|
||||||
self.overridden = self.overridden[:-1]
|
|
||||||
|
|
||||||
for provider in six.itervalues(self.providers):
|
|
||||||
provider.reset_last_overriding()
|
|
||||||
|
|
||||||
def reset_override(self):
|
|
||||||
"""Reset all overridings for each container providers.
|
|
||||||
|
|
||||||
:rtype: None
|
|
||||||
"""
|
|
||||||
self.overridden = tuple()
|
|
||||||
|
|
||||||
for provider in six.itervalues(self.providers):
|
|
||||||
provider.reset_override()
|
|
||||||
|
|
||||||
|
|
||||||
class DeclarativeContainerMetaClass(type):
|
|
||||||
"""Declarative inversion of control container meta class."""
|
|
||||||
|
|
||||||
def __new__(mcs, class_name, bases, attributes):
|
|
||||||
"""Declarative container class factory."""
|
|
||||||
cls_providers = tuple((name, provider)
|
|
||||||
for name, provider in six.iteritems(attributes)
|
|
||||||
if utils.is_provider(provider))
|
|
||||||
|
|
||||||
inherited_providers = tuple((name, provider)
|
|
||||||
for base in bases if utils.is_container(
|
|
||||||
base) and base is not DynamicContainer
|
|
||||||
for name, provider in six.iteritems(
|
|
||||||
base.cls_providers))
|
|
||||||
|
|
||||||
attributes['cls_providers'] = dict(cls_providers)
|
|
||||||
attributes['inherited_providers'] = dict(inherited_providers)
|
|
||||||
attributes['providers'] = dict(cls_providers + inherited_providers)
|
|
||||||
|
|
||||||
cls = type.__new__(mcs, class_name, bases, attributes)
|
|
||||||
|
|
||||||
for provider in six.itervalues(cls.providers):
|
|
||||||
_check_provider_type(cls, provider)
|
|
||||||
|
|
||||||
return cls
|
|
||||||
|
|
||||||
def __setattr__(cls, name, value):
|
|
||||||
"""Set class attribute.
|
|
||||||
|
|
||||||
If value of attribute is provider, it will be added into providers
|
|
||||||
dictionary.
|
|
||||||
|
|
||||||
:param name: Attribute's name
|
|
||||||
:type name: str
|
|
||||||
|
|
||||||
:param value: Attribute's value
|
|
||||||
:type value: object
|
|
||||||
|
|
||||||
:rtype: None
|
|
||||||
"""
|
|
||||||
if utils.is_provider(value):
|
|
||||||
_check_provider_type(cls, value)
|
|
||||||
cls.providers[name] = value
|
|
||||||
cls.cls_providers[name] = value
|
|
||||||
super(DeclarativeContainerMetaClass, cls).__setattr__(name, value)
|
|
||||||
|
|
||||||
def __delattr__(cls, name):
|
|
||||||
"""Delete class attribute.
|
|
||||||
|
|
||||||
If value of attribute is provider, it will be deleted from providers
|
|
||||||
dictionary.
|
|
||||||
|
|
||||||
:param name: Attribute's name
|
|
||||||
:type name: str
|
|
||||||
|
|
||||||
:rtype: None
|
|
||||||
"""
|
|
||||||
if name in cls.providers and name in cls.cls_providers:
|
|
||||||
del cls.providers[name]
|
|
||||||
del cls.cls_providers[name]
|
|
||||||
super(DeclarativeContainerMetaClass, cls).__delattr__(name)
|
|
||||||
|
|
||||||
|
|
||||||
@six.add_metaclass(DeclarativeContainerMetaClass)
|
|
||||||
class DeclarativeContainer(object):
|
|
||||||
"""Declarative inversion of control container.
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
class Services(DeclarativeContainer):
|
|
||||||
auth = providers.Factory(AuthService)
|
|
||||||
users = providers.Factory(UsersService,
|
|
||||||
auth_service=auth)
|
|
||||||
"""
|
|
||||||
|
|
||||||
__IS_CONTAINER__ = True
|
|
||||||
|
|
||||||
provider_type = providers.Provider
|
|
||||||
"""Type of providers that could be placed in container.
|
|
||||||
|
|
||||||
:type: type
|
|
||||||
"""
|
|
||||||
|
|
||||||
instance_type = DynamicContainer
|
|
||||||
"""Type of container that is returned on instantiating declarative
|
|
||||||
container.
|
|
||||||
|
|
||||||
:type: type
|
|
||||||
"""
|
|
||||||
|
|
||||||
providers = dict()
|
|
||||||
"""Read-only dictionary of all providers.
|
|
||||||
|
|
||||||
:type: dict[str, :py:class:`dependency_injector.providers.Provider`]
|
|
||||||
"""
|
|
||||||
|
|
||||||
cls_providers = dict()
|
|
||||||
"""Read-only dictionary of current container providers.
|
|
||||||
|
|
||||||
:type: dict[str, :py:class:`dependency_injector.providers.Provider`]
|
|
||||||
"""
|
|
||||||
|
|
||||||
inherited_providers = dict()
|
|
||||||
"""Read-only dictionary of inherited providers.
|
|
||||||
|
|
||||||
:type: dict[str, :py:class:`dependency_injector.providers.Provider`]
|
|
||||||
"""
|
|
||||||
|
|
||||||
overridden = tuple()
|
|
||||||
"""Tuple of overriding containers.
|
|
||||||
|
|
||||||
:type: tuple[:py:class:`DeclarativeContainer`]
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __new__(cls, *args, **kwargs):
|
|
||||||
"""Constructor.
|
|
||||||
|
|
||||||
:return: Dynamic container with copy of all providers.
|
|
||||||
:rtype: :py:class:`DynamicContainer`
|
|
||||||
"""
|
|
||||||
container = cls.instance_type(*args, **kwargs)
|
|
||||||
container.provider_type = cls.provider_type
|
|
||||||
|
|
||||||
for name, provider in six.iteritems(utils.deepcopy(cls.providers)):
|
|
||||||
setattr(container, name, provider)
|
|
||||||
|
|
||||||
return container
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def override(cls, overriding):
|
|
||||||
"""Override current container by overriding container.
|
|
||||||
|
|
||||||
:param overriding: Overriding container.
|
|
||||||
:type overriding: :py:class:`DeclarativeContainer`
|
|
||||||
|
|
||||||
:raise: :py:exc:`dependency_injector.errors.Error` if trying to
|
|
||||||
override container by itself or its subclasses
|
|
||||||
|
|
||||||
:rtype: None
|
|
||||||
"""
|
|
||||||
if issubclass(cls, overriding):
|
|
||||||
raise errors.Error('Container {0} could not be overridden '
|
|
||||||
'with itself or its subclasses'.format(cls))
|
|
||||||
|
|
||||||
cls.overridden += (overriding,)
|
|
||||||
|
|
||||||
for name, provider in six.iteritems(overriding.cls_providers):
|
|
||||||
try:
|
|
||||||
getattr(cls, name).override(provider)
|
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def reset_last_overriding(cls):
|
|
||||||
"""Reset last overriding provider for each container providers.
|
|
||||||
|
|
||||||
:rtype: None
|
|
||||||
"""
|
|
||||||
if not cls.overridden:
|
|
||||||
raise errors.Error('Container {0} is not overridden'.format(cls))
|
|
||||||
|
|
||||||
cls.overridden = cls.overridden[:-1]
|
|
||||||
|
|
||||||
for provider in six.itervalues(cls.providers):
|
|
||||||
provider.reset_last_overriding()
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def reset_override(cls):
|
|
||||||
"""Reset all overridings for each container providers.
|
|
||||||
|
|
||||||
:rtype: None
|
|
||||||
"""
|
|
||||||
cls.overridden = tuple()
|
|
||||||
|
|
||||||
for provider in six.itervalues(cls.providers):
|
|
||||||
provider.reset_override()
|
|
||||||
|
|
||||||
|
|
||||||
def override(container):
|
|
||||||
""":py:class:`DeclarativeContainer` overriding decorator.
|
|
||||||
|
|
||||||
:param container: Container that should be overridden by decorated
|
|
||||||
container.
|
|
||||||
:type container: :py:class:`DeclarativeContainer`
|
|
||||||
|
|
||||||
:return: Declarative container's overriding decorator.
|
|
||||||
:rtype: callable(:py:class:`DeclarativeContainer`)
|
|
||||||
"""
|
|
||||||
def _decorator(overriding_container):
|
|
||||||
"""Overriding decorator."""
|
|
||||||
container.override(overriding_container)
|
|
||||||
return overriding_container
|
|
||||||
return _decorator
|
|
||||||
|
|
||||||
|
|
||||||
def copy(container):
|
|
||||||
""":py:class:`DeclarativeContainer` copying decorator.
|
|
||||||
|
|
||||||
This decorator copy all providers from provided container to decorated one.
|
|
||||||
If one of the decorated container providers matches to source container
|
|
||||||
providers by name, it would be replaced by reference.
|
|
||||||
|
|
||||||
:param container: Container that should be copied by decorated container.
|
|
||||||
:type container: :py:class:`DeclarativeContainer`
|
|
||||||
|
|
||||||
:return: Declarative container's copying decorator.
|
|
||||||
:rtype: callable(:py:class:`DeclarativeContainer`)
|
|
||||||
"""
|
|
||||||
def _decorator(copied_container):
|
|
||||||
memo = dict()
|
|
||||||
for name, provider in six.iteritems(copied_container.cls_providers):
|
|
||||||
try:
|
|
||||||
source_provider = getattr(container, name)
|
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
memo[id(source_provider)] = provider
|
|
||||||
|
|
||||||
providers_copy = utils.deepcopy(container.providers, memo)
|
|
||||||
for name, provider in six.iteritems(providers_copy):
|
|
||||||
setattr(copied_container, name, provider)
|
|
||||||
|
|
||||||
return copied_container
|
|
||||||
return _decorator
|
|
||||||
|
|
||||||
|
|
||||||
def _check_provider_type(cls, provider):
|
|
||||||
if not isinstance(provider, cls.provider_type):
|
|
||||||
raise errors.Error('{0} can contain only {1} '
|
|
||||||
'instances'.format(cls, cls.provider_type))
|
|
26
src/dependency_injector/containers/__init__.py
Normal file
26
src/dependency_injector/containers/__init__.py
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
"""Dependency injector containers."""
|
||||||
|
|
||||||
|
from .declarative import (
|
||||||
|
DeclarativeContainerMetaClass,
|
||||||
|
DeclarativeContainer,
|
||||||
|
)
|
||||||
|
from .dynamic import (
|
||||||
|
DynamicContainer,
|
||||||
|
)
|
||||||
|
from .utils import (
|
||||||
|
is_container,
|
||||||
|
override,
|
||||||
|
copy,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = (
|
||||||
|
'DeclarativeContainerMetaClass',
|
||||||
|
'DeclarativeContainer',
|
||||||
|
|
||||||
|
'DynamicContainer',
|
||||||
|
|
||||||
|
'is_container',
|
||||||
|
'override',
|
||||||
|
'copy',
|
||||||
|
)
|
191
src/dependency_injector/containers/declarative.py
Normal file
191
src/dependency_injector/containers/declarative.py
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
"""Dependency injector declarative container."""
|
||||||
|
|
||||||
|
import six
|
||||||
|
|
||||||
|
from dependency_injector.providers import Provider
|
||||||
|
from dependency_injector.errors import Error
|
||||||
|
|
||||||
|
from .dynamic import DynamicContainer
|
||||||
|
from .utils import (
|
||||||
|
is_container,
|
||||||
|
deepcopy,
|
||||||
|
_check_provider_type,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class DeclarativeContainerMetaClass(type):
|
||||||
|
"""Declarative inversion of control container meta class."""
|
||||||
|
|
||||||
|
def __new__(mcs, class_name, bases, attributes):
|
||||||
|
"""Declarative container class factory."""
|
||||||
|
cls_providers = tuple((name, provider)
|
||||||
|
for name, provider in six.iteritems(attributes)
|
||||||
|
if isinstance(provider, Provider))
|
||||||
|
|
||||||
|
inherited_providers = tuple((name, provider)
|
||||||
|
for base in bases if is_container(
|
||||||
|
base) and base is not DynamicContainer
|
||||||
|
for name, provider in six.iteritems(
|
||||||
|
base.cls_providers))
|
||||||
|
|
||||||
|
attributes['cls_providers'] = dict(cls_providers)
|
||||||
|
attributes['inherited_providers'] = dict(inherited_providers)
|
||||||
|
attributes['providers'] = dict(cls_providers + inherited_providers)
|
||||||
|
|
||||||
|
cls = type.__new__(mcs, class_name, bases, attributes)
|
||||||
|
|
||||||
|
for provider in six.itervalues(cls.providers):
|
||||||
|
_check_provider_type(cls, provider)
|
||||||
|
|
||||||
|
return cls
|
||||||
|
|
||||||
|
def __setattr__(cls, name, value):
|
||||||
|
"""Set class attribute.
|
||||||
|
|
||||||
|
If value of attribute is provider, it will be added into providers
|
||||||
|
dictionary.
|
||||||
|
|
||||||
|
:param name: Attribute's name
|
||||||
|
:type name: str
|
||||||
|
|
||||||
|
:param value: Attribute's value
|
||||||
|
:type value: object
|
||||||
|
|
||||||
|
:rtype: None
|
||||||
|
"""
|
||||||
|
if isinstance(value, Provider):
|
||||||
|
_check_provider_type(cls, value)
|
||||||
|
cls.providers[name] = value
|
||||||
|
cls.cls_providers[name] = value
|
||||||
|
super(DeclarativeContainerMetaClass, cls).__setattr__(name, value)
|
||||||
|
|
||||||
|
def __delattr__(cls, name):
|
||||||
|
"""Delete class attribute.
|
||||||
|
|
||||||
|
If value of attribute is provider, it will be deleted from providers
|
||||||
|
dictionary.
|
||||||
|
|
||||||
|
:param name: Attribute's name
|
||||||
|
:type name: str
|
||||||
|
|
||||||
|
:rtype: None
|
||||||
|
"""
|
||||||
|
if name in cls.providers and name in cls.cls_providers:
|
||||||
|
del cls.providers[name]
|
||||||
|
del cls.cls_providers[name]
|
||||||
|
super(DeclarativeContainerMetaClass, cls).__delattr__(name)
|
||||||
|
|
||||||
|
|
||||||
|
@six.add_metaclass(DeclarativeContainerMetaClass)
|
||||||
|
class DeclarativeContainer(object):
|
||||||
|
"""Declarative inversion of control container.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
class Services(DeclarativeContainer):
|
||||||
|
auth = providers.Factory(AuthService)
|
||||||
|
users = providers.Factory(UsersService,
|
||||||
|
auth_service=auth)
|
||||||
|
"""
|
||||||
|
|
||||||
|
__IS_CONTAINER__ = True
|
||||||
|
|
||||||
|
provider_type = Provider
|
||||||
|
"""Type of providers that could be placed in container.
|
||||||
|
|
||||||
|
:type: type
|
||||||
|
"""
|
||||||
|
|
||||||
|
instance_type = DynamicContainer
|
||||||
|
"""Type of container that is returned on instantiating declarative
|
||||||
|
container.
|
||||||
|
|
||||||
|
:type: type
|
||||||
|
"""
|
||||||
|
|
||||||
|
providers = dict()
|
||||||
|
"""Read-only dictionary of all providers.
|
||||||
|
|
||||||
|
:type: dict[str, :py:class:`dependency_injector.providers.Provider`]
|
||||||
|
"""
|
||||||
|
|
||||||
|
cls_providers = dict()
|
||||||
|
"""Read-only dictionary of current container providers.
|
||||||
|
|
||||||
|
:type: dict[str, :py:class:`dependency_injector.providers.Provider`]
|
||||||
|
"""
|
||||||
|
|
||||||
|
inherited_providers = dict()
|
||||||
|
"""Read-only dictionary of inherited providers.
|
||||||
|
|
||||||
|
:type: dict[str, :py:class:`dependency_injector.providers.Provider`]
|
||||||
|
"""
|
||||||
|
|
||||||
|
overridden = tuple()
|
||||||
|
"""Tuple of overriding containers.
|
||||||
|
|
||||||
|
:type: tuple[:py:class:`DeclarativeContainer`]
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __new__(cls, *args, **kwargs):
|
||||||
|
"""Constructor.
|
||||||
|
|
||||||
|
:return: Dynamic container with copy of all providers.
|
||||||
|
:rtype: :py:class:`DynamicContainer`
|
||||||
|
"""
|
||||||
|
container = cls.instance_type(*args, **kwargs)
|
||||||
|
container.provider_type = cls.provider_type
|
||||||
|
|
||||||
|
for name, provider in six.iteritems(deepcopy(cls.providers)):
|
||||||
|
setattr(container, name, provider)
|
||||||
|
|
||||||
|
return container
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def override(cls, overriding):
|
||||||
|
"""Override current container by overriding container.
|
||||||
|
|
||||||
|
:param overriding: Overriding container.
|
||||||
|
:type overriding: :py:class:`DeclarativeContainer`
|
||||||
|
|
||||||
|
:raise: :py:exc:`dependency_injector.errors.Error` if trying to
|
||||||
|
override container by itself or its subclasses
|
||||||
|
|
||||||
|
:rtype: None
|
||||||
|
"""
|
||||||
|
if issubclass(cls, overriding):
|
||||||
|
raise Error('Container {0} could not be overridden '
|
||||||
|
'with itself or its subclasses'.format(cls))
|
||||||
|
|
||||||
|
cls.overridden += (overriding,)
|
||||||
|
|
||||||
|
for name, provider in six.iteritems(overriding.cls_providers):
|
||||||
|
try:
|
||||||
|
getattr(cls, name).override(provider)
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def reset_last_overriding(cls):
|
||||||
|
"""Reset last overriding provider for each container providers.
|
||||||
|
|
||||||
|
:rtype: None
|
||||||
|
"""
|
||||||
|
if not cls.overridden:
|
||||||
|
raise Error('Container {0} is not overridden'.format(cls))
|
||||||
|
|
||||||
|
cls.overridden = cls.overridden[:-1]
|
||||||
|
|
||||||
|
for provider in six.itervalues(cls.providers):
|
||||||
|
provider.reset_last_overriding()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def reset_override(cls):
|
||||||
|
"""Reset all overridings for each container providers.
|
||||||
|
|
||||||
|
:rtype: None
|
||||||
|
"""
|
||||||
|
cls.overridden = tuple()
|
||||||
|
|
||||||
|
for provider in six.itervalues(cls.providers):
|
||||||
|
provider.reset_override()
|
130
src/dependency_injector/containers/dynamic.py
Normal file
130
src/dependency_injector/containers/dynamic.py
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
"""Dependency injector dynamic container."""
|
||||||
|
|
||||||
|
import six
|
||||||
|
|
||||||
|
from dependency_injector.providers import Provider
|
||||||
|
from dependency_injector.errors import Error
|
||||||
|
|
||||||
|
from .utils import _check_provider_type
|
||||||
|
|
||||||
|
|
||||||
|
class DynamicContainer(object):
|
||||||
|
"""Dynamic inversion of control container.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
services = DynamicContainer()
|
||||||
|
services.auth = providers.Factory(AuthService)
|
||||||
|
services.users = providers.Factory(UsersService,
|
||||||
|
auth_service=services.auth)
|
||||||
|
|
||||||
|
.. py:attribute:: providers
|
||||||
|
|
||||||
|
Read-only dictionary of all providers.
|
||||||
|
|
||||||
|
:type: dict[str, :py:class:`dependency_injector.providers.Provider`]
|
||||||
|
|
||||||
|
.. py:attribute:: overridden
|
||||||
|
|
||||||
|
Tuple of overriding containers.
|
||||||
|
|
||||||
|
:type: tuple[:py:class:`DynamicContainer`]
|
||||||
|
|
||||||
|
.. py:attribute:: provider_type
|
||||||
|
|
||||||
|
Type of providers that could be placed in container.
|
||||||
|
|
||||||
|
:type: type
|
||||||
|
"""
|
||||||
|
|
||||||
|
__IS_CONTAINER__ = True
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""Initializer.
|
||||||
|
|
||||||
|
:rtype: None
|
||||||
|
"""
|
||||||
|
self.provider_type = Provider
|
||||||
|
self.providers = dict()
|
||||||
|
self.overridden = tuple()
|
||||||
|
super(DynamicContainer, self).__init__()
|
||||||
|
|
||||||
|
def __setattr__(self, name, value):
|
||||||
|
"""Set instance attribute.
|
||||||
|
|
||||||
|
If value of attribute is provider, it will be added into providers
|
||||||
|
dictionary.
|
||||||
|
|
||||||
|
:param name: Attribute's name
|
||||||
|
:type name: str
|
||||||
|
|
||||||
|
:param value: Attribute's value
|
||||||
|
:type value: object
|
||||||
|
|
||||||
|
:rtype: None
|
||||||
|
"""
|
||||||
|
if isinstance(value, Provider):
|
||||||
|
_check_provider_type(self, value)
|
||||||
|
self.providers[name] = value
|
||||||
|
super(DynamicContainer, self).__setattr__(name, value)
|
||||||
|
|
||||||
|
def __delattr__(self, name):
|
||||||
|
"""Delete instance attribute.
|
||||||
|
|
||||||
|
If value of attribute is provider, it will be deleted from providers
|
||||||
|
dictionary.
|
||||||
|
|
||||||
|
:param name: Attribute's name
|
||||||
|
:type name: str
|
||||||
|
|
||||||
|
:rtype: None
|
||||||
|
"""
|
||||||
|
if name in self.providers:
|
||||||
|
del self.providers[name]
|
||||||
|
super(DynamicContainer, self).__delattr__(name)
|
||||||
|
|
||||||
|
def override(self, overriding):
|
||||||
|
"""Override current container by overriding container.
|
||||||
|
|
||||||
|
:param overriding: Overriding container.
|
||||||
|
:type overriding: :py:class:`DynamicContainer`
|
||||||
|
|
||||||
|
:raise: :py:exc:`dependency_injector.errors.Error` if trying to
|
||||||
|
override container by itself
|
||||||
|
|
||||||
|
:rtype: None
|
||||||
|
"""
|
||||||
|
if overriding is self:
|
||||||
|
raise Error('Container {0} could not be overridden '
|
||||||
|
'with itself'.format(self))
|
||||||
|
|
||||||
|
self.overridden += (overriding,)
|
||||||
|
|
||||||
|
for name, provider in six.iteritems(overriding.providers):
|
||||||
|
try:
|
||||||
|
getattr(self, name).override(provider)
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def reset_last_overriding(self):
|
||||||
|
"""Reset last overriding provider for each container providers.
|
||||||
|
|
||||||
|
:rtype: None
|
||||||
|
"""
|
||||||
|
if not self.overridden:
|
||||||
|
raise Error('Container {0} is not overridden'.format(self))
|
||||||
|
|
||||||
|
self.overridden = self.overridden[:-1]
|
||||||
|
|
||||||
|
for provider in six.itervalues(self.providers):
|
||||||
|
provider.reset_last_overriding()
|
||||||
|
|
||||||
|
def reset_override(self):
|
||||||
|
"""Reset all overridings for each container providers.
|
||||||
|
|
||||||
|
:rtype: None
|
||||||
|
"""
|
||||||
|
self.overridden = tuple()
|
||||||
|
|
||||||
|
for provider in six.itervalues(self.providers):
|
||||||
|
provider.reset_override()
|
85
src/dependency_injector/containers/utils.py
Normal file
85
src/dependency_injector/containers/utils.py
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
"""Dependency injector container utils."""
|
||||||
|
|
||||||
|
import copy as _copy
|
||||||
|
import types
|
||||||
|
|
||||||
|
import six
|
||||||
|
|
||||||
|
from dependency_injector.errors import Error
|
||||||
|
|
||||||
|
|
||||||
|
if six.PY2: # pragma: no cover
|
||||||
|
_copy._deepcopy_dispatch[types.MethodType] = \
|
||||||
|
lambda obj, memo: type(obj)(obj.im_func,
|
||||||
|
_copy.deepcopy(obj.im_self, memo),
|
||||||
|
obj.im_class)
|
||||||
|
|
||||||
|
|
||||||
|
def is_container(instance):
|
||||||
|
"""Check if instance is container instance.
|
||||||
|
|
||||||
|
:param instance: Instance to be checked.
|
||||||
|
:type instance: object
|
||||||
|
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
return getattr(instance, '__IS_CONTAINER__', False) is True
|
||||||
|
|
||||||
|
|
||||||
|
def override(container):
|
||||||
|
""":py:class:`DeclarativeContainer` overriding decorator.
|
||||||
|
|
||||||
|
:param container: Container that should be overridden by decorated
|
||||||
|
container.
|
||||||
|
:type container: :py:class:`DeclarativeContainer`
|
||||||
|
|
||||||
|
:return: Declarative container's overriding decorator.
|
||||||
|
:rtype: callable(:py:class:`DeclarativeContainer`)
|
||||||
|
"""
|
||||||
|
def _decorator(overriding_container):
|
||||||
|
"""Overriding decorator."""
|
||||||
|
container.override(overriding_container)
|
||||||
|
return overriding_container
|
||||||
|
return _decorator
|
||||||
|
|
||||||
|
|
||||||
|
def copy(container):
|
||||||
|
""":py:class:`DeclarativeContainer` copying decorator.
|
||||||
|
|
||||||
|
This decorator copy all providers from provided container to decorated one.
|
||||||
|
If one of the decorated container providers matches to source container
|
||||||
|
providers by name, it would be replaced by reference.
|
||||||
|
|
||||||
|
:param container: Container that should be copied by decorated container.
|
||||||
|
:type container: :py:class:`DeclarativeContainer`
|
||||||
|
|
||||||
|
:return: Declarative container's copying decorator.
|
||||||
|
:rtype: callable(:py:class:`DeclarativeContainer`)
|
||||||
|
"""
|
||||||
|
def _decorator(copied_container):
|
||||||
|
memo = dict()
|
||||||
|
for name, provider in six.iteritems(copied_container.cls_providers):
|
||||||
|
try:
|
||||||
|
source_provider = getattr(container, name)
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
memo[id(source_provider)] = provider
|
||||||
|
|
||||||
|
providers_copy = deepcopy(container.providers, memo)
|
||||||
|
for name, provider in six.iteritems(providers_copy):
|
||||||
|
setattr(copied_container, name, provider)
|
||||||
|
|
||||||
|
return copied_container
|
||||||
|
return _decorator
|
||||||
|
|
||||||
|
|
||||||
|
def deepcopy(instance, memo=None):
|
||||||
|
"""Make full copy of instance."""
|
||||||
|
return _copy.deepcopy(instance, memo)
|
||||||
|
|
||||||
|
|
||||||
|
def _check_provider_type(cls, provider):
|
||||||
|
if not isinstance(provider, cls.provider_type):
|
||||||
|
raise Error('{0} can contain only {1} '
|
||||||
|
'instances'.format(cls, cls.provider_type))
|
|
@ -1,8 +0,0 @@
|
||||||
"""Dependency injector errors.
|
|
||||||
|
|
||||||
Powered by Cython.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
cdef class Error(Exception):
|
|
||||||
pass
|
|
|
@ -1,10 +1,7 @@
|
||||||
"""Dependency injector errors.
|
"""Dependency injector errors."""
|
||||||
|
|
||||||
Powered by Cython.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
cdef class Error(Exception):
|
class Error(Exception):
|
||||||
"""Base error.
|
"""Base error.
|
||||||
|
|
||||||
All dependency injector errors extend this error class.
|
All dependency injector errors extend this error class.
|
|
@ -1,6 +1,6 @@
|
||||||
"""Dependency injector providers package."""
|
"""Dependency injector providers."""
|
||||||
|
|
||||||
from dependency_injector.providers.base import (
|
from .base import (
|
||||||
Provider,
|
Provider,
|
||||||
Delegate,
|
Delegate,
|
||||||
Object,
|
Object,
|
||||||
|
@ -8,17 +8,22 @@ from dependency_injector.providers.base import (
|
||||||
OverridingContext,
|
OverridingContext,
|
||||||
override,
|
override,
|
||||||
)
|
)
|
||||||
from dependency_injector.providers.callable import (
|
from .callable import (
|
||||||
Callable,
|
Callable,
|
||||||
DelegatedCallable,
|
DelegatedCallable,
|
||||||
)
|
)
|
||||||
from dependency_injector.providers.creational import (
|
from .creational import (
|
||||||
Factory,
|
Factory,
|
||||||
DelegatedFactory,
|
DelegatedFactory,
|
||||||
Singleton,
|
Singleton,
|
||||||
DelegatedSingleton,
|
DelegatedSingleton,
|
||||||
ThreadLocalSingleton,
|
ThreadLocalSingleton,
|
||||||
DelegatedThreadLocalSingleton
|
DelegatedThreadLocalSingleton,
|
||||||
|
)
|
||||||
|
from .injections import (
|
||||||
|
Injection,
|
||||||
|
PositionalInjection,
|
||||||
|
NamedInjection,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -42,4 +47,8 @@ __all__ = (
|
||||||
|
|
||||||
'ThreadLocalSingleton',
|
'ThreadLocalSingleton',
|
||||||
'DelegatedThreadLocalSingleton',
|
'DelegatedThreadLocalSingleton',
|
||||||
|
|
||||||
|
'Injection',
|
||||||
|
'PositionalInjection',
|
||||||
|
'NamedInjection',
|
||||||
)
|
)
|
||||||
|
|
|
@ -5,7 +5,7 @@ Powered by Cython.
|
||||||
|
|
||||||
cimport cython
|
cimport cython
|
||||||
|
|
||||||
from .utils cimport (
|
from dependency_injector.utils import (
|
||||||
is_provider,
|
is_provider,
|
||||||
is_delegated,
|
is_delegated,
|
||||||
)
|
)
|
0
src/dependency_injector/providers/utils.py
Normal file
0
src/dependency_injector/providers/utils.py
Normal file
|
@ -1,16 +0,0 @@
|
||||||
"""Dependency injector utils.
|
|
||||||
|
|
||||||
Powered by Cython.
|
|
||||||
"""
|
|
||||||
|
|
||||||
cpdef bint is_provider(object instance)
|
|
||||||
|
|
||||||
cpdef object ensure_is_provider(object instance)
|
|
||||||
|
|
||||||
cpdef bint is_delegated(object instance)
|
|
||||||
|
|
||||||
cpdef bint is_container(object instance)
|
|
||||||
|
|
||||||
cpdef str represent_provider(object provider, object provides)
|
|
||||||
|
|
||||||
cpdef object deepcopy(object instance, dict memo=*)
|
|
|
@ -1,36 +1,19 @@
|
||||||
"""Dependency injector utils.
|
"""Dependency injector utils."""
|
||||||
|
|
||||||
Powered by Cython.
|
import six
|
||||||
"""
|
|
||||||
|
|
||||||
cimport cpython.version
|
|
||||||
|
|
||||||
from dependency_injector cimport errors
|
|
||||||
|
|
||||||
import copy as _copy
|
|
||||||
import types
|
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
|
from .errors import Error
|
||||||
|
|
||||||
GLOBAL_LOCK = threading.RLock()
|
GLOBAL_LOCK = threading.RLock()
|
||||||
"""Dependency injector global reentrant lock.
|
"""Global reentrant lock.
|
||||||
|
|
||||||
:type: :py:class:`threading.RLock`
|
:type: :py:class:`threading.RLock`
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if cpython.version.PY_MAJOR_VERSION < 3: # pragma: no cover
|
|
||||||
CLASS_TYPES = (type, types.ClassType)
|
|
||||||
|
|
||||||
_copy._deepcopy_dispatch[types.MethodType] = \
|
def is_provider(instance):
|
||||||
lambda obj, memo: type(obj)(obj.im_func,
|
|
||||||
_copy.deepcopy(obj.im_self, memo),
|
|
||||||
obj.im_class)
|
|
||||||
else: # pragma: no cover
|
|
||||||
CLASS_TYPES = (type,)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
cpdef bint is_provider(object instance):
|
|
||||||
"""Check if instance is provider instance.
|
"""Check if instance is provider instance.
|
||||||
|
|
||||||
:param instance: Instance to be checked.
|
:param instance: Instance to be checked.
|
||||||
|
@ -38,11 +21,11 @@ cpdef bint is_provider(object instance):
|
||||||
|
|
||||||
:rtype: bool
|
:rtype: bool
|
||||||
"""
|
"""
|
||||||
return (not isinstance(instance, CLASS_TYPES) and
|
return (not isinstance(instance, six.class_types) and
|
||||||
getattr(instance, '__IS_PROVIDER__', False) is True)
|
getattr(instance, '__IS_PROVIDER__', False) is True)
|
||||||
|
|
||||||
|
|
||||||
cpdef object ensure_is_provider(object instance):
|
def ensure_is_provider(instance):
|
||||||
"""Check if instance is provider instance and return it.
|
"""Check if instance is provider instance and return it.
|
||||||
|
|
||||||
:param instance: Instance to be checked.
|
:param instance: Instance to be checked.
|
||||||
|
@ -54,12 +37,12 @@ cpdef object ensure_is_provider(object instance):
|
||||||
:rtype: :py:class:`dependency_injector.providers.Provider`
|
:rtype: :py:class:`dependency_injector.providers.Provider`
|
||||||
"""
|
"""
|
||||||
if not is_provider(instance):
|
if not is_provider(instance):
|
||||||
raise errors.Error('Expected provider instance, '
|
raise Error('Expected provider instance, '
|
||||||
'got {0}'.format(str(instance)))
|
'got {0}'.format(str(instance)))
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
|
||||||
cpdef bint is_delegated(object instance):
|
def is_delegated(instance):
|
||||||
"""Check if instance is delegated provider.
|
"""Check if instance is delegated provider.
|
||||||
|
|
||||||
:param instance: Instance to be checked.
|
:param instance: Instance to be checked.
|
||||||
|
@ -67,22 +50,11 @@ cpdef bint is_delegated(object instance):
|
||||||
|
|
||||||
:rtype: bool
|
:rtype: bool
|
||||||
"""
|
"""
|
||||||
return (not isinstance(instance, CLASS_TYPES) and
|
return (not isinstance(instance, six.class_types) and
|
||||||
getattr(instance, '__IS_DELEGATED__', False) is True)
|
getattr(instance, '__IS_DELEGATED__', False) is True)
|
||||||
|
|
||||||
|
|
||||||
cpdef bint is_container(object instance):
|
def represent_provider(provider, provides):
|
||||||
"""Check if instance is container instance.
|
|
||||||
|
|
||||||
:param instance: Instance to be checked.
|
|
||||||
:type instance: object
|
|
||||||
|
|
||||||
:rtype: bool
|
|
||||||
"""
|
|
||||||
return getattr(instance, '__IS_CONTAINER__', False) is True
|
|
||||||
|
|
||||||
|
|
||||||
cpdef str represent_provider(object provider, object provides):
|
|
||||||
"""Return string representation of provider.
|
"""Return string representation of provider.
|
||||||
|
|
||||||
:param provider: Provider object
|
:param provider: Provider object
|
||||||
|
@ -99,9 +71,3 @@ cpdef str represent_provider(object provider, object provides):
|
||||||
provider.__class__.__name__)),
|
provider.__class__.__name__)),
|
||||||
provides=repr(provides) if provides is not None else '',
|
provides=repr(provides) if provides is not None else '',
|
||||||
address=hex(id(provider)))
|
address=hex(id(provider)))
|
||||||
|
|
||||||
|
|
||||||
cpdef object deepcopy(object instance, dict memo=None):
|
|
||||||
"""Make full copy of instance."""
|
|
||||||
return _copy.deepcopy(instance, memo)
|
|
||||||
|
|
1
tests/unit/containers/__init__.py
Normal file
1
tests/unit/containers/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
"""Dependency injector container unit tests."""
|
|
@ -1,4 +1,4 @@
|
||||||
"""Dependency injector container unit tests."""
|
"""Dependency injector declarative container unit tests."""
|
||||||
|
|
||||||
import unittest2 as unittest
|
import unittest2 as unittest
|
||||||
|
|
||||||
|
@ -10,27 +10,18 @@ from dependency_injector import (
|
||||||
|
|
||||||
|
|
||||||
class ContainerA(containers.DeclarativeContainer):
|
class ContainerA(containers.DeclarativeContainer):
|
||||||
"""Declarative IoC container A."""
|
|
||||||
|
|
||||||
p11 = providers.Provider()
|
p11 = providers.Provider()
|
||||||
p12 = providers.Provider()
|
p12 = providers.Provider()
|
||||||
|
|
||||||
|
|
||||||
class ContainerB(ContainerA):
|
class ContainerB(ContainerA):
|
||||||
"""Declarative IoC container B.
|
|
||||||
|
|
||||||
Extends container A.
|
|
||||||
"""
|
|
||||||
|
|
||||||
p21 = providers.Provider()
|
p21 = providers.Provider()
|
||||||
p22 = providers.Provider()
|
p22 = providers.Provider()
|
||||||
|
|
||||||
|
|
||||||
class DeclarativeContainerTests(unittest.TestCase):
|
class DeclarativeContainerTests(unittest.TestCase):
|
||||||
"""Declarative container tests."""
|
|
||||||
|
|
||||||
def test_providers_attribute(self):
|
def test_providers_attribute(self):
|
||||||
"""Test providers attribute."""
|
|
||||||
self.assertEqual(ContainerA.providers, dict(p11=ContainerA.p11,
|
self.assertEqual(ContainerA.providers, dict(p11=ContainerA.p11,
|
||||||
p12=ContainerA.p12))
|
p12=ContainerA.p12))
|
||||||
self.assertEqual(ContainerB.providers, dict(p11=ContainerA.p11,
|
self.assertEqual(ContainerB.providers, dict(p11=ContainerA.p11,
|
||||||
|
@ -39,21 +30,18 @@ class DeclarativeContainerTests(unittest.TestCase):
|
||||||
p22=ContainerB.p22))
|
p22=ContainerB.p22))
|
||||||
|
|
||||||
def test_cls_providers_attribute(self):
|
def test_cls_providers_attribute(self):
|
||||||
"""Test cls_providers attribute."""
|
|
||||||
self.assertEqual(ContainerA.cls_providers, dict(p11=ContainerA.p11,
|
self.assertEqual(ContainerA.cls_providers, dict(p11=ContainerA.p11,
|
||||||
p12=ContainerA.p12))
|
p12=ContainerA.p12))
|
||||||
self.assertEqual(ContainerB.cls_providers, dict(p21=ContainerB.p21,
|
self.assertEqual(ContainerB.cls_providers, dict(p21=ContainerB.p21,
|
||||||
p22=ContainerB.p22))
|
p22=ContainerB.p22))
|
||||||
|
|
||||||
def test_inherited_providers_attribute(self):
|
def test_inherited_providers_attribute(self):
|
||||||
"""Test inherited_providers attribute."""
|
|
||||||
self.assertEqual(ContainerA.inherited_providers, dict())
|
self.assertEqual(ContainerA.inherited_providers, dict())
|
||||||
self.assertEqual(ContainerB.inherited_providers,
|
self.assertEqual(ContainerB.inherited_providers,
|
||||||
dict(p11=ContainerA.p11,
|
dict(p11=ContainerA.p11,
|
||||||
p12=ContainerA.p12))
|
p12=ContainerA.p12))
|
||||||
|
|
||||||
def test_set_get_del_providers(self):
|
def test_set_get_del_providers(self):
|
||||||
"""Test set/get/del provider attributes."""
|
|
||||||
a_p13 = providers.Provider()
|
a_p13 = providers.Provider()
|
||||||
b_p23 = providers.Provider()
|
b_p23 = providers.Provider()
|
||||||
|
|
||||||
|
@ -92,7 +80,6 @@ class DeclarativeContainerTests(unittest.TestCase):
|
||||||
p22=ContainerB.p22))
|
p22=ContainerB.p22))
|
||||||
|
|
||||||
def test_declare_with_valid_provider_type(self):
|
def test_declare_with_valid_provider_type(self):
|
||||||
"""Test declaration of container with valid provider type."""
|
|
||||||
class _Container(containers.DeclarativeContainer):
|
class _Container(containers.DeclarativeContainer):
|
||||||
provider_type = providers.Object
|
provider_type = providers.Object
|
||||||
px = providers.Object(object())
|
px = providers.Object(object())
|
||||||
|
@ -100,14 +87,12 @@ class DeclarativeContainerTests(unittest.TestCase):
|
||||||
self.assertIsInstance(_Container.px, providers.Object)
|
self.assertIsInstance(_Container.px, providers.Object)
|
||||||
|
|
||||||
def test_declare_with_invalid_provider_type(self):
|
def test_declare_with_invalid_provider_type(self):
|
||||||
"""Test declaration of container with invalid provider type."""
|
|
||||||
with self.assertRaises(errors.Error):
|
with self.assertRaises(errors.Error):
|
||||||
class _Container(containers.DeclarativeContainer):
|
class _Container(containers.DeclarativeContainer):
|
||||||
provider_type = providers.Object
|
provider_type = providers.Object
|
||||||
px = providers.Provider()
|
px = providers.Provider()
|
||||||
|
|
||||||
def test_seth_valid_provider_type(self):
|
def test_seth_valid_provider_type(self):
|
||||||
"""Test setting of valid provider."""
|
|
||||||
class _Container(containers.DeclarativeContainer):
|
class _Container(containers.DeclarativeContainer):
|
||||||
provider_type = providers.Object
|
provider_type = providers.Object
|
||||||
|
|
||||||
|
@ -116,7 +101,6 @@ class DeclarativeContainerTests(unittest.TestCase):
|
||||||
self.assertIsInstance(_Container.px, providers.Object)
|
self.assertIsInstance(_Container.px, providers.Object)
|
||||||
|
|
||||||
def test_set_invalid_provider_type(self):
|
def test_set_invalid_provider_type(self):
|
||||||
"""Test setting of invalid provider."""
|
|
||||||
class _Container(containers.DeclarativeContainer):
|
class _Container(containers.DeclarativeContainer):
|
||||||
provider_type = providers.Object
|
provider_type = providers.Object
|
||||||
|
|
||||||
|
@ -124,7 +108,6 @@ class DeclarativeContainerTests(unittest.TestCase):
|
||||||
_Container.px = providers.Provider()
|
_Container.px = providers.Provider()
|
||||||
|
|
||||||
def test_override(self):
|
def test_override(self):
|
||||||
"""Test override."""
|
|
||||||
class _Container(containers.DeclarativeContainer):
|
class _Container(containers.DeclarativeContainer):
|
||||||
p11 = providers.Provider()
|
p11 = providers.Provider()
|
||||||
|
|
||||||
|
@ -146,17 +129,14 @@ class DeclarativeContainerTests(unittest.TestCase):
|
||||||
_OverridingContainer2.p11))
|
_OverridingContainer2.p11))
|
||||||
|
|
||||||
def test_override_with_itself(self):
|
def test_override_with_itself(self):
|
||||||
"""Test override with itself."""
|
|
||||||
with self.assertRaises(errors.Error):
|
with self.assertRaises(errors.Error):
|
||||||
ContainerA.override(ContainerA)
|
ContainerA.override(ContainerA)
|
||||||
|
|
||||||
def test_override_with_parent(self):
|
def test_override_with_parent(self):
|
||||||
"""Test override with parent."""
|
|
||||||
with self.assertRaises(errors.Error):
|
with self.assertRaises(errors.Error):
|
||||||
ContainerB.override(ContainerA)
|
ContainerB.override(ContainerA)
|
||||||
|
|
||||||
def test_override_decorator(self):
|
def test_override_decorator(self):
|
||||||
"""Test override decorator."""
|
|
||||||
class _Container(containers.DeclarativeContainer):
|
class _Container(containers.DeclarativeContainer):
|
||||||
p11 = providers.Provider()
|
p11 = providers.Provider()
|
||||||
|
|
||||||
|
@ -177,7 +157,6 @@ class DeclarativeContainerTests(unittest.TestCase):
|
||||||
_OverridingContainer2.p11))
|
_OverridingContainer2.p11))
|
||||||
|
|
||||||
def test_reset_last_overridding(self):
|
def test_reset_last_overridding(self):
|
||||||
"""Test reset of last overriding."""
|
|
||||||
class _Container(containers.DeclarativeContainer):
|
class _Container(containers.DeclarativeContainer):
|
||||||
p11 = providers.Provider()
|
p11 = providers.Provider()
|
||||||
|
|
||||||
|
@ -198,12 +177,10 @@ class DeclarativeContainerTests(unittest.TestCase):
|
||||||
(_OverridingContainer1.p11,))
|
(_OverridingContainer1.p11,))
|
||||||
|
|
||||||
def test_reset_last_overridding_when_not_overridden(self):
|
def test_reset_last_overridding_when_not_overridden(self):
|
||||||
"""Test reset of last overriding."""
|
|
||||||
with self.assertRaises(errors.Error):
|
with self.assertRaises(errors.Error):
|
||||||
ContainerA.reset_last_overriding()
|
ContainerA.reset_last_overriding()
|
||||||
|
|
||||||
def test_reset_override(self):
|
def test_reset_override(self):
|
||||||
"""Test reset all overridings."""
|
|
||||||
class _Container(containers.DeclarativeContainer):
|
class _Container(containers.DeclarativeContainer):
|
||||||
p11 = providers.Provider()
|
p11 = providers.Provider()
|
||||||
|
|
||||||
|
@ -222,7 +199,6 @@ class DeclarativeContainerTests(unittest.TestCase):
|
||||||
self.assertEqual(_Container.p11.overridden, tuple())
|
self.assertEqual(_Container.p11.overridden, tuple())
|
||||||
|
|
||||||
def test_copy(self):
|
def test_copy(self):
|
||||||
"""Test copy decorator."""
|
|
||||||
@containers.copy(ContainerA)
|
@containers.copy(ContainerA)
|
||||||
class _Container1(ContainerA):
|
class _Container1(ContainerA):
|
||||||
pass
|
pass
|
||||||
|
@ -241,7 +217,6 @@ class DeclarativeContainerTests(unittest.TestCase):
|
||||||
self.assertIsNot(_Container1.p12, _Container2.p12)
|
self.assertIsNot(_Container1.p12, _Container2.p12)
|
||||||
|
|
||||||
def test_copy_with_replacing(self):
|
def test_copy_with_replacing(self):
|
||||||
"""Test copy decorator with providers replacement."""
|
|
||||||
class _Container(containers.DeclarativeContainer):
|
class _Container(containers.DeclarativeContainer):
|
||||||
p11 = providers.Object(0)
|
p11 = providers.Object(0)
|
||||||
p12 = providers.Factory(dict, p11=p11)
|
p12 = providers.Factory(dict, p11=p11)
|
||||||
|
@ -275,163 +250,3 @@ class DeclarativeContainerTests(unittest.TestCase):
|
||||||
|
|
||||||
self.assertEqual(_Container1.p13(), 11)
|
self.assertEqual(_Container1.p13(), 11)
|
||||||
self.assertEqual(_Container2.p13(), 22)
|
self.assertEqual(_Container2.p13(), 22)
|
||||||
|
|
||||||
|
|
||||||
class DeclarativeContainerInstanceTests(unittest.TestCase):
|
|
||||||
"""Declarative container instance tests."""
|
|
||||||
|
|
||||||
def test_providers_attribute(self):
|
|
||||||
"""Test providers attribute."""
|
|
||||||
container_a1 = ContainerA()
|
|
||||||
container_a2 = ContainerA()
|
|
||||||
|
|
||||||
self.assertIsNot(container_a1.p11, container_a2.p11)
|
|
||||||
self.assertIsNot(container_a1.p12, container_a2.p12)
|
|
||||||
self.assertNotEqual(container_a1.providers, container_a2.providers)
|
|
||||||
|
|
||||||
def test_set_get_del_providers(self):
|
|
||||||
"""Test set/get/del provider attributes."""
|
|
||||||
p13 = providers.Provider()
|
|
||||||
|
|
||||||
container_a1 = ContainerA()
|
|
||||||
container_a2 = ContainerA()
|
|
||||||
|
|
||||||
container_a1.p13 = p13
|
|
||||||
container_a2.p13 = p13
|
|
||||||
|
|
||||||
self.assertEqual(ContainerA.providers, dict(p11=ContainerA.p11,
|
|
||||||
p12=ContainerA.p12))
|
|
||||||
self.assertEqual(ContainerA.cls_providers, dict(p11=ContainerA.p11,
|
|
||||||
p12=ContainerA.p12))
|
|
||||||
|
|
||||||
self.assertEqual(container_a1.providers, dict(p11=container_a1.p11,
|
|
||||||
p12=container_a1.p12,
|
|
||||||
p13=p13))
|
|
||||||
self.assertEqual(container_a2.providers, dict(p11=container_a2.p11,
|
|
||||||
p12=container_a2.p12,
|
|
||||||
p13=p13))
|
|
||||||
|
|
||||||
del container_a1.p13
|
|
||||||
self.assertEqual(container_a1.providers, dict(p11=container_a1.p11,
|
|
||||||
p12=container_a1.p12))
|
|
||||||
|
|
||||||
del container_a2.p13
|
|
||||||
self.assertEqual(container_a2.providers, dict(p11=container_a2.p11,
|
|
||||||
p12=container_a2.p12))
|
|
||||||
|
|
||||||
del container_a1.p11
|
|
||||||
del container_a1.p12
|
|
||||||
self.assertEqual(container_a1.providers, dict())
|
|
||||||
self.assertEqual(ContainerA.providers, dict(p11=ContainerA.p11,
|
|
||||||
p12=ContainerA.p12))
|
|
||||||
|
|
||||||
del container_a2.p11
|
|
||||||
del container_a2.p12
|
|
||||||
self.assertEqual(container_a2.providers, dict())
|
|
||||||
self.assertEqual(ContainerA.providers, dict(p11=ContainerA.p11,
|
|
||||||
p12=ContainerA.p12))
|
|
||||||
|
|
||||||
def test_set_invalid_provider_type(self):
|
|
||||||
"""Test setting of invalid provider."""
|
|
||||||
container_a = ContainerA()
|
|
||||||
container_a.provider_type = providers.Object
|
|
||||||
|
|
||||||
with self.assertRaises(errors.Error):
|
|
||||||
container_a.px = providers.Provider()
|
|
||||||
|
|
||||||
self.assertIs(ContainerA.provider_type,
|
|
||||||
containers.DeclarativeContainer.provider_type)
|
|
||||||
|
|
||||||
def test_override(self):
|
|
||||||
"""Test override."""
|
|
||||||
class _Container(containers.DeclarativeContainer):
|
|
||||||
p11 = providers.Provider()
|
|
||||||
|
|
||||||
class _OverridingContainer1(containers.DeclarativeContainer):
|
|
||||||
p11 = providers.Provider()
|
|
||||||
|
|
||||||
class _OverridingContainer2(containers.DeclarativeContainer):
|
|
||||||
p11 = providers.Provider()
|
|
||||||
p12 = providers.Provider()
|
|
||||||
|
|
||||||
container = _Container()
|
|
||||||
overriding_container1 = _OverridingContainer1()
|
|
||||||
overriding_container2 = _OverridingContainer2()
|
|
||||||
|
|
||||||
container.override(overriding_container1)
|
|
||||||
container.override(overriding_container2)
|
|
||||||
|
|
||||||
self.assertEqual(container.overridden,
|
|
||||||
(overriding_container1,
|
|
||||||
overriding_container2))
|
|
||||||
self.assertEqual(container.p11.overridden,
|
|
||||||
(overriding_container1.p11,
|
|
||||||
overriding_container2.p11))
|
|
||||||
|
|
||||||
self.assertEqual(_Container.overridden, tuple())
|
|
||||||
self.assertEqual(_Container.p11.overridden, tuple())
|
|
||||||
|
|
||||||
def test_override_with_itself(self):
|
|
||||||
"""Test override container with itself."""
|
|
||||||
container = ContainerA()
|
|
||||||
with self.assertRaises(errors.Error):
|
|
||||||
container.override(container)
|
|
||||||
|
|
||||||
def test_reset_last_overridding(self):
|
|
||||||
"""Test reset of last overriding."""
|
|
||||||
class _Container(containers.DeclarativeContainer):
|
|
||||||
p11 = providers.Provider()
|
|
||||||
|
|
||||||
class _OverridingContainer1(containers.DeclarativeContainer):
|
|
||||||
p11 = providers.Provider()
|
|
||||||
|
|
||||||
class _OverridingContainer2(containers.DeclarativeContainer):
|
|
||||||
p11 = providers.Provider()
|
|
||||||
p12 = providers.Provider()
|
|
||||||
|
|
||||||
container = _Container()
|
|
||||||
overriding_container1 = _OverridingContainer1()
|
|
||||||
overriding_container2 = _OverridingContainer2()
|
|
||||||
|
|
||||||
container.override(overriding_container1)
|
|
||||||
container.override(overriding_container2)
|
|
||||||
container.reset_last_overriding()
|
|
||||||
|
|
||||||
self.assertEqual(container.overridden,
|
|
||||||
(overriding_container1,))
|
|
||||||
self.assertEqual(container.p11.overridden,
|
|
||||||
(overriding_container1.p11,))
|
|
||||||
|
|
||||||
def test_reset_last_overridding_when_not_overridden(self):
|
|
||||||
"""Test reset of last overriding."""
|
|
||||||
container = ContainerA()
|
|
||||||
|
|
||||||
with self.assertRaises(errors.Error):
|
|
||||||
container.reset_last_overriding()
|
|
||||||
|
|
||||||
def test_reset_override(self):
|
|
||||||
"""Test reset all overridings."""
|
|
||||||
class _Container(containers.DeclarativeContainer):
|
|
||||||
p11 = providers.Provider()
|
|
||||||
|
|
||||||
class _OverridingContainer1(containers.DeclarativeContainer):
|
|
||||||
p11 = providers.Provider()
|
|
||||||
|
|
||||||
class _OverridingContainer2(containers.DeclarativeContainer):
|
|
||||||
p11 = providers.Provider()
|
|
||||||
p12 = providers.Provider()
|
|
||||||
|
|
||||||
container = _Container()
|
|
||||||
overriding_container1 = _OverridingContainer1()
|
|
||||||
overriding_container2 = _OverridingContainer2()
|
|
||||||
|
|
||||||
container.override(overriding_container1)
|
|
||||||
container.override(overriding_container2)
|
|
||||||
container.reset_override()
|
|
||||||
|
|
||||||
self.assertEqual(container.overridden, tuple())
|
|
||||||
self.assertEqual(container.p11.overridden, tuple())
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
unittest.main()
|
|
165
tests/unit/containers/test_dynamic.py
Normal file
165
tests/unit/containers/test_dynamic.py
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
"""Dependency injector dynamic container unit tests."""
|
||||||
|
|
||||||
|
import unittest2 as unittest
|
||||||
|
|
||||||
|
from dependency_injector import (
|
||||||
|
containers,
|
||||||
|
providers,
|
||||||
|
errors,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ContainerA(containers.DeclarativeContainer):
|
||||||
|
p11 = providers.Provider()
|
||||||
|
p12 = providers.Provider()
|
||||||
|
|
||||||
|
|
||||||
|
class DeclarativeContainerInstanceTests(unittest.TestCase):
|
||||||
|
|
||||||
|
def test_providers_attribute(self):
|
||||||
|
container_a1 = ContainerA()
|
||||||
|
container_a2 = ContainerA()
|
||||||
|
|
||||||
|
self.assertIsNot(container_a1.p11, container_a2.p11)
|
||||||
|
self.assertIsNot(container_a1.p12, container_a2.p12)
|
||||||
|
self.assertNotEqual(container_a1.providers, container_a2.providers)
|
||||||
|
|
||||||
|
def test_set_get_del_providers(self):
|
||||||
|
p13 = providers.Provider()
|
||||||
|
|
||||||
|
container_a1 = ContainerA()
|
||||||
|
container_a2 = ContainerA()
|
||||||
|
|
||||||
|
container_a1.p13 = p13
|
||||||
|
container_a2.p13 = p13
|
||||||
|
|
||||||
|
self.assertEqual(ContainerA.providers, dict(p11=ContainerA.p11,
|
||||||
|
p12=ContainerA.p12))
|
||||||
|
self.assertEqual(ContainerA.cls_providers, dict(p11=ContainerA.p11,
|
||||||
|
p12=ContainerA.p12))
|
||||||
|
|
||||||
|
self.assertEqual(container_a1.providers, dict(p11=container_a1.p11,
|
||||||
|
p12=container_a1.p12,
|
||||||
|
p13=p13))
|
||||||
|
self.assertEqual(container_a2.providers, dict(p11=container_a2.p11,
|
||||||
|
p12=container_a2.p12,
|
||||||
|
p13=p13))
|
||||||
|
|
||||||
|
del container_a1.p13
|
||||||
|
self.assertEqual(container_a1.providers, dict(p11=container_a1.p11,
|
||||||
|
p12=container_a1.p12))
|
||||||
|
|
||||||
|
del container_a2.p13
|
||||||
|
self.assertEqual(container_a2.providers, dict(p11=container_a2.p11,
|
||||||
|
p12=container_a2.p12))
|
||||||
|
|
||||||
|
del container_a1.p11
|
||||||
|
del container_a1.p12
|
||||||
|
self.assertEqual(container_a1.providers, dict())
|
||||||
|
self.assertEqual(ContainerA.providers, dict(p11=ContainerA.p11,
|
||||||
|
p12=ContainerA.p12))
|
||||||
|
|
||||||
|
del container_a2.p11
|
||||||
|
del container_a2.p12
|
||||||
|
self.assertEqual(container_a2.providers, dict())
|
||||||
|
self.assertEqual(ContainerA.providers, dict(p11=ContainerA.p11,
|
||||||
|
p12=ContainerA.p12))
|
||||||
|
|
||||||
|
def test_set_invalid_provider_type(self):
|
||||||
|
container_a = ContainerA()
|
||||||
|
container_a.provider_type = providers.Object
|
||||||
|
|
||||||
|
with self.assertRaises(errors.Error):
|
||||||
|
container_a.px = providers.Provider()
|
||||||
|
|
||||||
|
self.assertIs(ContainerA.provider_type,
|
||||||
|
containers.DeclarativeContainer.provider_type)
|
||||||
|
|
||||||
|
def test_override(self):
|
||||||
|
class _Container(containers.DeclarativeContainer):
|
||||||
|
p11 = providers.Provider()
|
||||||
|
|
||||||
|
class _OverridingContainer1(containers.DeclarativeContainer):
|
||||||
|
p11 = providers.Provider()
|
||||||
|
|
||||||
|
class _OverridingContainer2(containers.DeclarativeContainer):
|
||||||
|
p11 = providers.Provider()
|
||||||
|
p12 = providers.Provider()
|
||||||
|
|
||||||
|
container = _Container()
|
||||||
|
overriding_container1 = _OverridingContainer1()
|
||||||
|
overriding_container2 = _OverridingContainer2()
|
||||||
|
|
||||||
|
container.override(overriding_container1)
|
||||||
|
container.override(overriding_container2)
|
||||||
|
|
||||||
|
self.assertEqual(container.overridden,
|
||||||
|
(overriding_container1,
|
||||||
|
overriding_container2))
|
||||||
|
self.assertEqual(container.p11.overridden,
|
||||||
|
(overriding_container1.p11,
|
||||||
|
overriding_container2.p11))
|
||||||
|
|
||||||
|
self.assertEqual(_Container.overridden, tuple())
|
||||||
|
self.assertEqual(_Container.p11.overridden, tuple())
|
||||||
|
|
||||||
|
def test_override_with_itself(self):
|
||||||
|
container = ContainerA()
|
||||||
|
with self.assertRaises(errors.Error):
|
||||||
|
container.override(container)
|
||||||
|
|
||||||
|
def test_reset_last_overridding(self):
|
||||||
|
class _Container(containers.DeclarativeContainer):
|
||||||
|
p11 = providers.Provider()
|
||||||
|
|
||||||
|
class _OverridingContainer1(containers.DeclarativeContainer):
|
||||||
|
p11 = providers.Provider()
|
||||||
|
|
||||||
|
class _OverridingContainer2(containers.DeclarativeContainer):
|
||||||
|
p11 = providers.Provider()
|
||||||
|
p12 = providers.Provider()
|
||||||
|
|
||||||
|
container = _Container()
|
||||||
|
overriding_container1 = _OverridingContainer1()
|
||||||
|
overriding_container2 = _OverridingContainer2()
|
||||||
|
|
||||||
|
container.override(overriding_container1)
|
||||||
|
container.override(overriding_container2)
|
||||||
|
container.reset_last_overriding()
|
||||||
|
|
||||||
|
self.assertEqual(container.overridden,
|
||||||
|
(overriding_container1,))
|
||||||
|
self.assertEqual(container.p11.overridden,
|
||||||
|
(overriding_container1.p11,))
|
||||||
|
|
||||||
|
def test_reset_last_overridding_when_not_overridden(self):
|
||||||
|
container = ContainerA()
|
||||||
|
|
||||||
|
with self.assertRaises(errors.Error):
|
||||||
|
container.reset_last_overriding()
|
||||||
|
|
||||||
|
def test_reset_override(self):
|
||||||
|
class _Container(containers.DeclarativeContainer):
|
||||||
|
p11 = providers.Provider()
|
||||||
|
|
||||||
|
class _OverridingContainer1(containers.DeclarativeContainer):
|
||||||
|
p11 = providers.Provider()
|
||||||
|
|
||||||
|
class _OverridingContainer2(containers.DeclarativeContainer):
|
||||||
|
p11 = providers.Provider()
|
||||||
|
p12 = providers.Provider()
|
||||||
|
|
||||||
|
container = _Container()
|
||||||
|
overriding_container1 = _OverridingContainer1()
|
||||||
|
overriding_container2 = _OverridingContainer2()
|
||||||
|
|
||||||
|
container.override(overriding_container1)
|
||||||
|
container.override(overriding_container2)
|
||||||
|
container.reset_override()
|
||||||
|
|
||||||
|
self.assertEqual(container.overridden, tuple())
|
||||||
|
self.assertEqual(container.p11.overridden, tuple())
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
|
@ -2,22 +2,21 @@
|
||||||
|
|
||||||
import unittest2 as unittest
|
import unittest2 as unittest
|
||||||
|
|
||||||
from dependency_injector import injections
|
|
||||||
from dependency_injector import providers
|
from dependency_injector import providers
|
||||||
|
|
||||||
|
|
||||||
class PositionalInjectionTests(unittest.TestCase):
|
class PositionalInjectionTests(unittest.TestCase):
|
||||||
|
|
||||||
def test_isinstance(self):
|
def test_isinstance(self):
|
||||||
injection = injections.PositionalInjection(1)
|
injection = providers.PositionalInjection(1)
|
||||||
self.assertIsInstance(injection, injections.Injection)
|
self.assertIsInstance(injection, providers.Injection)
|
||||||
|
|
||||||
def test_get_value_with_not_provider(self):
|
def test_get_value_with_not_provider(self):
|
||||||
injection = injections.PositionalInjection(123)
|
injection = providers.PositionalInjection(123)
|
||||||
self.assertEquals(injection.get_value(), 123)
|
self.assertEquals(injection.get_value(), 123)
|
||||||
|
|
||||||
def test_get_value_with_factory(self):
|
def test_get_value_with_factory(self):
|
||||||
injection = injections.PositionalInjection(providers.Factory(object))
|
injection = providers.PositionalInjection(providers.Factory(object))
|
||||||
|
|
||||||
obj1 = injection.get_value()
|
obj1 = injection.get_value()
|
||||||
obj2 = injection.get_value()
|
obj2 = injection.get_value()
|
||||||
|
@ -30,20 +29,20 @@ class PositionalInjectionTests(unittest.TestCase):
|
||||||
class NamedInjectionTests(unittest.TestCase):
|
class NamedInjectionTests(unittest.TestCase):
|
||||||
|
|
||||||
def test_isinstance(self):
|
def test_isinstance(self):
|
||||||
injection = injections.NamedInjection('name', 1)
|
injection = providers.NamedInjection('name', 1)
|
||||||
self.assertIsInstance(injection, injections.Injection)
|
self.assertIsInstance(injection, providers.Injection)
|
||||||
|
|
||||||
def test_get_name(self):
|
def test_get_name(self):
|
||||||
injection = injections.NamedInjection('name', 123)
|
injection = providers.NamedInjection('name', 123)
|
||||||
self.assertEquals(injection.get_name(), 'name')
|
self.assertEquals(injection.get_name(), 'name')
|
||||||
|
|
||||||
def test_get_value_with_not_provider(self):
|
def test_get_value_with_not_provider(self):
|
||||||
injection = injections.NamedInjection('name', 123)
|
injection = providers.NamedInjection('name', 123)
|
||||||
self.assertEquals(injection.get_value(), 123)
|
self.assertEquals(injection.get_value(), 123)
|
||||||
|
|
||||||
def test_get_value_with_factory(self):
|
def test_get_value_with_factory(self):
|
||||||
injection = injections.NamedInjection('name',
|
injection = providers.NamedInjection('name',
|
||||||
providers.Factory(object))
|
providers.Factory(object))
|
||||||
|
|
||||||
obj1 = injection.get_value()
|
obj1 = injection.get_value()
|
||||||
obj2 = injection.get_value()
|
obj2 = injection.get_value()
|
Loading…
Reference in New Issue
Block a user