Merge branch 'release/3.30.3' into master

This commit is contained in:
Roman Mogylatov 2020-08-16 01:13:47 -04:00
commit 597f6794a9
27 changed files with 374 additions and 368 deletions

View File

@ -52,7 +52,7 @@ What is ``Dependency Injector``?
``Dependency Injector`` is a dependency injection framework for Python. ``Dependency Injector`` is a dependency injection framework for Python.
It helps you implement the dependency injection principle. It helps you in implementing the dependency injection principle.
What is dependency injection? What is dependency injection?
----------------------------- -----------------------------
@ -70,6 +70,9 @@ Before:
.. code-block:: python .. code-block:: python
import os
class ApiClient: class ApiClient:
def __init__(self): def __init__(self):
@ -82,10 +85,18 @@ Before:
def __init__(self): def __init__(self):
self.api_client = ApiClient() self.api_client = ApiClient()
if __name__ == '__main__':
service = Service()
After: After:
.. code-block:: python .. code-block:: python
import os
class ApiClient: class ApiClient:
def __init__(self, api_key: str, timeout: int): def __init__(self, api_key: str, timeout: int):
@ -98,13 +109,23 @@ After:
def __init__(self, api_client: ApiClient): def __init__(self, api_client: ApiClient):
self.api_client = api_client self.api_client = api_client
Who creates the objects now? Look at the next section.
if __name__ == '__main__':
service = Service(ApiClient(os.getenv('API_KEY'), os.getenv('TIMEOUT')))
Flexibility comes with a price: now you need to assemble your objects like this
``Service(ApiClient(os.getenv('API_KEY'), os.getenv('TIMEOUT')))``. The assembly code might get
duplicated and it'll become harder to change the application structure.
What does Dependency Injector do? What does Dependency Injector do?
--------------------------------- ---------------------------------
``Dependency Injector`` provides you with the container and the providers that help you build ``Dependency Injector`` helps you assemble the objects.
your application objects when you apply dependency injection principle:
It provides you the container and the providers that help you describe objects assembly. When you
need an object you get it from the container. The rest of the assembly work is done by the
framework:
.. code-block:: python .. code-block:: python
@ -142,12 +163,29 @@ your application objects when you apply dependency injection principle:
if __name__ == '__main__': if __name__ == '__main__':
container = Container() container = Container()
container.config.from_yaml('config.yml') container.config.api_key.from_env('API_KEY')
container.config.timeout.from_env('TIMEOUT')
service = container.service() service = container.service()
assert isinstance(service.api_client, ApiClient) assert isinstance(service.api_client, ApiClient)
Retrieving of the ``Service`` instance now is done like this ``container.service()``.
Also ``Dependency Injector`` provides a bonus in overriding any of the providers with the
``.override()`` method:
.. code-block:: python
from unittest import mock
with container.api_client.override(mock.Mock()):
service = container.service()
assert isinstance(service.api_client, mock.Mock)
It helps in a testing. Also you can use it for configuring project for the different environments:
replace an API client with a stub on the dev or stage.
`More examples <https://github.com/ets-labs/python-dependency-injector/tree/master/examples>`_ `More examples <https://github.com/ets-labs/python-dependency-injector/tree/master/examples>`_
Installation Installation

View File

@ -1,55 +1,43 @@
Declarative containers Declarative container
---------------------- ---------------------
.. currentmodule:: dependency_injector.containers .. currentmodule:: dependency_injector.containers
:py:class:`DeclarativeContainer` is inversion of control container that :py:class:`DeclarativeContainer` is a class-based style of the providers definition.
could be defined in declarative manner. It should cover most of the cases
when list of providers that would be included in container is deterministic
(container will not change its structure in runtime).
Declarative containers have to extend base declarative container class - You create the declarative container subclass, put the providers as attributes and create the
:py:class:`dependency_injector.containers.DeclarativeContainer`. container instance.
Declarative container's providers have to be defined like container's class
attributes. Every provider in container has name. This name should follow
``some_provider`` convention, that is standard naming convention for
attribute names in Python.
.. note::
Declarative containers have several features that could be useful
for some kind of operations on container's providers, please visit API
documentation for getting full list of features -
:py:class:`dependency_injector.containers.DeclarativeContainer`.
Here is an simple example of defining declarative container with several
factories:
.. image:: /images/containers/declarative.png
:width: 85%
:align: center
.. literalinclude:: ../../examples/containers/declarative.py .. literalinclude:: ../../examples/containers/declarative.py
:language: python :language: python
:lines: 3-
Example of declarative containers inheritance: The declarative container providers should only be used when you have the container instance.
Working with the providers of the container on the class level will influence all further
instances.
.. image:: /images/containers/declarative_inheritance.png The declarative container can not have any methods or any other attributes then providers.
:width: 100%
:align: center The container class provides next attributes:
- ``providers`` - the dictionary of all the container providers
- ``cls_providers`` - the dictionary of the container providers of the current container
- ``inherited_providers`` - the dictionary of all the inherited container providers
.. literalinclude:: ../../examples/containers/declarative_inheritance.py .. literalinclude:: ../../examples/containers/declarative_inheritance.py
:language: python :language: python
:lines: 3-
Example of declarative containers's provider injections: Injections in the declarative container are done the usual way:
.. image:: /images/containers/declarative_injections.png
:width: 100%
:align: center
.. literalinclude:: ../../examples/containers/declarative_injections.py .. literalinclude:: ../../examples/containers/declarative_injections.py
:language: python :language: python
:lines: 3-
You can override the container providers when you create the container instance:
.. literalinclude:: ../../examples/containers/declarative_override_providers.py
:language: python
:lines: 3-
.. disqus:: .. disqus::

View File

@ -1,28 +1,25 @@
Dynamic containers Dynamic container
------------------ -----------------
.. currentmodule:: dependency_injector.containers .. currentmodule:: dependency_injector.containers
:py:class:`DynamicContainer` is an inversion of control container with dynamic :py:class:`DynamicContainer` is a collection of the providers defined in the runtime.
structure. It should cover most of the cases when list of providers that
would be included in container is non-deterministic and depends on
application's flow or its configuration (container's structure could be
determined just after application will be started and will do some initial
work, like parsing list of container's providers from the configuration).
While :py:class:`DeclarativeContainer` acts on class-level, You create the dynamic container instance and put the providers as attributes.
:py:class:`DynamicContainer` does the same on instance-level.
Here is an simple example of defining dynamic container with several factories:
.. literalinclude:: ../../examples/containers/dynamic.py .. literalinclude:: ../../examples/containers/dynamic.py
:language: python :language: python
:lines: 3-
Next example demonstrates creation of dynamic container based on some The dynamic container is good for the case when your application structure depends on the
configuration: configuration file or some other source that you can reach only after application is already
running (database, api, etc).
In this example we use the configuration to fill in the dynamic container with the providers:
.. literalinclude:: ../../examples/containers/dynamic_runtime_creation.py .. literalinclude:: ../../examples/containers/dynamic_runtime_creation.py
:language: python :language: python
:lines: 3-
.. disqus:: .. disqus::

View File

@ -1,21 +1,16 @@
IoC Containers Containers
============== ==========
Containers are collections of providers. Main purpose of containers is to group Containers are collections of the providers.
providers.
There are, actually, several popular cases of containers usage: There are several use cases how you can use containers:
+ Keeping all providers in a single container. + Keeping all the providers in a single container (most common).
+ Grouping of providers from the same architectural layer (for example, + Grouping of the providers from the same architectural layer (for example,
``Services``, ``Models`` and ``Forms`` containers). ``Services``, ``Models`` and ``Forms`` containers).
+ Grouping of providers from the same functional groups (for example, + Grouping of providers from the same functional groups (for example,
container ``Users``, that contains all functional parts of ``Users`` container ``Users``, that contains all functional parts of the ``users``
component). package).
Also, for both of these and some other cases, it might be useful to attach
some init / shutdown functionality or something else, that deals with group
of providers.
Containers package API docs - :py:mod:`dependency_injector.containers`. Containers package API docs - :py:mod:`dependency_injector.containers`.

View File

@ -1,41 +1,24 @@
Overriding of containers Overriding of the container
------------------------ ---------------------------
.. currentmodule:: dependency_injector.containers .. currentmodule:: dependency_injector.containers
Containers can be overridden by other containers. This, actually, means that The container can be overridden by the other container. All of the providers from the overriding
all of the providers from overriding container will override providers with container will override the providers with the same names in the overridden container.
the same names in overridden container.
There are two ways to override :py:class:`DeclarativeContainer` with another .. literalinclude:: ../../examples/containers/override.py
container:
- Use :py:meth:`DeclarativeContainer.override` method.
- Use :py:func:`override` class decorator.
Example of overriding container using :py:meth:`DeclarativeContainer.override`
method:
.. literalinclude:: ../../examples/containers/override_declarative.py
:language: python :language: python
:lines: 3-
Example of overriding container using :py:func:`override` decorator: It helps in a testing. Also you can use it for configuring project for the different
environments: replace an API client with a stub on the dev or stage.
.. literalinclude:: ../../examples/containers/override_declarative_decorator.py The container also has:
:language: python
Also there are several useful :py:class:`DeclarativeContainer` methods and - ``container.overridden`` - tuple of all overriding containers.
properties that help to work with container overridings: - ``container.reset_last_overriding()`` - reset last overriding for each provider in the container.
- ``container.reset_override()`` - reset all overriding in the container.
- :py:attr:`DeclarativeContainer.overridden` - tuple of all overriding
containers.
- :py:meth:`DeclarativeContainer.reset_last_overriding()` - reset last
overriding provider for each container providers.
- :py:meth:`DeclarativeContainer.reset_override()` - reset all overridings
for each container providers.
:py:class:`DynamicContainer` has exactly the same functionality, except of
:py:func:`override` decorator.
:py:class:`DynamicContainer` has the same functionality.
.. disqus:: .. disqus::

View File

@ -1,25 +1,25 @@
Specialization of containers Specialization of the container provider type
---------------------------- ---------------------------------------------
.. currentmodule:: dependency_injector.containers .. currentmodule:: dependency_injector.containers
:py:class:`DeclarativeContainer` could be specialized for any kind of needs You can make a restriction of the :py:class:`DeclarativeContainer` provider type:
via declaring its subclasses.
One of such `builtin` features is a limitation for providers type.
Next example shows usage of this feature with :py:class:`DeclarativeContainer`
in couple with feature of :py:class:`dependency_injector.providers.Factory`
for limitation of its provided type:
.. literalinclude:: ../../examples/containers/declarative_provider_type.py .. literalinclude:: ../../examples/containers/declarative_provider_type.py
:language: python :language: python
:lines: 3-
:emphasize-lines: 29-31
Limitation for providers type could be used with :py:class:`DynamicContainer` The emphasized lines will cause an error because ``other_provider`` is not a subtype of the
as well: ``ServiceProvider``. This helps to control the content of the container.
The same works for the :py:class:`DynamicContainer`:
.. literalinclude:: ../../examples/containers/dynamic_provider_type.py .. literalinclude:: ../../examples/containers/dynamic_provider_type.py
:language: python :language: python
:lines: 3-
:emphasize-lines: 23
The emphasized line will also cause an error.
.. disqus:: .. disqus::

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

View File

@ -7,6 +7,11 @@ 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.30.3
------
- Update README.
- Update containers documentation and examples.
3.30.2 3.30.2
------ ------
- Update README. - Update README.

View File

@ -1,23 +1,23 @@
"""Declarative IoC container simple example.""" """Declarative container example."""
import dependency_injector.containers as containers from dependency_injector import containers, providers
import dependency_injector.providers as providers
# Defining declarative IoC container:
class Container(containers.DeclarativeContainer): class Container(containers.DeclarativeContainer):
"""Example IoC container."""
factory1 = providers.Factory(object) factory1 = providers.Factory(object)
factory2 = providers.Factory(object) factory2 = providers.Factory(object)
# Creating some objects: if __name__ == '__main__':
object1 = Container.factory1() container = Container()
object2 = Container.factory2()
# Making some asserts: object1 = container.factory1()
assert object1 is not object2 object2 = container.factory2()
assert isinstance(object1, object)
assert isinstance(object2, object) print(container.providers)
# {
# 'factory1': <dependency_injector.providers.Factory(...),
# 'factory2': <dependency_injector.providers.Factory(...),
# }

View File

@ -1,30 +1,34 @@
"""Declarative IoC containers inheritance example.""" """Declarative containers inheritance example."""
import dependency_injector.containers as containers from dependency_injector import containers, providers
import dependency_injector.providers as providers
class ContainerA(containers.DeclarativeContainer): class ContainerA(containers.DeclarativeContainer):
"""Example IoC container A."""
provider1 = providers.Factory(object) provider1 = providers.Factory(object)
class ContainerB(ContainerA): class ContainerB(ContainerA):
"""Example IoC container B."""
provider2 = providers.Singleton(object) provider2 = providers.Singleton(object)
# Making some asserts for `providers` attribute: assert ContainerA.providers == {
assert ContainerA.providers == dict(provider1=ContainerA.provider1) 'provider1': ContainerA.provider1,
assert ContainerB.providers == dict(provider1=ContainerA.provider1, }
provider2=ContainerB.provider2) assert ContainerB.providers == {
'provider1': ContainerA.provider1,
'provider2': ContainerB.provider2,
}
# Making some asserts for `cls_providers` attribute: assert ContainerA.cls_providers == {
assert ContainerA.cls_providers == dict(provider1=ContainerA.provider1) 'provider1': ContainerA.provider1,
assert ContainerB.cls_providers == dict(provider2=ContainerB.provider2) }
assert ContainerB.cls_providers == {
'provider2': ContainerB.provider2,
}
# Making some asserts for `inherited_providers` attribute: assert ContainerA.inherited_providers == {}
assert ContainerA.inherited_providers == dict() assert ContainerB.inherited_providers == {
assert ContainerB.inherited_providers == dict(provider1=ContainerB.provider1) 'provider1': ContainerA.provider1,
}

View File

@ -1,35 +1,42 @@
"""Declarative IoC container's provider injections example.""" """Declarative container injections example."""
import sqlite3 import sqlite3
import collections
import dependency_injector.containers as containers from dependency_injector import containers, providers
import dependency_injector.providers as providers
UsersService = collections.namedtuple('UsersService', ['db']) class UserService:
AuthService = collections.namedtuple('AuthService', ['db', 'users_service']) def __init__(self, db: sqlite3.Connection):
self.db = db
class Services(containers.DeclarativeContainer): class AuthService:
"""IoC container of service providers.""" def __init__(self, db: sqlite3.Connection, user_service: UserService):
self.db = db
self.user_service = user_service
class Container(containers.DeclarativeContainer):
database = providers.Singleton(sqlite3.connect, ':memory:') database = providers.Singleton(sqlite3.connect, ':memory:')
users = providers.Factory(UsersService, user_service = providers.Factory(
db=database) UserService,
auth = providers.Factory(AuthService,
db=database, db=database,
users_service=users) )
auth_service = providers.Factory(
AuthService,
db=database,
user_service=user_service,
)
# Retrieving service providers from container: if __name__ == '__main__':
users_service = Services.users() container = Container()
auth_service = Services.auth()
# Making some asserts: user_service = container.user_service()
assert users_service.db is auth_service.db is Services.database() auth_service = container.auth_service()
assert isinstance(auth_service.users_service, UsersService)
assert users_service is not Services.users() assert user_service.db is auth_service.db is container.database()
assert auth_service is not Services.auth() assert isinstance(auth_service.user_service, UserService)

View File

@ -0,0 +1,18 @@
"""Declarative container provider override example."""
import sqlite3
from unittest import mock
from dependency_injector import containers, providers
class Container(containers.DeclarativeContainer):
database = providers.Singleton(sqlite3.connect, ':memory:')
if __name__ == '__main__':
container = Container(database=mock.Mock(sqlite3.Connection))
database = container.database()
assert isinstance(database, mock.Mock)

View File

@ -1,48 +1,33 @@
"""Specializing declarative container and factory provider example.""" """Declarative container provider type restriction example."""
import collections import abc
import dependency_injector.containers as containers from dependency_injector import containers, providers
import dependency_injector.providers as providers
import dependency_injector.errors as errors
class SequenceProvider(providers.Factory): class Service(metaclass=abc.ABCMeta):
"""Sequence factory. ...
Can provide only sequence objects.
"""
provided_type = collections.Sequence
class SequencesContainer(containers.DeclarativeContainer): class UserService(Service):
"""IoC container. ...
Can contain only sequence providers.
"""
provider_type = SequenceProvider
if __name__ == '__main__': class ServiceProvider(providers.Factory):
try:
class _SequenceContainer1(SequencesContainer):
object_provider = providers.Factory(object)
except errors.Error as exception:
print(exception)
# <class '__main__._SequenceContainer1'> can contain only
# <class '__main__.SequenceProvider'> instances
try: provided_type = Service
class _SequenceContainer2(SequencesContainer):
object_provider = SequenceProvider(object)
except errors.Error as exception:
print(exception)
# <class '__main__.SequenceProvider'> can provide only
# <class '_abcoll.Sequence'> instances
class _SequenceContaier3(SequencesContainer):
list_provider = SequenceProvider(list)
assert _SequenceContaier3.list_provider() == list() class ServiceContainer(containers.DeclarativeContainer):
provider_type = ServiceProvider
class MyServices(ServiceContainer):
user_service = ServiceProvider(UserService)
class ImproperServices(ServiceContainer):
other_provider = providers.Factory(object)

View File

@ -1,18 +1,18 @@
"""Dynamic container simple example.""" """Dynamic container example."""
import dependency_injector.containers as containers from dependency_injector import containers, providers
import dependency_injector.providers as providers
# Defining dynamic container: if __name__ == '__main__':
container = containers.DynamicContainer() container = containers.DynamicContainer()
container.factory1 = providers.Factory(object) container.factory1 = providers.Factory(object)
container.factory2 = providers.Factory(object) container.factory2 = providers.Factory(object)
# Creating some objects: object1 = container.factory1()
object1 = container.factory1() object2 = container.factory2()
object2 = container.factory2()
# Making some asserts: print(container.providers)
assert object1 is not object2 # {
assert isinstance(object1, object) and isinstance(object2, object) # 'factory1': <dependency_injector.providers.Factory(...),
# 'factory2': <dependency_injector.providers.Factory(...),
# }

View File

@ -1,41 +1,25 @@
"""Specializing dynamic container and factory provider example.""" """Dynamic container provider type restriction example."""
import collections import abc
import dependency_injector.containers as containers from dependency_injector import containers, providers
import dependency_injector.providers as providers
import dependency_injector.errors as errors
class SequenceProvider(providers.Factory): class Service(metaclass=abc.ABCMeta):
"""Sequence factory. ...
Can provide only sequence objects.
"""
provided_type = collections.Sequence
sequences_container = containers.DynamicContainer() class UserService(Service):
sequences_container.provider_type = SequenceProvider ...
if __name__ == '__main__': class ServiceProvider(providers.Factory):
try:
sequences_container.object_provider = providers.Factory(object)
except errors.Error as exception:
print(exception)
# <dependency_injector.containers.DynamicContainer object at
# 0x107820ed0> can contain only <class '__main__.SequenceProvider'>
# instances
try: provided_type = Service
sequences_container.object_provider = SequenceProvider(object)
except errors.Error as exception:
print(exception)
# <class '__main__.SequenceProvider'> can provide only
# <class '_abcoll.Sequence'> instances
sequences_container.list_provider = SequenceProvider(list)
assert sequences_container.list_provider() == list() services = containers.DynamicContainer()
services.provider_type = ServiceProvider
services.user_service = ServiceProvider(UserService)
services.other_provider = providers.Factory(object)

View File

@ -1,59 +1,40 @@
"""Creation of dynamic container based on some configuration example.""" """Creation of dynamic container based on the configuration example."""
import collections from dependency_injector import containers, providers
import dependency_injector.containers as containers
# Defining several example services: class UserService:
UsersService = collections.namedtuple('UsersService', []) ...
AuthService = collections.namedtuple('AuthService', [])
def import_cls(cls_name): class AuthService:
"""Import class by its fully qualified name. ...
In terms of current example it is just a small helper function. Please,
don't use it in production approaches.
"""
path_components = cls_name.split('.')
module = __import__('.'.join(path_components[:-1]),
locals(),
globals(),
fromlist=path_components[-1:])
return getattr(module, path_components[-1])
# "Parsing" some configuration: def populate_container(container, providers_config):
config = { for provider_name, provider_info in providers_config.items():
'services': { provided_cls = globals().get(provider_info['class'])
'users': { provider_cls = getattr(providers, provider_info['provider_class'])
'class': '__main__.UsersService', setattr(container, provider_name, provider_cls(provided_cls))
'provider_class': 'dependency_injector.providers.Factory',
if __name__ == '__main__':
services_config = {
'user': {
'class': 'UserService',
'provider_class': 'Factory',
}, },
'auth': { 'auth': {
'class': '__main__.AuthService', 'class': 'AuthService',
'provider_class': 'dependency_injector.providers.Factory', 'provider_class': 'Factory',
},
} }
} services = containers.DynamicContainer()
}
# Creating empty container of service providers: populate_container(services, services_config)
services = containers.DynamicContainer()
# Filling dynamic container with service providers using configuration: user_service = services.user()
for service_name, service_info in config['services'].iteritems(): auth_service = services.auth()
# Runtime importing of service and service provider classes:
service_cls = import_cls(service_info['class'])
service_provider_cls = import_cls(service_info['provider_class'])
# Binding service provider to the dynamic service providers catalog: assert isinstance(user_service, UserService)
setattr(services, service_name, service_provider_cls(service_cls)) assert isinstance(auth_service, AuthService)
# Creating some objects:
users_service = services.users()
auth_service = services.auth()
# Making some asserts:
assert isinstance(users_service, UsersService)
assert isinstance(auth_service, AuthService)

View File

@ -0,0 +1,31 @@
"""Container overriding example."""
from dependency_injector import containers, providers
class Service:
...
class ServiceStub:
...
class Container(containers.DeclarativeContainer):
service = providers.Factory(Service)
class OverridingContainer(containers.DeclarativeContainer):
service = providers.Factory(ServiceStub)
if __name__ == '__main__':
container = Container()
overriding_container = OverridingContainer()
container.override(overriding_container)
service = container.service()
assert isinstance(service, ServiceStub)

View File

@ -1,28 +0,0 @@
"""Declarative IoC container overriding example."""
import dependency_injector.containers as containers
import dependency_injector.providers as providers
class Container(containers.DeclarativeContainer):
"""IoC container."""
sequence_factory = providers.Factory(list)
class OverridingContainer(containers.DeclarativeContainer):
"""Overriding IoC container."""
sequence_factory = providers.Factory(tuple)
# Overriding `Container` with `OverridingContainer`:
Container.override(OverridingContainer)
# Creating some objects using overridden container:
sequence_1 = Container.sequence_factory([1, 2, 3])
sequence_2 = Container.sequence_factory([3, 2, 1])
# Making some asserts:
assert Container.overridden == (OverridingContainer,)
assert sequence_1 == (1, 2, 3) and sequence_2 == (3, 2, 1)

View File

@ -1,27 +0,0 @@
"""Declarative IoC container overriding using `@override()` decorator."""
import dependency_injector.containers as containers
import dependency_injector.providers as providers
class Container(containers.DeclarativeContainer):
"""IoC container."""
sequence_factory = providers.Factory(list)
# Overriding `Container` with `OverridingContainer`:
@containers.override(Container)
class OverridingContainer(containers.DeclarativeContainer):
"""Overriding IoC container."""
sequence_factory = providers.Factory(tuple)
# Creating some objects using overridden container:
sequence_1 = Container.sequence_factory([1, 2, 3])
sequence_2 = Container.sequence_factory([3, 2, 1])
# Making some asserts:
assert Container.overridden == (OverridingContainer,)
assert sequence_1 == (1, 2, 3) and sequence_2 == (3, 2, 1)

View File

@ -1,2 +0,0 @@
api_key: test-key
timeout: 5

View File

@ -32,8 +32,8 @@ class Container(containers.DeclarativeContainer):
if __name__ == '__main__': if __name__ == '__main__':
container = Container() container = Container()
container.config.from_yaml('config.yml') container.config.api_key.from_env('API_KEY')
container.config.timeout.from_env('TIMEOUT')
service = container.service() service = container.service()
assert isinstance(service.api_client, ApiClient) assert isinstance(service.api_client, ApiClient)

View File

@ -0,0 +1,18 @@
import os
class ApiClient:
def __init__(self, api_key: str, timeout: int):
self.api_key = api_key
self.timeout = timeout
class Service:
def __init__(self, api_client: ApiClient):
self.api_client = api_client
if __name__ == '__main__':
service = Service(ApiClient(os.getenv('API_KEY'), os.getenv('TIMEOUT')))

View File

@ -0,0 +1,18 @@
import os
class ApiClient:
def __init__(self):
self.api_key = os.getenv('API_KEY')
self.timeout = os.getenv('TIMEOUT')
class Service:
def __init__(self):
self.api_client = ApiClient()
if __name__ == '__main__':
service = Service()

11
examples/di_demo2/test.py Normal file
View File

@ -0,0 +1,11 @@
from unittest import mock
from demo import Container
if __name__ == '__main__':
container = Container()
with container.api_client.override(mock.Mock()):
service = container.service()
assert isinstance(service.api_client, mock.Mock)

View File

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