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.
It helps you implement the dependency injection principle.
It helps you in implementing the dependency injection principle.
What is dependency injection?
-----------------------------
@ -70,6 +70,9 @@ Before:
.. code-block:: python
import os
class ApiClient:
def __init__(self):
@ -82,10 +85,18 @@ Before:
def __init__(self):
self.api_client = ApiClient()
if __name__ == '__main__':
service = Service()
After:
.. code-block:: python
import os
class ApiClient:
def __init__(self, api_key: str, timeout: int):
@ -98,55 +109,82 @@ After:
def __init__(self, api_client: ApiClient):
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?
---------------------------------
``Dependency Injector`` provides you with the container and the providers that help you build
your application objects when you apply dependency injection principle:
``Dependency Injector`` helps you assemble the objects.
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
from dependency_injector import containers, providers
from dependency_injector import containers, providers
class ApiClient:
class ApiClient:
def __init__(self, api_key: str, timeout: int):
self.api_key = api_key
self.timeout = timeout
def __init__(self, api_key: str, timeout: int):
self.api_key = api_key
self.timeout = timeout
class Service:
class Service:
def __init__(self, api_client: ApiClient):
self.api_client = api_client
def __init__(self, api_client: ApiClient):
self.api_client = api_client
class Container(containers.DeclarativeContainer):
class Container(containers.DeclarativeContainer):
config = providers.Configuration()
config = providers.Configuration()
api_client = providers.Singleton(
ApiClient,
api_key=config.api_key,
timeout=config.timeout,
)
api_client = providers.Singleton(
ApiClient,
api_key=config.api_key,
timeout=config.timeout,
)
service = providers.Factory(
Service,
api_client=api_client,
)
service = providers.Factory(
Service,
api_client=api_client,
)
if __name__ == '__main__':
container = Container()
container.config.from_yaml('config.yml')
if __name__ == '__main__':
container = Container()
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>`_

View File

@ -1,55 +1,43 @@
Declarative containers
----------------------
Declarative container
---------------------
.. currentmodule:: dependency_injector.containers
:py:class:`DeclarativeContainer` is inversion of control container that
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).
:py:class:`DeclarativeContainer` is a class-based style of the providers definition.
Declarative containers have to extend base declarative container class -
:py:class:`dependency_injector.containers.DeclarativeContainer`.
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
You create the declarative container subclass, put the providers as attributes and create the
container instance.
.. literalinclude:: ../../examples/containers/declarative.py
: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
:width: 100%
:align: center
The declarative container can not have any methods or any other attributes then providers.
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
:language: python
:lines: 3-
Example of declarative containers's provider injections:
.. image:: /images/containers/declarative_injections.png
:width: 100%
:align: center
Injections in the declarative container are done the usual way:
.. literalinclude:: ../../examples/containers/declarative_injections.py
: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::

View File

@ -1,28 +1,25 @@
Dynamic containers
------------------
Dynamic container
-----------------
.. currentmodule:: dependency_injector.containers
:py:class:`DynamicContainer` is an inversion of control container with dynamic
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).
:py:class:`DynamicContainer` is a collection of the providers defined in the runtime.
While :py:class:`DeclarativeContainer` acts on class-level,
:py:class:`DynamicContainer` does the same on instance-level.
Here is an simple example of defining dynamic container with several factories:
You create the dynamic container instance and put the providers as attributes.
.. literalinclude:: ../../examples/containers/dynamic.py
:language: python
:lines: 3-
Next example demonstrates creation of dynamic container based on some
configuration:
The dynamic container is good for the case when your application structure depends on the
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
:language: python
:lines: 3-
.. disqus::

View File

@ -1,21 +1,16 @@
IoC Containers
==============
Containers
==========
Containers are collections of providers. Main purpose of containers is to group
providers.
Containers are collections of the 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.
+ Grouping of providers from the same architectural layer (for example,
+ Keeping all the providers in a single container (most common).
+ Grouping of the providers from the same architectural layer (for example,
``Services``, ``Models`` and ``Forms`` containers).
+ Grouping of providers from the same functional groups (for example,
container ``Users``, that contains all functional parts of ``Users``
component).
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.
container ``Users``, that contains all functional parts of the ``users``
package).
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
Containers can be overridden by other containers. This, actually, means that
all of the providers from overriding container will override providers with
the same names in overridden container.
The container can be overridden by the other container. All of the providers from the overriding
container will override the providers with the same names in the overridden container.
There are two ways to override :py:class:`DeclarativeContainer` with another
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
.. literalinclude:: ../../examples/containers/override.py
: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
:language: python
The container also has:
Also there are several useful :py:class:`DeclarativeContainer` methods and
properties that help to work with container overridings:
- :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.
- ``container.overridden`` - tuple of all overriding containers.
- ``container.reset_last_overriding()`` - reset last overriding for each provider in the container.
- ``container.reset_override()`` - reset all overriding in the container.
:py:class:`DynamicContainer` has the same functionality.
.. disqus::

View File

@ -1,25 +1,25 @@
Specialization of containers
----------------------------
Specialization of the container provider type
---------------------------------------------
.. currentmodule:: dependency_injector.containers
:py:class:`DeclarativeContainer` could be specialized for any kind of needs
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:
You can make a restriction of the :py:class:`DeclarativeContainer` provider type:
.. literalinclude:: ../../examples/containers/declarative_provider_type.py
:language: python
:lines: 3-
:emphasize-lines: 29-31
Limitation for providers type could be used with :py:class:`DynamicContainer`
as well:
The emphasized lines will cause an error because ``other_provider`` is not a subtype of the
``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
:language: python
:lines: 3-
:emphasize-lines: 23
The emphasized line will also cause an error.
.. 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
follows `Semantic versioning`_
3.30.3
------
- Update README.
- Update containers documentation and examples.
3.30.2
------
- Update README.

View File

@ -1,23 +1,23 @@
"""Declarative IoC container simple example."""
"""Declarative container example."""
import dependency_injector.containers as containers
import dependency_injector.providers as providers
from dependency_injector import containers, providers
# Defining declarative IoC container:
class Container(containers.DeclarativeContainer):
"""Example IoC container."""
factory1 = providers.Factory(object)
factory2 = providers.Factory(object)
# Creating some objects:
object1 = Container.factory1()
object2 = Container.factory2()
if __name__ == '__main__':
container = Container()
# Making some asserts:
assert object1 is not object2
assert isinstance(object1, object)
assert isinstance(object2, object)
object1 = container.factory1()
object2 = container.factory2()
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
import dependency_injector.providers as providers
from dependency_injector import containers, providers
class ContainerA(containers.DeclarativeContainer):
"""Example IoC container A."""
provider1 = providers.Factory(object)
class ContainerB(ContainerA):
"""Example IoC container B."""
provider2 = providers.Singleton(object)
# Making some asserts for `providers` attribute:
assert ContainerA.providers == dict(provider1=ContainerA.provider1)
assert ContainerB.providers == dict(provider1=ContainerA.provider1,
provider2=ContainerB.provider2)
assert ContainerA.providers == {
'provider1': ContainerA.provider1,
}
assert ContainerB.providers == {
'provider1': ContainerA.provider1,
'provider2': ContainerB.provider2,
}
# Making some asserts for `cls_providers` attribute:
assert ContainerA.cls_providers == dict(provider1=ContainerA.provider1)
assert ContainerB.cls_providers == dict(provider2=ContainerB.provider2)
assert ContainerA.cls_providers == {
'provider1': ContainerA.provider1,
}
assert ContainerB.cls_providers == {
'provider2': ContainerB.provider2,
}
# Making some asserts for `inherited_providers` attribute:
assert ContainerA.inherited_providers == dict()
assert ContainerB.inherited_providers == dict(provider1=ContainerB.provider1)
assert ContainerA.inherited_providers == {}
assert ContainerB.inherited_providers == {
'provider1': ContainerA.provider1,
}

View File

@ -1,35 +1,42 @@
"""Declarative IoC container's provider injections example."""
"""Declarative container injections example."""
import sqlite3
import collections
import dependency_injector.containers as containers
import dependency_injector.providers as providers
from dependency_injector import containers, providers
UsersService = collections.namedtuple('UsersService', ['db'])
AuthService = collections.namedtuple('AuthService', ['db', 'users_service'])
class UserService:
def __init__(self, db: sqlite3.Connection):
self.db = db
class Services(containers.DeclarativeContainer):
"""IoC container of service providers."""
class AuthService:
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:')
users = providers.Factory(UsersService,
db=database)
user_service = providers.Factory(
UserService,
db=database,
)
auth = providers.Factory(AuthService,
db=database,
users_service=users)
auth_service = providers.Factory(
AuthService,
db=database,
user_service=user_service,
)
# Retrieving service providers from container:
users_service = Services.users()
auth_service = Services.auth()
if __name__ == '__main__':
container = Container()
# Making some asserts:
assert users_service.db is auth_service.db is Services.database()
assert isinstance(auth_service.users_service, UsersService)
assert users_service is not Services.users()
assert auth_service is not Services.auth()
user_service = container.user_service()
auth_service = container.auth_service()
assert user_service.db is auth_service.db is container.database()
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
import dependency_injector.providers as providers
import dependency_injector.errors as errors
from dependency_injector import containers, providers
class SequenceProvider(providers.Factory):
"""Sequence factory.
Can provide only sequence objects.
"""
provided_type = collections.Sequence
class Service(metaclass=abc.ABCMeta):
...
class SequencesContainer(containers.DeclarativeContainer):
"""IoC container.
Can contain only sequence providers.
"""
provider_type = SequenceProvider
class UserService(Service):
...
if __name__ == '__main__':
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
class ServiceProvider(providers.Factory):
try:
class _SequenceContainer2(SequencesContainer):
object_provider = SequenceProvider(object)
except errors.Error as exception:
print(exception)
# <class '__main__.SequenceProvider'> can provide only
# <class '_abcoll.Sequence'> instances
provided_type = Service
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
import dependency_injector.providers as providers
from dependency_injector import containers, providers
# Defining dynamic container:
container = containers.DynamicContainer()
container.factory1 = providers.Factory(object)
container.factory2 = providers.Factory(object)
if __name__ == '__main__':
container = containers.DynamicContainer()
container.factory1 = providers.Factory(object)
container.factory2 = providers.Factory(object)
# Creating some objects:
object1 = container.factory1()
object2 = container.factory2()
object1 = container.factory1()
object2 = container.factory2()
# Making some asserts:
assert object1 is not object2
assert isinstance(object1, object) and isinstance(object2, object)
print(container.providers)
# {
# '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
import dependency_injector.providers as providers
import dependency_injector.errors as errors
from dependency_injector import containers, providers
class SequenceProvider(providers.Factory):
"""Sequence factory.
Can provide only sequence objects.
"""
provided_type = collections.Sequence
class Service(metaclass=abc.ABCMeta):
...
sequences_container = containers.DynamicContainer()
sequences_container.provider_type = SequenceProvider
class UserService(Service):
...
if __name__ == '__main__':
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
class ServiceProvider(providers.Factory):
try:
sequences_container.object_provider = SequenceProvider(object)
except errors.Error as exception:
print(exception)
# <class '__main__.SequenceProvider'> can provide only
# <class '_abcoll.Sequence'> instances
provided_type = Service
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
import dependency_injector.containers as containers
from dependency_injector import containers, providers
# Defining several example services:
UsersService = collections.namedtuple('UsersService', [])
AuthService = collections.namedtuple('AuthService', [])
class UserService:
...
def import_cls(cls_name):
"""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])
class AuthService:
...
# "Parsing" some configuration:
config = {
'services': {
'users': {
'class': '__main__.UsersService',
'provider_class': 'dependency_injector.providers.Factory',
def populate_container(container, providers_config):
for provider_name, provider_info in providers_config.items():
provided_cls = globals().get(provider_info['class'])
provider_cls = getattr(providers, provider_info['provider_class'])
setattr(container, provider_name, provider_cls(provided_cls))
if __name__ == '__main__':
services_config = {
'user': {
'class': 'UserService',
'provider_class': 'Factory',
},
'auth': {
'class': '__main__.AuthService',
'provider_class': 'dependency_injector.providers.Factory',
}
'class': 'AuthService',
'provider_class': 'Factory',
},
}
}
services = containers.DynamicContainer()
# Creating empty container of service providers:
services = containers.DynamicContainer()
populate_container(services, services_config)
# Filling dynamic container with service providers using configuration:
for service_name, service_info in config['services'].iteritems():
# Runtime importing of service and service provider classes:
service_cls = import_cls(service_info['class'])
service_provider_cls = import_cls(service_info['provider_class'])
user_service = services.user()
auth_service = services.auth()
# Binding service provider to the dynamic service providers catalog:
setattr(services, service_name, service_provider_cls(service_cls))
# Creating some objects:
users_service = services.users()
auth_service = services.auth()
# Making some asserts:
assert isinstance(users_service, UsersService)
assert isinstance(auth_service, AuthService)
assert isinstance(user_service, UserService)
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__':
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()
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."""
__version__ = '3.30.2'
__version__ = '3.30.3'
"""Version number that follows semantic versioning.
:type: str