about summary refs log tree commit diff
path: root/.venv/lib/python3.12/site-packages/pydash/helpers.py
diff options
context:
space:
mode:
authorS. Solomon Darnell2025-03-28 21:52:21 -0500
committerS. Solomon Darnell2025-03-28 21:52:21 -0500
commit4a52a71956a8d46fcb7294ac71734504bb09bcc2 (patch)
treeee3dc5af3b6313e921cd920906356f5d4febc4ed /.venv/lib/python3.12/site-packages/pydash/helpers.py
parentcc961e04ba734dd72309fb548a2f97d67d578813 (diff)
downloadgn-ai-4a52a71956a8d46fcb7294ac71734504bb09bcc2.tar.gz
two version of R2R are here HEAD master
Diffstat (limited to '.venv/lib/python3.12/site-packages/pydash/helpers.py')
-rw-r--r--.venv/lib/python3.12/site-packages/pydash/helpers.py327
1 files changed, 327 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/pydash/helpers.py b/.venv/lib/python3.12/site-packages/pydash/helpers.py
new file mode 100644
index 00000000..ee9accb1
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pydash/helpers.py
@@ -0,0 +1,327 @@
+"""Generic utility methods not part of main API."""
+
+from __future__ import annotations
+
+import builtins
+from collections.abc import Hashable, Iterable, Mapping, Sequence
+from decimal import Decimal
+from functools import wraps
+import inspect
+import operator
+import warnings
+
+import pydash as pyd
+
+
+#: Singleton object that differentiates between an explicit ``None`` value and an unset value.
+#: As a class so it has its own type
+class Unset: ...
+
+
+UNSET = Unset()
+
+#: Tuple of number types.
+NUMBER_TYPES = (int, float, Decimal)
+
+#: Dictionary of builtins with keys as the builtin function and values as the string name.
+BUILTINS = {value: key for key, value in builtins.__dict__.items() if isinstance(value, Hashable)}
+
+#: Object keys that are restricted from access via path access.
+RESTRICTED_KEYS = ("__globals__", "__builtins__")
+
+#: Inspect signature parameter kinds that correspond to positional arguments.
+POSITIONAL_PARAMETERS = (
+    inspect.Parameter.VAR_POSITIONAL,
+    inspect.Parameter.POSITIONAL_ONLY,
+    inspect.Parameter.POSITIONAL_OR_KEYWORD,
+)
+
+
+def callit(iteratee, *args, **kwargs):
+    """Inspect argspec of `iteratee` function and only pass the supported arguments when calling
+    it."""
+    maxargs = len(args)
+    argcount = kwargs["argcount"] if "argcount" in kwargs else getargcount(iteratee, maxargs)
+    argstop = min([maxargs, argcount])
+
+    return iteratee(*args[:argstop])
+
+
+def getargcount(iteratee, maxargs):
+    """Return argument count of iteratee function."""
+    if hasattr(iteratee, "_argcount"):
+        # Optimization feature where argcount of iteratee is known and properly
+        # set by initiator.
+        # It should always be right, but it can be `None` for the function wrappers
+        # in `pydash.function` as the wrapped functions are out of our control and
+        # can support an unknown number of arguments.
+        argcount = iteratee._argcount
+        return argcount if argcount is not None else maxargs
+
+    if isinstance(iteratee, type) or pyd.is_builtin(iteratee):
+        # Only pass single argument to type iteratees or builtins.
+        argcount = 1
+    else:
+        argcount = 1
+
+        try:
+            argcount = _getargcount(iteratee, maxargs)
+        except TypeError:  # pragma: no cover
+            pass
+
+    return argcount
+
+
+def _getargcount(iteratee, maxargs):
+    argcount = None
+
+    try:
+        # PY2: inspect.signature was added in Python 3.
+        # Try to use inspect.signature when possible since it works better for our purpose of
+        # getting the iteratee argcount since it takes into account the "self" argument in callable
+        # classes.
+        sig = inspect.signature(iteratee)
+    except (TypeError, ValueError, AttributeError):  # pragma: no cover
+        pass
+    else:
+        # VAR_POSITIONAL corresponds to *args so we only want to count parameters if there isn't a
+        # catch-all for positional args.
+        params = list(sig.parameters.values())
+        if not any(param.kind == inspect.Parameter.VAR_POSITIONAL for param in params):
+            positional_params = [p for p in params if p.kind in POSITIONAL_PARAMETERS]
+            argcount = len(positional_params)
+
+    if argcount is None:
+        # Signatures were added these operator methods in Python 3.12.3 and 3.11.9 but their
+        # instance objects are incorrectly reported as accepting varargs when they only accept a
+        # single argument.
+        if isinstance(iteratee, (operator.itemgetter, operator.attrgetter, operator.methodcaller)):
+            argcount = 1
+        else:
+            argspec = inspect.getfullargspec(iteratee)
+            if argspec and not argspec.varargs:  # pragma: no cover
+                # Use inspected arg count.
+                argcount = len(argspec.args)
+
+    if argcount is None:
+        # Assume all args are handleable.
+        argcount = maxargs
+
+    return argcount
+
+
+def iteriteratee(obj, iteratee=None, reverse=False):
+    """Return iterative iteratee based on collection type."""
+    if iteratee is None:
+        cbk = pyd.identity
+        argcount = 1
+    else:
+        cbk = pyd.iteratee(iteratee)
+        argcount = getargcount(cbk, maxargs=3)
+
+    items = iterator(obj)
+
+    if reverse:
+        items = reversed(tuple(items))
+
+    for key, item in items:
+        yield callit(cbk, item, key, obj, argcount=argcount), item, key, obj
+
+
+def iterator(obj):
+    """Return iterative based on object type."""
+    if isinstance(obj, Mapping):
+        return obj.items()
+    elif hasattr(obj, "iteritems"):
+        return obj.iteritems()  # noqa: B301
+    elif hasattr(obj, "items"):
+        return iter(obj.items())
+    elif isinstance(obj, Iterable):
+        return enumerate(obj)
+    else:
+        return getattr(obj, "__dict__", {}).items()
+
+
+def base_get(obj, key, default=UNSET):
+    """
+    Safely get an item by `key` from a sequence or mapping object when `default` provided.
+
+    Args:
+        obj: Sequence or mapping to retrieve item from.
+        key: Key or index identifying which item to retrieve.
+        default: Default value to return if `key` not found in `obj`.
+
+    Returns:
+        `obj[key]`, `obj.key`, or `default`.
+
+    Raises:
+        KeyError: If `obj` is missing key, index, or attribute and no default value provided.
+    """
+    if isinstance(obj, dict):
+        value = _base_get_dict(obj, key, default=default)
+    elif not isinstance(obj, (Mapping, Sequence)) or (
+        isinstance(obj, tuple) and hasattr(obj, "_fields")
+    ):
+        # Don't use getattr for dict/list objects since we don't want class methods/attributes
+        # returned for them but do allow getattr for namedtuple.
+        value = _base_get_object(obj, key, default=default)
+    else:
+        value = _base_get_item(obj, key, default=default)
+
+    if value is UNSET:
+        # Raise if there's no default provided.
+        raise KeyError(f'Object "{repr(obj)}" does not have key "{key}"')
+
+    return value
+
+
+def _base_get_dict(obj, key, default=UNSET):
+    value = obj.get(key, UNSET)
+    if value is UNSET:
+        value = default
+        if not isinstance(key, int):
+            # Try integer key fallback.
+            try:
+                value = obj.get(int(key), default)
+            except Exception:
+                pass
+    return value
+
+
+def _base_get_item(obj, key, default=UNSET):
+    try:
+        return obj[key]
+    except Exception:
+        pass
+
+    if not isinstance(key, int):
+        try:
+            return obj[int(key)]
+        except Exception:
+            pass
+
+    return default
+
+
+def _base_get_object(obj, key, default=UNSET):
+    value = _base_get_item(obj, key, default=UNSET)
+    if value is UNSET:
+        _raise_if_restricted_key(key)
+        value = default
+        try:
+            value = getattr(obj, key)
+        except Exception:
+            pass
+    return value
+
+
+def _raise_if_restricted_key(key):
+    # Prevent access to restricted keys for security reasons.
+    if key in RESTRICTED_KEYS:
+        raise KeyError(f"access to restricted key {key!r} is not allowed")
+
+
+def base_set(obj, key, value, allow_override=True):
+    """
+    Set an object's `key` to `value`. If `obj` is a ``list`` and the `key` is the next available
+    index position, append to list; otherwise, pad the list of ``None`` and then append to the list.
+
+    Args:
+        obj: Object to assign value to.
+        key: Key or index to assign to.
+        value: Value to assign.
+        allow_override: Whether to allow overriding a previously set key.
+    """
+    if isinstance(obj, dict):
+        if allow_override or key not in obj:
+            obj[key] = value
+    elif isinstance(obj, list):
+        key = int(key)
+
+        if key < len(obj):
+            if allow_override:
+                obj[key] = value
+        else:
+            if key > len(obj):
+                # Pad list object with None values up to the index key, so we can append the value
+                # into the key index.
+                obj[:] = (obj + [None] * key)[:key]
+            obj.append(value)
+    elif (allow_override or not hasattr(obj, key)) and obj is not None:
+        _raise_if_restricted_key(key)
+        setattr(obj, key, value)
+
+    return obj
+
+
+def cmp(a, b):  # pragma: no cover
+    """
+    Replacement for built-in function ``cmp`` that was removed in Python 3.
+
+    Note: Mainly used for comparison during sorting.
+    """
+    if a is None and b is None:
+        return 0
+    elif a is None:
+        return -1
+    elif b is None:
+        return 1
+    return (a > b) - (a < b)
+
+
+def parse_iteratee(iteratee_keyword, *args, **kwargs):
+    """Try to find iteratee function passed in either as a keyword argument or as the last
+    positional argument in `args`."""
+    iteratee = kwargs.get(iteratee_keyword)
+    last_arg = args[-1]
+
+    if iteratee is None and (
+        callable(last_arg) or isinstance(last_arg, (dict, str)) or last_arg is None
+    ):
+        iteratee = last_arg
+        args = args[:-1]
+
+    return iteratee, args
+
+
+class iterator_with_default(object):
+    """A wrapper around an iterator object that provides a default."""
+
+    def __init__(self, collection, default):
+        self.iter = iter(collection)
+        self.default = default
+
+    def __iter__(self):
+        return self
+
+    def next_default(self):
+        ret = self.default
+        self.default = UNSET
+        return ret
+
+    def __next__(self):
+        ret = next(self.iter, self.next_default())
+        if ret is UNSET:
+            raise StopIteration
+        return ret
+
+    next = __next__
+
+
+def deprecated(func):  # pragma: no cover
+    """
+    This is a decorator which can be used to mark functions as deprecated.
+
+    It will result in a warning being emitted when the function is used.
+    """
+
+    @wraps(func)
+    def wrapper(*args, **kwargs):
+        warnings.warn(
+            f"Call to deprecated function {func.__name__}.",
+            category=DeprecationWarning,
+            stacklevel=3,
+        )
+        return func(*args, **kwargs)
+
+    return wrapper