diff options
author | S. Solomon Darnell | 2025-03-28 21:52:21 -0500 |
---|---|---|
committer | S. Solomon Darnell | 2025-03-28 21:52:21 -0500 |
commit | 4a52a71956a8d46fcb7294ac71734504bb09bcc2 (patch) | |
tree | ee3dc5af3b6313e921cd920906356f5d4febc4ed /.venv/lib/python3.12/site-packages/pydash/chaining/chaining.py | |
parent | cc961e04ba734dd72309fb548a2f97d67d578813 (diff) | |
download | gn-ai-master.tar.gz |
Diffstat (limited to '.venv/lib/python3.12/site-packages/pydash/chaining/chaining.py')
-rw-r--r-- | .venv/lib/python3.12/site-packages/pydash/chaining/chaining.py | 264 |
1 files changed, 264 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/pydash/chaining/chaining.py b/.venv/lib/python3.12/site-packages/pydash/chaining/chaining.py new file mode 100644 index 00000000..09a364c5 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydash/chaining/chaining.py @@ -0,0 +1,264 @@ +""" +Method chaining interface. + +.. versionadded:: 1.0.0 +""" + +import typing as t + +import pydash as pyd +from pydash.exceptions import InvalidMethod + +from ..helpers import UNSET, Unset +from .all_funcs import AllFuncs + + +__all__ = ( + "chain", + "tap", +) + +ValueT_co = t.TypeVar("ValueT_co", covariant=True) +T = t.TypeVar("T") +T2 = t.TypeVar("T2") + + +class Chain(AllFuncs, t.Generic[ValueT_co]): + """Enables chaining of :attr:`module` functions.""" + + #: Object that contains attribute references to available methods. + module = pyd + invalid_method_exception = InvalidMethod + + def __init__(self, value: t.Union[ValueT_co, Unset] = UNSET) -> None: + self._value = value + + def _wrap(self, func) -> "ChainWrapper[t.Union[ValueT_co, Unset]]": + """Implement `AllFuncs` interface.""" + return ChainWrapper(self._value, func) + + def value(self) -> ValueT_co: + """ + Return current value of the chain operations. + + Returns: + Current value of chain operations. + """ + return self(self._value) + + def to_string(self) -> str: + """ + Return current value as string. + + Returns: + Current value of chain operations casted to ``str``. + """ + return self.module.to_string(self.value()) + + def commit(self) -> "Chain[ValueT_co]": + """ + Executes the chained sequence and returns the wrapped result. + + Returns: + New instance of :class:`Chain` with resolved value from + previous :class:`Class`. + """ + return Chain(self.value()) + + def plant(self, value: t.Any) -> "Chain[ValueT_co]": + """ + Return a clone of the chained sequence planting `value` as the wrapped value. + + Args: + value: Value to plant as the initial chain value. + """ + # pylint: disable=no-member,maybe-no-member + wrapper = self._value + wrappers = [] + + if hasattr(wrapper, "_value"): + wrappers = [wrapper] + + while isinstance(wrapper._value, ChainWrapper): + wrapper = wrapper._value # type: ignore + wrappers.insert(0, wrapper) + + clone: Chain[t.Any] = Chain(value) + + for wrap in wrappers: + clone = ChainWrapper(clone._value, wrap.method)( # type: ignore + *wrap.args, # type: ignore + **wrap.kwargs, # type: ignore + ) + + return clone + + def __call__(self, value) -> ValueT_co: + """ + Return result of passing `value` through chained methods. + + Args: + value: Initial value to pass through chained methods. + + Returns: + Result of method chain evaluation of `value`. + """ + if isinstance(self._value, ChainWrapper): + # pylint: disable=maybe-no-member + value = self._value.unwrap(value) + return value + + +class ChainWrapper(t.Generic[ValueT_co]): + """Wrap :class:`Chain` method call within a :class:`ChainWrapper` context.""" + + def __init__(self, value: ValueT_co, method) -> None: + self._value = value + self.method = method + self.args = () + self.kwargs: t.Dict[t.Any, t.Any] = {} + + def _generate(self): + """Generate a copy of this instance.""" + # pylint: disable=attribute-defined-outside-init + new = self.__class__.__new__(self.__class__) + new.__dict__ = self.__dict__.copy() + return new + + def unwrap(self, value=UNSET): + """ + Execute :meth:`method` with :attr:`_value`, :attr:`args`, and :attr:`kwargs`. + + If :attr:`_value` is an instance of :class:`ChainWrapper`, then unwrap it before calling + :attr:`method`. + """ + # Generate a copy of ourself so that we don't modify the chain wrapper + # _value directly. This way if we are late passing a value, we don't + # "freeze" the chain wrapper value when a value is first passed. + # Otherwise, we'd locked the chain wrapper value permanently and not be + # able to reuse it. + wrapper = self._generate() + + if isinstance(wrapper._value, ChainWrapper): + # pylint: disable=no-member,maybe-no-member + wrapper._value = wrapper._value.unwrap(value) + elif not isinstance(value, ChainWrapper) and value is not UNSET: + # Override wrapper's initial value. + wrapper._value = value + + if wrapper._value is not UNSET: + value = wrapper._value + + return wrapper.method(value, *wrapper.args, **wrapper.kwargs) + + def __call__(self, *args, **kwargs): + """ + Invoke the :attr:`method` with :attr:`value` as the first argument and return a new + :class:`Chain` object with the return value. + + Returns: + New instance of :class:`Chain` with the results of :attr:`method` passed in as + value. + """ + self.args = args + self.kwargs = kwargs + return Chain(self) + + +class _Dash(object): + """Class that provides attribute access to valid :mod:`pydash` methods and callable access to + :mod:`pydash` method chaining.""" + + def __getattr__(self, attr): + """Proxy to :meth:`Chain.get_method`.""" + return Chain.get_method(attr) + + def __call__(self, value: t.Union[ValueT_co, Unset] = UNSET) -> Chain[ValueT_co]: + """Return a new instance of :class:`Chain` with `value` as the seed.""" + return Chain(value) + + +def chain(value: t.Union[T, Unset] = UNSET) -> Chain[T]: + """ + Creates a :class:`Chain` object which wraps the given value to enable intuitive method chaining. + Chaining is lazy and won't compute a final value until :meth:`Chain.value` is called. + + Args: + value: Value to initialize chain operations with. + + Returns: + Instance of :class:`Chain` initialized with `value`. + + Example: + + >>> chain([1, 2, 3, 4]).map(lambda x: x * 2).sum().value() + 20 + >>> chain().map(lambda x: x * 2).sum()([1, 2, 3, 4]) + 20 + + >>> summer = chain([1, 2, 3, 4]).sum() + >>> new_summer = summer.plant([1, 2]) + >>> new_summer.value() + 3 + >>> summer.value() + 10 + + >>> def echo(item): + ... print(item) + >>> summer = chain([1, 2, 3, 4]).for_each(echo).sum() + >>> committed = summer.commit() + 1 + 2 + 3 + 4 + >>> committed.value() + 10 + >>> summer.value() + 1 + 2 + 3 + 4 + 10 + + .. versionadded:: 1.0.0 + + .. versionchanged:: 2.0.0 + Made chaining lazy. + + .. versionchanged:: 3.0.0 + + - Added support for late passing of `value`. + - Added :meth:`Chain.plant` for replacing initial chain value. + - Added :meth:`Chain.commit` for returning a new :class:`Chain` instance initialized with + the results from calling :meth:`Chain.value`. + """ + return Chain(value) + + +def tap(value: T, interceptor: t.Callable[[T], t.Any]) -> T: + """ + Invokes `interceptor` with the `value` as the first argument and then returns `value`. The + purpose of this method is to "tap into" a method chain in order to perform operations on + intermediate results within the chain. + + Args: + value: Current value of chain operation. + interceptor: Function called on `value`. + + Returns: + `value` after `interceptor` call. + + Example: + + >>> data = [] + >>> def log(value): + ... data.append(value) + >>> chain([1, 2, 3, 4]).map(lambda x: x * 2).tap(log).value() + [2, 4, 6, 8] + >>> data + [[2, 4, 6, 8]] + + .. versionadded:: 1.0.0 + """ + interceptor(value) + return value |