mirror of
https://github.com/ets-labs/python-dependency-injector.git
synced 2024-11-22 17:47:02 +03:00
Update documentation and example on creating a custom provider
This commit is contained in:
parent
aeace8cba5
commit
dd2ded7321
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 |
|
@ -11,6 +11,7 @@ Development version
|
|||
-------------------
|
||||
- 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.
|
||||
|
||||
3.35.1
|
||||
------
|
||||
|
|
|
@ -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
|
||||
|
||||
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 -
|
||||
:py:class:`Provider`.
|
||||
2. Custom provider's ``__init__()`` could be overridden, but parent's
|
||||
initializer (:py:meth:`Provider.__init__`) has to be called.
|
||||
3. Providing strategy has to be implemented in custom provider's
|
||||
:py:meth:`Provider.__call__` method.
|
||||
4. If custom provider is based on some standard providers, it is better to
|
||||
use delegation of standard providers, then extending of them.
|
||||
5. If custom provider defines any attributes, it is good to list them in
|
||||
``__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
|
||||
1. New provider class should inherit :py:class:`Provider`.
|
||||
2. You need to implement the ``Provider._provide()`` method.
|
||||
3. You need to implement the ``Provider.__deepcopy__()`` method. It should return an
|
||||
equivalent copy of a provider. All providers must be copied with a ``deepcopy()`` function
|
||||
from the ``providers`` module. After the a new provider object is created use
|
||||
``Provider._copy_overriding()`` method to copy all overriding providers. See the example
|
||||
below.
|
||||
4. If the new provider has a ``__init__()`` method, it should call the parent
|
||||
``Provider.__init__()``.
|
||||
|
||||
.. literalinclude:: ../../examples/providers/custom_factory.py
|
||||
: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::
|
||||
|
|
|
@ -1,40 +1,42 @@
|
|||
"""Custom `Factory` example."""
|
||||
"""Custom provider example."""
|
||||
|
||||
import dependency_injector.providers as providers
|
||||
from dependency_injector import providers
|
||||
|
||||
|
||||
class User:
|
||||
"""Example class User."""
|
||||
|
||||
|
||||
class UsersFactory(providers.Provider):
|
||||
"""Example users factory."""
|
||||
class CustomFactory(providers.Provider):
|
||||
|
||||
__slots__ = ('_factory',)
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize instance."""
|
||||
self._factory = providers.Factory(User)
|
||||
def __init__(self, provides, *args, **kwargs):
|
||||
self._factory = providers.Factory(provides, *args, **kwargs)
|
||||
super().__init__()
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
"""Return provided object.
|
||||
def __deepcopy__(self, memo):
|
||||
copied = memo.get(id(self))
|
||||
if copied is not None:
|
||||
return copied
|
||||
|
||||
Callable interface implementation.
|
||||
"""
|
||||
if self.last_overriding is not None:
|
||||
return self.last_overriding._provide(args, kwargs)
|
||||
copied = self.__class__(
|
||||
self._factory.provides,
|
||||
*providers.deepcopy(self._factory.args, memo),
|
||||
**providers.deepcopy(self._factory.kwargs, memo),
|
||||
)
|
||||
self._copy_overridings(copied, memo)
|
||||
|
||||
return copied
|
||||
|
||||
def _provide(self, args, kwargs):
|
||||
return self._factory(*args, **kwargs)
|
||||
|
||||
|
||||
# Users factory:
|
||||
users_factory = UsersFactory()
|
||||
factory = CustomFactory(object)
|
||||
|
||||
# Creating several User objects:
|
||||
user1 = users_factory()
|
||||
user2 = users_factory()
|
||||
|
||||
# Making some asserts:
|
||||
assert isinstance(user1, User)
|
||||
assert isinstance(user2, User)
|
||||
assert user1 is not user2
|
||||
if __name__ == '__main__':
|
||||
object1 = factory()
|
||||
assert isinstance(object1, object)
|
||||
|
||||
object2 = factory()
|
||||
assert isinstance(object1, object)
|
||||
|
||||
assert object1 is not object2
|
||||
|
|
|
@ -43,6 +43,7 @@ class Provider(_Provider):
|
|||
def delegate(self) -> Provider: ...
|
||||
@property
|
||||
def provider(self) -> Provider: ...
|
||||
def _copy_overridings(self, copied: Provider, memo: Optional[Dict[str, Any]]) -> None: ...
|
||||
|
||||
|
||||
class Object(Provider, Generic[T]):
|
||||
|
|
Loading…
Reference in New Issue
Block a user