Merge branch 'release/3.36.0' into master

This commit is contained in:
Roman Mogylatov 2020-09-02 21:28:29 -04:00
commit f5758d842f
31 changed files with 198 additions and 271 deletions

View File

@ -52,7 +52,7 @@ master_doc = 'index'
# General information about the project. # General information about the project.
project = u'Dependency Injector' project = u'Dependency Injector'
copyright = u'2017, ETS Labs' copyright = u'2020, ETS Labs'
author = u'ETS Labs' author = u'ETS Labs'
# The version info for the project you're documenting, acts as replacement for # The version info for the project you're documenting, acts as replacement for

View File

@ -1,5 +1,5 @@
Overriding of the container Container overriding
--------------------------- --------------------
.. currentmodule:: dependency_injector.containers .. currentmodule:: dependency_injector.containers

Binary file not shown.

Before

Width:  |  Height:  |  Size: 295 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

View File

@ -7,6 +7,15 @@ 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`_
3.36.0
------
- Update providers overriding documentation and rework examples.
- Update documentation on injecting provided object attributes, items or method calls.
- Update documentation and example on creating a custom provider.
- Update providers index documentation page to give better overview of providers functionality.
- Fix mypy stub of the ``Provider`` to specify the protected ``._copy_overridings()`` method.
- Update copyright year in the documentation.
3.35.1 3.35.1
------ ------
- Fix minor issues in the providers documentation and examples. - Fix minor issues in the providers documentation and examples.

View File

@ -1,5 +1,5 @@
Callable provider Callable provider
----------------- =================
.. meta:: .. meta::
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Function,Method,Example :keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Function,Method,Example

View File

@ -1,5 +1,5 @@
Configuration provider Configuration provider
---------------------- ======================
.. meta:: .. meta::
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Configuration,Injection, :keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Configuration,Injection,
@ -20,7 +20,7 @@ Configuration provider
It implements the principle "use first, define later". It implements the principle "use first, define later".
Loading from an INI file Loading from an INI file
~~~~~~~~~~~~~~~~~~~~~~~~ ------------------------
``Configuration`` provider can load configuration from an ``ini`` file using the ``Configuration`` provider can load configuration from an ``ini`` file using the
:py:meth:`Configuration.from_ini` method: :py:meth:`Configuration.from_ini` method:
@ -40,7 +40,7 @@ where ``examples/providers/configuration/config.ini`` is:
variable ``ENV_NAME``. variable ``ENV_NAME``.
Loading from a YAML file Loading from a YAML file
~~~~~~~~~~~~~~~~~~~~~~~~ ------------------------
``Configuration`` provider can load configuration from a ``yaml`` file using the ``Configuration`` provider can load configuration from a ``yaml`` file using the
:py:meth:`Configuration.from_yaml` method: :py:meth:`Configuration.from_yaml` method:
@ -74,7 +74,7 @@ variable ``ENV_NAME``.
*Don't forget to mirror the changes in the requirements file.* *Don't forget to mirror the changes in the requirements file.*
Loading from a dictionary Loading from a dictionary
~~~~~~~~~~~~~~~~~~~~~~~~~ -------------------------
``Configuration`` provider can load configuration from a Python ``dict`` using the ``Configuration`` provider can load configuration from a Python ``dict`` using the
:py:meth:`Configuration.from_dict` method: :py:meth:`Configuration.from_dict` method:
@ -85,7 +85,7 @@ Loading from a dictionary
:emphasize-lines: 6-13 :emphasize-lines: 6-13
Loading from an environment variable Loading from an environment variable
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ------------------------------------
``Configuration`` provider can load configuration from an environment variable using the ``Configuration`` provider can load configuration from an environment variable using the
:py:meth:`Configuration.from_env` method: :py:meth:`Configuration.from_env` method:
@ -96,7 +96,7 @@ Loading from an environment variable
:emphasize-lines: 6-8 :emphasize-lines: 6-8
Loading from the multiple sources Loading from the multiple sources
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ---------------------------------
``Configuration`` provider can load configuration from the multiple sources. Loaded ``Configuration`` provider can load configuration from the multiple sources. Loaded
configuration is merged recursively over the existing configuration. configuration is merged recursively over the existing configuration.
@ -112,7 +112,7 @@ where ``examples/providers/configuration/config.local.yml`` is:
:language: ini :language: ini
Specifying the value type Specifying the value type
~~~~~~~~~~~~~~~~~~~~~~~~~ -------------------------
You can specify the type of the injected configuration value explicitly. You can specify the type of the injected configuration value explicitly.

View File

@ -1,5 +1,5 @@
Coroutine provider Coroutine provider
------------------ ==================
.. meta:: .. meta::
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Coroutine,Asynchronous, :keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Coroutine,Asynchronous,

View File

@ -1,35 +1,45 @@
Writing of custom providers .. _create-provider:
---------------------------
Creating a custom provider
==========================
.. meta::
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Custom provider, Create
:description: This page demonstrates how to create a custom provider.
.. currentmodule:: dependency_injector.providers .. currentmodule:: dependency_injector.providers
List of *Dependency Injector* providers could be widened with custom providers. You can create a custom provider.
Below are some tips and recommendations that have to be met: To create a custom provider you need to follow these rules:
1. Every custom provider has to extend base provider class - 1. New provider class should inherit :py:class:`Provider`.
:py:class:`Provider`. 2. You need to implement the ``Provider._provide()`` method.
2. Custom provider's ``__init__()`` could be overridden, but parent's 3. You need to implement the ``Provider.__deepcopy__()`` method. It should return an
initializer (:py:meth:`Provider.__init__`) has to be called. equivalent copy of a provider. All providers must be copied with a ``deepcopy()`` function
3. Providing strategy has to be implemented in custom provider's from the ``providers`` module. After the a new provider object is created use
:py:meth:`Provider.__call__` method. ``Provider._copy_overriding()`` method to copy all overriding providers. See the example
4. If custom provider is based on some standard providers, it is better to below.
use delegation of standard providers, then extending of them. 4. If the new provider has a ``__init__()`` method, it should call the parent
5. If custom provider defines any attributes, it is good to list them in ``Provider.__init__()``.
``__slots__`` attribute (as *Dependency Injector* does). It can save
some memory.
6. If custom provider deals with injections, it is strongly recommended
to be consistent with :py:class:`Factory`, :py:class:`Singleton` and
:py:class:`Callable` providers style.
Example:
.. image:: /images/providers/custom_provider.png
:width: 100%
:align: center
.. literalinclude:: ../../examples/providers/custom_factory.py .. literalinclude:: ../../examples/providers/custom_factory.py
:language: python :language: python
:lines: 3-
.. note::
1. Prefer delegation over inheritance. If you choose between inheriting a ``Factory`` or
inheriting a ``Provider`` and use ``Factory`` internally - the last is better.
2. When create a new provider follow the ``Factory``-like injections style. Consistency matters.
3. Use the ``__slots__`` attribute to make sure nothing could be attached to your provider. You
will also save some memory.
.. note::
If you don't find needed provider in the ``providers`` module and experience troubles creating
one by your own - open a
`Github Issue <https://github.com/ets-labs/python-dependency-injector/issues>`_.
I'll help you to resolve the issue if that's possible. If the new provider can be useful for
others I'll include it into the ``providers`` module.
.. disqus:: .. disqus::

View File

@ -1,5 +1,5 @@
Dependency provider Dependency provider
------------------- ===================
.. currentmodule:: dependency_injector.providers .. currentmodule:: dependency_injector.providers

View File

@ -41,7 +41,7 @@ injected following these rules:
:lines: 3- :lines: 3-
Passing arguments to the underlying providers Passing arguments to the underlying providers
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ---------------------------------------------
``Factory`` provider can pass the arguments to the underlying providers. This helps when you need ``Factory`` provider can pass the arguments to the underlying providers. This helps when you need
to assemble a nested objects graph and pass the arguments deep inside. to assemble a nested objects graph and pass the arguments deep inside.
@ -88,7 +88,7 @@ If ``<dependency>`` is found the underlying provider will receive the
.. _factory_providers_delegation: .. _factory_providers_delegation:
Passing providers to the objects Passing providers to the objects
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --------------------------------
When you need to inject the provider itself, but not the result of its call, use the ``.provider`` When you need to inject the provider itself, but not the result of its call, use the ``.provider``
attribute of the provider that you're going to inject. attribute of the provider that you're going to inject.
@ -105,7 +105,7 @@ attribute of the provider that you're going to inject.
.. _factory-specialize-provided-type: .. _factory-specialize-provided-type:
Specializing the provided type Specializing the provided type
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ------------------------------
You can create a specialized ``Factory`` provider that provides only specific type. For doing You can create a specialized ``Factory`` provider that provides only specific type. For doing
this you need to create a subclass of the ``Factory`` provider and define the ``provided_type`` this you need to create a subclass of the ``Factory`` provider and define the ``provided_type``
@ -119,7 +119,7 @@ class attribute.
.. _abstract-factory: .. _abstract-factory:
Abstract factory Abstract factory
~~~~~~~~~~~~~~~~ ----------------
:py:class:`AbstractFactory` provider helps when you need to create a provider of some base class :py:class:`AbstractFactory` provider helps when you need to create a provider of some base class
and the particular implementation is not yet know. ``AbstractFactory`` provider is a ``Factory`` and the particular implementation is not yet know. ``AbstractFactory`` provider is a ``Factory``
@ -138,7 +138,7 @@ provider with two peculiarities:
:emphasize-lines: 32 :emphasize-lines: 32
Factory aggregate Factory aggregate
~~~~~~~~~~~~~~~~~ -----------------
:py:class:`FactoryAggregate` provider aggregates multiple factories. When you call the :py:class:`FactoryAggregate` provider aggregates multiple factories. When you call the
``FactoryAggregate`` it delegates the call to one of the factories. ``FactoryAggregate`` it delegates the call to one of the factories.

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -7,7 +7,8 @@ Providers help to assemble the objects. They create objects and inject the depen
Each provider is a callable. You call the provider like a function when you need to create an Each provider is a callable. You call the provider like a function when you need to create an
object. Provider retrieves the underlying dependencies and inject them into the created object. object. Provider retrieves the underlying dependencies and inject them into the created object.
It causes the cascade effect that helps to assemble object graphs. It causes the cascade effect that helps to assemble object graphs. See ``Factory``, ``Singleton``,
``Callable`` and other provider docs below.
.. code-block:: bash .. code-block:: bash
@ -23,10 +24,13 @@ It causes the cascade effect that helps to assemble object graphs.
└──> provider6() └──> provider6()
Another providers feature is an overriding. Any of the providers can be overridden by another Another providers feature is an overriding. You can override any provider by another provider.
provider. When provider is overridden it calls to the overriding provider instead of providing This helps in testing. This also helps in overriding API clients with stubs for the development
the object by its own. This helps in testing. This also helps in overriding API clients with or staging environment. See the example at :ref:`provider-overriding`.
stubs for the development or staging environment.
If you need to inject not the whole object but the parts see :ref:`provided-instance`.
To create a new provider see :ref:`create-provider`.
Providers module API docs - :py:mod:`dependency_injector.providers` Providers module API docs - :py:mod:`dependency_injector.providers`

View File

@ -1,5 +1,5 @@
List provider List provider
------------- =============
.. meta:: .. meta::
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control,List,Injection :keywords: Python,DI,Dependency injection,IoC,Inversion of Control,List,Injection

View File

@ -1,5 +1,5 @@
Object provider Object provider
--------------- ===============
.. meta:: .. meta::
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Object :keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Object

View File

@ -1,47 +1,43 @@
Overriding of providers .. _provider-overriding:
-----------------------
Provider overriding
===================
.. meta::
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Override,Test,Unit
:description: This page demonstrates how to implement providers overriding. This helps in
testing and configuring the system for the multiple environments.
.. currentmodule:: dependency_injector.providers .. currentmodule:: dependency_injector.providers
Every provider could be overridden by another provider. You can override any provider by another provider.
This gives opportunity to make system behaviour more flexible at some point. When provider is overridden it calls to the overriding provider instead of providing
The main feature is that while your code is using providers, it depends on the object by its own.
providers, but not on the objects that providers provide. As a result of this,
you can change providing by provider object to a different one, but still
compatible one, without changing your previously written code.
Provider overriding functionality has such interface: This helps in testing. This also helps in overriding API clients with stubs for the development
or staging environment.
.. image:: /images/providers/provider_override.png To override a provider you need to call the ``Provider.override()`` method. This method receives
:width: 55% a single argument called ``overriding``. If the ``overriding`` value is a provider, this provider
:align: center is called instead of the original. If value is not a provider, this value is returned instead of
calling the original provider.
+ :py:meth:`Provider.override()` - takes another provider that will be used .. image:: images/overriding.png
instead of current provider. This method could be called several times.
In such case, last passed provider would be used as overriding one.
+ :py:meth:`Provider.reset_override()` - resets all overriding providers.
Provider starts to behave itself like usual.
+ :py:meth:`Provider.reset_last_overriding()` - remove last overriding
provider from stack of overriding providers.
Example:
.. image:: /images/providers/overriding_simple.png
:width: 80% :width: 80%
:align: center :align: center
.. literalinclude:: ../../examples/providers/overriding_simple.py .. literalinclude:: ../../examples/providers/overriding.py
:language: python :language: python
:lines: 3-
Example: You can override a provider multiple times. In that case the latest ``overriding`` value will be
used. The rest of the overriding values will form a stack.
.. image:: /images/providers/overriding_users_model.png To reset an overriding you can use the ``Provider.reset_override()`` or
:width: 100% ``Provider.reset_last_overriding()`` methods.
:align: center
.. literalinclude:: ../../examples/providers/overriding_users_model.py
:language: python
You can use a context manager for overriding a provider ``with Provider.override():``. The
overriding will be reset when context closed.
.. disqus:: .. disqus::

View File

@ -1,13 +1,16 @@
Injecting attributes, items, or call methods of the provided instance .. _provided-instance:
---------------------------------------------------------------------
Injecting provided object attributes, items, or call its methods
================================================================
.. meta::
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Attribute,Method,Call
:description: This page demonstrates how to inject attributes, items or call method of the
provided instance.
.. currentmodule:: dependency_injector.providers .. currentmodule:: dependency_injector.providers
In this section you will know how to inject provided instance attribute or item into the other You can inject provided object attribute, item or result of its method call.
provider.
It also describes how to call a method of the provided instance and use the result of
this call as an injection value.
.. literalinclude:: ../../examples/providers/provided_instance.py .. literalinclude:: ../../examples/providers/provided_instance.py
:language: python :language: python
@ -15,14 +18,14 @@ this call as an injection value.
:lines: 3- :lines: 3-
To use the feature you should use the ``.provided`` attribute of the injected provider. This To use the feature you should use the ``.provided`` attribute of the injected provider. This
attribute helps to specify what happens with the provided instance. You can retrieve an injection attribute helps to specify what happens with the provided instance before the injection. You can
value from: use any combination of the following:
- an attribute of the provided instance - an attribute of the provided object
- an item of the provided instance - an item of the provided object
- a call of the provided instance method - a call of the provided object method
When you use the call of the provided instance method you can specify the injections into this When you use a call of the provided instance method you can specify the injections for this
method like you do with any other provider. method like you do with any other provider.
You can do nested constructions: You can do nested constructions:
@ -32,35 +35,24 @@ You can do nested constructions:
:emphasize-lines: 24-30 :emphasize-lines: 24-30
:lines: 3- :lines: 3-
Attribute ``.provided`` is available for the providers that return instances. Providers that The ``.provided`` attribute is available for the next providers:
have ``.provided`` attribute:
- :py:class:`Callable` and its subclasses
- :py:class:`Factory` and its subclasses - :py:class:`Factory` and its subclasses
- :py:class:`Singleton` and its subclasses - :py:class:`Singleton` and its subclasses
- :py:class:`Callable` and its subclasses
- :py:class:`Object` - :py:class:`Object`
- :py:class:`List` - :py:class:`List`
- :py:class:`Selector` - :py:class:`Selector`
- :py:class:`Dependency` - :py:class:`Dependency`
Special providers like :py:class:`Configuration` or :py:class:`Delegate` do not have the
``.provided`` attribute.
Provider subclasses
-------------------
When you create a new provider subclass and want to implement the ``.provided`` attribute, you When you create a new provider subclass and want to implement the ``.provided`` attribute, you
should use the :py:class:`ProvidedInstance` provider. should use the :py:class:`ProvidedInstance` provider. Add the ``.provided`` property
implementation to a new subclass:
.. code-block:: python .. code-block:: python
@property @property
def provided(self): def provided(self):
"""Return :py:class:`ProvidedInstance` provider."""
return ProvidedInstance(self) return ProvidedInstance(self)
In all other cases you should not use :py:class:`ProvidedInstance`, :py:class:`AttributeGetter`,
:py:class:`ItemGetter`, or :py:class:`MethodCaller` providers directly. Use the ``.provided``
attribute of the injected provider instead.
.. disqus:: .. disqus::

View File

@ -1,7 +1,7 @@
.. _selector-provider: .. _selector-provider:
Selector provider Selector provider
----------------- =================
.. meta:: .. meta::
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Configuration,Injection, :keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Configuration,Injection,

View File

@ -1,5 +1,5 @@
Singleton provider Singleton provider
------------------ ==================
.. meta:: .. meta::
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Singleton,Pattern,Example, :keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Singleton,Pattern,Example,
@ -33,7 +33,7 @@ factories:
- :ref:`abstract-factory` - :ref:`abstract-factory`
Resetting memorized object Resetting memorized object
~~~~~~~~~~~~~~~~~~~~~~~~~~ --------------------------
To reset a memorized object you need to call the ``.reset()`` method of the ``Singleton`` To reset a memorized object you need to call the ``.reset()`` method of the ``Singleton``
provider. provider.
@ -48,7 +48,7 @@ provider.
managed by the garbage collector. managed by the garbage collector.
Using singleton with multiple threads Using singleton with multiple threads
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -------------------------------------
``Singleton`` provider is NOT thread-safe. You need to explicitly establish a synchronization for ``Singleton`` provider is NOT thread-safe. You need to explicitly establish a synchronization for
using the ``Singleton`` provider in the multi-threading application. Otherwise you could trap using the ``Singleton`` provider in the multi-threading application. Otherwise you could trap
@ -67,7 +67,7 @@ There are two thread-safe singleton implementations out of the box:
:emphasize-lines: 11,12 :emphasize-lines: 11,12
Implementing scopes Implementing scopes
~~~~~~~~~~~~~~~~~~~ -------------------
To implement a scoped singleton provider use a ``Singleton`` provider and reset its scope when To implement a scoped singleton provider use a ``Singleton`` provider and reset its scope when
needed. needed.

View File

@ -1,40 +1,42 @@
"""Custom `Factory` example.""" """Custom provider example."""
import dependency_injector.providers as providers from dependency_injector import providers
class User: class CustomFactory(providers.Provider):
"""Example class User."""
class UsersFactory(providers.Provider):
"""Example users factory."""
__slots__ = ('_factory',) __slots__ = ('_factory',)
def __init__(self): def __init__(self, provides, *args, **kwargs):
"""Initialize instance.""" self._factory = providers.Factory(provides, *args, **kwargs)
self._factory = providers.Factory(User)
super().__init__() super().__init__()
def __call__(self, *args, **kwargs): def __deepcopy__(self, memo):
"""Return provided object. copied = memo.get(id(self))
if copied is not None:
return copied
Callable interface implementation. copied = self.__class__(
""" self._factory.provides,
if self.last_overriding is not None: *providers.deepcopy(self._factory.args, memo),
return self.last_overriding._provide(args, kwargs) **providers.deepcopy(self._factory.kwargs, memo),
)
self._copy_overridings(copied, memo)
return copied
def _provide(self, args, kwargs):
return self._factory(*args, **kwargs) return self._factory(*args, **kwargs)
# Users factory: factory = CustomFactory(object)
users_factory = UsersFactory()
# Creating several User objects:
user1 = users_factory()
user2 = users_factory()
# Making some asserts: if __name__ == '__main__':
assert isinstance(user1, User) object1 = factory()
assert isinstance(user2, User) assert isinstance(object1, object)
assert user1 is not user2
object2 = factory()
assert isinstance(object1, object)
assert object1 is not object2

View File

@ -0,0 +1,42 @@
"""Simple providers overriding example."""
import unittest.mock
from dependency_injector import providers
class ApiClient:
...
class ApiClientStub(ApiClient):
...
class Service:
def __init__(self, api_client: ApiClient):
self._api_client = api_client
api_client_factory = providers.Factory(ApiClient)
service_factory = providers.Factory(
Service,
api_client=api_client_factory,
)
if __name__ == '__main__':
# 1. Use .override() to replace the API client with stub
api_client_factory.override(providers.Factory(ApiClientStub))
service1 = service_factory()
assert isinstance(service1.api_client, ApiClientStub)
# 2. Use .override() as a context manager to mock the API client in testing
with api_client_factory.override(unittest.mock.Mock(ApiClient)):
service3 = service_factory()
assert isinstance(service3.api_client, unittest.mock.Mock)
# 3. Use .reset_override() to get back to normal
api_client_factory.reset_override()
service3 = service_factory()
assert isinstance(service3.api_client, ApiClient)

View File

@ -1,36 +0,0 @@
"""Simple providers overriding example."""
import dependency_injector.providers as providers
class User:
"""Example class User."""
# Users factory:
users_factory = providers.Factory(User)
# Creating several User objects:
user1 = users_factory()
user2 = users_factory()
# Making some asserts:
assert user1 is not user2
assert isinstance(user1, User) and isinstance(user2, User)
# Extending User:
class SuperUser(User):
"""Example class SuperUser."""
# Overriding users factory:
users_factory.override(providers.Factory(SuperUser))
# Creating some more User objects using overridden users factory:
user3 = users_factory()
user4 = users_factory()
# Making some asserts:
assert user4 is not user3
assert isinstance(user3, SuperUser) and isinstance(user4, SuperUser)

View File

@ -1,95 +0,0 @@
"""Overriding user's model example."""
import dependency_injector.providers as providers
class User:
"""Example class User."""
def __init__(self, id, password):
"""Initialize instance."""
self.id = id
self.password = password
super().__init__()
class UsersService:
"""Example class UsersService."""
def __init__(self, user_cls):
"""Initialize instance."""
self.user_cls = user_cls
super().__init__()
def get_by_id(self, id):
"""Find user by his id and return user model."""
return self.user_cls(id=id, password='secret' + str(id))
# Users factory and UsersService provider:
users_service = providers.Factory(UsersService, user_cls=User)
# Getting several users and making some asserts:
user1 = users_service().get_by_id(1)
user2 = users_service().get_by_id(2)
assert isinstance(user1, User)
assert user1.id == 1
assert user1.password == 'secret1'
assert isinstance(user2, User)
assert user2.id == 2
assert user2.password == 'secret2'
assert user1 is not user2
# Extending user model and user service for adding custom attributes without
# making any changes to client's code.
class ExtendedUser(User):
"""Example class ExtendedUser."""
def __init__(self, id, password, first_name=None, last_name=None,
gender=None):
"""Initialize instance."""
self.first_name = first_name
self.last_name = last_name
self.gender = gender
super().__init__(id, password)
class ExtendedUsersService(UsersService):
"""Example class ExtendedUsersService."""
def get_by_id(self, id):
"""Find user by his id and return user model."""
user = super(ExtendedUsersService, self).get_by_id(id)
user.first_name = 'John' + str(id)
user.last_name = 'Smith' + str(id)
user.gender = 'male'
return user
# Overriding users_service provider:
extended_users_service = providers.Factory(ExtendedUsersService,
user_cls=ExtendedUser)
users_service.override(extended_users_service)
# Getting few other users users and making some asserts:
user3 = users_service().get_by_id(3)
user4 = users_service().get_by_id(4)
assert isinstance(user3, ExtendedUser)
assert user3.id == 3
assert user3.password == 'secret3'
assert user3.first_name == 'John3'
assert user3.last_name == 'Smith3'
assert isinstance(user4, ExtendedUser)
assert user4.id == 4
assert user4.password == 'secret4'
assert user4.first_name == 'John4'
assert user4.last_name == 'Smith4'
assert user3 is not user4

View File

@ -33,6 +33,7 @@ client_factory = providers.Factory(
value4=service.provided.get_value.call(), value4=service.provided.get_value.call(),
) )
if __name__ == '__main__': if __name__ == '__main__':
client = client_factory() client = client_factory()
assert client.value1 == client.value2 == client.value3 == 'foo' assert client.value1 == client.value2 == client.value3 == 'foo'

View File

@ -31,6 +31,7 @@ demo_list = providers.List(
dependency.provided['foo']['baz'].call(service)['arg'].get_value.call(), dependency.provided['foo']['baz'].call(service)['arg'].get_value.call(),
) )
if __name__ == '__main__': if __name__ == '__main__':
assert demo_list() == [ assert demo_list() == [
10, 10,

View File

@ -1,6 +1,6 @@
"""Dependency injector top-level package.""" """Dependency injector top-level package."""
__version__ = '3.35.1' __version__ = '3.36.0'
"""Version number that follows semantic versioning. """Version number that follows semantic versioning.
:type: str :type: str

View File

@ -43,6 +43,7 @@ class Provider(_Provider):
def delegate(self) -> Provider: ... def delegate(self) -> Provider: ...
@property @property
def provider(self) -> Provider: ... def provider(self) -> Provider: ...
def _copy_overridings(self, copied: Provider, memo: Optional[Dict[str, Any]]) -> None: ...
class Object(Provider, Generic[T]): class Object(Provider, Generic[T]):