mirror of
https://github.com/ets-labs/python-dependency-injector.git
synced 2024-11-22 01:26:51 +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."""
|
||||
|
||||
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)
|
||||
|
|
|
@ -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
|
||||
-------------------
|
||||
- No features.
|
||||
- Remove ``@inject`` decorator.
|
||||
|
||||
.. - No features.
|
||||
|
||||
|
||||
2.2.10
|
||||
------
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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