diff --git a/setup.py b/setup.py index df827713..cda5c098 100644 --- a/setup.py +++ b/setup.py @@ -40,22 +40,15 @@ setup(name='dependency-injector', install_requires=requirements, packages=[ 'dependency_injector', + 'dependency_injector.containers', 'dependency_injector.providers', ], package_dir={ '': 'src', }, ext_modules=[ - Extension('dependency_injector.injections', - ['src/dependency_injector/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'], + Extension('dependency_injector.providers.injections', + ['src/dependency_injector/providers/injections.c'], define_macros=defined_macros, extra_compile_args=['-O2']), ], diff --git a/src/dependency_injector/containers.py b/src/dependency_injector/containers.py deleted file mode 100644 index 8fc8bea9..00000000 --- a/src/dependency_injector/containers.py +++ /dev/null @@ -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)) diff --git a/src/dependency_injector/containers/__init__.py b/src/dependency_injector/containers/__init__.py new file mode 100644 index 00000000..bf0fef20 --- /dev/null +++ b/src/dependency_injector/containers/__init__.py @@ -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', +) diff --git a/src/dependency_injector/containers/declarative.py b/src/dependency_injector/containers/declarative.py new file mode 100644 index 00000000..1a7018d0 --- /dev/null +++ b/src/dependency_injector/containers/declarative.py @@ -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() diff --git a/src/dependency_injector/containers/dynamic.py b/src/dependency_injector/containers/dynamic.py new file mode 100644 index 00000000..d2af5702 --- /dev/null +++ b/src/dependency_injector/containers/dynamic.py @@ -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() diff --git a/src/dependency_injector/containers/utils.py b/src/dependency_injector/containers/utils.py new file mode 100644 index 00000000..62b6f74f --- /dev/null +++ b/src/dependency_injector/containers/utils.py @@ -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)) diff --git a/src/dependency_injector/errors.pxd b/src/dependency_injector/errors.pxd deleted file mode 100644 index 8a274eef..00000000 --- a/src/dependency_injector/errors.pxd +++ /dev/null @@ -1,8 +0,0 @@ -"""Dependency injector errors. - -Powered by Cython. -""" - - -cdef class Error(Exception): - pass diff --git a/src/dependency_injector/errors.pyx b/src/dependency_injector/errors.py similarity index 51% rename from src/dependency_injector/errors.pyx rename to src/dependency_injector/errors.py index f37c28dc..548b0057 100644 --- a/src/dependency_injector/errors.pyx +++ b/src/dependency_injector/errors.py @@ -1,10 +1,7 @@ -"""Dependency injector errors. - -Powered by Cython. -""" +"""Dependency injector errors.""" -cdef class Error(Exception): +class Error(Exception): """Base error. All dependency injector errors extend this error class. diff --git a/src/dependency_injector/providers/__init__.py b/src/dependency_injector/providers/__init__.py index b289ec39..0485998a 100644 --- a/src/dependency_injector/providers/__init__.py +++ b/src/dependency_injector/providers/__init__.py @@ -1,6 +1,6 @@ -"""Dependency injector providers package.""" +"""Dependency injector providers.""" -from dependency_injector.providers.base import ( +from .base import ( Provider, Delegate, Object, @@ -8,17 +8,22 @@ from dependency_injector.providers.base import ( OverridingContext, override, ) -from dependency_injector.providers.callable import ( +from .callable import ( Callable, DelegatedCallable, ) -from dependency_injector.providers.creational import ( +from .creational import ( Factory, DelegatedFactory, Singleton, DelegatedSingleton, ThreadLocalSingleton, - DelegatedThreadLocalSingleton + DelegatedThreadLocalSingleton, +) +from .injections import ( + Injection, + PositionalInjection, + NamedInjection, ) @@ -42,4 +47,8 @@ __all__ = ( 'ThreadLocalSingleton', 'DelegatedThreadLocalSingleton', + + 'Injection', + 'PositionalInjection', + 'NamedInjection', ) diff --git a/src/dependency_injector/injections.pxd b/src/dependency_injector/providers/injections.pxd similarity index 100% rename from src/dependency_injector/injections.pxd rename to src/dependency_injector/providers/injections.pxd diff --git a/src/dependency_injector/injections.pyx b/src/dependency_injector/providers/injections.pyx similarity index 98% rename from src/dependency_injector/injections.pyx rename to src/dependency_injector/providers/injections.pyx index 39e41593..124d56a6 100644 --- a/src/dependency_injector/injections.pyx +++ b/src/dependency_injector/providers/injections.pyx @@ -5,7 +5,7 @@ Powered by Cython. cimport cython -from .utils cimport ( +from dependency_injector.utils import ( is_provider, is_delegated, ) diff --git a/src/dependency_injector/providers/utils.py b/src/dependency_injector/providers/utils.py new file mode 100644 index 00000000..e69de29b diff --git a/src/dependency_injector/utils.pxd b/src/dependency_injector/utils.pxd deleted file mode 100644 index 6fb93278..00000000 --- a/src/dependency_injector/utils.pxd +++ /dev/null @@ -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=*) diff --git a/src/dependency_injector/utils.pyx b/src/dependency_injector/utils.py similarity index 51% rename from src/dependency_injector/utils.pyx rename to src/dependency_injector/utils.py index 8433389e..bb4ffc86 100644 --- a/src/dependency_injector/utils.pyx +++ b/src/dependency_injector/utils.py @@ -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 +from .errors import Error GLOBAL_LOCK = threading.RLock() -"""Dependency injector global reentrant lock. +"""Global reentrant lock. :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] = \ - 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): +def is_provider(instance): """Check if instance is provider instance. :param instance: Instance to be checked. @@ -38,11 +21,11 @@ cpdef bint is_provider(object instance): :rtype: bool """ - return (not isinstance(instance, CLASS_TYPES) and + return (not isinstance(instance, six.class_types) and 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. :param instance: Instance to be checked. @@ -54,12 +37,12 @@ cpdef object ensure_is_provider(object instance): :rtype: :py:class:`dependency_injector.providers.Provider` """ if not is_provider(instance): - raise errors.Error('Expected provider instance, ' - 'got {0}'.format(str(instance))) + raise Error('Expected provider instance, ' + 'got {0}'.format(str(instance))) return instance -cpdef bint is_delegated(object instance): +def is_delegated(instance): """Check if instance is delegated provider. :param instance: Instance to be checked. @@ -67,22 +50,11 @@ cpdef bint is_delegated(object instance): :rtype: bool """ - return (not isinstance(instance, CLASS_TYPES) and + return (not isinstance(instance, six.class_types) and getattr(instance, '__IS_DELEGATED__', False) is True) -cpdef bint is_container(object 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 - - -cpdef str represent_provider(object provider, object provides): +def represent_provider(provider, provides): """Return string representation of provider. :param provider: Provider object @@ -99,9 +71,3 @@ cpdef str represent_provider(object provider, object provides): provider.__class__.__name__)), provides=repr(provides) if provides is not None else '', address=hex(id(provider))) - - -cpdef object deepcopy(object instance, dict memo=None): - """Make full copy of instance.""" - return _copy.deepcopy(instance, memo) - diff --git a/tests/unit/containers/__init__.py b/tests/unit/containers/__init__.py new file mode 100644 index 00000000..69cef935 --- /dev/null +++ b/tests/unit/containers/__init__.py @@ -0,0 +1 @@ +"""Dependency injector container unit tests.""" diff --git a/tests/unit/test_containers.py b/tests/unit/containers/test_declarative.py similarity index 57% rename from tests/unit/test_containers.py rename to tests/unit/containers/test_declarative.py index 353e770b..d8a50a33 100644 --- a/tests/unit/test_containers.py +++ b/tests/unit/containers/test_declarative.py @@ -1,4 +1,4 @@ -"""Dependency injector container unit tests.""" +"""Dependency injector declarative container unit tests.""" import unittest2 as unittest @@ -10,27 +10,18 @@ from dependency_injector import ( class ContainerA(containers.DeclarativeContainer): - """Declarative IoC container A.""" - p11 = providers.Provider() p12 = providers.Provider() class ContainerB(ContainerA): - """Declarative IoC container B. - - Extends container A. - """ - p21 = providers.Provider() p22 = providers.Provider() class DeclarativeContainerTests(unittest.TestCase): - """Declarative container tests.""" def test_providers_attribute(self): - """Test providers attribute.""" self.assertEqual(ContainerA.providers, dict(p11=ContainerA.p11, p12=ContainerA.p12)) self.assertEqual(ContainerB.providers, dict(p11=ContainerA.p11, @@ -39,21 +30,18 @@ class DeclarativeContainerTests(unittest.TestCase): p22=ContainerB.p22)) def test_cls_providers_attribute(self): - """Test cls_providers attribute.""" self.assertEqual(ContainerA.cls_providers, dict(p11=ContainerA.p11, p12=ContainerA.p12)) self.assertEqual(ContainerB.cls_providers, dict(p21=ContainerB.p21, p22=ContainerB.p22)) def test_inherited_providers_attribute(self): - """Test inherited_providers attribute.""" self.assertEqual(ContainerA.inherited_providers, dict()) self.assertEqual(ContainerB.inherited_providers, dict(p11=ContainerA.p11, p12=ContainerA.p12)) def test_set_get_del_providers(self): - """Test set/get/del provider attributes.""" a_p13 = providers.Provider() b_p23 = providers.Provider() @@ -92,7 +80,6 @@ class DeclarativeContainerTests(unittest.TestCase): p22=ContainerB.p22)) def test_declare_with_valid_provider_type(self): - """Test declaration of container with valid provider type.""" class _Container(containers.DeclarativeContainer): provider_type = providers.Object px = providers.Object(object()) @@ -100,14 +87,12 @@ class DeclarativeContainerTests(unittest.TestCase): self.assertIsInstance(_Container.px, providers.Object) def test_declare_with_invalid_provider_type(self): - """Test declaration of container with invalid provider type.""" with self.assertRaises(errors.Error): class _Container(containers.DeclarativeContainer): provider_type = providers.Object px = providers.Provider() def test_seth_valid_provider_type(self): - """Test setting of valid provider.""" class _Container(containers.DeclarativeContainer): provider_type = providers.Object @@ -116,7 +101,6 @@ class DeclarativeContainerTests(unittest.TestCase): self.assertIsInstance(_Container.px, providers.Object) def test_set_invalid_provider_type(self): - """Test setting of invalid provider.""" class _Container(containers.DeclarativeContainer): provider_type = providers.Object @@ -124,7 +108,6 @@ class DeclarativeContainerTests(unittest.TestCase): _Container.px = providers.Provider() def test_override(self): - """Test override.""" class _Container(containers.DeclarativeContainer): p11 = providers.Provider() @@ -146,17 +129,14 @@ class DeclarativeContainerTests(unittest.TestCase): _OverridingContainer2.p11)) def test_override_with_itself(self): - """Test override with itself.""" with self.assertRaises(errors.Error): ContainerA.override(ContainerA) def test_override_with_parent(self): - """Test override with parent.""" with self.assertRaises(errors.Error): ContainerB.override(ContainerA) def test_override_decorator(self): - """Test override decorator.""" class _Container(containers.DeclarativeContainer): p11 = providers.Provider() @@ -177,7 +157,6 @@ class DeclarativeContainerTests(unittest.TestCase): _OverridingContainer2.p11)) def test_reset_last_overridding(self): - """Test reset of last overriding.""" class _Container(containers.DeclarativeContainer): p11 = providers.Provider() @@ -198,12 +177,10 @@ class DeclarativeContainerTests(unittest.TestCase): (_OverridingContainer1.p11,)) def test_reset_last_overridding_when_not_overridden(self): - """Test reset of last overriding.""" with self.assertRaises(errors.Error): ContainerA.reset_last_overriding() def test_reset_override(self): - """Test reset all overridings.""" class _Container(containers.DeclarativeContainer): p11 = providers.Provider() @@ -222,7 +199,6 @@ class DeclarativeContainerTests(unittest.TestCase): self.assertEqual(_Container.p11.overridden, tuple()) def test_copy(self): - """Test copy decorator.""" @containers.copy(ContainerA) class _Container1(ContainerA): pass @@ -241,7 +217,6 @@ class DeclarativeContainerTests(unittest.TestCase): self.assertIsNot(_Container1.p12, _Container2.p12) def test_copy_with_replacing(self): - """Test copy decorator with providers replacement.""" class _Container(containers.DeclarativeContainer): p11 = providers.Object(0) p12 = providers.Factory(dict, p11=p11) @@ -275,163 +250,3 @@ class DeclarativeContainerTests(unittest.TestCase): self.assertEqual(_Container1.p13(), 11) 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() diff --git a/tests/unit/containers/test_dynamic.py b/tests/unit/containers/test_dynamic.py new file mode 100644 index 00000000..70b20e17 --- /dev/null +++ b/tests/unit/containers/test_dynamic.py @@ -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() diff --git a/tests/unit/test_injections.py b/tests/unit/providers/test_injections.py similarity index 61% rename from tests/unit/test_injections.py rename to tests/unit/providers/test_injections.py index 5ee6036c..ea2a4924 100644 --- a/tests/unit/test_injections.py +++ b/tests/unit/providers/test_injections.py @@ -2,22 +2,21 @@ import unittest2 as unittest -from dependency_injector import injections from dependency_injector import providers class PositionalInjectionTests(unittest.TestCase): def test_isinstance(self): - injection = injections.PositionalInjection(1) - self.assertIsInstance(injection, injections.Injection) + injection = providers.PositionalInjection(1) + self.assertIsInstance(injection, providers.Injection) def test_get_value_with_not_provider(self): - injection = injections.PositionalInjection(123) + injection = providers.PositionalInjection(123) self.assertEquals(injection.get_value(), 123) def test_get_value_with_factory(self): - injection = injections.PositionalInjection(providers.Factory(object)) + injection = providers.PositionalInjection(providers.Factory(object)) obj1 = injection.get_value() obj2 = injection.get_value() @@ -30,20 +29,20 @@ class PositionalInjectionTests(unittest.TestCase): class NamedInjectionTests(unittest.TestCase): def test_isinstance(self): - injection = injections.NamedInjection('name', 1) - self.assertIsInstance(injection, injections.Injection) + injection = providers.NamedInjection('name', 1) + self.assertIsInstance(injection, providers.Injection) def test_get_name(self): - injection = injections.NamedInjection('name', 123) + injection = providers.NamedInjection('name', 123) self.assertEquals(injection.get_name(), 'name') def test_get_value_with_not_provider(self): - injection = injections.NamedInjection('name', 123) + injection = providers.NamedInjection('name', 123) self.assertEquals(injection.get_value(), 123) def test_get_value_with_factory(self): - injection = injections.NamedInjection('name', - providers.Factory(object)) + injection = providers.NamedInjection('name', + providers.Factory(object)) obj1 = injection.get_value() obj2 = injection.get_value()