Update documentation and example on creating a custom provider

This commit is contained in:
Roman Mogylatov 2020-09-02 20:59:55 -04:00
parent aeace8cba5
commit dd2ded7321
8 changed files with 62 additions and 50 deletions

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

View File

@ -11,6 +11,7 @@ Development version
------------------- -------------------
- Update providers overriding documentation and rework examples. - Update providers overriding documentation and rework examples.
- Update documentation on injecting provided object attributes, items or method calls. - Update documentation on injecting provided object attributes, items or method calls.
- Update documentation and example on creating a custom provider.
3.35.1 3.35.1
------ ------

View File

@ -1,35 +1,43 @@
Writing of custom providers Creating a custom providers
--------------------------- ===========================
.. 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,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

@ -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]):