mirror of
https://github.com/ets-labs/python-dependency-injector.git
synced 2025-02-07 07:00:49 +03:00
Update providers overriding documentation and rework examples
This commit is contained in:
parent
dcc59ab0f4
commit
f8648adaf7
Binary file not shown.
Before Width: | Height: | Size: 10 KiB |
|
@ -7,6 +7,10 @@ that were made in every particular version.
|
|||
From version 0.7.6 *Dependency Injector* framework strictly
|
||||
follows `Semantic versioning`_
|
||||
|
||||
Development version
|
||||
-------------------
|
||||
- Update providers overriding documentation and rework examples.
|
||||
|
||||
3.35.1
|
||||
------
|
||||
- Fix minor issues in the providers documentation and examples.
|
||||
|
|
BIN
docs/providers/images/overriding.png
Normal file
BIN
docs/providers/images/overriding.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
|
@ -1,47 +1,41 @@
|
|||
Overriding of providers
|
||||
-----------------------
|
||||
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
|
||||
|
||||
Every provider could be overridden by another provider.
|
||||
Any provider can be overridden by another provider.
|
||||
|
||||
This gives opportunity to make system behaviour more flexible at some point.
|
||||
The main feature is that while your code is using providers, it depends on
|
||||
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.
|
||||
When provider is overridden it calls to the overriding provider instead of providing
|
||||
the object by its own.
|
||||
|
||||
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
|
||||
:width: 55%
|
||||
:align: center
|
||||
To override a provider you need to call the ``Provider.override()`` method. This method receives
|
||||
a single argument called ``overriding``. If the ``overriding`` value is a provider, this provider
|
||||
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
|
||||
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
|
||||
.. image:: images/overriding.png
|
||||
:width: 80%
|
||||
:align: center
|
||||
|
||||
.. literalinclude:: ../../examples/providers/overriding_simple.py
|
||||
.. literalinclude:: ../../examples/providers/overriding.py
|
||||
: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
|
||||
:width: 100%
|
||||
:align: center
|
||||
|
||||
.. literalinclude:: ../../examples/providers/overriding_users_model.py
|
||||
:language: python
|
||||
To reset an overriding you can use the ``Provider.reset_override()`` or
|
||||
``Provider.reset_last_overriding()`` methods.
|
||||
|
||||
You can use a context manager for overriding a provider ``with Provider.override():``. The
|
||||
overriding will be reset when context closed.
|
||||
|
||||
.. disqus::
|
||||
|
|
42
examples/providers/overriding.py
Normal file
42
examples/providers/overriding.py
Normal 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)
|
|
@ -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)
|
|
@ -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
|
Loading…
Reference in New Issue
Block a user