mirror of
https://github.com/ets-labs/python-dependency-injector.git
synced 2025-06-17 12:03:13 +03:00
Merge remote-tracking branch 'origin/inject_for_classes'
This commit is contained in:
commit
53e8f62c89
|
@ -1,11 +1,21 @@
|
||||||
"""Injections module."""
|
"""Injections module."""
|
||||||
|
|
||||||
|
import sys
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from .utils import is_provider
|
from .utils import is_provider
|
||||||
from .utils import ensure_is_injection
|
from .utils import ensure_is_injection
|
||||||
from .utils import get_injectable_kwargs
|
from .utils import get_injectable_kwargs
|
||||||
|
|
||||||
|
from .errors import Error
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
class Injection(object):
|
class Injection(object):
|
||||||
|
|
||||||
|
@ -60,8 +70,22 @@ def inject(*args, **kwargs):
|
||||||
injections += tuple(ensure_is_injection(injection)
|
injections += tuple(ensure_is_injection(injection)
|
||||||
for injection in args)
|
for injection in args)
|
||||||
|
|
||||||
def decorator(callback):
|
def decorator(callback_or_cls):
|
||||||
"""Dependency injection decorator."""
|
"""Dependency injection decorator."""
|
||||||
|
if isinstance(callback_or_cls, six.class_types):
|
||||||
|
cls = callback_or_cls
|
||||||
|
try:
|
||||||
|
cls_init = six.get_unbound_function(cls.__init__)
|
||||||
|
assert cls_init is not OBJECT_INIT
|
||||||
|
except (AttributeError, AssertionError):
|
||||||
|
raise 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, 'injections'):
|
if hasattr(callback, 'injections'):
|
||||||
callback.injections += injections
|
callback.injections += injections
|
||||||
return callback
|
return callback
|
||||||
|
|
|
@ -18,10 +18,23 @@ called to provide injectable values.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
.. literalinclude:: ../../examples/advanced_usage/inject_decorator_simple.py
|
.. literalinclude:: ../../examples/advanced_usage/inject_simple.py
|
||||||
:language: python
|
:language: python
|
||||||
|
|
||||||
Example of usage ``@di.inject()`` decorator with Flask:
|
Example of usage ``@di.inject()`` decorator with Flask:
|
||||||
|
|
||||||
.. literalinclude:: ../../examples/advanced_usage/inject_decorator_flask.py
|
.. literalinclude:: ../../examples/advanced_usage/inject_flask.py
|
||||||
|
:language: python
|
||||||
|
|
||||||
|
|
||||||
|
@inject decorator with classes
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
``@di.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 ``di.Error`` will be raised.
|
||||||
|
|
||||||
|
Example of usage ``@di.inject()`` with Flask class-based view:
|
||||||
|
|
||||||
|
.. literalinclude:: ../../examples/advanced_usage/inject_flask_class_based.py
|
||||||
:language: python
|
:language: python
|
||||||
|
|
|
@ -11,7 +11,7 @@ follows `Semantic versioning`_
|
||||||
Development version
|
Development version
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
- No featues.
|
- Add functionality for decorating classes with ``@di.inject``.
|
||||||
|
|
||||||
0.9.5
|
0.9.5
|
||||||
-----
|
-----
|
||||||
|
|
42
examples/advanced_usage/inject_flask_class_based.py
Normal file
42
examples/advanced_usage/inject_flask_class_based.py
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
"""`@di.inject()` decorator with classes example."""
|
||||||
|
|
||||||
|
import sqlite3
|
||||||
|
import flask
|
||||||
|
import flask.views
|
||||||
|
import dependency_injector as di
|
||||||
|
|
||||||
|
|
||||||
|
database = di.Singleton(sqlite3.Connection,
|
||||||
|
database=':memory:',
|
||||||
|
timeout=30,
|
||||||
|
detect_types=True,
|
||||||
|
isolation_level='EXCLUSIVE')
|
||||||
|
|
||||||
|
app = flask.Flask(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@di.inject(database=database)
|
||||||
|
@di.inject(some_setting=777)
|
||||||
|
class HelloView(flask.views.View):
|
||||||
|
|
||||||
|
"""Example flask class-based view."""
|
||||||
|
|
||||||
|
def __init__(self, database, some_setting):
|
||||||
|
"""Initializer."""
|
||||||
|
self.database = database
|
||||||
|
self.some_setting = some_setting
|
||||||
|
|
||||||
|
def dispatch_request(self):
|
||||||
|
"""Handle example request."""
|
||||||
|
one = self.database.execute('SELECT 1').fetchone()[0]
|
||||||
|
one *= self.some_setting
|
||||||
|
return 'Query returned {0}, db connection {1}'.format(one, database)
|
||||||
|
|
||||||
|
|
||||||
|
app.add_url_rule('/', view_func=HelloView.as_view('hello_view'))
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app.run()
|
||||||
|
|
||||||
|
# Example output of "GET / HTTP/1.1" is:
|
||||||
|
# Query returned 777, db connection <sqlite3.Connection object at 0x1057e4030>
|
|
@ -151,3 +151,47 @@ class InjectTests(unittest.TestCase):
|
||||||
def test_decorate_with_not_injection(self):
|
def test_decorate_with_not_injection(self):
|
||||||
"""Test `inject()` decorator with not an injection instance."""
|
"""Test `inject()` decorator with not an injection instance."""
|
||||||
self.assertRaises(di.Error, di.inject, object)
|
self.assertRaises(di.Error, di.inject, object)
|
||||||
|
|
||||||
|
def test_decorate_class_method(self):
|
||||||
|
"""Test `inject()` decorator with class method."""
|
||||||
|
class Test(object):
|
||||||
|
|
||||||
|
"""Test class."""
|
||||||
|
|
||||||
|
@di.inject(arg1=123)
|
||||||
|
@di.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__."""
|
||||||
|
@di.inject(arg1=123)
|
||||||
|
@di.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(di.Error):
|
||||||
|
@di.inject(arg1=123)
|
||||||
|
class Test(object):
|
||||||
|
|
||||||
|
"""Test class."""
|
||||||
|
|
Loading…
Reference in New Issue
Block a user