mirror of
https://github.com/ets-labs/python-dependency-injector.git
synced 2024-11-26 11:33:58 +03:00
Merge branch 'release/4.1.0' into master
This commit is contained in:
commit
90af46da98
|
@ -1,4 +1,4 @@
|
||||||
Copyright (c) 2017, ETS Labs
|
Copyright (c) 2020, ETS Labs
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
|
10
README.rst
10
README.rst
|
@ -57,16 +57,20 @@ It helps implementing the dependency injection principle.
|
||||||
Key features of the ``Dependency Injector``:
|
Key features of the ``Dependency Injector``:
|
||||||
|
|
||||||
- **Providers**. Provides ``Factory``, ``Singleton``, ``Callable``, ``Coroutine``, ``Object``,
|
- **Providers**. Provides ``Factory``, ``Singleton``, ``Callable``, ``Coroutine``, ``Object``,
|
||||||
``List``, ``Configuration``, ``Dependency`` and ``Selector`` providers that help assembling your
|
``List``, ``Dict``, ``Configuration``, ``Resource``, ``Dependency`` and ``Selector`` providers
|
||||||
objects. See `Providers <https://python-dependency-injector.ets-labs.org/providers/index.html>`_.
|
that help assembling your objects.
|
||||||
|
See `Providers <https://python-dependency-injector.ets-labs.org/providers/index.html>`_.
|
||||||
- **Overriding**. Can override any provider by another provider on the fly. This helps in testing
|
- **Overriding**. Can override any provider by another provider on the fly. This helps in testing
|
||||||
and configuring dev / stage environment to replace API clients with stubs etc. See
|
and configuring dev / stage environment to replace API clients with stubs etc. See
|
||||||
`Provider overriding <https://python-dependency-injector.ets-labs.org/providers/overriding.html>`_.
|
`Provider overriding <https://python-dependency-injector.ets-labs.org/providers/overriding.html>`_.
|
||||||
- **Configuration**. Read configuration from ``yaml`` & ``ini`` files, environment variables
|
- **Configuration**. Reads configuration from ``yaml`` & ``ini`` files, environment variables
|
||||||
and dictionaries.
|
and dictionaries.
|
||||||
See `Configuration provider <https://python-dependency-injector.ets-labs.org/providers/configuration.html>`_.
|
See `Configuration provider <https://python-dependency-injector.ets-labs.org/providers/configuration.html>`_.
|
||||||
- **Containers**. Provides declarative and dynamic containers.
|
- **Containers**. Provides declarative and dynamic containers.
|
||||||
See `Containers <https://python-dependency-injector.ets-labs.org/containers/index.html>`_.
|
See `Containers <https://python-dependency-injector.ets-labs.org/containers/index.html>`_.
|
||||||
|
- **Resources**. Helps with initialization and configuring of logging, event loop, thread
|
||||||
|
or process pool, etc.
|
||||||
|
See `Resource provider <https://python-dependency-injector.ets-labs.org/providers/resource.html>`_.
|
||||||
- **Wiring**. Injects dependencies into functions and methods. Helps integrating with
|
- **Wiring**. Injects dependencies into functions and methods. Helps integrating with
|
||||||
other frameworks: Django, Flask, Aiohttp, etc.
|
other frameworks: Django, Flask, Aiohttp, etc.
|
||||||
See `Wiring <https://python-dependency-injector.ets-labs.org/wiring.html>`_.
|
See `Wiring <https://python-dependency-injector.ets-labs.org/wiring.html>`_.
|
||||||
|
|
|
@ -141,7 +141,7 @@ html_theme_path = [alabaster.get_path()]
|
||||||
# The name of an image file (within the static path) to use as favicon of the
|
# The name of an image file (within the static path) to use as favicon of the
|
||||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||||
# pixels large.
|
# pixels large.
|
||||||
#html_favicon = None
|
html_favicon = 'favicon.ico'
|
||||||
|
|
||||||
# Add any paths that contain custom static files (such as style sheets) here,
|
# Add any paths that contain custom static files (such as style sheets) here,
|
||||||
# relative to this directory. They are copied after the builtin static files,
|
# relative to this directory. They are copied after the builtin static files,
|
||||||
|
|
|
@ -21,4 +21,20 @@ The container also has:
|
||||||
|
|
||||||
:py:class:`DynamicContainer` has the same functionality.
|
:py:class:`DynamicContainer` has the same functionality.
|
||||||
|
|
||||||
|
Another possible way to override container providers on declarative level is
|
||||||
|
``@containers.override()`` decorator:
|
||||||
|
|
||||||
|
.. literalinclude:: ../../examples/containers/declarative_override_decorator.py
|
||||||
|
:language: python
|
||||||
|
:lines: 3-
|
||||||
|
:emphasize-lines: 12-16
|
||||||
|
|
||||||
|
Decorator ``@containers.override()`` takes a container for overriding as an argument.
|
||||||
|
This container providers will be overridden by the providers with the same names from
|
||||||
|
the decorated container.
|
||||||
|
|
||||||
|
It helps to change the behaviour of application by importing extension modules but not a code change.
|
||||||
|
Imported module can override providers in main container. While the code uses main container as
|
||||||
|
before, the overridden providers provide components defined in the extension module.
|
||||||
|
|
||||||
.. disqus::
|
.. disqus::
|
||||||
|
|
BIN
docs/favicon.ico
Normal file
BIN
docs/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
|
@ -34,15 +34,15 @@ Dependency Injector --- Dependency injection framework for Python
|
||||||
:target: https://pypi.org/project/dependency-injector/
|
:target: https://pypi.org/project/dependency-injector/
|
||||||
:alt: Supported Python implementations
|
:alt: Supported Python implementations
|
||||||
|
|
||||||
.. image:: https://pepy.tech/badge/dependency-injector
|
.. image:: https://static.pepy.tech/badge/dependency-injector
|
||||||
:target: https://pepy.tech/project/dependency-injector
|
:target: https://pepy.tech/project/dependency-injector
|
||||||
:alt: Downloads
|
:alt: Downloads
|
||||||
|
|
||||||
.. image:: https://pepy.tech/badge/dependency-injector/month
|
.. image:: https://static.pepy.tech/badge/dependency-injector/month
|
||||||
:target: https://pepy.tech/project/dependency-injector
|
:target: https://pepy.tech/project/dependency-injector
|
||||||
:alt: Downloads
|
:alt: Downloads
|
||||||
|
|
||||||
.. image:: https://pepy.tech/badge/dependency-injector/week
|
.. image:: https://static.pepy.tech/badge/dependency-injector/week
|
||||||
:target: https://pepy.tech/project/dependency-injector
|
:target: https://pepy.tech/project/dependency-injector
|
||||||
:alt: Downloads
|
:alt: Downloads
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ Dependency Injector --- Dependency injection framework for Python
|
||||||
:target: https://pypi.org/project/dependency-injector/
|
:target: https://pypi.org/project/dependency-injector/
|
||||||
:alt: Wheel
|
:alt: Wheel
|
||||||
|
|
||||||
.. image:: https://travis-ci.org/ets-labs/python-dependency-injector.svg?branch=master
|
.. image:: https://api.travis-ci.org/ets-labs/python-dependency-injector.svg?branch=master
|
||||||
:target: https://travis-ci.org/ets-labs/python-dependency-injector
|
:target: https://travis-ci.org/ets-labs/python-dependency-injector
|
||||||
:alt: Build Status
|
:alt: Build Status
|
||||||
|
|
||||||
|
@ -69,13 +69,15 @@ It helps implementing the dependency injection principle.
|
||||||
Key features of the ``Dependency Injector``:
|
Key features of the ``Dependency Injector``:
|
||||||
|
|
||||||
- **Providers**. Provides ``Factory``, ``Singleton``, ``Callable``, ``Coroutine``, ``Object``,
|
- **Providers**. Provides ``Factory``, ``Singleton``, ``Callable``, ``Coroutine``, ``Object``,
|
||||||
``List``, ``Configuration``, ``Dependency`` and ``Selector`` providers that help assembling your
|
``List``, ``Dict``, ``Configuration``, ``Resource``, ``Dependency`` and ``Selector`` providers
|
||||||
objects. See :ref:`providers`.
|
that help assembling your objects. See :ref:`providers`.
|
||||||
- **Overriding**. Can override any provider by another provider on the fly. This helps in testing
|
- **Overriding**. Can override any provider by another provider on the fly. This helps in testing
|
||||||
and configuring dev / stage environment to replace API clients with stubs etc. See
|
and configuring dev / stage environment to replace API clients with stubs etc. See
|
||||||
:ref:`provider-overriding`.
|
:ref:`provider-overriding`.
|
||||||
- **Configuration**. Read configuration from ``yaml`` & ``ini`` files, environment variables
|
- **Configuration**. Reads configuration from ``yaml`` & ``ini`` files, environment variables
|
||||||
and dictionaries. See :ref:`configuration-provider`.
|
and dictionaries. See :ref:`configuration-provider`.
|
||||||
|
- **Resources**. Helps with initialization and configuring of logging, event loop, thread
|
||||||
|
or process pool, etc. See :ref:`resource-provider`.
|
||||||
- **Containers**. Provides declarative and dynamic containers. See :ref:`containers`.
|
- **Containers**. Provides declarative and dynamic containers. See :ref:`containers`.
|
||||||
- **Wiring**. Injects dependencies into functions and methods. Helps integrating with
|
- **Wiring**. Injects dependencies into functions and methods. Helps integrating with
|
||||||
other frameworks: Django, Flask, Aiohttp, etc. See :ref:`wiring`.
|
other frameworks: Django, Flask, Aiohttp, etc. See :ref:`wiring`.
|
||||||
|
|
|
@ -11,13 +11,15 @@ Key features
|
||||||
Key features of the ``Dependency Injector``:
|
Key features of the ``Dependency Injector``:
|
||||||
|
|
||||||
- **Providers**. Provides ``Factory``, ``Singleton``, ``Callable``, ``Coroutine``, ``Object``,
|
- **Providers**. Provides ``Factory``, ``Singleton``, ``Callable``, ``Coroutine``, ``Object``,
|
||||||
``List``, ``Configuration``, ``Dependency`` and ``Selector`` providers that help assembling your
|
``List``, ``Dict``, ``Configuration``, ``Resource``, ``Dependency`` and ``Selector`` providers
|
||||||
objects. See :ref:`providers`.
|
that help assembling your objects. See :ref:`providers`.
|
||||||
- **Overriding**. Can override any provider by another provider on the fly. This helps in testing
|
- **Overriding**. Can override any provider by another provider on the fly. This helps in testing
|
||||||
and configuring dev / stage environment to replace API clients with stubs etc. See
|
and configuring dev / stage environment to replace API clients with stubs etc. See
|
||||||
:ref:`provider-overriding`.
|
:ref:`provider-overriding`.
|
||||||
- **Configuration**. Read configuration from ``yaml`` & ``ini`` files, environment variables
|
- **Configuration**. Reads configuration from ``yaml`` & ``ini`` files, environment variables
|
||||||
and dictionaries. See :ref:`configuration-provider`.
|
and dictionaries. See :ref:`configuration-provider`.
|
||||||
|
- **Resources**. Helps with initialization and configuring of logging, event loop, thread
|
||||||
|
or process pool, etc. See :ref:`resource-provider`.
|
||||||
- **Containers**. Provides declarative and dynamic containers. See :ref:`containers`.
|
- **Containers**. Provides declarative and dynamic containers. See :ref:`containers`.
|
||||||
- **Wiring**. Injects dependencies into functions and methods. Helps integrating with
|
- **Wiring**. Injects dependencies into functions and methods. Helps integrating with
|
||||||
other frameworks: Django, Flask, Aiohttp, etc. See :ref:`wiring`.
|
other frameworks: Django, Flask, Aiohttp, etc. See :ref:`wiring`.
|
||||||
|
|
|
@ -7,6 +7,18 @@ 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`_
|
||||||
|
|
||||||
|
4.1.0
|
||||||
|
-----
|
||||||
|
- Add ``Resource`` provider.
|
||||||
|
- Add ``Dict`` provider.
|
||||||
|
- "Un-deprecate" ``@containers.override()`` and ``@containers.copy()`` decorators (
|
||||||
|
see `Issue 301 <https://github.com/ets-labs/python-dependency-injector/issues/301>`_
|
||||||
|
for more information).
|
||||||
|
- Add favicon.
|
||||||
|
- Remove redirects that occur while getting badge images to optimize docs load speed.
|
||||||
|
- Update license year.
|
||||||
|
- Update short description on PyPI.
|
||||||
|
|
||||||
4.0.6
|
4.0.6
|
||||||
-----
|
-----
|
||||||
- Fix wiring for top-level package ``__init__.py``.
|
- Fix wiring for top-level package ``__init__.py``.
|
||||||
|
@ -51,8 +63,6 @@ Deprecations:
|
||||||
- Deprecate ``ext.aiohttp`` module in favor of ``wiring`` feature.
|
- Deprecate ``ext.aiohttp`` module in favor of ``wiring`` feature.
|
||||||
- Deprecate ``ext.flask`` module in favor of ``wiring`` feature.
|
- Deprecate ``ext.flask`` module in favor of ``wiring`` feature.
|
||||||
- Deprecate ``.delegate()`` provider method in favor of ``.provider`` attribute.
|
- Deprecate ``.delegate()`` provider method in favor of ``.provider`` attribute.
|
||||||
- Deprecate ``@containers.override()`` decorator in favor of overriding container on instance level.
|
|
||||||
- Deprecate ``@containers.copy()`` decorator.
|
|
||||||
|
|
||||||
Removals:
|
Removals:
|
||||||
|
|
||||||
|
|
23
docs/providers/dict.rst
Normal file
23
docs/providers/dict.rst
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
Dict provider
|
||||||
|
=============
|
||||||
|
|
||||||
|
.. meta::
|
||||||
|
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Dict,Injection
|
||||||
|
:description: Dict provider helps to inject a dictionary of the dependencies. This page demonstrates
|
||||||
|
how to use Dict provider.
|
||||||
|
|
||||||
|
.. currentmodule:: dependency_injector.providers
|
||||||
|
|
||||||
|
:py:class:`Dict` provider provides a dictionary of values.
|
||||||
|
|
||||||
|
.. literalinclude:: ../../examples/providers/dict.py
|
||||||
|
:language: python
|
||||||
|
:lines: 3-
|
||||||
|
:emphasize-lines: 21-24
|
||||||
|
|
||||||
|
``Dict`` provider handles keyword arguments the same way as a :ref:`factory-provider`.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
Positional argument are not supported.
|
||||||
|
|
||||||
|
.. disqus::
|
|
@ -43,7 +43,9 @@ Providers module API docs - :py:mod:`dependency_injector.providers`
|
||||||
coroutine
|
coroutine
|
||||||
object
|
object
|
||||||
list
|
list
|
||||||
|
dict
|
||||||
configuration
|
configuration
|
||||||
|
resource
|
||||||
selector
|
selector
|
||||||
dependency
|
dependency
|
||||||
overriding
|
overriding
|
||||||
|
|
206
docs/providers/resource.rst
Normal file
206
docs/providers/resource.rst
Normal file
|
@ -0,0 +1,206 @@
|
||||||
|
.. _resource-provider:
|
||||||
|
|
||||||
|
Resource provider
|
||||||
|
=================
|
||||||
|
|
||||||
|
.. meta::
|
||||||
|
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Resource,Injection,
|
||||||
|
Logging,Event Loop,Thread Pool
|
||||||
|
:description: Resource provider provides a component with initialization and shutdown. It works
|
||||||
|
well for configuring logging, event loop, thread or process pool, etc.
|
||||||
|
This page demonstrates how to use resource provider.
|
||||||
|
|
||||||
|
.. currentmodule:: dependency_injector.providers
|
||||||
|
|
||||||
|
:py:class:`Resource` provider provides a component with initialization and shutdown.
|
||||||
|
|
||||||
|
.. literalinclude:: ../../examples/providers/resource.py
|
||||||
|
:language: python
|
||||||
|
:lines: 3-
|
||||||
|
|
||||||
|
Resource providers help to initialize and configure logging, event loop, thread or process pool, etc.
|
||||||
|
|
||||||
|
Resource provider is similar to ``Singleton``. Resource initialization happens only once.
|
||||||
|
You can do injections and use provided instance the same way like you do with any other provider.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
:emphasize-lines: 12
|
||||||
|
|
||||||
|
class Container(containers.DeclarativeContainer):
|
||||||
|
|
||||||
|
config = providers.Configuration()
|
||||||
|
|
||||||
|
thread_pool = providers.Resource(
|
||||||
|
init_threat_pool,
|
||||||
|
max_workers=config.max_workers,
|
||||||
|
)
|
||||||
|
|
||||||
|
dispatcher = providers.Factory(
|
||||||
|
TaskDispatcher,
|
||||||
|
executor=thread_pool,
|
||||||
|
)
|
||||||
|
|
||||||
|
Container has an interface to initialize and shutdown all resources:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
container = Container()
|
||||||
|
container.init_resources()
|
||||||
|
container.shutdown_resources()
|
||||||
|
|
||||||
|
You also can initialize and shutdown resources one-by-one using ``init()`` and
|
||||||
|
``shutdown()`` methods of the provider:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
container = Container()
|
||||||
|
container.thread_pool.init()
|
||||||
|
container.thread_pool.shutdown()
|
||||||
|
|
||||||
|
Resource provider supports 3 types of initializers:
|
||||||
|
|
||||||
|
- Function
|
||||||
|
- Generator
|
||||||
|
- Subclass of ``resources.Resource``
|
||||||
|
|
||||||
|
Function initializer
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
Function is the most common way to specify resource initialization:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
def init_resource(argument1=..., argument2=...):
|
||||||
|
return SomeResource()
|
||||||
|
|
||||||
|
|
||||||
|
class Container(containers.DeclarativeContainer):
|
||||||
|
|
||||||
|
resource = providers.Resource(
|
||||||
|
init_resource,
|
||||||
|
argument1=...,
|
||||||
|
argument2=...,
|
||||||
|
)
|
||||||
|
|
||||||
|
Function initializer may not return a value. This often happens when
|
||||||
|
you configure global resource:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
import logging.config
|
||||||
|
|
||||||
|
|
||||||
|
class Container(containers.DeclarativeContainer):
|
||||||
|
|
||||||
|
configure_logging = providers.Resource(
|
||||||
|
logging.config.fileConfig,
|
||||||
|
fname='logging.ini',
|
||||||
|
)
|
||||||
|
|
||||||
|
Function initializer does not support shutdown.
|
||||||
|
|
||||||
|
Generator initializer
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
Resource provider can use 2-step generators:
|
||||||
|
|
||||||
|
- First step of generator is an initialization phase
|
||||||
|
- The second is step is a shutdown phase
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
def init_resource(argument1=..., argument2=...):
|
||||||
|
resource = SomeResource() # initialization
|
||||||
|
|
||||||
|
yield resource
|
||||||
|
|
||||||
|
# shutdown
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class Container(containers.DeclarativeContainer):
|
||||||
|
|
||||||
|
resource = providers.Resource(
|
||||||
|
init_resource,
|
||||||
|
argument1=...,
|
||||||
|
argument2=...,
|
||||||
|
)
|
||||||
|
|
||||||
|
Generator initialization phase ends on the first ``yield`` statement. You can return a
|
||||||
|
resource object using ``yield resource`` like in the example above. Returning of the
|
||||||
|
object is not mandatory. You can leave ``yield`` statement empty:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
def init_resource(argument1=..., argument2=...):
|
||||||
|
# initialization
|
||||||
|
...
|
||||||
|
|
||||||
|
yield
|
||||||
|
|
||||||
|
# shutdown
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class Container(containers.DeclarativeContainer):
|
||||||
|
|
||||||
|
resource = providers.Resource(
|
||||||
|
init_resource,
|
||||||
|
argument1=...,
|
||||||
|
argument2=...,
|
||||||
|
)
|
||||||
|
|
||||||
|
Subclass initializer
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
You can create resource initializer by implementing a subclass of the ``resources.Resource``:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from dependency_injector import resources
|
||||||
|
|
||||||
|
|
||||||
|
class MyResource(resources.Resource):
|
||||||
|
|
||||||
|
def init(self, argument1=..., argument2=...) -> SomeResource:
|
||||||
|
return SomeResource()
|
||||||
|
|
||||||
|
def shutdown(self, resource: SomeResource) -> None:
|
||||||
|
# shutdown
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class Container(containers.DeclarativeContainer):
|
||||||
|
|
||||||
|
resource = providers.Resource(
|
||||||
|
MyResource,
|
||||||
|
argument1=...,
|
||||||
|
argument2=...,
|
||||||
|
)
|
||||||
|
|
||||||
|
Subclass must implement two methods: ``init()`` and ``shutdown()``.
|
||||||
|
|
||||||
|
Method ``init()`` receives arguments specified in resource provider.
|
||||||
|
It performs initialization and returns resource object. Returning of the object
|
||||||
|
is not mandatory.
|
||||||
|
|
||||||
|
Method ``shutdown()`` receives resource object returned from ``init()``. If ``init()``
|
||||||
|
didn't return an object ``shutdown()`` method will be called anyway with ``None`` as a
|
||||||
|
first argument.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from dependency_injector import resources
|
||||||
|
|
||||||
|
|
||||||
|
class MyResource(resources.Resource):
|
||||||
|
|
||||||
|
def init(self, argument1=..., argument2=...) -> None:
|
||||||
|
# initialization
|
||||||
|
...
|
||||||
|
|
||||||
|
def shutdown(self, _: None) -> None:
|
||||||
|
# shutdown
|
||||||
|
...
|
||||||
|
|
||||||
|
.. disqus::
|
25
examples/containers/declarative_override_decorator.py
Normal file
25
examples/containers/declarative_override_decorator.py
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
"""Declarative container provider overriding with `@override()` decorator."""
|
||||||
|
|
||||||
|
import sqlite3
|
||||||
|
from unittest import mock
|
||||||
|
|
||||||
|
from dependency_injector import containers, providers
|
||||||
|
|
||||||
|
|
||||||
|
class Container(containers.DeclarativeContainer):
|
||||||
|
|
||||||
|
database = providers.Singleton(sqlite3.connect, ':memory:')
|
||||||
|
|
||||||
|
|
||||||
|
# Overriding `Container` with `OverridingContainer`:
|
||||||
|
@containers.override(Container)
|
||||||
|
class OverridingContainer(containers.DeclarativeContainer):
|
||||||
|
|
||||||
|
database = providers.Singleton(mock.Mock)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
container = Container()
|
||||||
|
|
||||||
|
database = container.database()
|
||||||
|
assert isinstance(database, mock.Mock)
|
45
examples/providers/dict.py
Normal file
45
examples/providers/dict.py
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
"""`Dict` provider example."""
|
||||||
|
|
||||||
|
import dataclasses
|
||||||
|
from typing import Dict
|
||||||
|
|
||||||
|
from dependency_injector import containers, providers
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class Module:
|
||||||
|
name: str
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class Dispatcher:
|
||||||
|
modules: Dict[str, Module]
|
||||||
|
|
||||||
|
|
||||||
|
class Container(containers.DeclarativeContainer):
|
||||||
|
|
||||||
|
dispatcher_factory = providers.Factory(
|
||||||
|
Dispatcher,
|
||||||
|
modules=providers.Dict(
|
||||||
|
module1=providers.Factory(Module, name='m1'),
|
||||||
|
module2=providers.Factory(Module, name='m2'),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
container = Container()
|
||||||
|
|
||||||
|
dispatcher = container.dispatcher_factory()
|
||||||
|
|
||||||
|
assert isinstance(dispatcher.modules, dict)
|
||||||
|
assert dispatcher.modules['module1'].name == 'm1'
|
||||||
|
assert dispatcher.modules['module2'].name == 'm2'
|
||||||
|
|
||||||
|
# Call "dispatcher = container.dispatcher_factory()" is equivalent to:
|
||||||
|
# dispatcher = Dispatcher(
|
||||||
|
# modules={
|
||||||
|
# 'module1': Module(name='m1'),
|
||||||
|
# 'module2': Module(name='m2'),
|
||||||
|
# },
|
||||||
|
# )
|
41
examples/providers/resource.py
Normal file
41
examples/providers/resource.py
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
"""`Resource` provider example."""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import logging
|
||||||
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
|
|
||||||
|
from dependency_injector import containers, providers
|
||||||
|
|
||||||
|
|
||||||
|
def init_threat_pool(max_workers: int):
|
||||||
|
thread_pool = ThreadPoolExecutor(max_workers=max_workers)
|
||||||
|
yield thread_pool
|
||||||
|
thread_pool.shutdown(wait=True)
|
||||||
|
|
||||||
|
|
||||||
|
class Container(containers.DeclarativeContainer):
|
||||||
|
|
||||||
|
config = providers.Configuration()
|
||||||
|
|
||||||
|
thread_pool = providers.Resource(
|
||||||
|
init_threat_pool,
|
||||||
|
max_workers=config.max_workers,
|
||||||
|
)
|
||||||
|
|
||||||
|
logging = providers.Resource(
|
||||||
|
logging.basicConfig,
|
||||||
|
level=logging.INFO,
|
||||||
|
stream=sys.stdout,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
container = Container(config={'max_workers': 4})
|
||||||
|
|
||||||
|
container.init_resources()
|
||||||
|
|
||||||
|
logging.info('Resources are initialized')
|
||||||
|
thread_pool = container.thread_pool()
|
||||||
|
thread_pool.map(print, range(10))
|
||||||
|
|
||||||
|
container.shutdown_resources()
|
4
setup.py
4
setup.py
|
@ -31,11 +31,11 @@ if os.environ.get('DEPENDENCY_INJECTOR_DEBUG_MODE') == '1':
|
||||||
|
|
||||||
setup(name='dependency-injector',
|
setup(name='dependency-injector',
|
||||||
version=version,
|
version=version,
|
||||||
description='Dependency injection microframework for Python',
|
description='Dependency injection framework for Python',
|
||||||
long_description=description,
|
long_description=description,
|
||||||
author='ETS Labs',
|
author='ETS Labs',
|
||||||
author_email='rmogilatov@gmail.com',
|
author_email='rmogilatov@gmail.com',
|
||||||
maintainer='Roman Mogilatov',
|
maintainer='Roman Mogylatov',
|
||||||
maintainer_email='rmogilatov@gmail.com',
|
maintainer_email='rmogilatov@gmail.com',
|
||||||
url='https://github.com/ets-labs/python-dependency-injector',
|
url='https://github.com/ets-labs/python-dependency-injector',
|
||||||
download_url='https://pypi.python.org/pypi/dependency_injector',
|
download_url='https://pypi.python.org/pypi/dependency_injector',
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
"""Top-level package."""
|
"""Top-level package."""
|
||||||
|
|
||||||
__version__ = '4.0.6'
|
__version__ = '4.1.0'
|
||||||
"""Version number.
|
"""Version number.
|
||||||
|
|
||||||
:type: str
|
:type: str
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -25,6 +25,8 @@ class Container:
|
||||||
def resolve_provider_name(self, provider_to_resolve: Provider) -> Optional[str]: ...
|
def resolve_provider_name(self, provider_to_resolve: Provider) -> Optional[str]: ...
|
||||||
def wire(self, modules: Optional[Iterable[ModuleType]] = None, packages: Optional[Iterable[ModuleType]] = None) -> None: ...
|
def wire(self, modules: Optional[Iterable[ModuleType]] = None, packages: Optional[Iterable[ModuleType]] = None) -> None: ...
|
||||||
def unwire(self) -> None: ...
|
def unwire(self) -> None: ...
|
||||||
|
def init_resources(self) -> None: ...
|
||||||
|
def shutdown_resources(self) -> None: ...
|
||||||
|
|
||||||
|
|
||||||
class DynamicContainer(Container): ...
|
class DynamicContainer(Container): ...
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
"""Containers module."""
|
"""Containers module."""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import warnings
|
|
||||||
|
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from .errors import Error
|
from .errors import Error
|
||||||
from .providers cimport (
|
from .providers cimport (
|
||||||
Provider,
|
Provider,
|
||||||
|
Resource,
|
||||||
deepcopy,
|
deepcopy,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -214,6 +214,19 @@ class DynamicContainer(object):
|
||||||
self.wired_to_modules.clear()
|
self.wired_to_modules.clear()
|
||||||
self.wired_to_packages.clear()
|
self.wired_to_packages.clear()
|
||||||
|
|
||||||
|
def init_resources(self):
|
||||||
|
"""Initialize all container resources."""
|
||||||
|
for provider in self.providers.values():
|
||||||
|
if not isinstance(provider, Resource):
|
||||||
|
continue
|
||||||
|
provider.init()
|
||||||
|
|
||||||
|
def shutdown_resources(self):
|
||||||
|
"""Shutdown all container resources."""
|
||||||
|
for provider in self.providers.values():
|
||||||
|
if not isinstance(provider, Resource):
|
||||||
|
continue
|
||||||
|
provider.shutdown()
|
||||||
|
|
||||||
|
|
||||||
class DeclarativeContainerMetaClass(type):
|
class DeclarativeContainerMetaClass(type):
|
||||||
|
@ -428,11 +441,6 @@ def override(object container):
|
||||||
:return: Declarative container's overriding decorator.
|
:return: Declarative container's overriding decorator.
|
||||||
:rtype: callable(:py:class:`DeclarativeContainer`)
|
:rtype: callable(:py:class:`DeclarativeContainer`)
|
||||||
"""
|
"""
|
||||||
warnings.warn(
|
|
||||||
'Decorator "@override()" is deprecated since version 4.0.3. '
|
|
||||||
'Use overriding on instance level instead "container.override(AnotherContainer())".',
|
|
||||||
category=DeprecationWarning,
|
|
||||||
)
|
|
||||||
def _decorator(object overriding_container):
|
def _decorator(object overriding_container):
|
||||||
"""Overriding decorator."""
|
"""Overriding decorator."""
|
||||||
container.override(overriding_container)
|
container.override(overriding_container)
|
||||||
|
@ -453,10 +461,6 @@ def copy(object container):
|
||||||
:return: Declarative container's copying decorator.
|
:return: Declarative container's copying decorator.
|
||||||
:rtype: callable(:py:class:`DeclarativeContainer`)
|
:rtype: callable(:py:class:`DeclarativeContainer`)
|
||||||
"""
|
"""
|
||||||
warnings.warn(
|
|
||||||
'Decorator "@copy()" is deprecated since version 4.0.3.',
|
|
||||||
category=DeprecationWarning,
|
|
||||||
)
|
|
||||||
def _decorator(copied_container):
|
def _decorator(copied_container):
|
||||||
cdef dict memo = dict()
|
cdef dict memo = dict()
|
||||||
for name, provider in six.iteritems(copied_container.cls_providers):
|
for name, provider in six.iteritems(copied_container.cls_providers):
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -184,6 +184,28 @@ cdef class List(Provider):
|
||||||
cpdef object _provide(self, tuple args, dict kwargs)
|
cpdef object _provide(self, tuple args, dict kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
cdef class Dict(Provider):
|
||||||
|
cdef tuple __kwargs
|
||||||
|
cdef int __kwargs_len
|
||||||
|
|
||||||
|
cpdef object _provide(self, tuple args, dict kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
cdef class Resource(Provider):
|
||||||
|
cdef object __initializer
|
||||||
|
cdef bint __initialized
|
||||||
|
cdef object __shutdowner
|
||||||
|
cdef object __resource
|
||||||
|
|
||||||
|
cdef tuple __args
|
||||||
|
cdef int __args_len
|
||||||
|
|
||||||
|
cdef tuple __kwargs
|
||||||
|
cdef int __kwargs_len
|
||||||
|
|
||||||
|
cpdef object _provide(self, tuple args, dict kwargs)
|
||||||
|
|
||||||
|
|
||||||
cdef class Container(Provider):
|
cdef class Container(Provider):
|
||||||
cdef object __container_cls
|
cdef object __container_cls
|
||||||
cdef dict __overriding_providers
|
cdef dict __overriding_providers
|
||||||
|
|
|
@ -9,12 +9,17 @@ from typing import (
|
||||||
Any,
|
Any,
|
||||||
Tuple,
|
Tuple,
|
||||||
List as _List,
|
List as _List,
|
||||||
|
Dict as _Dict,
|
||||||
Optional,
|
Optional,
|
||||||
Dict,
|
|
||||||
Union,
|
Union,
|
||||||
Coroutine as _Coroutine,
|
Coroutine as _Coroutine,
|
||||||
|
Iterator as _Iterator,
|
||||||
|
Generator as _Generator,
|
||||||
|
overload,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from . import resources
|
||||||
|
|
||||||
|
|
||||||
Injection = Any
|
Injection = Any
|
||||||
T = TypeVar('T')
|
T = TypeVar('T')
|
||||||
|
@ -29,7 +34,7 @@ class OverridingContext:
|
||||||
class Provider(Generic[T]):
|
class Provider(Generic[T]):
|
||||||
def __init__(self) -> None: ...
|
def __init__(self) -> None: ...
|
||||||
def __call__(self, *args: Injection, **kwargs: Injection) -> T: ...
|
def __call__(self, *args: Injection, **kwargs: Injection) -> T: ...
|
||||||
def __deepcopy__(self, memo: Optional[Dict[Any, Any]]) -> Provider: ...
|
def __deepcopy__(self, memo: Optional[_Dict[Any, Any]]) -> Provider: ...
|
||||||
def __str__(self) -> str: ...
|
def __str__(self) -> str: ...
|
||||||
def __repr__(self) -> str: ...
|
def __repr__(self) -> str: ...
|
||||||
@property
|
@property
|
||||||
|
@ -44,7 +49,7 @@ class Provider(Generic[T]):
|
||||||
def provider(self) -> Provider: ...
|
def provider(self) -> Provider: ...
|
||||||
@property
|
@property
|
||||||
def provided(self) -> ProvidedInstance: ...
|
def provided(self) -> ProvidedInstance: ...
|
||||||
def _copy_overridings(self, copied: Provider, memo: Optional[Dict[Any, Any]]) -> None: ...
|
def _copy_overridings(self, copied: Provider, memo: Optional[_Dict[Any, Any]]) -> None: ...
|
||||||
|
|
||||||
|
|
||||||
class Object(Provider, Generic[T]):
|
class Object(Provider, Generic[T]):
|
||||||
|
@ -74,7 +79,7 @@ class DependenciesContainer(Object):
|
||||||
def __init__(self, **dependencies: Provider) -> None: ...
|
def __init__(self, **dependencies: Provider) -> None: ...
|
||||||
def __getattr__(self, name: str) -> Provider: ...
|
def __getattr__(self, name: str) -> Provider: ...
|
||||||
@property
|
@property
|
||||||
def providers(self) -> Dict[str, Provider]: ...
|
def providers(self) -> _Dict[str, Provider]: ...
|
||||||
|
|
||||||
|
|
||||||
class Callable(Provider, Generic[T]):
|
class Callable(Provider, Generic[T]):
|
||||||
|
@ -88,7 +93,7 @@ class Callable(Provider, Generic[T]):
|
||||||
def set_args(self, *args: Injection) -> Callable[T]: ...
|
def set_args(self, *args: Injection) -> Callable[T]: ...
|
||||||
def clear_args(self) -> Callable[T]: ...
|
def clear_args(self) -> Callable[T]: ...
|
||||||
@property
|
@property
|
||||||
def kwargs(self) -> Dict[str, Injection]: ...
|
def kwargs(self) -> _Dict[str, Injection]: ...
|
||||||
def add_kwargs(self, **kwargs: Injection) -> Callable[T]: ...
|
def add_kwargs(self, **kwargs: Injection) -> Callable[T]: ...
|
||||||
def set_kwargs(self, **kwargs: Injection) -> Callable[T]: ...
|
def set_kwargs(self, **kwargs: Injection) -> Callable[T]: ...
|
||||||
def clear_kwargs(self) -> Callable[T]: ...
|
def clear_kwargs(self) -> Callable[T]: ...
|
||||||
|
@ -135,7 +140,7 @@ class ConfigurationOption(Provider):
|
||||||
def update(self, value: Any) -> None: ...
|
def update(self, value: Any) -> None: ...
|
||||||
def from_ini(self, filepath: Union[Path, str]) -> None: ...
|
def from_ini(self, filepath: Union[Path, str]) -> None: ...
|
||||||
def from_yaml(self, filepath: Union[Path, str]) -> None: ...
|
def from_yaml(self, filepath: Union[Path, str]) -> None: ...
|
||||||
def from_dict(self, options: Dict[str, Any]) -> None: ...
|
def from_dict(self, options: _Dict[str, Any]) -> None: ...
|
||||||
def from_env(self, name: str, default: Optional[Any] = None) -> None: ...
|
def from_env(self, name: str, default: Optional[Any] = None) -> None: ...
|
||||||
|
|
||||||
|
|
||||||
|
@ -156,7 +161,7 @@ class Configuration(Object):
|
||||||
def update(self, value: Any) -> None: ...
|
def update(self, value: Any) -> None: ...
|
||||||
def from_ini(self, filepath: Union[Path, str]) -> None: ...
|
def from_ini(self, filepath: Union[Path, str]) -> None: ...
|
||||||
def from_yaml(self, filepath: Union[Path, str]) -> None: ...
|
def from_yaml(self, filepath: Union[Path, str]) -> None: ...
|
||||||
def from_dict(self, options: Dict[str, Any]) -> None: ...
|
def from_dict(self, options: _Dict[str, Any]) -> None: ...
|
||||||
def from_env(self, name: str, default: Optional[Any] = None) -> None: ...
|
def from_env(self, name: str, default: Optional[Any] = None) -> None: ...
|
||||||
|
|
||||||
|
|
||||||
|
@ -174,12 +179,12 @@ class Factory(Provider, Generic[T]):
|
||||||
def set_args(self, *args: Injection) -> Factory[T]: ...
|
def set_args(self, *args: Injection) -> Factory[T]: ...
|
||||||
def clear_args(self) -> Factory[T]: ...
|
def clear_args(self) -> Factory[T]: ...
|
||||||
@property
|
@property
|
||||||
def kwargs(self) -> Dict[str, Injection]: ...
|
def kwargs(self) -> _Dict[str, Injection]: ...
|
||||||
def add_kwargs(self, **kwargs: Injection) -> Factory[T]: ...
|
def add_kwargs(self, **kwargs: Injection) -> Factory[T]: ...
|
||||||
def set_kwargs(self, **kwargs: Injection) -> Factory[T]: ...
|
def set_kwargs(self, **kwargs: Injection) -> Factory[T]: ...
|
||||||
def clear_kwargs(self) -> Factory[T]: ...
|
def clear_kwargs(self) -> Factory[T]: ...
|
||||||
@property
|
@property
|
||||||
def attributes(self) -> Dict[str, Injection]: ...
|
def attributes(self) -> _Dict[str, Injection]: ...
|
||||||
def add_attributes(self, **kwargs: Injection) -> Factory[T]: ...
|
def add_attributes(self, **kwargs: Injection) -> Factory[T]: ...
|
||||||
def set_attributes(self, **kwargs: Injection) -> Factory[T]: ...
|
def set_attributes(self, **kwargs: Injection) -> Factory[T]: ...
|
||||||
def clear_attributes(self) -> Factory[T]: ...
|
def clear_attributes(self) -> Factory[T]: ...
|
||||||
|
@ -201,7 +206,7 @@ class FactoryAggregate(Provider):
|
||||||
def __call__(self, factory_name: str, *args: Injection, **kwargs: Injection) -> Any: ...
|
def __call__(self, factory_name: str, *args: Injection, **kwargs: Injection) -> Any: ...
|
||||||
def __getattr__(self, factory_name: str) -> Factory: ...
|
def __getattr__(self, factory_name: str) -> Factory: ...
|
||||||
@property
|
@property
|
||||||
def factories(self) -> Dict[str, Factory]: ...
|
def factories(self) -> _Dict[str, Factory]: ...
|
||||||
|
|
||||||
|
|
||||||
class BaseSingleton(Provider, Generic[T]):
|
class BaseSingleton(Provider, Generic[T]):
|
||||||
|
@ -216,12 +221,12 @@ class BaseSingleton(Provider, Generic[T]):
|
||||||
def set_args(self, *args: Injection) -> Factory[T]: ...
|
def set_args(self, *args: Injection) -> Factory[T]: ...
|
||||||
def clear_args(self) -> Factory[T]: ...
|
def clear_args(self) -> Factory[T]: ...
|
||||||
@property
|
@property
|
||||||
def kwargs(self) -> Dict[str, Injection]: ...
|
def kwargs(self) -> _Dict[str, Injection]: ...
|
||||||
def add_kwargs(self, **kwargs: Injection) -> Factory[T]: ...
|
def add_kwargs(self, **kwargs: Injection) -> Factory[T]: ...
|
||||||
def set_kwargs(self, **kwargs: Injection) -> Factory[T]: ...
|
def set_kwargs(self, **kwargs: Injection) -> Factory[T]: ...
|
||||||
def clear_kwargs(self) -> Factory[T]: ...
|
def clear_kwargs(self) -> Factory[T]: ...
|
||||||
@property
|
@property
|
||||||
def attributes(self) -> Dict[str, Injection]: ...
|
def attributes(self) -> _Dict[str, Injection]: ...
|
||||||
def add_attributes(self, **kwargs: Injection) -> Factory[T]: ...
|
def add_attributes(self, **kwargs: Injection) -> Factory[T]: ...
|
||||||
def set_attributes(self, **kwargs: Injection) -> Factory[T]: ...
|
def set_attributes(self, **kwargs: Injection) -> Factory[T]: ...
|
||||||
def clear_attributes(self) -> Factory[T]: ...
|
def clear_attributes(self) -> Factory[T]: ...
|
||||||
|
@ -264,6 +269,40 @@ class List(Provider):
|
||||||
def clear_args(self) -> List: ...
|
def clear_args(self) -> List: ...
|
||||||
|
|
||||||
|
|
||||||
|
class Dict(Provider):
|
||||||
|
def __init__(self, **kwargs: Injection): ...
|
||||||
|
def __call__(self, *args: Injection, **kwargs: Injection) -> _Dict[Any, Any]: ...
|
||||||
|
@property
|
||||||
|
def kwargs(self) -> _Dict[Any, Injection]: ...
|
||||||
|
def add_kwargs(self, **kwargs: Injection) -> Dict: ...
|
||||||
|
def set_kwargs(self, **kwargs: Injection) -> Dict: ...
|
||||||
|
def clear_kwargs(self) -> Dict: ...
|
||||||
|
|
||||||
|
|
||||||
|
class Resource(Provider, Generic[T]):
|
||||||
|
@overload
|
||||||
|
def __init__(self, initializer: _Callable[..., resources.Resource[T]], *args: Injection, **kwargs: Injection) -> None: ...
|
||||||
|
@overload
|
||||||
|
def __init__(self, initializer: _Callable[..., _Iterator[T]], *args: Injection, **kwargs: Injection) -> None: ...
|
||||||
|
@overload
|
||||||
|
def __init__(self, initializer: _Callable[..., T], *args: Injection, **kwargs: Injection) -> None: ...
|
||||||
|
def __call__(self, *args: Injection, **kwargs: Injection) -> T: ...
|
||||||
|
@property
|
||||||
|
def args(self) -> Tuple[Injection]: ...
|
||||||
|
def add_args(self, *args: Injection) -> Resource: ...
|
||||||
|
def set_args(self, *args: Injection) -> Resource: ...
|
||||||
|
def clear_args(self) -> Resource: ...
|
||||||
|
@property
|
||||||
|
def kwargs(self) -> _Dict[Any, Injection]: ...
|
||||||
|
def add_kwargs(self, **kwargs: Injection) -> Resource: ...
|
||||||
|
def set_kwargs(self, **kwargs: Injection) -> Resource: ...
|
||||||
|
def clear_kwargs(self) -> Resource: ...
|
||||||
|
@property
|
||||||
|
def initialized(self) -> bool: ...
|
||||||
|
def init(self) -> T: ...
|
||||||
|
def shutdown(self) -> None: ...
|
||||||
|
|
||||||
|
|
||||||
class Container(Provider):
|
class Container(Provider):
|
||||||
|
|
||||||
def __init__(self, container_cls: Type[T], container: Optional[T] = None, **overriding_providers: Provider) -> None: ...
|
def __init__(self, container_cls: Type[T], container: Optional[T] = None, **overriding_providers: Provider) -> None: ...
|
||||||
|
@ -278,7 +317,7 @@ class Selector(Provider):
|
||||||
def __call__(self, *args: Injection, **kwargs: Injection) -> Any: ...
|
def __call__(self, *args: Injection, **kwargs: Injection) -> Any: ...
|
||||||
def __getattr__(self, name: str) -> Provider: ...
|
def __getattr__(self, name: str) -> Provider: ...
|
||||||
@property
|
@property
|
||||||
def providers(self) -> Dict[str, Provider]: ...
|
def providers(self) -> _Dict[str, Provider]: ...
|
||||||
|
|
||||||
|
|
||||||
class ProvidedInstanceFluentInterface:
|
class ProvidedInstanceFluentInterface:
|
||||||
|
@ -315,7 +354,7 @@ def is_delegated(instance: Any) -> bool: ...
|
||||||
def represent_provider(provider: Provider, provides: Any) -> str: ...
|
def represent_provider(provider: Provider, provides: Any) -> str: ...
|
||||||
|
|
||||||
|
|
||||||
def deepcopy(instance: Any, memo: Optional[Dict[Any, Any]]): Any: ...
|
def deepcopy(instance: Any, memo: Optional[_Dict[Any, Any]] = None): Any: ...
|
||||||
|
|
||||||
|
|
||||||
def merge_dicts(dict1: Dict[Any, Any], dict2: Dict[Any, Any]) -> Dict[Any, Any]: ...
|
def merge_dicts(dict1: _Dict[Any, Any], dict2: _Dict[Any, Any]) -> _Dict[Any, Any]: ...
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
|
import inspect
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
@ -2449,6 +2450,313 @@ cdef class List(Provider):
|
||||||
return list(__provide_positional_args(args, self.__args, self.__args_len))
|
return list(__provide_positional_args(args, self.__args, self.__args_len))
|
||||||
|
|
||||||
|
|
||||||
|
cdef class Dict(Provider):
|
||||||
|
"""Dict provider provides a dictionary of values.
|
||||||
|
|
||||||
|
:py:class:`Dict` provider is needed for injecting a dictionary of dependencies. It handles
|
||||||
|
keyword argument injections the same way as :py:class:`Factory` provider.
|
||||||
|
|
||||||
|
Positional argument injections are not supported.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
dispatcher_factory = Factory(
|
||||||
|
Dispatcher,
|
||||||
|
modules=Dict(
|
||||||
|
module1=Factory(ModuleA, dependency_a),
|
||||||
|
module2=Factory(ModuleB, dependency_b),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
dispatcher = dispatcher_factory()
|
||||||
|
|
||||||
|
# is equivalent to:
|
||||||
|
|
||||||
|
dispatcher = Dispatcher(
|
||||||
|
modules={
|
||||||
|
'module1': ModuleA(dependency_a),
|
||||||
|
'module2': ModuleB(dependency_b),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
"""Initializer."""
|
||||||
|
self.__kwargs = tuple()
|
||||||
|
self.__kwargs_len = 0
|
||||||
|
self.set_kwargs(**kwargs)
|
||||||
|
super(Dict, self).__init__()
|
||||||
|
|
||||||
|
def __deepcopy__(self, memo):
|
||||||
|
"""Create and return full copy of provider."""
|
||||||
|
copied = memo.get(id(self))
|
||||||
|
if copied is not None:
|
||||||
|
return copied
|
||||||
|
|
||||||
|
copied = self.__class__(**deepcopy(self.kwargs, memo))
|
||||||
|
self._copy_overridings(copied, memo)
|
||||||
|
|
||||||
|
return copied
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
"""Return string representation of provider.
|
||||||
|
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
return represent_provider(provider=self, provides=self.kwargs)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def kwargs(self):
|
||||||
|
"""Return keyword argument injections."""
|
||||||
|
cdef int index
|
||||||
|
cdef NamedInjection kwarg
|
||||||
|
cdef dict kwargs
|
||||||
|
|
||||||
|
kwargs = dict()
|
||||||
|
for index in range(self.__kwargs_len):
|
||||||
|
kwarg = self.__kwargs[index]
|
||||||
|
kwargs[kwarg.__name] = kwarg.__value
|
||||||
|
return kwargs
|
||||||
|
|
||||||
|
def add_kwargs(self, **kwargs):
|
||||||
|
"""Add keyword argument injections.
|
||||||
|
|
||||||
|
:return: Reference ``self``
|
||||||
|
"""
|
||||||
|
self.__kwargs += parse_named_injections(kwargs)
|
||||||
|
self.__kwargs_len = len(self.__kwargs)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def set_kwargs(self, **kwargs):
|
||||||
|
"""Set keyword argument injections.
|
||||||
|
|
||||||
|
Existing keyword argument injections are dropped.
|
||||||
|
|
||||||
|
:return: Reference ``self``
|
||||||
|
"""
|
||||||
|
self.__kwargs = parse_named_injections(kwargs)
|
||||||
|
self.__kwargs_len = len(self.__kwargs)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def clear_kwargs(self):
|
||||||
|
"""Drop keyword argument injections.
|
||||||
|
|
||||||
|
:return: Reference ``self``
|
||||||
|
"""
|
||||||
|
self.__kwargs = tuple()
|
||||||
|
self.__kwargs_len = len(self.__kwargs)
|
||||||
|
return self
|
||||||
|
|
||||||
|
cpdef object _provide(self, tuple args, dict kwargs):
|
||||||
|
"""Return result of provided callable's call."""
|
||||||
|
return __provide_keyword_args(kwargs, self.__kwargs, self.__kwargs_len)
|
||||||
|
|
||||||
|
|
||||||
|
cdef class Resource(Provider):
|
||||||
|
"""Resource provider provides a component with initialization and shutdown."""
|
||||||
|
|
||||||
|
def __init__(self, initializer, *args, **kwargs):
|
||||||
|
self.__initializer = initializer
|
||||||
|
self.__initialized = False
|
||||||
|
self.__resource = None
|
||||||
|
self.__shutdowner = None
|
||||||
|
|
||||||
|
self.__args = tuple()
|
||||||
|
self.__args_len = 0
|
||||||
|
self.set_args(*args)
|
||||||
|
|
||||||
|
self.__kwargs = tuple()
|
||||||
|
self.__kwargs_len = 0
|
||||||
|
self.set_kwargs(**kwargs)
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
def __deepcopy__(self, memo):
|
||||||
|
"""Create and return full copy of provider."""
|
||||||
|
copied = memo.get(id(self))
|
||||||
|
if copied is not None:
|
||||||
|
return copied
|
||||||
|
|
||||||
|
if self.__initialized:
|
||||||
|
raise Error('Can not copy initialized resource')
|
||||||
|
|
||||||
|
copied = self.__class__(
|
||||||
|
self.__initializer,
|
||||||
|
*deepcopy(self.args, memo),
|
||||||
|
**deepcopy(self.kwargs, memo),
|
||||||
|
)
|
||||||
|
self._copy_overridings(copied, memo)
|
||||||
|
|
||||||
|
return copied
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return (
|
||||||
|
f'{self.__class__.__name__}({self.__initializer}, '
|
||||||
|
f'initialized={self.__initialized})'
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def args(self):
|
||||||
|
"""Return positional argument injections."""
|
||||||
|
cdef int index
|
||||||
|
cdef PositionalInjection arg
|
||||||
|
cdef list args
|
||||||
|
|
||||||
|
args = list()
|
||||||
|
for index in range(self.__args_len):
|
||||||
|
arg = self.__args[index]
|
||||||
|
args.append(arg.__value)
|
||||||
|
return tuple(args)
|
||||||
|
|
||||||
|
def add_args(self, *args):
|
||||||
|
"""Add positional argument injections.
|
||||||
|
|
||||||
|
:return: Reference ``self``
|
||||||
|
"""
|
||||||
|
self.__args += parse_positional_injections(args)
|
||||||
|
self.__args_len = len(self.__args)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def set_args(self, *args):
|
||||||
|
"""Set positional argument injections.
|
||||||
|
|
||||||
|
Existing positional argument injections are dropped.
|
||||||
|
|
||||||
|
:return: Reference ``self``
|
||||||
|
"""
|
||||||
|
self.__args = parse_positional_injections(args)
|
||||||
|
self.__args_len = len(self.__args)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def clear_args(self):
|
||||||
|
"""Drop positional argument injections.
|
||||||
|
|
||||||
|
:return: Reference ``self``
|
||||||
|
"""
|
||||||
|
self.__args = tuple()
|
||||||
|
self.__args_len = len(self.__args)
|
||||||
|
return self
|
||||||
|
|
||||||
|
@property
|
||||||
|
def kwargs(self):
|
||||||
|
"""Return keyword argument injections."""
|
||||||
|
cdef int index
|
||||||
|
cdef NamedInjection kwarg
|
||||||
|
cdef dict kwargs
|
||||||
|
|
||||||
|
kwargs = dict()
|
||||||
|
for index in range(self.__kwargs_len):
|
||||||
|
kwarg = self.__kwargs[index]
|
||||||
|
kwargs[kwarg.__name] = kwarg.__value
|
||||||
|
return kwargs
|
||||||
|
|
||||||
|
def add_kwargs(self, **kwargs):
|
||||||
|
"""Add keyword argument injections.
|
||||||
|
|
||||||
|
:return: Reference ``self``
|
||||||
|
"""
|
||||||
|
self.__kwargs += parse_named_injections(kwargs)
|
||||||
|
self.__kwargs_len = len(self.__kwargs)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def set_kwargs(self, **kwargs):
|
||||||
|
"""Set keyword argument injections.
|
||||||
|
|
||||||
|
Existing keyword argument injections are dropped.
|
||||||
|
|
||||||
|
:return: Reference ``self``
|
||||||
|
"""
|
||||||
|
self.__kwargs = parse_named_injections(kwargs)
|
||||||
|
self.__kwargs_len = len(self.__kwargs)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def clear_kwargs(self):
|
||||||
|
"""Drop keyword argument injections.
|
||||||
|
|
||||||
|
:return: Reference ``self``
|
||||||
|
"""
|
||||||
|
self.__kwargs = tuple()
|
||||||
|
self.__kwargs_len = len(self.__kwargs)
|
||||||
|
return self
|
||||||
|
|
||||||
|
@property
|
||||||
|
def initialized(self):
|
||||||
|
"""Check if resource is initialized."""
|
||||||
|
return self.__initialized
|
||||||
|
|
||||||
|
def init(self):
|
||||||
|
"""Initialize resource."""
|
||||||
|
return self.__call__()
|
||||||
|
|
||||||
|
def shutdown(self):
|
||||||
|
"""Shutdown resource."""
|
||||||
|
if not self.__initialized:
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.__shutdowner:
|
||||||
|
try:
|
||||||
|
self.__shutdowner(self.__resource)
|
||||||
|
except StopIteration:
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.__resource = None
|
||||||
|
self.__initialized = False
|
||||||
|
self.__shutdowner = None
|
||||||
|
|
||||||
|
cpdef object _provide(self, tuple args, dict kwargs):
|
||||||
|
if self.__initialized:
|
||||||
|
return self.__resource
|
||||||
|
|
||||||
|
if self._is_resource_subclass(self.__initializer):
|
||||||
|
initializer = self.__initializer()
|
||||||
|
self.__resource = __call(
|
||||||
|
initializer.init,
|
||||||
|
args,
|
||||||
|
self.__args,
|
||||||
|
self.__args_len,
|
||||||
|
kwargs,
|
||||||
|
self.__kwargs,
|
||||||
|
self.__kwargs_len,
|
||||||
|
)
|
||||||
|
self.__shutdowner = initializer.shutdown
|
||||||
|
elif inspect.isgeneratorfunction(self.__initializer):
|
||||||
|
initializer = __call(
|
||||||
|
self.__initializer,
|
||||||
|
args,
|
||||||
|
self.__args,
|
||||||
|
self.__args_len,
|
||||||
|
kwargs,
|
||||||
|
self.__kwargs,
|
||||||
|
self.__kwargs_len,
|
||||||
|
)
|
||||||
|
self.__resource = next(initializer)
|
||||||
|
self.__shutdowner = initializer.send
|
||||||
|
elif callable(self.__initializer):
|
||||||
|
self.__resource = __call(
|
||||||
|
self.__initializer,
|
||||||
|
args,
|
||||||
|
self.__args,
|
||||||
|
self.__args_len,
|
||||||
|
kwargs,
|
||||||
|
self.__kwargs,
|
||||||
|
self.__kwargs_len,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise Error('Unknown type of resource initializer')
|
||||||
|
|
||||||
|
self.__initialized = True
|
||||||
|
return self.__resource
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _is_resource_subclass(instance):
|
||||||
|
if sys.version_info < (3, 5):
|
||||||
|
return False
|
||||||
|
if not isinstance(instance, CLASS_TYPES):
|
||||||
|
return
|
||||||
|
from . import resources
|
||||||
|
return issubclass(instance, resources.Resource)
|
||||||
|
|
||||||
|
|
||||||
cdef class Container(Provider):
|
cdef class Container(Provider):
|
||||||
"""Container provider provides an instance of declarative container.
|
"""Container provider provides an instance of declarative container.
|
||||||
|
|
||||||
|
|
31
src/dependency_injector/resources.py
Normal file
31
src/dependency_injector/resources.py
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
"""Resources module."""
|
||||||
|
|
||||||
|
import abc
|
||||||
|
import sys
|
||||||
|
from typing import TypeVar, Generic
|
||||||
|
|
||||||
|
if sys.version_info < (3, 7):
|
||||||
|
from typing import GenericMeta
|
||||||
|
else:
|
||||||
|
class GenericMeta(type):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
T = TypeVar('T')
|
||||||
|
|
||||||
|
|
||||||
|
class ResourceMeta(GenericMeta, abc.ABCMeta):
|
||||||
|
def __getitem__(cls, item):
|
||||||
|
# Spike for Python 3.6
|
||||||
|
return cls(item)
|
||||||
|
|
||||||
|
|
||||||
|
class Resource(Generic[T], metaclass=ResourceMeta):
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def init(self, *args, **kwargs) -> T:
|
||||||
|
...
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def shutdown(self, resource: T) -> None:
|
||||||
|
...
|
43
tests/typing/resource.py
Normal file
43
tests/typing/resource.py
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
from typing import List, Iterator, Generator
|
||||||
|
|
||||||
|
from dependency_injector import providers, resources
|
||||||
|
|
||||||
|
|
||||||
|
# Test 1: to check the return type with function
|
||||||
|
def init1() -> List[int]:
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
provider1 = providers.Resource(init1)
|
||||||
|
var1: List[int] = provider1()
|
||||||
|
|
||||||
|
|
||||||
|
# Test 2: to check the return type with iterator
|
||||||
|
def init2() -> Iterator[List[int]]:
|
||||||
|
yield []
|
||||||
|
|
||||||
|
|
||||||
|
provider2 = providers.Resource(init2)
|
||||||
|
var2: List[int] = provider2()
|
||||||
|
|
||||||
|
|
||||||
|
# Test 3: to check the return type with generator
|
||||||
|
def init3() -> Generator[List[int], None, None]:
|
||||||
|
yield []
|
||||||
|
|
||||||
|
|
||||||
|
provider3 = providers.Resource(init3)
|
||||||
|
var3: List[int] = provider3()
|
||||||
|
|
||||||
|
|
||||||
|
# Test 4: to check the return type with resource subclass
|
||||||
|
class MyResource4(resources.Resource[List[int]]):
|
||||||
|
def init(self, *args, **kwargs) -> List[int]:
|
||||||
|
return []
|
||||||
|
|
||||||
|
def shutdown(self, resource: List[int]) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
provider4 = providers.Resource(MyResource4)
|
||||||
|
var4: List[int] = provider4()
|
|
@ -186,6 +186,52 @@ class DeclarativeContainerInstanceTests(unittest.TestCase):
|
||||||
self.assertEqual(container.overridden, tuple())
|
self.assertEqual(container.overridden, tuple())
|
||||||
self.assertEqual(container.p11.overridden, tuple())
|
self.assertEqual(container.p11.overridden, tuple())
|
||||||
|
|
||||||
|
def test_init_shutdown_resources(self):
|
||||||
|
def _init1():
|
||||||
|
_init1.init_counter += 1
|
||||||
|
yield
|
||||||
|
_init1.shutdown_counter += 1
|
||||||
|
|
||||||
|
_init1.init_counter = 0
|
||||||
|
_init1.shutdown_counter = 0
|
||||||
|
|
||||||
|
def _init2():
|
||||||
|
_init2.init_counter += 1
|
||||||
|
yield
|
||||||
|
_init2.shutdown_counter += 1
|
||||||
|
|
||||||
|
_init2.init_counter = 0
|
||||||
|
_init2.shutdown_counter = 0
|
||||||
|
|
||||||
|
class Container(containers.DeclarativeContainer):
|
||||||
|
resource1 = providers.Resource(_init1)
|
||||||
|
resource2 = providers.Resource(_init2)
|
||||||
|
|
||||||
|
container = Container()
|
||||||
|
self.assertEqual(_init1.init_counter, 0)
|
||||||
|
self.assertEqual(_init1.shutdown_counter, 0)
|
||||||
|
self.assertEqual(_init2.init_counter, 0)
|
||||||
|
self.assertEqual(_init2.shutdown_counter, 0)
|
||||||
|
|
||||||
|
container.init_resources()
|
||||||
|
self.assertEqual(_init1.init_counter, 1)
|
||||||
|
self.assertEqual(_init1.shutdown_counter, 0)
|
||||||
|
self.assertEqual(_init2.init_counter, 1)
|
||||||
|
self.assertEqual(_init2.shutdown_counter, 0)
|
||||||
|
|
||||||
|
container.shutdown_resources()
|
||||||
|
self.assertEqual(_init1.init_counter, 1)
|
||||||
|
self.assertEqual(_init1.shutdown_counter, 1)
|
||||||
|
self.assertEqual(_init2.init_counter, 1)
|
||||||
|
self.assertEqual(_init2.shutdown_counter, 1)
|
||||||
|
|
||||||
|
container.init_resources()
|
||||||
|
container.shutdown_resources()
|
||||||
|
self.assertEqual(_init1.init_counter, 2)
|
||||||
|
self.assertEqual(_init1.shutdown_counter, 2)
|
||||||
|
self.assertEqual(_init2.init_counter, 2)
|
||||||
|
self.assertEqual(_init2.shutdown_counter, 2)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
149
tests/unit/providers/test_dict_py2_py3.py
Normal file
149
tests/unit/providers/test_dict_py2_py3.py
Normal file
|
@ -0,0 +1,149 @@
|
||||||
|
"""Dependency injector dict provider unit tests."""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import unittest2 as unittest
|
||||||
|
|
||||||
|
from dependency_injector import providers
|
||||||
|
|
||||||
|
|
||||||
|
class DictTests(unittest.TestCase):
|
||||||
|
|
||||||
|
def test_is_provider(self):
|
||||||
|
self.assertTrue(providers.is_provider(providers.Dict()))
|
||||||
|
|
||||||
|
def test_provided_instance_provider(self):
|
||||||
|
provider = providers.Dict()
|
||||||
|
self.assertIsInstance(provider.provided, providers.ProvidedInstance)
|
||||||
|
|
||||||
|
def test_call_with_init_keyword_args(self):
|
||||||
|
provider = providers.Dict(a1='i1', a2='i2')
|
||||||
|
|
||||||
|
dict1 = provider()
|
||||||
|
dict2 = provider()
|
||||||
|
|
||||||
|
self.assertEqual(dict1, {'a1': 'i1', 'a2': 'i2'})
|
||||||
|
self.assertEqual(dict2, {'a1': 'i1', 'a2': 'i2'})
|
||||||
|
|
||||||
|
self.assertIsNot(dict1, dict2)
|
||||||
|
|
||||||
|
def test_call_with_context_keyword_args(self):
|
||||||
|
provider = providers.Dict(a1='i1', a2='i2')
|
||||||
|
self.assertEqual(
|
||||||
|
provider(a3='i3', a4='i4'),
|
||||||
|
{'a1': 'i1', 'a2': 'i2', 'a3': 'i3', 'a4': 'i4'},
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_call_with_provider(self):
|
||||||
|
provider = providers.Dict(
|
||||||
|
a1=providers.Factory(str, 'i1'),
|
||||||
|
a2=providers.Factory(str, 'i2'),
|
||||||
|
)
|
||||||
|
self.assertEqual(provider(), {'a1': 'i1', 'a2': 'i2'})
|
||||||
|
|
||||||
|
def test_fluent_interface(self):
|
||||||
|
provider = providers.Dict() \
|
||||||
|
.add_kwargs(a1='i1', a2='i2')
|
||||||
|
self.assertEqual(provider(), {'a1': 'i1', 'a2': 'i2'})
|
||||||
|
|
||||||
|
def test_set_kwargs(self):
|
||||||
|
provider = providers.Dict() \
|
||||||
|
.add_kwargs(a1='i1', a2='i2') \
|
||||||
|
.set_kwargs(a3='i3', a4='i4')
|
||||||
|
self.assertEqual(provider.kwargs, {'a3': 'i3', 'a4': 'i4'})
|
||||||
|
|
||||||
|
def test_clear_kwargs(self):
|
||||||
|
provider = providers.Dict() \
|
||||||
|
.add_kwargs(a1='i1', a2='i2') \
|
||||||
|
.clear_kwargs()
|
||||||
|
self.assertEqual(provider.kwargs, {})
|
||||||
|
|
||||||
|
def test_call_overridden(self):
|
||||||
|
provider = providers.Dict(a1='i1', a2='i2')
|
||||||
|
overriding_provider1 = providers.Dict(a2='i2', a3='i3')
|
||||||
|
overriding_provider2 = providers.Dict(a3='i3', a4='i4')
|
||||||
|
|
||||||
|
provider.override(overriding_provider1)
|
||||||
|
provider.override(overriding_provider2)
|
||||||
|
|
||||||
|
instance1 = provider()
|
||||||
|
instance2 = provider()
|
||||||
|
|
||||||
|
self.assertIsNot(instance1, instance2)
|
||||||
|
self.assertEqual(instance1, {'a3': 'i3', 'a4': 'i4'})
|
||||||
|
self.assertEqual(instance2, {'a3': 'i3', 'a4': 'i4'})
|
||||||
|
|
||||||
|
def test_deepcopy(self):
|
||||||
|
provider = providers.Dict(a1='i1', a2='i2')
|
||||||
|
|
||||||
|
provider_copy = providers.deepcopy(provider)
|
||||||
|
|
||||||
|
self.assertIsNot(provider, provider_copy)
|
||||||
|
self.assertEqual(provider.kwargs, provider_copy.kwargs)
|
||||||
|
self.assertIsInstance(provider, providers.Dict)
|
||||||
|
|
||||||
|
def test_deepcopy_from_memo(self):
|
||||||
|
provider = providers.Dict(a1='i1', a2='i2')
|
||||||
|
provider_copy_memo = providers.Dict(a1='i1', a2='i2')
|
||||||
|
|
||||||
|
provider_copy = providers.deepcopy(
|
||||||
|
provider,
|
||||||
|
memo={id(provider): provider_copy_memo},
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertIs(provider_copy, provider_copy_memo)
|
||||||
|
|
||||||
|
def test_deepcopy_kwargs(self):
|
||||||
|
provider = providers.Dict()
|
||||||
|
dependent_provider1 = providers.Factory(list)
|
||||||
|
dependent_provider2 = providers.Factory(dict)
|
||||||
|
|
||||||
|
provider.add_kwargs(d1=dependent_provider1, d2=dependent_provider2)
|
||||||
|
|
||||||
|
provider_copy = providers.deepcopy(provider)
|
||||||
|
dependent_provider_copy1 = provider_copy.kwargs['d1']
|
||||||
|
dependent_provider_copy2 = provider_copy.kwargs['d2']
|
||||||
|
|
||||||
|
self.assertNotEqual(provider.kwargs, provider_copy.kwargs)
|
||||||
|
|
||||||
|
self.assertIs(dependent_provider1.cls, dependent_provider_copy1.cls)
|
||||||
|
self.assertIsNot(dependent_provider1, dependent_provider_copy1)
|
||||||
|
|
||||||
|
self.assertIs(dependent_provider2.cls, dependent_provider_copy2.cls)
|
||||||
|
self.assertIsNot(dependent_provider2, dependent_provider_copy2)
|
||||||
|
|
||||||
|
def test_deepcopy_overridden(self):
|
||||||
|
provider = providers.Dict()
|
||||||
|
object_provider = providers.Object(object())
|
||||||
|
|
||||||
|
provider.override(object_provider)
|
||||||
|
|
||||||
|
provider_copy = providers.deepcopy(provider)
|
||||||
|
object_provider_copy = provider_copy.overridden[0]
|
||||||
|
|
||||||
|
self.assertIsNot(provider, provider_copy)
|
||||||
|
self.assertEqual(provider.kwargs, provider_copy.kwargs)
|
||||||
|
self.assertIsInstance(provider, providers.Dict)
|
||||||
|
|
||||||
|
self.assertIsNot(object_provider, object_provider_copy)
|
||||||
|
self.assertIsInstance(object_provider_copy, providers.Object)
|
||||||
|
|
||||||
|
def test_deepcopy_with_sys_streams(self):
|
||||||
|
provider = providers.Dict()
|
||||||
|
provider.add_kwargs(stdin=sys.stdin, stdout=sys.stdout, stderr=sys.stderr)
|
||||||
|
|
||||||
|
provider_copy = providers.deepcopy(provider)
|
||||||
|
|
||||||
|
self.assertIsNot(provider, provider_copy)
|
||||||
|
self.assertIsInstance(provider_copy, providers.Dict)
|
||||||
|
self.assertIs(provider.kwargs['stdin'], sys.stdin)
|
||||||
|
self.assertIs(provider.kwargs['stdout'], sys.stdout)
|
||||||
|
self.assertIs(provider.kwargs['stderr'], sys.stderr)
|
||||||
|
|
||||||
|
def test_repr(self):
|
||||||
|
provider = providers.Dict(a1=1, a2=2)
|
||||||
|
self.assertEqual(repr(provider),
|
||||||
|
'<dependency_injector.providers.'
|
||||||
|
'Dict({0}) at {1}>'.format(
|
||||||
|
repr(provider.kwargs),
|
||||||
|
hex(id(provider))))
|
322
tests/unit/providers/test_resource_py35.py
Normal file
322
tests/unit/providers/test_resource_py35.py
Normal file
|
@ -0,0 +1,322 @@
|
||||||
|
"""Dependency injector resource provider unit tests."""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import unittest2 as unittest
|
||||||
|
|
||||||
|
from dependency_injector import containers, providers, resources, errors
|
||||||
|
|
||||||
|
|
||||||
|
def init_fn(*args, **kwargs):
|
||||||
|
return args, kwargs
|
||||||
|
|
||||||
|
|
||||||
|
class ResourceTests(unittest.TestCase):
|
||||||
|
|
||||||
|
def test_is_provider(self):
|
||||||
|
self.assertTrue(providers.is_provider(providers.Resource(init_fn)))
|
||||||
|
|
||||||
|
def test_provided_instance_provider(self):
|
||||||
|
provider = providers.Resource(init_fn)
|
||||||
|
self.assertIsInstance(provider.provided, providers.ProvidedInstance)
|
||||||
|
|
||||||
|
def test_injection(self):
|
||||||
|
resource = object()
|
||||||
|
|
||||||
|
def _init():
|
||||||
|
_init.counter += 1
|
||||||
|
return resource
|
||||||
|
_init.counter = 0
|
||||||
|
|
||||||
|
class Container(containers.DeclarativeContainer):
|
||||||
|
resource = providers.Resource(_init)
|
||||||
|
dependency1 = providers.List(resource)
|
||||||
|
dependency2 = providers.List(resource)
|
||||||
|
|
||||||
|
container = Container()
|
||||||
|
list1 = container.dependency1()
|
||||||
|
list2 = container.dependency2()
|
||||||
|
|
||||||
|
self.assertEqual(list1, [resource])
|
||||||
|
self.assertIs(list1[0], resource)
|
||||||
|
|
||||||
|
self.assertEqual(list2, [resource])
|
||||||
|
self.assertIs(list2[0], resource)
|
||||||
|
|
||||||
|
self.assertEqual(_init.counter, 1)
|
||||||
|
|
||||||
|
def test_init_function(self):
|
||||||
|
def _init():
|
||||||
|
_init.counter += 1
|
||||||
|
_init.counter = 0
|
||||||
|
|
||||||
|
provider = providers.Resource(_init)
|
||||||
|
|
||||||
|
result1 = provider()
|
||||||
|
self.assertIsNone(result1)
|
||||||
|
self.assertEqual(_init.counter, 1)
|
||||||
|
|
||||||
|
result2 = provider()
|
||||||
|
self.assertIsNone(result2)
|
||||||
|
self.assertEqual(_init.counter, 1)
|
||||||
|
|
||||||
|
provider.shutdown()
|
||||||
|
|
||||||
|
def test_init_generator(self):
|
||||||
|
def _init():
|
||||||
|
_init.init_counter += 1
|
||||||
|
yield
|
||||||
|
_init.shutdown_counter += 1
|
||||||
|
|
||||||
|
_init.init_counter = 0
|
||||||
|
_init.shutdown_counter = 0
|
||||||
|
|
||||||
|
provider = providers.Resource(_init)
|
||||||
|
|
||||||
|
result1 = provider()
|
||||||
|
self.assertIsNone(result1)
|
||||||
|
self.assertEqual(_init.init_counter, 1)
|
||||||
|
self.assertEqual(_init.shutdown_counter, 0)
|
||||||
|
|
||||||
|
provider.shutdown()
|
||||||
|
self.assertEqual(_init.init_counter, 1)
|
||||||
|
self.assertEqual(_init.shutdown_counter, 1)
|
||||||
|
|
||||||
|
result2 = provider()
|
||||||
|
self.assertIsNone(result2)
|
||||||
|
self.assertEqual(_init.init_counter, 2)
|
||||||
|
self.assertEqual(_init.shutdown_counter, 1)
|
||||||
|
|
||||||
|
provider.shutdown()
|
||||||
|
self.assertEqual(_init.init_counter, 2)
|
||||||
|
self.assertEqual(_init.shutdown_counter, 2)
|
||||||
|
|
||||||
|
def test_init_class(self):
|
||||||
|
class TestResource(resources.Resource):
|
||||||
|
init_counter = 0
|
||||||
|
shutdown_counter = 0
|
||||||
|
|
||||||
|
def init(self):
|
||||||
|
self.__class__.init_counter += 1
|
||||||
|
|
||||||
|
def shutdown(self, _):
|
||||||
|
self.__class__.shutdown_counter += 1
|
||||||
|
|
||||||
|
provider = providers.Resource(TestResource)
|
||||||
|
|
||||||
|
result1 = provider()
|
||||||
|
self.assertIsNone(result1)
|
||||||
|
self.assertEqual(TestResource.init_counter, 1)
|
||||||
|
self.assertEqual(TestResource.shutdown_counter, 0)
|
||||||
|
|
||||||
|
provider.shutdown()
|
||||||
|
self.assertEqual(TestResource.init_counter, 1)
|
||||||
|
self.assertEqual(TestResource.shutdown_counter, 1)
|
||||||
|
|
||||||
|
result2 = provider()
|
||||||
|
self.assertIsNone(result2)
|
||||||
|
self.assertEqual(TestResource.init_counter, 2)
|
||||||
|
self.assertEqual(TestResource.shutdown_counter, 1)
|
||||||
|
|
||||||
|
provider.shutdown()
|
||||||
|
self.assertEqual(TestResource.init_counter, 2)
|
||||||
|
self.assertEqual(TestResource.shutdown_counter, 2)
|
||||||
|
|
||||||
|
def test_init_not_callable(self):
|
||||||
|
provider = providers.Resource(1)
|
||||||
|
with self.assertRaises(errors.Error):
|
||||||
|
provider.init()
|
||||||
|
|
||||||
|
def test_init_and_shutdown(self):
|
||||||
|
def _init():
|
||||||
|
_init.init_counter += 1
|
||||||
|
yield
|
||||||
|
_init.shutdown_counter += 1
|
||||||
|
|
||||||
|
_init.init_counter = 0
|
||||||
|
_init.shutdown_counter = 0
|
||||||
|
|
||||||
|
provider = providers.Resource(_init)
|
||||||
|
|
||||||
|
result1 = provider.init()
|
||||||
|
self.assertIsNone(result1)
|
||||||
|
self.assertEqual(_init.init_counter, 1)
|
||||||
|
self.assertEqual(_init.shutdown_counter, 0)
|
||||||
|
|
||||||
|
provider.shutdown()
|
||||||
|
self.assertEqual(_init.init_counter, 1)
|
||||||
|
self.assertEqual(_init.shutdown_counter, 1)
|
||||||
|
|
||||||
|
result2 = provider.init()
|
||||||
|
self.assertIsNone(result2)
|
||||||
|
self.assertEqual(_init.init_counter, 2)
|
||||||
|
self.assertEqual(_init.shutdown_counter, 1)
|
||||||
|
|
||||||
|
provider.shutdown()
|
||||||
|
self.assertEqual(_init.init_counter, 2)
|
||||||
|
self.assertEqual(_init.shutdown_counter, 2)
|
||||||
|
|
||||||
|
def test_initialized(self):
|
||||||
|
provider = providers.Resource(init_fn)
|
||||||
|
self.assertFalse(provider.initialized)
|
||||||
|
|
||||||
|
provider.init()
|
||||||
|
self.assertTrue(provider.initialized)
|
||||||
|
|
||||||
|
provider.shutdown()
|
||||||
|
self.assertFalse(provider.initialized)
|
||||||
|
|
||||||
|
def test_call_with_context_args(self):
|
||||||
|
provider = providers.Resource(init_fn, 'i1', 'i2')
|
||||||
|
self.assertEqual(provider('i3', i4=4), (('i1', 'i2', 'i3'), {'i4': 4}))
|
||||||
|
|
||||||
|
def test_fluent_interface(self):
|
||||||
|
provider = providers.Resource(init_fn) \
|
||||||
|
.add_args(1, 2) \
|
||||||
|
.add_kwargs(a3=3, a4=4)
|
||||||
|
|
||||||
|
self.assertEqual(provider(), ((1, 2), {'a3': 3, 'a4': 4}))
|
||||||
|
|
||||||
|
def test_set_args(self):
|
||||||
|
provider = providers.Resource(init_fn) \
|
||||||
|
.add_args(1, 2) \
|
||||||
|
.set_args(3, 4)
|
||||||
|
self.assertEqual(provider.args, tuple([3, 4]))
|
||||||
|
|
||||||
|
def test_clear_args(self):
|
||||||
|
provider = providers.Resource(init_fn) \
|
||||||
|
.add_args(1, 2) \
|
||||||
|
.clear_args()
|
||||||
|
self.assertEqual(provider.args, tuple())
|
||||||
|
|
||||||
|
def test_set_kwargs(self):
|
||||||
|
provider = providers.Resource(init_fn) \
|
||||||
|
.add_kwargs(a1='i1', a2='i2') \
|
||||||
|
.set_kwargs(a3='i3', a4='i4')
|
||||||
|
self.assertEqual(provider.kwargs, {'a3': 'i3', 'a4': 'i4'})
|
||||||
|
|
||||||
|
def test_clear_kwargs(self):
|
||||||
|
provider = providers.Resource(init_fn) \
|
||||||
|
.add_kwargs(a1='i1', a2='i2') \
|
||||||
|
.clear_kwargs()
|
||||||
|
self.assertEqual(provider.kwargs, {})
|
||||||
|
|
||||||
|
def test_call_overridden(self):
|
||||||
|
provider = providers.Resource(init_fn, 1)
|
||||||
|
overriding_provider1 = providers.Resource(init_fn, 2)
|
||||||
|
overriding_provider2 = providers.Resource(init_fn, 3)
|
||||||
|
|
||||||
|
provider.override(overriding_provider1)
|
||||||
|
provider.override(overriding_provider2)
|
||||||
|
|
||||||
|
instance1 = provider()
|
||||||
|
instance2 = provider()
|
||||||
|
|
||||||
|
self.assertIs(instance1, instance2)
|
||||||
|
self.assertEqual(instance1, ((3,), {}))
|
||||||
|
self.assertEqual(instance2, ((3,), {}))
|
||||||
|
|
||||||
|
def test_deepcopy(self):
|
||||||
|
provider = providers.Resource(init_fn, 1, 2, a3=3, a4=4)
|
||||||
|
|
||||||
|
provider_copy = providers.deepcopy(provider)
|
||||||
|
|
||||||
|
self.assertIsNot(provider, provider_copy)
|
||||||
|
self.assertEqual(provider.args, provider_copy.args)
|
||||||
|
self.assertEqual(provider.kwargs, provider_copy.kwargs)
|
||||||
|
self.assertIsInstance(provider, providers.Resource)
|
||||||
|
|
||||||
|
def test_deepcopy_initialized(self):
|
||||||
|
provider = providers.Resource(init_fn)
|
||||||
|
provider.init()
|
||||||
|
|
||||||
|
with self.assertRaises(errors.Error):
|
||||||
|
providers.deepcopy(provider)
|
||||||
|
|
||||||
|
def test_deepcopy_from_memo(self):
|
||||||
|
provider = providers.Resource(init_fn)
|
||||||
|
provider_copy_memo = providers.Resource(init_fn)
|
||||||
|
|
||||||
|
provider_copy = providers.deepcopy(
|
||||||
|
provider,
|
||||||
|
memo={id(provider): provider_copy_memo},
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertIs(provider_copy, provider_copy_memo)
|
||||||
|
|
||||||
|
def test_deepcopy_args(self):
|
||||||
|
provider = providers.Resource(init_fn)
|
||||||
|
dependent_provider1 = providers.Factory(list)
|
||||||
|
dependent_provider2 = providers.Factory(dict)
|
||||||
|
|
||||||
|
provider.add_args(dependent_provider1, dependent_provider2)
|
||||||
|
|
||||||
|
provider_copy = providers.deepcopy(provider)
|
||||||
|
dependent_provider_copy1 = provider_copy.args[0]
|
||||||
|
dependent_provider_copy2 = provider_copy.args[1]
|
||||||
|
|
||||||
|
self.assertNotEqual(provider.args, provider_copy.args)
|
||||||
|
|
||||||
|
self.assertIs(dependent_provider1.cls, dependent_provider_copy1.cls)
|
||||||
|
self.assertIsNot(dependent_provider1, dependent_provider_copy1)
|
||||||
|
|
||||||
|
self.assertIs(dependent_provider2.cls, dependent_provider_copy2.cls)
|
||||||
|
self.assertIsNot(dependent_provider2, dependent_provider_copy2)
|
||||||
|
|
||||||
|
def test_deepcopy_kwargs(self):
|
||||||
|
provider = providers.Resource(init_fn)
|
||||||
|
dependent_provider1 = providers.Factory(list)
|
||||||
|
dependent_provider2 = providers.Factory(dict)
|
||||||
|
|
||||||
|
provider.add_kwargs(d1=dependent_provider1, d2=dependent_provider2)
|
||||||
|
|
||||||
|
provider_copy = providers.deepcopy(provider)
|
||||||
|
dependent_provider_copy1 = provider_copy.kwargs['d1']
|
||||||
|
dependent_provider_copy2 = provider_copy.kwargs['d2']
|
||||||
|
|
||||||
|
self.assertNotEqual(provider.kwargs, provider_copy.kwargs)
|
||||||
|
|
||||||
|
self.assertIs(dependent_provider1.cls, dependent_provider_copy1.cls)
|
||||||
|
self.assertIsNot(dependent_provider1, dependent_provider_copy1)
|
||||||
|
|
||||||
|
self.assertIs(dependent_provider2.cls, dependent_provider_copy2.cls)
|
||||||
|
self.assertIsNot(dependent_provider2, dependent_provider_copy2)
|
||||||
|
|
||||||
|
def test_deepcopy_overridden(self):
|
||||||
|
provider = providers.Resource(init_fn)
|
||||||
|
object_provider = providers.Object(object())
|
||||||
|
|
||||||
|
provider.override(object_provider)
|
||||||
|
|
||||||
|
provider_copy = providers.deepcopy(provider)
|
||||||
|
object_provider_copy = provider_copy.overridden[0]
|
||||||
|
|
||||||
|
self.assertIsNot(provider, provider_copy)
|
||||||
|
self.assertEqual(provider.args, provider_copy.args)
|
||||||
|
self.assertIsInstance(provider, providers.Resource)
|
||||||
|
|
||||||
|
self.assertIsNot(object_provider, object_provider_copy)
|
||||||
|
self.assertIsInstance(object_provider_copy, providers.Object)
|
||||||
|
|
||||||
|
def test_deepcopy_with_sys_streams(self):
|
||||||
|
provider = providers.Resource(init_fn)
|
||||||
|
provider.add_args(sys.stdin, sys.stdout, sys.stderr)
|
||||||
|
|
||||||
|
provider_copy = providers.deepcopy(provider)
|
||||||
|
|
||||||
|
self.assertIsNot(provider, provider_copy)
|
||||||
|
self.assertIsInstance(provider_copy, providers.Resource)
|
||||||
|
self.assertIs(provider.args[0], sys.stdin)
|
||||||
|
self.assertIs(provider.args[1], sys.stdout)
|
||||||
|
self.assertIs(provider.args[2], sys.stderr)
|
||||||
|
|
||||||
|
def test_repr(self):
|
||||||
|
provider = providers.Resource(init_fn)
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
repr(provider),
|
||||||
|
'Resource({0}, initialized={1})'.format(
|
||||||
|
init_fn,
|
||||||
|
provider.initialized,
|
||||||
|
)
|
||||||
|
)
|
8
tox.ini
8
tox.ini
|
@ -5,6 +5,8 @@ envlist=
|
||||||
[testenv]
|
[testenv]
|
||||||
deps=
|
deps=
|
||||||
unittest2
|
unittest2
|
||||||
|
# TODO: Hotfix, remove when fixed https://github.com/aio-libs/aiohttp/issues/5107
|
||||||
|
typing_extensions
|
||||||
extras=
|
extras=
|
||||||
yaml
|
yaml
|
||||||
flask
|
flask
|
||||||
|
@ -28,6 +30,8 @@ commands=
|
||||||
coveralls
|
coveralls
|
||||||
|
|
||||||
[testenv:py27]
|
[testenv:py27]
|
||||||
|
deps=
|
||||||
|
unittest2
|
||||||
extras=
|
extras=
|
||||||
yaml
|
yaml
|
||||||
flask
|
flask
|
||||||
|
@ -35,6 +39,8 @@ commands=
|
||||||
unit2 discover -s tests/unit -p test_*_py2_py3.py
|
unit2 discover -s tests/unit -p test_*_py2_py3.py
|
||||||
|
|
||||||
[testenv:py34]
|
[testenv:py34]
|
||||||
|
deps=
|
||||||
|
unittest2
|
||||||
extras=
|
extras=
|
||||||
flask
|
flask
|
||||||
commands=
|
commands=
|
||||||
|
@ -48,6 +54,8 @@ commands=
|
||||||
unit2 discover -s tests/unit -p test_*_py3.py
|
unit2 discover -s tests/unit -p test_*_py3.py
|
||||||
|
|
||||||
[testenv:pypy]
|
[testenv:pypy]
|
||||||
|
deps=
|
||||||
|
unittest2
|
||||||
extras=
|
extras=
|
||||||
yaml
|
yaml
|
||||||
flask
|
flask
|
||||||
|
|
Loading…
Reference in New Issue
Block a user