diff --git a/dependency_injector/injections.py b/dependency_injector/injections.py deleted file mode 100644 index 14c9bd68..00000000 --- a/dependency_injector/injections.py +++ /dev/null @@ -1,113 +0,0 @@ -"""Dependency injector injections module.""" - -import warnings - -import six - -from dependency_injector.providers.base import ( - _parse_positional_injections, - _parse_keyword_injections, -) -from dependency_injector import utils -from dependency_injector import errors - - -def inject(*args, **kwargs): - """Dependency injection decorator. - - .. warning:: - - :py:func:`inject` decorator has been deprecated since version 2.2.0. - - Usage of :py:func:`inject` decorator can lead to bad design and could - be considered as anti-pattern. - - :py:func:`inject` decorator can be used for making inline dependency - injections. It patches decorated callable in such way that dependency - injection will be done during every call of decorated callable. - - :py:func:`inject` decorator supports different syntaxes of passing - injections: - - .. code-block:: python - - # Positional arguments injections: - @inject(1, 2) - def some_function(arg1, arg2): - pass - - # Keyword arguments injections: - @inject(arg1=1) - @inject(arg2=2) - def some_function(arg1, arg2): - pass - - # Keyword arguments injections into class init: - @inject(arg1=1) - @inject(arg2=2) - class SomeClass(object): - - def __init__(self, arg1, arg2): - pass - - .. deprecated:: 2.2.0 - Usage of :py:func:`inject` decorator can lead to bad design and could - be considered as anti-pattern. - - :param args: Tuple of context positional arguments. - :type args: tuple[object] - - :param kwargs: Dictionary of context keyword arguments. - :type kwargs: dict[str, object] - - :return: Class / callable decorator - :rtype: (callable) -> (type | callable) - """ - warnings.warn(message='Call to a deprecated decorator - @{0}.{1}' - .format(inject.__module__, inject.__name__), - category=DeprecationWarning, - stacklevel=2) - - arg_injections = _parse_positional_injections(args) - kwarg_injections = _parse_keyword_injections(kwargs) - - def decorator(callback_or_cls): - """Dependency injection decorator.""" - if isinstance(callback_or_cls, six.class_types): - cls = callback_or_cls - cls_init = utils.fetch_cls_init(cls) - if not cls_init: - raise errors.Error( - 'Class {0}.{1} has no __init__() '.format(cls.__module__, - cls.__name__) + - 'method and could not be decorated with @inject decorator') - cls.__init__ = decorator(cls_init) - return cls - - callback = callback_or_cls - - if hasattr(callback, '__INJECT_DECORATED__'): - callback.args += arg_injections - callback.kwargs.update(kwarg_injections) - return callback - - @six.wraps(callback) - def decorated(*args, **kwargs): - """Decorated with dependency injection callback.""" - if decorated.args: - args = tuple(arg.provide_injection() - for arg in decorated.args) + args - - for name, arg in six.iteritems(decorated.kwargs): - if name not in kwargs: - kwargs[name] = arg.provide_injection() - - return callback(*args, **kwargs) - - decorated.__INJECT_DECORATED__ = True - decorated.origin = callback - decorated.args = arg_injections - decorated.kwargs = kwarg_injections - - return decorated - return decorator diff --git a/dependency_injector/utils.py b/dependency_injector/utils.py index f113bb71..cfe8b31e 100644 --- a/dependency_injector/utils.py +++ b/dependency_injector/utils.py @@ -1,6 +1,5 @@ """Dependency injector utils module.""" -import sys import copy as _copy import types import threading @@ -16,12 +15,6 @@ GLOBAL_LOCK = threading.RLock() :type: :py:class:`threading.RLock` """ -_IS_PYPY = '__pypy__' in sys.builtin_module_names -if _IS_PYPY or six.PY3: # pragma: no cover - _OBJECT_INIT = six.get_unbound_function(object.__init__) -else: # pragma: no cover - _OBJECT_INIT = None - if six.PY2: # pragma: no cover _copy._deepcopy_dispatch[types.MethodType] = \ lambda obj, memo: type(obj)(obj.im_func, @@ -90,24 +83,6 @@ def represent_provider(provider, provides): address=hex(id(provider))) -def fetch_cls_init(cls): - """Return reference to the class.__init__() method if it is defined. - - :param cls: Class instance - :type cls: type - - :return: Reference to the class.__init__() if any, or None otherwise. - :rtype: unbound method | None - """ - try: - cls_init = six.get_unbound_function(cls.__init__) - assert cls_init is not _OBJECT_INIT - except (AttributeError, AssertionError): - return None - else: - return cls_init - - def deepcopy(instance, memo=None): """Make full copy of instance.""" return _copy.deepcopy(instance, memo) diff --git a/docs/advanced_usage/index.rst b/docs/advanced_usage/index.rst deleted file mode 100644 index cd4ce33a..00000000 --- a/docs/advanced_usage/index.rst +++ /dev/null @@ -1,70 +0,0 @@ -Advanced usage -============== - -Current section of documentation describes advanced usage of -*Dependency Injector*. - -@inject decorator ------------------ - -.. currentmodule:: dependency_injector.injections - -.. warning:: - - :py:func:`inject` decorator has been deprecated since version 2.2.0. - -:py:func:`inject` decorator is a part of -:py:mod:`dependency_injector.injections` module. - -:py:func:`inject` decorator can be used for making *inline* dependency -injections. It *patches* decorated callable in such way that dependency -injection will be done during every call of decorated callable. - -:py:func:`inject` takes a various number of positional and keyword arguments -that are used as decorated callable injections. Every time, when -:py:func:`inject` is called, positional and keyword argument injections would -be passed as an callable arguments. - -Such behaviour is very similar to the standard Python ``functools.partial`` -object, except of one thing: all injectable values are provided -*"as is"*, except of providers (subclasses of -:py:class:`dependency_injector.providers.Provider`). Providers -will be called every time, when injection needs to be done. For example, -if injectable value of injection is a -:py:class:`dependency_injector.providers.Factory`, it will provide new one -instance (as a result of its call) every time, when injection needs to be done. - -:py:func:`inject` behaviour with context positional and keyword arguments is -very like a standard Python ``functools.partial``: - -- Positional context arguments will be appended after :py:func:`inject` - positional injections. -- Keyword context arguments have priority on :py:func:`inject` keyword - injections and will be merged over them. - -Example: - -.. literalinclude:: ../../examples/advanced_usage/inject_simple.py - :language: python - :linenos: - -Example of usage :py:func:`inject` decorator with Flask: - -.. literalinclude:: ../../examples/advanced_usage/inject_flask.py - :language: python - :linenos: - - -@inject decorator with classes -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -:py:func:`inject` could be applied for classes. In such case, it will look for -class ``__init__()`` method and pass injection to it. If decorated class has -no ``__init__()`` method, appropriate -:py:exc:`dependency_injector.errors.Error` will be raised. - -Example of usage :py:func:`inject` with Flask class-based view: - -.. literalinclude:: ../../examples/advanced_usage/inject_flask_class_based.py - :language: python - :linenos: diff --git a/docs/api/injections.rst b/docs/api/injections.rst deleted file mode 100644 index 794176c9..00000000 --- a/docs/api/injections.rst +++ /dev/null @@ -1,7 +0,0 @@ -``dependency_injector.injections`` ----------------------------------- - -.. automodule:: dependency_injector.injections - :members: - :inherited-members: - :show-inheritance: diff --git a/docs/main/changelog.rst b/docs/main/changelog.rst index 76b22b9b..4de4f4e6 100644 --- a/docs/main/changelog.rst +++ b/docs/main/changelog.rst @@ -9,7 +9,10 @@ follows `Semantic versioning`_ Development version ------------------- -- No features. +- Remove ``@inject`` decorator. + +.. - No features. + 2.2.10 ------ diff --git a/tests/providers/test_creational.py b/tests/providers/test_creational.py index fc4c71b2..7bb7938c 100644 --- a/tests/providers/test_creational.py +++ b/tests/providers/test_creational.py @@ -2,7 +2,7 @@ import unittest2 as unittest -from dependency_injector import providers, injections, utils, errors +from dependency_injector import providers, utils, errors class Example(object): @@ -731,26 +731,6 @@ class DelegatedThreadLocalSingletonTests(unittest.TestCase): class FactoryAsDecoratorTests(unittest.TestCase): """Factory as decorator tests.""" - def test_decoration(self): - """Test decoration of some class with Factory provider.""" - @providers.Factory - class AuthService(object): - """Auth service.""" - - @providers.Factory - @injections.inject(auth_service=AuthService) - class UsersService(object): - """Users service.""" - - def __init__(self, auth_service): - """Initializer.""" - self.auth_service = auth_service - - users_service = UsersService() - - self.assertIsInstance(users_service, UsersService.cls) - self.assertIsInstance(users_service.auth_service, AuthService.cls) - def test_decoration_and_overriding(self): """Test decoration of some class with Factory provider.""" @providers.Factory diff --git a/tests/test_injections.py b/tests/test_injections.py deleted file mode 100644 index 302ea8de..00000000 --- a/tests/test_injections.py +++ /dev/null @@ -1,202 +0,0 @@ -"""Dependency injector injections unittests.""" - -import warnings - -import unittest2 as unittest - -from dependency_injector import injections -from dependency_injector import providers -from dependency_injector import errors - - -class InjectTests(unittest.TestCase): - """Inject decorator test cases.""" - - def test_decorated_args(self): - """Test `inject()` decoration with args.""" - provider1 = providers.Factory(object) - provider2 = providers.Factory(list) - - @injections.inject(provider1, provider2) - def test(a, b): - return a, b - - a1, b1 = test() - a2, b2 = test() - - self.assertIsInstance(a1, object) - self.assertIsInstance(a2, object) - self.assertIsNot(a1, a2) - - self.assertIsInstance(b1, list) - self.assertIsInstance(b2, list) - self.assertIsNot(b1, b2) - - def test_decorated_args_several_times(self): - """Test `inject()` decoration with args several times.""" - provider1 = providers.Factory(object) - provider2 = providers.Factory(list) - - @injections.inject(provider2) - @injections.inject(provider1) - def test(a, b): - return a, b - - a1, b1 = test() - a2, b2 = test() - - self.assertIsInstance(a1, object) - self.assertIsInstance(a2, object) - self.assertIsNot(a1, a2) - - self.assertIsInstance(b1, list) - self.assertIsInstance(b2, list) - self.assertIsNot(b1, b2) - - def test_decorated_context_args(self): - """Test `inject()` decoration with context args.""" - provider1 = providers.Factory(object) - provider2 = providers.Factory(list) - - @injections.inject(provider1) - def test(a, b): - return a, b - - a1, b1 = test(provider2()) - a2, b2 = test(provider2()) - - self.assertIsInstance(a1, object) - self.assertIsInstance(a2, object) - self.assertIsNot(a1, a2) - - self.assertIsInstance(b1, list) - self.assertIsInstance(b2, list) - self.assertIsNot(b1, b2) - - def test_decorated_kwargs(self): - """Test `inject()` decoration with kwargs.""" - provider1 = providers.Factory(object) - provider2 = providers.Factory(list) - - @injections.inject(a=provider1) - @injections.inject(b=provider2) - def test(a, b): - return a, b - - a1, b1 = test() - a2, b2 = test() - - self.assertIsInstance(a1, object) - self.assertIsInstance(a2, object) - self.assertIsNot(a1, a2) - - self.assertIsInstance(b1, list) - self.assertIsInstance(b2, list) - self.assertIsNot(b1, b2) - - def test_decorated_kwargs_priority(self): - """Test `inject()` decorated callback kwargs priority.""" - provider1 = providers.Factory(object) - provider2 = providers.Factory(list) - object_a = object() - - @injections.inject(a=provider1) - @injections.inject(b=provider2) - def test(a, b): - return a, b - - a1, b1 = test(a=object_a) - a2, b2 = test(a=object_a) - - self.assertIsInstance(a1, object) - self.assertIsInstance(a2, object) - self.assertIs(a1, object_a) - self.assertIs(a2, object_a) - - self.assertIsInstance(b1, list) - self.assertIsInstance(b2, list) - self.assertIsNot(b1, b2) - - def test_decorated_with_args(self): - """Test `inject()` decorated callback with args.""" - provider = providers.Factory(list) - object_a = object() - - @injections.inject(b=provider) - def test(a, b): - return a, b - - a1, b1 = test(object_a) - a2, b2 = test(object_a) - - self.assertIsInstance(a1, object) - self.assertIsInstance(a2, object) - self.assertIs(a1, object_a) - self.assertIs(a2, object_a) - - self.assertIsInstance(b1, list) - self.assertIsInstance(b2, list) - self.assertIsNot(b1, b2) - - def test_decorate_class_method(self): - """Test `inject()` decorator with class method.""" - class Test(object): - """Test class.""" - - @injections.inject(arg1=123) - @injections.inject(arg2=456) - def some_method(self, arg1, arg2): - """Some test method.""" - return arg1, arg2 - - test_object = Test() - arg1, arg2 = test_object.some_method() - - self.assertEquals(arg1, 123) - self.assertEquals(arg2, 456) - - def test_decorate_class_with_init(self): - """Test `inject()` decorator that decorate class with __init__.""" - @injections.inject(arg1=123) - @injections.inject(arg2=456) - class Test(object): - """Test class.""" - - def __init__(self, arg1, arg2): - """Init.""" - self.arg1 = arg1 - self.arg2 = arg2 - - test_object = Test() - - self.assertEquals(test_object.arg1, 123) - self.assertEquals(test_object.arg2, 456) - - def test_decorate_class_without_init(self): - """Test `inject()` decorator that decorate class without __init__.""" - with self.assertRaises(errors.Error): - @injections.inject(arg1=123) - class Test(object): - """Test class.""" - - -class InjectDeprecationTests(unittest.TestCase): - """Deprecation of `@inject()` tests.""" - - def test_deprecation_warning_on_usage(self): - """Test that DeprecationWarning is produced when `@inject` is used.""" - with warnings.catch_warnings(record=True) as caught_warnings: - warnings.simplefilter('always') - - @injections.inject(1) - def _example(arg): - pass - - warnings.simplefilter('default') - - self.assertEquals(len(caught_warnings), 1) - self.assertEquals(caught_warnings[-1].category, DeprecationWarning) - self.assertIn('Call to a deprecated decorator', - str(caught_warnings[-1].message)) - self.assertIn('@dependency_injector.injections.inject', - str(caught_warnings[-1].message))