Readd support for decorating classes with @inject
As a bonus this provides dataclass support.
This commit is contained in:
parent
da842ed281
commit
53dee44872
|
@ -15,7 +15,7 @@ matrix:
|
|||
include:
|
||||
- { python: "3.7", dist: xenial, sudo: true }
|
||||
install:
|
||||
- pip install --upgrade coveralls pytest "typing$TYPING_VERSION" "pytest-cov>=2.5.1"
|
||||
- pip install --upgrade coveralls pytest "typing$TYPING_VERSION" "pytest-cov>=2.5.1" dataclasses
|
||||
# mypy can't be installed on pypy
|
||||
- if [[ "${TRAVIS_PYTHON_VERSION}" != "pypy"* ]] ; then pip install mypy ; fi
|
||||
# Black is Python 3.6+-only
|
||||
|
|
7
CHANGES
7
CHANGES
|
@ -1,6 +1,13 @@
|
|||
Injector Change Log
|
||||
===================
|
||||
|
||||
0.16.2
|
||||
------
|
||||
|
||||
- (Re)added support for decorating classes themselves with :func:`@inject <injector.inject>`. This is the same
|
||||
as decorating their constructors. Among other things this gives us
|
||||
`dataclasses <https://docs.python.org/3/library/dataclasses.html>`_ integration.
|
||||
|
||||
0.16.1
|
||||
------
|
||||
|
||||
|
|
20
README.md
20
README.md
|
@ -50,6 +50,26 @@ A Quick Example
|
|||
|
||||
```
|
||||
|
||||
Or with `dataclasses` if you like:
|
||||
|
||||
```python
|
||||
from dataclasses import dataclass
|
||||
from injector import Injector, inject
|
||||
class Inner:
|
||||
def __init__(self):
|
||||
self.forty_two = 42
|
||||
|
||||
@inject
|
||||
@dataclass
|
||||
class Outer:
|
||||
inner: Inner
|
||||
|
||||
injector = Injector()
|
||||
outer = injector.get(Outer)
|
||||
print(outer.inner.forty_two) # Prints 42
|
||||
```
|
||||
|
||||
|
||||
A Full Example
|
||||
--------------
|
||||
|
||||
|
|
|
@ -899,7 +899,7 @@ def provider(function):
|
|||
return function
|
||||
|
||||
|
||||
def inject(function):
|
||||
def inject(constructor_or_class):
|
||||
"""Decorator declaring parameters to be injected.
|
||||
|
||||
eg.
|
||||
|
@ -923,15 +923,40 @@ def inject(function):
|
|||
>>> a = Injector(configure).get(A)
|
||||
[123, 'Bob', [1, 2, 3]]
|
||||
|
||||
As a convenience one can decorate a class itself:
|
||||
|
||||
>>> @inject
|
||||
... class B:
|
||||
... def __init__(self, dependency: Dependency):
|
||||
... self.dependency = dependency
|
||||
|
||||
This is equivalent to decorating its constructor. In particular this provides integration with
|
||||
`dataclasses <https://docs.python.org/3/library/dataclasses.html>`_ (the order of decorator
|
||||
application is important here):
|
||||
|
||||
>>> @inject
|
||||
... @dataclass
|
||||
... class C:
|
||||
... dependency: Dependency
|
||||
|
||||
.. note::
|
||||
|
||||
This decorator is to be used on class constructors. Using it on non-constructor
|
||||
methods worked in the past but it was an implementation detail rather than
|
||||
a design decision.
|
||||
This decorator is to be used on class constructors (or, as a convenience, on classes).
|
||||
Using it on non-constructor methods worked in the past but it was an implementation
|
||||
detail rather than a design decision.
|
||||
|
||||
Third party libraries may, however, provide support for injecting dependencies
|
||||
into non-constructor methods or free functions in one form or another.
|
||||
|
||||
.. versionchanged:: 0.16.2
|
||||
|
||||
(Re)added support for decorating classes with @inject.
|
||||
"""
|
||||
if isinstance(constructor_or_class, type) and hasattr(constructor_or_class, '__init__'):
|
||||
inject(constructor_or_class.__init__)
|
||||
return constructor_or_class
|
||||
|
||||
function = constructor_or_class
|
||||
try:
|
||||
bindings = _infer_injected_bindings(function)
|
||||
except _BindingNotYetAvailable:
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
from contextlib import contextmanager
|
||||
from typing import Any, NewType
|
||||
import abc
|
||||
import sys
|
||||
import threading
|
||||
import traceback
|
||||
import warnings
|
||||
|
@ -1393,3 +1394,26 @@ def test_newtype_integration_works():
|
|||
|
||||
injector = Injector([configure])
|
||||
assert injector.get(UserID) == 123
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.version_info < (3, 6), reason="Requires Python 3.6+")
|
||||
def test_dataclass_integration_works():
|
||||
import dataclasses
|
||||
|
||||
# Python 3.6+-only syntax below
|
||||
exec(
|
||||
"""
|
||||
@inject
|
||||
@dataclasses.dataclass
|
||||
class Data:
|
||||
name: str
|
||||
""",
|
||||
locals(),
|
||||
globals(),
|
||||
)
|
||||
|
||||
def configure(binder):
|
||||
binder.bind(str, to='data')
|
||||
|
||||
injector = Injector([configure])
|
||||
assert injector.get(Data).name == 'data'
|
||||
|
|
Loading…
Reference in New Issue