Source code for hydra_slayer.factory
from typing import Any, Callable, Mapping, Tuple, Type, Union
import copy
import functools
import inspect
__all__ = [
"metafactory_factory",
"call_meta_factory",
"partial_meta_factory",
"default_meta_factory",
]
Factory = Union[Type, Callable[..., Any]]
DEFAULT_FROM_PARAMS_KEY = "get_from_params"
DEFAULT_META_FACTORY_KEY = "_meta_factory_"
DEFAULT_CALL_MODE_KEY = "_mode_" # TODO: discuss with @scitator and rename
[docs]def metafactory_factory(factory: Factory, args: Tuple, kwargs: Mapping):
"""Returns a new instance or a new partial object.
* _mode_='auto'
Creates a new instance from ``factory`` if ``factory`` is class
(like :py:func:`call_meta_factory`), else returns a new partial object
(like :py:func:`partial_meta_factory`).
* _mode_='call'
Returns a result of the factory called with the positional arguments
``args`` and keyword arguments ``kwargs``.
* _mode_='partial'
Returns a new partial object which when called will behave like factory
called with the positional arguments ``args`` and keyword arguments
``kwargs``.
Args:
factory: factory to create instance from
args: positional arguments to be passed into the factory
kwargs: keyword arguments to be passed into the factory
Returns:
Instance.
Raises:
ValueError: if mode not in list: ``'auto'``, ``'call'``, ``'partial'``.
Examples:
>>> metafactory_factory(int, (42,))
42
>>> metafactory_factory(lambda x: x, (42,))() # note that additional () are used
42
>>> metafactory_factory(lambda x: x, (42,), {'_mode_': 'call'})
42
>>> metafactory_factory(int, ('2A'), {'base': 16})
42
>>> hex_to_dec = metafactory_factory(int, (), {'_mode_': 'partial', 'base': 16})
>>> hex_to_dec('2A')
42
"""
# make a copy of kwargs since we don't want to modify them directly
kwargs = copy.copy(kwargs)
meta_factory = kwargs.pop(DEFAULT_META_FACTORY_KEY, None)
meta_factory_name = kwargs.pop(DEFAULT_CALL_MODE_KEY, "auto")
# legacy, for the compatibility with the Catalyst library
if hasattr(factory, DEFAULT_FROM_PARAMS_KEY):
return getattr(factory, DEFAULT_FROM_PARAMS_KEY)(*args, **kwargs)
if meta_factory is None:
meta_factories = {
"auto": default_meta_factory,
"call": call_meta_factory,
"partial": partial_meta_factory,
}
if meta_factory_name not in meta_factories.keys():
raise ValueError(f"'{meta_factory_name}' is not a valid call mode")
meta_factory = meta_factories[meta_factory_name]
return meta_factory(factory, args=args, kwargs=kwargs)
[docs]def call_meta_factory(factory: Factory, args: Tuple, kwargs: Mapping):
"""Creates a new instance from ``factory``.
Args:
factory: factory to create instance from
args: positional arguments to be passed into the factory
kwargs: keyword arguments to be passed into the factory
Returns:
Instance.
Examples:
>>> call_meta_factory(int, (42,), {})
42
>>> call_meta_factory(int, ('2A',), {'base': 16})
66
>>> call_meta_factory(lambda x: x, (42,), {})
42
"""
return factory(*args, **kwargs)
[docs]def partial_meta_factory(factory: Factory, args: Tuple, kwargs: Mapping):
"""
Returns a new partial object which when called will behave like func called
with the positional arguments ``args`` and keyword arguments ``kwargs``.
Args:
factory: factory to create instance from
args: positional arguments to be merged into the factory
kwargs: keyword arguments to be merged into the factory
Returns:
Partial object.
Examples:
>>> get_answer_to_life = partial_meta_factory(lambda x: x, (42,), {})
>>> get_answer_to_life()
42
>>> hex_to_dec = partial_meta_factory(int, (), {'base': 16})
>>> hex_to_dec('2A')
42
"""
return functools.partial(factory, *args, **kwargs)
[docs]def default_meta_factory(factory: Factory, args: Tuple, kwargs: Mapping):
"""Returns a new instance or a new partial object.
Creates a new instance from ``factory`` if ``factory`` is class
(behaves like :py:func:`call_meta_factory`), else returns a new
partial object (behaves like :py:func:`partial_meta_factory`).
Args:
factory: factory to create instance from
args: positional arguments to be passed into the factory
kwargs: keyword arguments to be passed into the factory
Returns:
Instance.
Raises:
ValueError: if factory object is not callable.
Examples:
>>> default_meta_factory(int, (42,), {})
42
>>> default_meta_factory(int, ('2A',), {'base': 16})
42
>>> get_answer_to_life = default_meta_factory(lambda x: x, (42,), {})
>>> get_answer_to_life()
42
"""
if inspect.isclass(factory):
obj = call_meta_factory(factory, args, kwargs)
elif inspect.ismethod(factory) or inspect.isfunction(factory):
obj = partial_meta_factory(factory, args, kwargs)
else:
raise ValueError(f"factory '{factory}' is not callable")
return obj