mirror of
https://github.com/ets-labs/python-dependency-injector.git
synced 2024-11-22 01:26:51 +03:00
Merge branch 'release/4.36.0' into master
This commit is contained in:
commit
cef6d35cfd
|
@ -3,5 +3,8 @@ source = src/dependency_injector
|
||||||
omit = tests/unit
|
omit = tests/unit
|
||||||
plugins = Cython.Coverage
|
plugins = Cython.Coverage
|
||||||
|
|
||||||
|
[report]
|
||||||
|
show_missing = true
|
||||||
|
|
||||||
[html]
|
[html]
|
||||||
directory=reports/unittests/
|
directory=reports/unittests/
|
||||||
|
|
|
@ -18,3 +18,5 @@ Dependency Injector Contributors
|
||||||
+ Shubhendra Singh Chauhan (withshubh)
|
+ Shubhendra Singh Chauhan (withshubh)
|
||||||
+ sonthonaxrk (sonthonaxrk)
|
+ sonthonaxrk (sonthonaxrk)
|
||||||
+ Ngo Thanh Loi (Leonn) (loingo95)
|
+ Ngo Thanh Loi (Leonn) (loingo95)
|
||||||
|
+ Thiago Hiromi (thiromi)
|
||||||
|
+ Felipe Rubio (krouw)
|
||||||
|
|
|
@ -7,12 +7,32 @@ 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.35.3
|
4.36.0
|
||||||
------
|
------
|
||||||
|
- Add support of non-string keys for ``FactoryAggregate`` provider.
|
||||||
|
- Improve ``FactoryAggregate`` typing stub.
|
||||||
|
- Improve resource subclasses typing and make shutdown definition optional
|
||||||
|
`PR #492 <https://github.com/ets-labs/python-dependency-injector/pull/492>`_.
|
||||||
|
Thanks to `@EdwardBlair <https://github.com/EdwardBlair>`_ for suggesting the improvement.
|
||||||
|
- Fix type annotations for ``.provides``.
|
||||||
|
Thanks to `Thiago Hiromi @thiromi <https://github.com/thiromi>`_ for the fix
|
||||||
|
`PR #491 <https://github.com/ets-labs/python-dependency-injector/pull/491>`_.
|
||||||
|
- Fix environment variables interpolation examples in configuration provider docs ``{$ENV} -> ${ENV}``.
|
||||||
|
Thanks to `Felipe Rubio @krouw <https://github.com/krouw>`_ for reporting the issue and
|
||||||
|
fixing yaml example `PR #494 <https://github.com/ets-labs/python-dependency-injector/pull/494>`_.
|
||||||
- Fix ``@containers.copy()`` decorator to respect dependencies on parent providers.
|
- Fix ``@containers.copy()`` decorator to respect dependencies on parent providers.
|
||||||
See issue `#477 <https://github.com/ets-labs/python-dependency-injector/issues/477>`_.
|
See issue `#477 <https://github.com/ets-labs/python-dependency-injector/issues/477>`_.
|
||||||
Thanks to `Andrey Torsunov @gtors <https://github.com/gtors>`_ for reporting the issue.
|
Thanks to `Andrey Torsunov @gtors <https://github.com/gtors>`_ for reporting the issue.
|
||||||
- Fix typing stub for ``container.override_providers()`` to accept other types besides ``Provider``.
|
- Fix typing stub for ``container.override_providers()`` to accept other types besides ``Provider``.
|
||||||
|
- Fix runtime issue with generic typing in resource initializer classes ``resources.Resource``
|
||||||
|
and ``resources.AsyncResource``.
|
||||||
|
See issue `#488 <https://github.com/ets-labs/python-dependency-injector/issues/488>`_.
|
||||||
|
Thanks to `@EdwardBlair <https://github.com/EdwardBlair>`_ for reporting the issue.
|
||||||
|
|
||||||
|
4.35.3
|
||||||
|
------
|
||||||
|
- *This release was removed from PyPI. It was inconsistently published because project has
|
||||||
|
reached a PyPI size limit. Changes from this release are published on PyPI in next version.*
|
||||||
|
|
||||||
4.35.2
|
4.35.2
|
||||||
------
|
------
|
||||||
|
|
|
@ -50,9 +50,9 @@ where ``examples/providers/configuration/config.ini`` is:
|
||||||
.. code-block:: ini
|
.. code-block:: ini
|
||||||
|
|
||||||
[section]
|
[section]
|
||||||
option1 = {$ENV_VAR}
|
option1 = ${ENV_VAR}
|
||||||
option2 = {$ENV_VAR}/path
|
option2 = ${ENV_VAR}/path
|
||||||
option3 = {$ENV_VAR:default}
|
option3 = ${ENV_VAR:default}
|
||||||
|
|
||||||
See also: :ref:`configuration-envs-interpolation`.
|
See also: :ref:`configuration-envs-interpolation`.
|
||||||
|
|
||||||
|
@ -77,9 +77,9 @@ where ``examples/providers/configuration/config.yml`` is:
|
||||||
.. code-block:: ini
|
.. code-block:: ini
|
||||||
|
|
||||||
section:
|
section:
|
||||||
option1: {$ENV_VAR}
|
option1: ${ENV_VAR}
|
||||||
option2: {$ENV_VAR}/path
|
option2: ${ENV_VAR}/path
|
||||||
option3: {$ENV_VAR:default}
|
option3: ${ENV_VAR:default}
|
||||||
|
|
||||||
See also: :ref:`configuration-envs-interpolation`.
|
See also: :ref:`configuration-envs-interpolation`.
|
||||||
|
|
||||||
|
@ -208,7 +208,7 @@ variable ``ENV_NAME`` is undefined, configuration provider will substitute value
|
||||||
.. code-block:: ini
|
.. code-block:: ini
|
||||||
|
|
||||||
[section]
|
[section]
|
||||||
option = {$ENV_NAME:default}
|
option = ${ENV_NAME:default}
|
||||||
|
|
||||||
If you'd like to specify a default value for environment variable inside of the application you can use
|
If you'd like to specify a default value for environment variable inside of the application you can use
|
||||||
``os.environ.setdefault()``.
|
``os.environ.setdefault()``.
|
||||||
|
@ -380,7 +380,7 @@ an undefined environment variable without a default value.
|
||||||
.. code-block:: ini
|
.. code-block:: ini
|
||||||
|
|
||||||
section:
|
section:
|
||||||
option: {$UNDEFINED}
|
option: ${UNDEFINED}
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
|
|
|
@ -148,13 +148,11 @@ provider with two peculiarities:
|
||||||
Factory aggregate
|
Factory aggregate
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
:py:class:`FactoryAggregate` provider aggregates multiple factories. When you call the
|
:py:class:`FactoryAggregate` provider aggregates multiple factories.
|
||||||
``FactoryAggregate`` it delegates the call to one of the factories.
|
|
||||||
|
|
||||||
The aggregated factories are associated with the string names. When you call the
|
The aggregated factories are associated with the string keys. When you call the
|
||||||
``FactoryAggregate`` you have to provide one of the these names as a first argument.
|
``FactoryAggregate`` you have to provide one of the these keys as a first argument.
|
||||||
``FactoryAggregate`` looks for the factory with a matching name and delegates it the work. The
|
``FactoryAggregate`` looks for the factory with a matching key and calls it with the rest of the arguments.
|
||||||
rest of the arguments are passed to the delegated ``Factory``.
|
|
||||||
|
|
||||||
.. image:: images/factory_aggregate.png
|
.. image:: images/factory_aggregate.png
|
||||||
:width: 100%
|
:width: 100%
|
||||||
|
@ -165,12 +163,12 @@ rest of the arguments are passed to the delegated ``Factory``.
|
||||||
:lines: 3-
|
:lines: 3-
|
||||||
:emphasize-lines: 33-37,47
|
:emphasize-lines: 33-37,47
|
||||||
|
|
||||||
You can get a dictionary of the aggregated factories using the ``.factories`` attribute of the
|
You can get a dictionary of the aggregated factories using the ``.factories`` attribute.
|
||||||
``FactoryAggregate``. To get a game factories dictionary from the previous example you can use
|
To get a game factories dictionary from the previous example you can use
|
||||||
``game_factory.factories`` attribute.
|
``game_factory.factories`` attribute.
|
||||||
|
|
||||||
You can also access an aggregated factory as an attribute. To create the ``Chess`` object from the
|
You can also access an aggregated factory as an attribute. To create the ``Chess`` object from the
|
||||||
previous example you can do ``chess = game_factory.chess('John', 'Jane')``.
|
previous example you can do ``chess = game_factory.chess("John", "Jane")``.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
You can not override the ``FactoryAggregate`` provider.
|
You can not override the ``FactoryAggregate`` provider.
|
||||||
|
@ -178,4 +176,22 @@ previous example you can do ``chess = game_factory.chess('John', 'Jane')``.
|
||||||
.. note::
|
.. note::
|
||||||
When you inject the ``FactoryAggregate`` provider it is passed "as is".
|
When you inject the ``FactoryAggregate`` provider it is passed "as is".
|
||||||
|
|
||||||
|
To use non-string keys or keys with ``.`` and ``-`` you can provide a dictionary as a positional argument:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
providers.FactoryAggregate({
|
||||||
|
SomeClass: providers.Factory(...),
|
||||||
|
"key.with.periods": providers.Factory(...),
|
||||||
|
"key-with-dashes": providers.Factory(...),
|
||||||
|
})
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
.. literalinclude:: ../../examples/providers/factory_aggregate_non_string_keys.py
|
||||||
|
:language: python
|
||||||
|
:lines: 3-
|
||||||
|
:emphasize-lines: 30-33,39-40
|
||||||
|
|
||||||
|
|
||||||
.. disqus::
|
.. disqus::
|
||||||
|
|
45
examples/providers/factory_aggregate_non_string_keys.py
Normal file
45
examples/providers/factory_aggregate_non_string_keys.py
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
"""`FactoryAggregate` provider with non-string keys example."""
|
||||||
|
|
||||||
|
from dependency_injector import containers, providers
|
||||||
|
|
||||||
|
|
||||||
|
class Command:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class CommandA(Command):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class CommandB(Command):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class Handler:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class HandlerA(Handler):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class HandlerB(Handler):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class Container(containers.DeclarativeContainer):
|
||||||
|
|
||||||
|
handler_factory = providers.FactoryAggregate({
|
||||||
|
CommandA: providers.Factory(HandlerA),
|
||||||
|
CommandB: providers.Factory(HandlerB),
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
container = Container()
|
||||||
|
|
||||||
|
handler_a = container.handler_factory(CommandA)
|
||||||
|
handler_b = container.handler_factory(CommandB)
|
||||||
|
|
||||||
|
assert isinstance(handler_a, HandlerA)
|
||||||
|
assert isinstance(handler_b, HandlerB)
|
|
@ -1,6 +1,6 @@
|
||||||
"""Top-level package."""
|
"""Top-level package."""
|
||||||
|
|
||||||
__version__ = '4.35.3'
|
__version__ = '4.36.0'
|
||||||
"""Version number.
|
"""Version number.
|
||||||
|
|
||||||
:type: str
|
:type: str
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -142,7 +142,7 @@ cdef class FactoryDelegate(Delegate):
|
||||||
cdef class FactoryAggregate(Provider):
|
cdef class FactoryAggregate(Provider):
|
||||||
cdef dict __factories
|
cdef dict __factories
|
||||||
|
|
||||||
cdef Factory __get_factory(self, str factory_name)
|
cdef Factory __get_factory(self, object factory_name)
|
||||||
|
|
||||||
|
|
||||||
# Singleton providers
|
# Singleton providers
|
||||||
|
|
|
@ -84,6 +84,7 @@ class Provider(Generic[T]):
|
||||||
|
|
||||||
class Object(Provider[T]):
|
class Object(Provider[T]):
|
||||||
def __init__(self, provides: Optional[T] = None) -> None: ...
|
def __init__(self, provides: Optional[T] = None) -> None: ...
|
||||||
|
@property
|
||||||
def provides(self) -> Optional[T]: ...
|
def provides(self) -> Optional[T]: ...
|
||||||
def set_provides(self, provides: Optional[T]) -> Object: ...
|
def set_provides(self, provides: Optional[T]) -> Object: ...
|
||||||
|
|
||||||
|
@ -144,7 +145,7 @@ class DependenciesContainer(Object):
|
||||||
class Callable(Provider[T]):
|
class Callable(Provider[T]):
|
||||||
def __init__(self, provides: Optional[_Callable[..., T]] = None, *args: Injection, **kwargs: Injection) -> None: ...
|
def __init__(self, provides: Optional[_Callable[..., T]] = None, *args: Injection, **kwargs: Injection) -> None: ...
|
||||||
@property
|
@property
|
||||||
def provides(self) -> Optional[T]: ...
|
def provides(self) -> Optional[_Callable[..., T]]: ...
|
||||||
def set_provides(self, provides: Optional[_Callable[..., T]]) -> Callable[T]: ...
|
def set_provides(self, provides: Optional[_Callable[..., T]]) -> Callable[T]: ...
|
||||||
@property
|
@property
|
||||||
def args(self) -> Tuple[Injection]: ...
|
def args(self) -> Tuple[Injection]: ...
|
||||||
|
@ -249,9 +250,9 @@ class Factory(Provider[T]):
|
||||||
provided_type: Optional[Type]
|
provided_type: Optional[Type]
|
||||||
def __init__(self, provides: Optional[_Callable[..., T]] = None, *args: Injection, **kwargs: Injection) -> None: ...
|
def __init__(self, provides: Optional[_Callable[..., T]] = None, *args: Injection, **kwargs: Injection) -> None: ...
|
||||||
@property
|
@property
|
||||||
def cls(self) -> T: ...
|
def cls(self) -> Type[T]: ...
|
||||||
@property
|
@property
|
||||||
def provides(self) -> T: ...
|
def provides(self) -> Optional[_Callable[..., T]]: ...
|
||||||
def set_provides(self, provides: Optional[_Callable[..., T]]) -> Factory[T]: ...
|
def set_provides(self, provides: Optional[_Callable[..., T]]) -> Factory[T]: ...
|
||||||
@property
|
@property
|
||||||
def args(self) -> Tuple[Injection]: ...
|
def args(self) -> Tuple[Injection]: ...
|
||||||
|
@ -281,28 +282,28 @@ class FactoryDelegate(Delegate):
|
||||||
def __init__(self, factory: Factory): ...
|
def __init__(self, factory: Factory): ...
|
||||||
|
|
||||||
|
|
||||||
class FactoryAggregate(Provider):
|
class FactoryAggregate(Provider[T]):
|
||||||
def __init__(self, **factories: Factory): ...
|
def __init__(self, dict_: Optional[_Dict[Any, Factory[T]]] = None, **factories: Factory[T]): ...
|
||||||
def __getattr__(self, factory_name: str) -> Factory: ...
|
def __getattr__(self, factory_name: Any) -> Factory[T]: ...
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def __call__(self, factory_name: str, *args: Injection, **kwargs: Injection) -> Any: ...
|
def __call__(self, factory_name: Any, *args: Injection, **kwargs: Injection) -> T: ...
|
||||||
@overload
|
@overload
|
||||||
def __call__(self, factory_name: str, *args: Injection, **kwargs: Injection) -> Awaitable[Any]: ...
|
def __call__(self, factory_name: Any, *args: Injection, **kwargs: Injection) -> Awaitable[T]: ...
|
||||||
def async_(self, factory_name: str, *args: Injection, **kwargs: Injection) -> Awaitable[Any]: ...
|
def async_(self, factory_name: Any, *args: Injection, **kwargs: Injection) -> Awaitable[T]: ...
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def factories(self) -> _Dict[str, Factory]: ...
|
def factories(self) -> _Dict[Any, Factory[T]]: ...
|
||||||
def set_factories(self, **factories: Factory) -> FactoryAggregate: ...
|
def set_factories(self, dict_: Optional[_Dict[Any, Factory[T]]] = None, **factories: Factory[T]) -> FactoryAggregate[T]: ...
|
||||||
|
|
||||||
|
|
||||||
class BaseSingleton(Provider[T]):
|
class BaseSingleton(Provider[T]):
|
||||||
provided_type = Optional[Type]
|
provided_type = Optional[Type]
|
||||||
def __init__(self, provides: Optional[_Callable[..., T]] = None, *args: Injection, **kwargs: Injection) -> None: ...
|
def __init__(self, provides: Optional[_Callable[..., T]] = None, *args: Injection, **kwargs: Injection) -> None: ...
|
||||||
@property
|
@property
|
||||||
def cls(self) -> T: ...
|
def cls(self) -> Type[T]: ...
|
||||||
@property
|
@property
|
||||||
def provides(self) -> T: ...
|
def provides(self) -> Optional[_Callable[..., T]]: ...
|
||||||
def set_provides(self, provides: Optional[_Callable[..., T]]) -> BaseSingleton[T]: ...
|
def set_provides(self, provides: Optional[_Callable[..., T]]) -> BaseSingleton[T]: ...
|
||||||
@property
|
@property
|
||||||
def args(self) -> Tuple[Injection]: ...
|
def args(self) -> Tuple[Injection]: ...
|
||||||
|
|
|
@ -2486,10 +2486,10 @@ cdef class FactoryAggregate(Provider):
|
||||||
|
|
||||||
__IS_DELEGATED__ = True
|
__IS_DELEGATED__ = True
|
||||||
|
|
||||||
def __init__(self, **factories):
|
def __init__(self, factories_dict_=None, **factories_kwargs):
|
||||||
"""Initialize provider."""
|
"""Initialize provider."""
|
||||||
self.__factories = {}
|
self.__factories = {}
|
||||||
self.set_factories(**factories)
|
self.set_factories(factories_dict_, **factories_kwargs)
|
||||||
super(FactoryAggregate, self).__init__()
|
super(FactoryAggregate, self).__init__()
|
||||||
|
|
||||||
def __deepcopy__(self, memo):
|
def __deepcopy__(self, memo):
|
||||||
|
@ -2499,7 +2499,7 @@ cdef class FactoryAggregate(Provider):
|
||||||
return copied
|
return copied
|
||||||
|
|
||||||
copied = _memorized_duplicate(self, memo)
|
copied = _memorized_duplicate(self, memo)
|
||||||
copied.set_factories(**deepcopy(self.factories, memo))
|
copied.set_factories(deepcopy(self.factories, memo))
|
||||||
|
|
||||||
self._copy_overridings(copied, memo)
|
self._copy_overridings(copied, memo)
|
||||||
|
|
||||||
|
@ -2521,13 +2521,23 @@ cdef class FactoryAggregate(Provider):
|
||||||
"""Return dictionary of factories, read-only."""
|
"""Return dictionary of factories, read-only."""
|
||||||
return self.__factories
|
return self.__factories
|
||||||
|
|
||||||
def set_factories(self, **factories):
|
def set_factories(self, factories_dict_=None, **factories_kwargs):
|
||||||
"""Set factories."""
|
"""Set factories."""
|
||||||
|
factories = {}
|
||||||
|
factories.update(factories_kwargs)
|
||||||
|
if factories_dict_:
|
||||||
|
factories.update(factories_dict_)
|
||||||
|
|
||||||
for factory in factories.values():
|
for factory in factories.values():
|
||||||
if isinstance(factory, Factory) is False:
|
if isinstance(factory, Factory) is False:
|
||||||
raise Error(
|
raise Error(
|
||||||
'{0} can aggregate only instances of {1}, given - {2}'
|
'{0} can aggregate only instances of {1}, given - {2}'.format(
|
||||||
.format(self.__class__, Factory, factory))
|
self.__class__,
|
||||||
|
Factory,
|
||||||
|
factory,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
self.__factories = factories
|
self.__factories = factories
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
@ -2539,8 +2549,7 @@ cdef class FactoryAggregate(Provider):
|
||||||
:return: Overriding context.
|
:return: Overriding context.
|
||||||
:rtype: :py:class:`OverridingContext`
|
:rtype: :py:class:`OverridingContext`
|
||||||
"""
|
"""
|
||||||
raise Error(
|
raise Error('{0} providers could not be overridden'.format(self.__class__))
|
||||||
'{0} providers could not be overridden'.format(self.__class__))
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def related(self):
|
def related(self):
|
||||||
|
@ -2561,12 +2570,10 @@ cdef class FactoryAggregate(Provider):
|
||||||
|
|
||||||
return self.__get_factory(factory_name)(*args, **kwargs)
|
return self.__get_factory(factory_name)(*args, **kwargs)
|
||||||
|
|
||||||
cdef Factory __get_factory(self, str factory_name):
|
cdef Factory __get_factory(self, object factory_key):
|
||||||
if factory_name not in self.__factories:
|
if factory_key not in self.__factories:
|
||||||
raise NoSuchProviderError(
|
raise NoSuchProviderError('{0} does not contain factory with name {1}'.format(self, factory_key))
|
||||||
'{0} does not contain factory with name {1}'.format(
|
return <Factory> self.__factories[factory_key]
|
||||||
self, factory_name))
|
|
||||||
return <Factory> self.__factories[factory_name]
|
|
||||||
|
|
||||||
|
|
||||||
cdef class BaseSingleton(Provider):
|
cdef class BaseSingleton(Provider):
|
||||||
|
|
|
@ -1,42 +1,27 @@
|
||||||
"""Resources module."""
|
"""Resources module."""
|
||||||
|
|
||||||
import abc
|
import abc
|
||||||
import sys
|
from typing import TypeVar, Generic, Optional
|
||||||
from typing import TypeVar, Generic
|
|
||||||
|
|
||||||
if sys.version_info < (3, 7):
|
|
||||||
from typing import GenericMeta
|
|
||||||
else:
|
|
||||||
class GenericMeta(type):
|
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
T = TypeVar('T')
|
T = TypeVar('T')
|
||||||
|
|
||||||
|
|
||||||
class ResourceMeta(GenericMeta, abc.ABCMeta):
|
class Resource(Generic[T], metaclass=abc.ABCMeta):
|
||||||
def __getitem__(cls, item):
|
|
||||||
# Spike for Python 3.6
|
|
||||||
return cls(item)
|
|
||||||
|
|
||||||
|
|
||||||
class Resource(Generic[T], metaclass=ResourceMeta):
|
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def init(self, *args, **kwargs) -> T:
|
def init(self, *args, **kwargs) -> Optional[T]:
|
||||||
...
|
...
|
||||||
|
|
||||||
@abc.abstractmethod
|
def shutdown(self, resource: Optional[T]) -> None:
|
||||||
def shutdown(self, resource: T) -> None:
|
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
||||||
class AsyncResource(Generic[T], metaclass=ResourceMeta):
|
class AsyncResource(Generic[T], metaclass=abc.ABCMeta):
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
async def init(self, *args, **kwargs) -> T:
|
async def init(self, *args, **kwargs) -> Optional[T]:
|
||||||
...
|
...
|
||||||
|
|
||||||
@abc.abstractmethod
|
async def shutdown(self, resource: Optional[T]) -> None:
|
||||||
async def shutdown(self, resource: T) -> None:
|
|
||||||
...
|
...
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from typing import Tuple, Any, Dict
|
from typing import Callable, Optional, Tuple, Any, Dict, Type
|
||||||
|
|
||||||
from dependency_injector import providers
|
from dependency_injector import providers
|
||||||
|
|
||||||
|
@ -56,3 +56,13 @@ provider9 = providers.Callable(Cat)
|
||||||
async def _async9() -> None:
|
async def _async9() -> None:
|
||||||
animal1: Animal = await provider9(1, 2, 3, b='1', c=2, e=0.0) # type: ignore
|
animal1: Animal = await provider9(1, 2, 3, b='1', c=2, e=0.0) # type: ignore
|
||||||
animal2: Animal = await provider9.async_(1, 2, 3, b='1', c=2, e=0.0)
|
animal2: Animal = await provider9.async_(1, 2, 3, b='1', c=2, e=0.0)
|
||||||
|
|
||||||
|
# Test 10: to check the .provides
|
||||||
|
provider10 = providers.Callable(Cat)
|
||||||
|
provides10: Optional[Callable[..., Cat]] = provider10.provides
|
||||||
|
assert provides10 is Cat
|
||||||
|
|
||||||
|
# Test 11: to check the .provides for explicit typevar
|
||||||
|
provider11 = providers.Callable[Animal](Cat)
|
||||||
|
provides11: Optional[Callable[..., Animal]] = provider11.provides
|
||||||
|
assert provides11 is Cat
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
from dependency_injector import providers
|
from typing import Optional
|
||||||
|
|
||||||
|
from dependency_injector import providers
|
||||||
|
|
||||||
# Test 1: to check the return type
|
# Test 1: to check the return type
|
||||||
provider1 = providers.Delegate(providers.Provider())
|
provider1 = providers.Delegate(providers.Provider())
|
||||||
|
@ -10,3 +11,7 @@ provider2 = providers.Delegate(providers.Provider())
|
||||||
async def _async2() -> None:
|
async def _async2() -> None:
|
||||||
var1: providers.Provider = await provider2() # type: ignore
|
var1: providers.Provider = await provider2() # type: ignore
|
||||||
var2: providers.Provider = await provider2.async_()
|
var2: providers.Provider = await provider2.async_()
|
||||||
|
|
||||||
|
# Test 3: to check class type from provider
|
||||||
|
provider3 = providers.Delegate(providers.Provider())
|
||||||
|
provided_provides: Optional[providers.Provider] = provider3.provides
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from typing import Tuple, Any, Dict
|
from typing import Callable, Optional, Tuple, Any, Dict, Type
|
||||||
|
|
||||||
from dependency_injector import providers
|
from dependency_injector import providers
|
||||||
|
|
||||||
|
@ -55,13 +55,26 @@ animal7: Animal = provider7(1, 2, 3, b='1', c=2, e=0.0)
|
||||||
provider8 = providers.FactoryDelegate(providers.Factory(object))
|
provider8 = providers.FactoryDelegate(providers.Factory(object))
|
||||||
|
|
||||||
# Test 9: to check FactoryAggregate provider
|
# Test 9: to check FactoryAggregate provider
|
||||||
provider9 = providers.FactoryAggregate(
|
provider9: providers.FactoryAggregate[str] = providers.FactoryAggregate(
|
||||||
a=providers.Factory(object),
|
a=providers.Factory(str, "str1"),
|
||||||
b=providers.Factory(object),
|
b=providers.Factory(str, "str2"),
|
||||||
)
|
)
|
||||||
factory_a_9: providers.Factory = provider9.a
|
factory_a_9: providers.Factory[str] = provider9.a
|
||||||
factory_b_9: providers.Factory = provider9.b
|
factory_b_9: providers.Factory[str] = provider9.b
|
||||||
val9: Any = provider9('a')
|
val9: str = provider9('a')
|
||||||
|
|
||||||
|
provider9_set_non_string_keys: providers.FactoryAggregate[str] = providers.FactoryAggregate()
|
||||||
|
provider9_set_non_string_keys.set_factories({Cat: providers.Factory(str, "str")})
|
||||||
|
factory_set_non_string_9: providers.Factory[str] = provider9_set_non_string_keys.factories[Cat]
|
||||||
|
|
||||||
|
provider9_new_non_string_keys: providers.FactoryAggregate[str] = providers.FactoryAggregate(
|
||||||
|
{Cat: providers.Factory(str, "str")},
|
||||||
|
)
|
||||||
|
factory_new_non_string_9: providers.Factory[str] = provider9_new_non_string_keys.factories[Cat]
|
||||||
|
|
||||||
|
provider9_no_explicit_typing = providers.FactoryAggregate(a=providers.Factory(str, "str"))
|
||||||
|
provider9_no_explicit_typing_factory: providers.Factory[str] = provider9_no_explicit_typing.factories["a"]
|
||||||
|
provider9_no_explicit_typing_object: str = provider9_no_explicit_typing("a")
|
||||||
|
|
||||||
# Test 10: to check the explicit typing
|
# Test 10: to check the explicit typing
|
||||||
factory10: providers.Provider[Animal] = providers.Factory(Cat)
|
factory10: providers.Provider[Animal] = providers.Factory(Cat)
|
||||||
|
@ -72,3 +85,17 @@ provider11 = providers.Factory(Cat)
|
||||||
async def _async11() -> None:
|
async def _async11() -> None:
|
||||||
animal1: Animal = await provider11(1, 2, 3, b='1', c=2, e=0.0) # type: ignore
|
animal1: Animal = await provider11(1, 2, 3, b='1', c=2, e=0.0) # type: ignore
|
||||||
animal2: Animal = await provider11.async_(1, 2, 3, b='1', c=2, e=0.0)
|
animal2: Animal = await provider11.async_(1, 2, 3, b='1', c=2, e=0.0)
|
||||||
|
|
||||||
|
# Test 12: to check class type from .provides
|
||||||
|
provider12 = providers.Factory(Cat)
|
||||||
|
provided_cls12: Type[Animal] = provider12.cls
|
||||||
|
assert issubclass(provided_cls12, Animal)
|
||||||
|
provided_provides12: Optional[Callable[..., Animal]] = provider12.provides
|
||||||
|
assert provided_provides12 is not None and provided_provides12() == Cat()
|
||||||
|
|
||||||
|
# Test 13: to check class from .provides with explicit typevar
|
||||||
|
provider13 = providers.Factory[Animal](Cat)
|
||||||
|
provided_cls13: Type[Animal] = provider13.cls
|
||||||
|
assert issubclass(provided_cls13, Animal)
|
||||||
|
provided_provides13: Optional[Callable[..., Animal]] = provider13.provides
|
||||||
|
assert provided_provides13 is not None and provided_provides13() == Cat()
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
from typing import Type, Optional
|
||||||
|
|
||||||
from dependency_injector import providers
|
from dependency_injector import providers
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,3 +19,7 @@ provider3 = providers.Object(int(3))
|
||||||
async def _async3() -> None:
|
async def _async3() -> None:
|
||||||
var1: int = await provider3() # type: ignore
|
var1: int = await provider3() # type: ignore
|
||||||
var2: int = await provider3.async_()
|
var2: int = await provider3.async_()
|
||||||
|
|
||||||
|
# Test 4: to check class type from provider
|
||||||
|
provider4 = providers.Object(int('1'))
|
||||||
|
provided_provides: Optional[int] = provider4.provides
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from typing import List, Iterator, Generator, AsyncIterator, AsyncGenerator
|
from typing import List, Iterator, Generator, AsyncIterator, AsyncGenerator, Optional
|
||||||
|
|
||||||
from dependency_injector import providers, resources
|
from dependency_injector import providers, resources
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ class MyResource4(resources.Resource[List[int]]):
|
||||||
def init(self, *args, **kwargs) -> List[int]:
|
def init(self, *args, **kwargs) -> List[int]:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def shutdown(self, resource: List[int]) -> None:
|
def shutdown(self, resource: Optional[List[int]]) -> None:
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
||||||
|
@ -87,7 +87,7 @@ class MyResource8(resources.AsyncResource[List[int]]):
|
||||||
async def init(self, *args, **kwargs) -> List[int]:
|
async def init(self, *args, **kwargs) -> List[int]:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
async def shutdown(self, resource: List[int]) -> None:
|
async def shutdown(self, resource: Optional[List[int]]) -> None:
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from typing import Tuple, Any, Dict
|
from typing import Callable, Optional, Tuple, Any, Dict, Type
|
||||||
|
|
||||||
from dependency_injector import providers
|
from dependency_injector import providers
|
||||||
|
|
||||||
|
@ -75,3 +75,17 @@ provider13 = providers.Singleton(Cat)
|
||||||
async def _async13() -> None:
|
async def _async13() -> None:
|
||||||
animal1: Animal = await provider13(1, 2, 3, b='1', c=2, e=0.0) # type: ignore
|
animal1: Animal = await provider13(1, 2, 3, b='1', c=2, e=0.0) # type: ignore
|
||||||
animal2: Animal = await provider13.async_(1, 2, 3, b='1', c=2, e=0.0)
|
animal2: Animal = await provider13.async_(1, 2, 3, b='1', c=2, e=0.0)
|
||||||
|
|
||||||
|
# Test 14: to check class from .provides
|
||||||
|
provider14 = providers.Singleton(Cat)
|
||||||
|
provided_cls14: Type[Cat] = provider14.cls
|
||||||
|
assert issubclass(provided_cls14, Cat)
|
||||||
|
provided_provides14: Optional[Callable[..., Cat]] = provider14.provides
|
||||||
|
assert provided_provides14 is not None and provided_provides14() == Cat()
|
||||||
|
|
||||||
|
# Test 15: to check class from .provides with explicit typevar
|
||||||
|
provider15 = providers.Singleton[Animal](Cat)
|
||||||
|
provided_cls15: Type[Animal] = provider15.cls
|
||||||
|
assert issubclass(provided_cls15, Animal)
|
||||||
|
provided_provides15: Optional[Callable[..., Animal]] = provider15.provides
|
||||||
|
assert provided_provides15 is not None and provided_provides15() == Cat()
|
||||||
|
|
|
@ -5,6 +5,7 @@ import sys
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from dependency_injector import (
|
from dependency_injector import (
|
||||||
|
containers,
|
||||||
providers,
|
providers,
|
||||||
errors,
|
errors,
|
||||||
)
|
)
|
||||||
|
@ -498,7 +499,8 @@ class FactoryAggregateTests(unittest.TestCase):
|
||||||
self.example_b_factory = providers.Factory(self.ExampleB)
|
self.example_b_factory = providers.Factory(self.ExampleB)
|
||||||
self.factory_aggregate = providers.FactoryAggregate(
|
self.factory_aggregate = providers.FactoryAggregate(
|
||||||
example_a=self.example_a_factory,
|
example_a=self.example_a_factory,
|
||||||
example_b=self.example_b_factory)
|
example_b=self.example_b_factory,
|
||||||
|
)
|
||||||
|
|
||||||
def test_is_provider(self):
|
def test_is_provider(self):
|
||||||
self.assertTrue(providers.is_provider(self.factory_aggregate))
|
self.assertTrue(providers.is_provider(self.factory_aggregate))
|
||||||
|
@ -506,6 +508,35 @@ class FactoryAggregateTests(unittest.TestCase):
|
||||||
def test_is_delegated_provider(self):
|
def test_is_delegated_provider(self):
|
||||||
self.assertTrue(providers.is_delegated(self.factory_aggregate))
|
self.assertTrue(providers.is_delegated(self.factory_aggregate))
|
||||||
|
|
||||||
|
def test_init_with_non_string_keys(self):
|
||||||
|
factory = providers.FactoryAggregate({
|
||||||
|
self.ExampleA: self.example_a_factory,
|
||||||
|
self.ExampleB: self.example_b_factory,
|
||||||
|
})
|
||||||
|
|
||||||
|
object_a = factory(self.ExampleA, 1, 2, init_arg3=3, init_arg4=4)
|
||||||
|
object_b = factory(self.ExampleB, 11, 22, init_arg3=33, init_arg4=44)
|
||||||
|
|
||||||
|
self.assertIsInstance(object_a, self.ExampleA)
|
||||||
|
self.assertEqual(object_a.init_arg1, 1)
|
||||||
|
self.assertEqual(object_a.init_arg2, 2)
|
||||||
|
self.assertEqual(object_a.init_arg3, 3)
|
||||||
|
self.assertEqual(object_a.init_arg4, 4)
|
||||||
|
|
||||||
|
self.assertIsInstance(object_b, self.ExampleB)
|
||||||
|
self.assertEqual(object_b.init_arg1, 11)
|
||||||
|
self.assertEqual(object_b.init_arg2, 22)
|
||||||
|
self.assertEqual(object_b.init_arg3, 33)
|
||||||
|
self.assertEqual(object_b.init_arg4, 44)
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
factory.factories,
|
||||||
|
{
|
||||||
|
self.ExampleA: self.example_a_factory,
|
||||||
|
self.ExampleB: self.example_b_factory,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
def test_init_with_not_a_factory(self):
|
def test_init_with_not_a_factory(self):
|
||||||
with self.assertRaises(errors.Error):
|
with self.assertRaises(errors.Error):
|
||||||
providers.FactoryAggregate(
|
providers.FactoryAggregate(
|
||||||
|
@ -528,7 +559,37 @@ class FactoryAggregateTests(unittest.TestCase):
|
||||||
self.assertIsInstance(provider('example_a'), self.ExampleA)
|
self.assertIsInstance(provider('example_a'), self.ExampleA)
|
||||||
self.assertIsInstance(provider('example_b'), self.ExampleB)
|
self.assertIsInstance(provider('example_b'), self.ExampleB)
|
||||||
|
|
||||||
def test_set_provides_returns_self(self):
|
def test_set_factories_with_non_string_keys(self):
|
||||||
|
factory = providers.FactoryAggregate()
|
||||||
|
factory.set_factories({
|
||||||
|
self.ExampleA: self.example_a_factory,
|
||||||
|
self.ExampleB: self.example_b_factory,
|
||||||
|
})
|
||||||
|
|
||||||
|
object_a = factory(self.ExampleA, 1, 2, init_arg3=3, init_arg4=4)
|
||||||
|
object_b = factory(self.ExampleB, 11, 22, init_arg3=33, init_arg4=44)
|
||||||
|
|
||||||
|
self.assertIsInstance(object_a, self.ExampleA)
|
||||||
|
self.assertEqual(object_a.init_arg1, 1)
|
||||||
|
self.assertEqual(object_a.init_arg2, 2)
|
||||||
|
self.assertEqual(object_a.init_arg3, 3)
|
||||||
|
self.assertEqual(object_a.init_arg4, 4)
|
||||||
|
|
||||||
|
self.assertIsInstance(object_b, self.ExampleB)
|
||||||
|
self.assertEqual(object_b.init_arg1, 11)
|
||||||
|
self.assertEqual(object_b.init_arg2, 22)
|
||||||
|
self.assertEqual(object_b.init_arg3, 33)
|
||||||
|
self.assertEqual(object_b.init_arg4, 44)
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
factory.factories,
|
||||||
|
{
|
||||||
|
self.ExampleA: self.example_a_factory,
|
||||||
|
self.ExampleB: self.example_b_factory,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_set_factories_returns_self(self):
|
||||||
provider = providers.FactoryAggregate()
|
provider = providers.FactoryAggregate()
|
||||||
self.assertIs(provider.set_factories(example_a=self.example_a_factory), provider)
|
self.assertIs(provider.set_factories(example_a=self.example_a_factory), provider)
|
||||||
|
|
||||||
|
@ -603,6 +664,24 @@ class FactoryAggregateTests(unittest.TestCase):
|
||||||
self.assertIsInstance(self.factory_aggregate.example_b, type(provider_copy.example_b))
|
self.assertIsInstance(self.factory_aggregate.example_b, type(provider_copy.example_b))
|
||||||
self.assertIs(self.factory_aggregate.example_b.cls, provider_copy.example_b.cls)
|
self.assertIs(self.factory_aggregate.example_b.cls, provider_copy.example_b.cls)
|
||||||
|
|
||||||
|
def test_deepcopy_with_non_string_keys(self):
|
||||||
|
factory_aggregate = providers.FactoryAggregate({
|
||||||
|
self.ExampleA: self.example_a_factory,
|
||||||
|
self.ExampleB: self.example_b_factory,
|
||||||
|
})
|
||||||
|
provider_copy = providers.deepcopy(factory_aggregate)
|
||||||
|
|
||||||
|
self.assertIsNot(factory_aggregate, provider_copy)
|
||||||
|
self.assertIsInstance(provider_copy, type(factory_aggregate))
|
||||||
|
|
||||||
|
self.assertIsNot(factory_aggregate.factories[self.ExampleA], provider_copy.factories[self.ExampleA])
|
||||||
|
self.assertIsInstance(factory_aggregate.factories[self.ExampleA], type(provider_copy.factories[self.ExampleA]))
|
||||||
|
self.assertIs(factory_aggregate.factories[self.ExampleA].cls, provider_copy.factories[self.ExampleA].cls)
|
||||||
|
|
||||||
|
self.assertIsNot(factory_aggregate.factories[self.ExampleB], provider_copy.factories[self.ExampleB])
|
||||||
|
self.assertIsInstance(factory_aggregate.factories[self.ExampleB], type(provider_copy.factories[self.ExampleB]))
|
||||||
|
self.assertIs(factory_aggregate.factories[self.ExampleB].cls, provider_copy.factories[self.ExampleB].cls)
|
||||||
|
|
||||||
def test_repr(self):
|
def test_repr(self):
|
||||||
self.assertEqual(repr(self.factory_aggregate),
|
self.assertEqual(repr(self.factory_aggregate),
|
||||||
'<dependency_injector.providers.'
|
'<dependency_injector.providers.'
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
"""Dependency injector resource provider unit tests."""
|
"""Dependency injector resource provider unit tests."""
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import inspect
|
||||||
import unittest
|
import unittest
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from dependency_injector import containers, providers, resources, errors
|
from dependency_injector import containers, providers, resources, errors
|
||||||
|
|
||||||
|
@ -145,6 +146,35 @@ class ResourceTests(unittest.TestCase):
|
||||||
self.assertEqual(TestResource.init_counter, 2)
|
self.assertEqual(TestResource.init_counter, 2)
|
||||||
self.assertEqual(TestResource.shutdown_counter, 2)
|
self.assertEqual(TestResource.shutdown_counter, 2)
|
||||||
|
|
||||||
|
def test_init_class_generic_typing(self):
|
||||||
|
# See issue: https://github.com/ets-labs/python-dependency-injector/issues/488
|
||||||
|
class TestDependency:
|
||||||
|
...
|
||||||
|
|
||||||
|
class TestResource(resources.Resource[TestDependency]):
|
||||||
|
def init(self, *args: Any, **kwargs: Any) -> TestDependency:
|
||||||
|
return TestDependency()
|
||||||
|
|
||||||
|
def shutdown(self, resource: TestDependency) -> None: ...
|
||||||
|
|
||||||
|
self.assertTrue(issubclass(TestResource, resources.Resource))
|
||||||
|
|
||||||
|
def test_init_class_abc_init_definition_is_required(self):
|
||||||
|
class TestResource(resources.Resource):
|
||||||
|
...
|
||||||
|
|
||||||
|
with self.assertRaises(TypeError) as context:
|
||||||
|
TestResource()
|
||||||
|
|
||||||
|
self.assertIn("Can't instantiate abstract class TestResource", str(context.exception))
|
||||||
|
self.assertIn("init", str(context.exception))
|
||||||
|
|
||||||
|
def test_init_class_abc_shutdown_definition_is_not_required(self):
|
||||||
|
class TestResource(resources.Resource):
|
||||||
|
def init(self):
|
||||||
|
...
|
||||||
|
self.assertTrue(hasattr(TestResource(), 'shutdown'))
|
||||||
|
|
||||||
def test_init_not_callable(self):
|
def test_init_not_callable(self):
|
||||||
provider = providers.Resource(1)
|
provider = providers.Resource(1)
|
||||||
with self.assertRaises(errors.Error):
|
with self.assertRaises(errors.Error):
|
||||||
|
@ -449,6 +479,36 @@ class AsyncResourceTest(AsyncTestCase):
|
||||||
self.assertEqual(TestResource.init_counter, 2)
|
self.assertEqual(TestResource.init_counter, 2)
|
||||||
self.assertEqual(TestResource.shutdown_counter, 2)
|
self.assertEqual(TestResource.shutdown_counter, 2)
|
||||||
|
|
||||||
|
def test_init_async_class_generic_typing(self):
|
||||||
|
# See issue: https://github.com/ets-labs/python-dependency-injector/issues/488
|
||||||
|
class TestDependency:
|
||||||
|
...
|
||||||
|
|
||||||
|
class TestAsyncResource(resources.AsyncResource[TestDependency]):
|
||||||
|
async def init(self, *args: Any, **kwargs: Any) -> TestDependency:
|
||||||
|
return TestDependency()
|
||||||
|
|
||||||
|
async def shutdown(self, resource: TestDependency) -> None: ...
|
||||||
|
|
||||||
|
self.assertTrue(issubclass(TestAsyncResource, resources.AsyncResource))
|
||||||
|
|
||||||
|
def test_init_async_class_abc_init_definition_is_required(self):
|
||||||
|
class TestAsyncResource(resources.AsyncResource):
|
||||||
|
...
|
||||||
|
|
||||||
|
with self.assertRaises(TypeError) as context:
|
||||||
|
TestAsyncResource()
|
||||||
|
|
||||||
|
self.assertIn("Can't instantiate abstract class TestAsyncResource", str(context.exception))
|
||||||
|
self.assertIn("init", str(context.exception))
|
||||||
|
|
||||||
|
def test_init_async_class_abc_shutdown_definition_is_not_required(self):
|
||||||
|
class TestAsyncResource(resources.AsyncResource):
|
||||||
|
async def init(self):
|
||||||
|
...
|
||||||
|
self.assertTrue(hasattr(TestAsyncResource(), 'shutdown'))
|
||||||
|
self.assertTrue(inspect.iscoroutinefunction(TestAsyncResource.shutdown))
|
||||||
|
|
||||||
def test_init_with_error(self):
|
def test_init_with_error(self):
|
||||||
async def _init():
|
async def _init():
|
||||||
raise RuntimeError()
|
raise RuntimeError()
|
||||||
|
|
Loading…
Reference in New Issue
Block a user