mirror of
https://github.com/ets-labs/python-dependency-injector.git
synced 2024-11-22 09:36:48 +03:00
Remove @inject decorator
This commit is contained in:
parent
d1b1196d6d
commit
c8a5db7691
|
@ -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
|
|
|
@ -1,6 +1,5 @@
|
||||||
"""Dependency injector utils module."""
|
"""Dependency injector utils module."""
|
||||||
|
|
||||||
import sys
|
|
||||||
import copy as _copy
|
import copy as _copy
|
||||||
import types
|
import types
|
||||||
import threading
|
import threading
|
||||||
|
@ -16,12 +15,6 @@ GLOBAL_LOCK = threading.RLock()
|
||||||
:type: :py:class:`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
|
if six.PY2: # pragma: no cover
|
||||||
_copy._deepcopy_dispatch[types.MethodType] = \
|
_copy._deepcopy_dispatch[types.MethodType] = \
|
||||||
lambda obj, memo: type(obj)(obj.im_func,
|
lambda obj, memo: type(obj)(obj.im_func,
|
||||||
|
@ -90,24 +83,6 @@ def represent_provider(provider, provides):
|
||||||
address=hex(id(provider)))
|
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):
|
def deepcopy(instance, memo=None):
|
||||||
"""Make full copy of instance."""
|
"""Make full copy of instance."""
|
||||||
return _copy.deepcopy(instance, memo)
|
return _copy.deepcopy(instance, memo)
|
||||||
|
|
|
@ -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:
|
|
|
@ -1,7 +0,0 @@
|
||||||
``dependency_injector.injections``
|
|
||||||
----------------------------------
|
|
||||||
|
|
||||||
.. automodule:: dependency_injector.injections
|
|
||||||
:members:
|
|
||||||
:inherited-members:
|
|
||||||
:show-inheritance:
|
|
|
@ -9,7 +9,10 @@ follows `Semantic versioning`_
|
||||||
|
|
||||||
Development version
|
Development version
|
||||||
-------------------
|
-------------------
|
||||||
- No features.
|
- Remove ``@inject`` decorator.
|
||||||
|
|
||||||
|
.. - No features.
|
||||||
|
|
||||||
|
|
||||||
2.2.10
|
2.2.10
|
||||||
------
|
------
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
import unittest2 as unittest
|
import unittest2 as unittest
|
||||||
|
|
||||||
from dependency_injector import providers, injections, utils, errors
|
from dependency_injector import providers, utils, errors
|
||||||
|
|
||||||
|
|
||||||
class Example(object):
|
class Example(object):
|
||||||
|
@ -731,26 +731,6 @@ class DelegatedThreadLocalSingletonTests(unittest.TestCase):
|
||||||
class FactoryAsDecoratorTests(unittest.TestCase):
|
class FactoryAsDecoratorTests(unittest.TestCase):
|
||||||
"""Factory as decorator tests."""
|
"""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):
|
def test_decoration_and_overriding(self):
|
||||||
"""Test decoration of some class with Factory provider."""
|
"""Test decoration of some class with Factory provider."""
|
||||||
@providers.Factory
|
@providers.Factory
|
||||||
|
|
|
@ -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))
|
|
Loading…
Reference in New Issue
Block a user