Merge branch 'release/4.25.0' into master

This commit is contained in:
Roman Mogylatov 2021-02-18 17:52:46 -05:00
commit 6a73b9d3fd
12 changed files with 5968 additions and 5663 deletions

View File

@ -7,6 +7,14 @@ that were made in every particular version.
From version 0.7.6 *Dependency Injector* framework strictly From version 0.7.6 *Dependency Injector* framework strictly
follows `Semantic versioning`_ follows `Semantic versioning`_
4.25.0
------
- Add ``application-multiple-containers-runtime-overriding`` example. This example demonstrates
how to build application from multiple containers and override one container config from
another one in the runtime.
See issue: `#207 <https://github.com/ets-labs/python-dependency-injector/issues/207>`_.
- Add attributes forwarding for the ``Dependency`` provider.
4.24.0 4.24.0
------ ------
- Add docs on ``@containers.copy()`` decorator. - Add docs on ``@containers.copy()`` decorator.

View File

@ -0,0 +1,17 @@
Application example (multiple containers, runtime config overriding)
====================================================================
This example demonstrates how to build application from multiple containers
and override one container config from another one in the runtime.
Run:
.. code-block:: bash
python -m example
You should see:
.. code-block:: bash
Adapter=[DB Path=[~/test]], queue=[Some storage]

View File

@ -0,0 +1 @@
"""Top-level package."""

View File

@ -0,0 +1,12 @@
"""Main module."""
from .containers import Application
if __name__ == '__main__':
application = Application()
config = application.service.config()
config.build()
print(application.repository.site())
# Output: Adapter=[DB Path=[~/test]], queue=[Some storage]

View File

@ -0,0 +1,45 @@
"""Containers module."""
from dependency_injector import containers, providers
from .services import ConfigService
class Core(containers.DeclarativeContainer):
config = providers.Configuration('config')
class Storage(containers.DeclarativeContainer):
queue = providers.Singleton(lambda: 'Some storage')
class Adapter(containers.DeclarativeContainer):
core = providers.DependenciesContainer(config=providers.Configuration())
tinydb = providers.Singleton(
lambda db_path: f'DB Path=[{db_path}]',
db_path=core.config.default.db_path,
)
class Repository(containers.DeclarativeContainer):
adapter = providers.DependenciesContainer()
storage = providers.DependenciesContainer()
site = providers.Singleton(
lambda adapter, queue: f'Adapter=[{adapter}], queue=[{queue}]',
adapter=adapter.tinydb,
queue=storage.queue,
)
class Service(containers.DeclarativeContainer):
core = providers.DependenciesContainer()
config = providers.Singleton(ConfigService, core.config.provider)
class Application(containers.DeclarativeContainer):
core = providers.Container(Core)
storage = providers.Container(Storage)
adapter = providers.Container(Adapter, core=core)
repository = providers.Container(Repository, adapter=adapter, storage=storage)
service = providers.Container(Service, core=core)

View File

@ -0,0 +1,9 @@
"""Services module."""
class ConfigService:
def __init__(self, config):
self._config = config
def build(self):
self._config.from_dict({'default': {'db_path': '~/test'}})

View File

@ -1,6 +1,6 @@
"""Top-level package.""" """Top-level package."""
__version__ = '4.24.0' __version__ = '4.25.0'
"""Version number. """Version number.
:type: str :type: str

View File

@ -14646,7 +14646,7 @@ static PyObject *__pyx_pf_19dependency_injector_10containers_4override(CYTHON_UN
/* Python wrapper */ /* Python wrapper */
static PyObject *__pyx_pw_19dependency_injector_10containers_7copy(PyObject *__pyx_self, PyObject *__pyx_v_container); /*proto*/ static PyObject *__pyx_pw_19dependency_injector_10containers_7copy(PyObject *__pyx_self, PyObject *__pyx_v_container); /*proto*/
static char __pyx_doc_19dependency_injector_10containers_6copy[] = ":py:class:`DeclarativeContainer` copying decorator.\n\n This decorator copy all providers from provided container to decorated one.\n If one of the decorated container providers matches to source container\n providers by name, it would be replaced by reference.\n\n :param container: Container that should be copied by decorated container.\n :type container: :py:class:`DeclarativeContainer`\n\n :return: Declarative container's copying decorator.\n :rtype: callable(:py:class:`DeclarativeContainer`)\n "; static char __pyx_doc_19dependency_injector_10containers_6copy[] = ":py:class:`DeclarativeContainer` copying decorator.\n\n This decorator copies all providers from provided container to decorated one.\n If one of the decorated container providers matches to source container\n providers by name, it would be replaced by reference.\n\n :param container: Container that should be copied by decorated container.\n :type container: :py:class:`DeclarativeContainer`\n\n :return: Declarative container's copying decorator.\n :rtype: callable(:py:class:`DeclarativeContainer`)\n ";
static PyMethodDef __pyx_mdef_19dependency_injector_10containers_7copy = {"copy", (PyCFunction)__pyx_pw_19dependency_injector_10containers_7copy, METH_O, __pyx_doc_19dependency_injector_10containers_6copy}; static PyMethodDef __pyx_mdef_19dependency_injector_10containers_7copy = {"copy", (PyCFunction)__pyx_pw_19dependency_injector_10containers_7copy, METH_O, __pyx_doc_19dependency_injector_10containers_6copy};
static PyObject *__pyx_pw_19dependency_injector_10containers_7copy(PyObject *__pyx_self, PyObject *__pyx_v_container) { static PyObject *__pyx_pw_19dependency_injector_10containers_7copy(PyObject *__pyx_self, PyObject *__pyx_v_container) {
PyObject *__pyx_r = 0; PyObject *__pyx_r = 0;

File diff suppressed because it is too large Load Diff

View File

@ -102,6 +102,7 @@ class Delegate(Provider[Provider]):
class Dependency(Provider[T]): class Dependency(Provider[T]):
def __init__(self, instance_of: Type[T] = object, default: Optional[Union[Provider, Any]] = None) -> None: ... def __init__(self, instance_of: Type[T] = object, default: Optional[Union[Provider, Any]] = None) -> None: ...
def __getattr__(self, name: str) -> Any: ...
@property @property
def instance_of(self) -> Type[T]: ... def instance_of(self) -> Type[T]: ...
@property @property

View File

@ -680,6 +680,13 @@ cdef class Dependency(Provider):
self._check_instance_type(result) self._check_instance_type(result)
return result return result
def __getattr__(self, name):
if self.__last_overriding:
return getattr(self.__last_overriding, name)
elif self.__default is not UNDEFINED:
return getattr(self.__default, name)
return super().__getattr__(name)
def __str__(self): def __str__(self):
"""Return string representation of provider. """Return string representation of provider.

View File

@ -496,6 +496,28 @@ class DependencyTests(unittest.TestCase):
self.assertIsNot(container.name, copied.name) self.assertIsNot(container.name, copied.name)
self.assertIsNot(container.name.parent, copied.name.parent) self.assertIsNot(container.name.parent, copied.name.parent)
def test_forward_attr_to_default(self):
default = providers.Configuration()
provider = providers.Dependency(default=default)
provider.from_dict({'foo': 'bar'})
self.assertEqual(default(), {'foo': 'bar'})
def test_forward_attr_to_overriding(self):
overriding = providers.Configuration()
provider = providers.Dependency()
provider.override(overriding)
provider.from_dict({'foo': 'bar'})
self.assertEqual(overriding(), {'foo': 'bar'})
def test_forward_attr_to_none(self):
provider = providers.Dependency()
with self.assertRaises(AttributeError):
provider.from_dict
def test_deepcopy(self): def test_deepcopy(self):
provider = providers.Dependency(int) provider = providers.Dependency(int)