mirror of
				https://github.com/ets-labs/python-dependency-injector.git
				synced 2025-11-04 09:57:37 +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