Is it possible to provide default factory for FactoryAggregate and/or Selector?
Closed this issue · 1 comments
Igreh commented
What am I trying to achieve:
from dependency_injector.containers import DeclarativeContainer
from dependency_injector import providers
from dependency_injector.errors import NoSuchProviderError
class Obj:
def __init__(self, name):
self.name = name
def __repr__(self):
return f'<Obj name={self.name}>'
class C(DeclarativeContainer):
agg_factory = providers.FactoryAggregate(
option1=providers.Factory(Obj, 'option1'),
option2=providers.Factory(Obj, 'option2'),
# default (any) option
_=providers.Factory(Obj, "I'm default")
)
def callable_provider(option):
if option in ('option1', 'option2'):
return Obj(name=option)
return Obj("I'm default")
callable = providers.Callable(
callable_provider
)
container = C()
print(container.agg_factory('option1'))
try:
container.agg_factory('random') # NoSuchProviderError
except NoSuchProviderError:
print('=(')
# this works but it's not as "clean" as FactoryAggregate solution
print(container.callable('option1'))
print(container.callable('random'))Code above results in:
<Obj name=option1>
=(
<Obj name=option1>
<Obj name=I'm default>
So, is it possible use Callable power for FactoryAggregate or Selector to make container.agg_factory('random') return '_' option?
Igreh commented
Made custom provider according to https://python-dependency-injector.ets-labs.org/providers/custom.html and
from dependency_injector.containers import DeclarativeContainer
from dependency_injector import providers
from dependency_injector.errors import NoSuchProviderError
class Obj:
def __init__(self, name):
self.name = name
def __repr__(self):
return f'<Obj name={self.name}>'
class DefaultFactoryAggregate(providers.Provider):
DEFAULT_OPTION = '_'
__slots__ = ('_agg_factory')
def __init__(self, provider_dict=None, **provider_kwargs):
self._agg_factory = providers.FactoryAggregate(provider_dict, **provider_kwargs)
super().__init__()
@property
def related(self):
"""Return related providers generator."""
yield from [self._agg_factory]
yield from super().related
def __deepcopy__(self, memo):
copied = memo.get(id(self))
if copied is not None:
return copied
copied = self.__class__(
self._agg_factory.providers,
)
self._copy_overridings(copied, memo)
return copied
def _provide(self, args, kwargs):
try:
return self._agg_factory(*args, **kwargs)
except NoSuchProviderError:
kwargs.pop('factory_name', None)
args = (self.DEFAULT_OPTION, ) + args[1:]
return self._agg_factory(*args, **kwargs)
class C(DeclarativeContainer):
agg_factory = DefaultFactoryAggregate(
option1=providers.Factory(Obj, 'option1'),
option2=providers.Factory(Obj, 'option2'),
_=providers.Factory(Obj, "I'm default")
)
container = C()
print(container.agg_factory('option1'))
print(container.agg_factory(factory_name='option2'))
print(container.agg_factory('123'))
print(container.agg_factory(None))<Obj name=option1>
<Obj name=option2>
<Obj name=I'm default>
<Obj name=I'm default>Inherited version much shorter:
class InheritedDefaultFactoryAggregate(providers.FactoryAggregate):
DEFAULT_OPTION = '_'
def _provide(self, args: tuple, kwargs: dict):
try:
return super()._provide(args, kwargs)
except NoSuchProviderError:
kwargs.pop('factory_name', None)
args = (self.DEFAULT_OPTION,) + args[1:]
return super()._provide(args, kwargs)but Prefer delegation over inheritance documentation says =) Have no idea why actually...