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