Add .providers attribute and .set_providers() method to FactoryAggregate provider

This commit is contained in:
Roman Mogylatov 2021-11-26 19:50:19 +03:00
parent 541131e338
commit 7238482402
8 changed files with 4512 additions and 4220 deletions

View File

@ -7,6 +7,14 @@ that were made in every particular version.
From version 0.7.6 *Dependency Injector* framework strictly
follows `Semantic versioning`_
Development version
-------------------
- Add ``.providers`` attribute to the ``FactoryAggregate`` provider. It is an alias for
``FactoryAggregate.factories`` attribute.
- Add ``.set_providers()`` method to the ``FactoryAggregate`` provider. It is an alias for
``FactoryAggregate.set_factories()`` method.
- Refactor ``FactoryAggregate`` provider internals.
4.37.0
------
- Add support of Python 3.10.

View File

@ -163,9 +163,9 @@ The aggregated factories are associated with the string keys. When you call the
:lines: 3-
:emphasize-lines: 33-37,47
You can get a dictionary of the aggregated factories using the ``.factories`` attribute.
To get a game factories dictionary from the previous example you can use
``game_factory.factories`` attribute.
You can get a dictionary of the aggregated providers using ``.providers`` attribute.
To get a game provider dictionary from the previous example you can use
``game_factory.providers`` attribute.
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")``.
@ -176,7 +176,7 @@ previous example you can do ``chess = game_factory.chess("John", "Jane")``.
.. note::
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:
To use non-string keys or string keys with ``.`` and ``-``, you can provide a dictionary as a positional argument:
.. code-block:: python

View File

@ -1209,12 +1209,12 @@ struct __pyx_obj_19dependency_injector_9providers_FactoryDelegate {
*
*
* cdef class FactoryAggregate(Provider): # <<<<<<<<<<<<<<
* cdef dict __factories
* cdef dict __providers
*
*/
struct __pyx_obj_19dependency_injector_9providers_FactoryAggregate {
struct __pyx_obj_19dependency_injector_9providers_Provider __pyx_base;
PyObject *__pyx___factories;
PyObject *__pyx___providers;
};
@ -2077,13 +2077,13 @@ static struct __pyx_vtabstruct_19dependency_injector_9providers_FactoryDelegate
*
*
* cdef class FactoryAggregate(Provider): # <<<<<<<<<<<<<<
* cdef dict __factories
* cdef dict __providers
*
*/
struct __pyx_vtabstruct_19dependency_injector_9providers_FactoryAggregate {
struct __pyx_vtabstruct_19dependency_injector_9providers_Provider __pyx_base;
struct __pyx_obj_19dependency_injector_9providers_Factory *(*__pyx___get_factory)(struct __pyx_obj_19dependency_injector_9providers_FactoryAggregate *, PyObject *);
struct __pyx_obj_19dependency_injector_9providers_Provider *(*__pyx___get_provider)(struct __pyx_obj_19dependency_injector_9providers_FactoryAggregate *, PyObject *);
};
static struct __pyx_vtabstruct_19dependency_injector_9providers_FactoryAggregate *__pyx_vtabptr_19dependency_injector_9providers_FactoryAggregate;

File diff suppressed because it is too large Load Diff

View File

@ -143,9 +143,9 @@ cdef class FactoryDelegate(Delegate):
cdef class FactoryAggregate(Provider):
cdef dict __factories
cdef dict __providers
cdef Factory __get_factory(self, object factory_name)
cdef Provider __get_provider(self, object provider_name)
# Singleton providers

View File

@ -303,18 +303,22 @@ class FactoryDelegate(Delegate):
class FactoryAggregate(Provider[T]):
def __init__(self, dict_: Optional[_Dict[Any, Factory[T]]] = None, **factories: Factory[T]): ...
def __getattr__(self, factory_name: Any) -> Factory[T]: ...
def __init__(self, provider_dict: Optional[_Dict[Any, Provider[T]]] = None, **provider_kwargs: Provider[T]): ...
def __getattr__(self, provider_name: Any) -> Provider[T]: ...
@overload
def __call__(self, factory_name: Any, *args: Injection, **kwargs: Injection) -> T: ...
def __call__(self, provider_name: Optional[Any] = None, *args: Injection, **kwargs: Injection) -> T: ...
@overload
def __call__(self, factory_name: Any, *args: Injection, **kwargs: Injection) -> Awaitable[T]: ...
def async_(self, factory_name: Any, *args: Injection, **kwargs: Injection) -> Awaitable[T]: ...
def __call__(self, provider_name: Optional[Any] = None, *args: Injection, **kwargs: Injection) -> Awaitable[T]: ...
def async_(self, provider_name: Optional[Any] = None, *args: Injection, **kwargs: Injection) -> Awaitable[T]: ...
@property
def factories(self) -> _Dict[Any, Factory[T]]: ...
def set_factories(self, dict_: Optional[_Dict[Any, Factory[T]]] = None, **factories: Factory[T]) -> FactoryAggregate[T]: ...
def providers(self) -> _Dict[Any, Provider[T]]: ...
def set_providers(self, provider_dict: Optional[_Dict[Any, Provider[T]]] = None, **provider_kwargs: Provider[T]) -> FactoryAggregate[T]: ...
@property
def factories(self) -> _Dict[Any, Provider[T]]: ...
def set_factories(self, provider_dict: Optional[_Dict[Any, Provider[T]]] = None, **provider_kwargs: Provider[T]) -> FactoryAggregate[T]: ...
class BaseSingleton(Provider[T]):

View File

@ -2558,17 +2558,17 @@ cdef class FactoryAggregate(Provider):
:py:class:`FactoryAggregate` is a delegated provider, meaning that it is
injected "as is".
All aggregated factories could be retrieved as a read-only
dictionary :py:attr:`FactoryAggregate.factories` or just as an attribute of
All aggregated providers can be retrieved as a read-only
dictionary :py:attr:`FactoryAggregate.providers` or as an attribute of
:py:class:`FactoryAggregate`.
"""
__IS_DELEGATED__ = True
def __init__(self, factories_dict_=None, **factories_kwargs):
def __init__(self, provider_dict=None, **provider_kwargs):
"""Initialize provider."""
self.__factories = {}
self.set_factories(factories_dict_, **factories_kwargs)
self.__providers = {}
self.set_providers(provider_dict, **provider_kwargs)
super(FactoryAggregate, self).__init__()
def __deepcopy__(self, memo):
@ -2578,48 +2578,69 @@ cdef class FactoryAggregate(Provider):
return copied
copied = _memorized_duplicate(self, memo)
copied.set_factories(deepcopy(self.factories, memo))
copied.set_providers(deepcopy(self.providers, memo))
self._copy_overridings(copied, memo)
return copied
def __getattr__(self, factory_name):
"""Return aggregated factory."""
return self.__get_factory(factory_name)
"""Return aggregated provider."""
return self.__get_provider(factory_name)
def __str__(self):
"""Return string representation of provider.
:rtype: str
"""
return represent_provider(provider=self, provides=self.factories)
return represent_provider(provider=self, provides=self.providers)
@property
def factories(self):
"""Return dictionary of factories, read-only."""
return self.__factories
def providers(self):
"""Return dictionary of providers, read-only.
def set_factories(self, factories_dict_=None, **factories_kwargs):
"""Set factories."""
factories = {}
factories.update(factories_kwargs)
if factories_dict_:
factories.update(factories_dict_)
Alias for ``.factories`` attribute.
"""
return dict(self.__providers)
for factory in factories.values():
if isinstance(factory, Factory) is False:
def set_providers(self, provider_dict=None, **provider_kwargs):
"""Set providers.
Alias for ``.set_factories()`` method.
"""
providers = {}
providers.update(provider_kwargs)
if provider_dict:
providers.update(provider_dict)
for provider in providers.values():
if not is_provider(provider):
raise Error(
'{0} can aggregate only instances of {1}, given - {2}'.format(
self.__class__,
Factory,
factory,
Provider,
provider,
),
)
self.__factories = factories
self.__providers = providers
return self
@property
def factories(self):
"""Return dictionary of factories, read-only.
Alias for ``.providers()`` attribute.
"""
return self.providers
def set_factories(self, factory_dict=None, **factory_kwargs):
"""Set factories.
Alias for ``.set_providers()`` method.
"""
return self.set_providers(factory_dict, **factory_kwargs)
def override(self, _):
"""Override provider with another provider.
@ -2633,26 +2654,26 @@ cdef class FactoryAggregate(Provider):
@property
def related(self):
"""Return related providers generator."""
yield from self.__factories.values()
yield from self.__providers.values()
yield from super().related
cpdef object _provide(self, tuple args, dict kwargs):
try:
factory_name = args[0]
provider_name = args[0]
except IndexError:
try:
factory_name = kwargs.pop('factory_name')
provider_name = kwargs.pop("factory_name")
except KeyError:
raise TypeError('Factory missing 1 required positional argument: \'factory_name\'')
raise TypeError("Missing 1st required positional argument: \"provider_name\"")
else:
args = args[1:]
return self.__get_factory(factory_name)(*args, **kwargs)
return self.__get_provider(provider_name)(*args, **kwargs)
cdef Factory __get_factory(self, object factory_key):
if factory_key not in self.__factories:
raise NoSuchProviderError('{0} does not contain factory with name {1}'.format(self, factory_key))
return <Factory> self.__factories[factory_key]
cdef Provider __get_provider(self, object provider_name):
if provider_name not in self.__providers:
raise NoSuchProviderError("{0} does not contain provider with name {1}".format(self, provider_name))
return <Provider> self.__providers[provider_name]
cdef class BaseSingleton(Provider):

View File

@ -78,6 +78,52 @@ def test_init_with_not_a_factory():
)
@mark.parametrize("factory_type", ["empty"])
def test_init_optional_providers(factory_aggregate, factory_a, factory_b):
factory_aggregate.set_providers(
example_a=factory_a,
example_b=factory_b,
)
assert factory_aggregate.providers == {
"example_a": factory_a,
"example_b": factory_b,
}
assert isinstance(factory_aggregate("example_a"), ExampleA)
assert isinstance(factory_aggregate("example_b"), ExampleB)
@mark.parametrize("factory_type", ["non-string-keys"])
def test_set_factories_with_non_string_keys(factory_aggregate, factory_a, factory_b):
factory_aggregate.set_providers({
ExampleA: factory_a,
ExampleB: factory_b,
})
object_a = factory_aggregate(ExampleA, 1, 2, init_arg3=3, init_arg4=4)
object_b = factory_aggregate(ExampleB, 11, 22, init_arg3=33, init_arg4=44)
assert isinstance(object_a, ExampleA)
assert object_a.init_arg1 == 1
assert object_a.init_arg2 == 2
assert object_a.init_arg3 == 3
assert object_a.init_arg4 == 4
assert isinstance(object_b, ExampleB)
assert object_b.init_arg1 == 11
assert object_b.init_arg2 == 22
assert object_b.init_arg3 == 33
assert object_b.init_arg4 == 44
assert factory_aggregate.providers == {
ExampleA: factory_a,
ExampleB: factory_b,
}
def test_set_providers_returns_self(factory_aggregate, factory_a):
assert factory_aggregate.set_providers(example_a=factory_a) is factory_aggregate
@mark.parametrize("factory_type", ["empty"])
def test_init_optional_factories(factory_aggregate, factory_a, factory_b):
factory_aggregate.set_factories(