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/objects.py | |
parent | cc961e04ba734dd72309fb548a2f97d67d578813 (diff) | |
download | gn-ai-master.tar.gz |
Diffstat (limited to '.venv/lib/python3.12/site-packages/pydash/objects.py')
-rw-r--r-- | .venv/lib/python3.12/site-packages/pydash/objects.py | 2633 |
1 files changed, 2633 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/pydash/objects.py b/.venv/lib/python3.12/site-packages/pydash/objects.py new file mode 100644 index 00000000..442cf722 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydash/objects.py @@ -0,0 +1,2633 @@ +""" +Functions that operate on lists, dicts, and other objects. + +.. versionadded:: 1.0.0 +""" + +from __future__ import annotations + +import copy +from functools import partial +import math +import re +import typing as t + +import pydash as pyd + +from .helpers import UNSET, Unset, base_get, base_set, callit, getargcount, iterator, iteriteratee +from .types import IterateeObjT, PathT +from .utilities import PathToken, to_path, to_path_tokens + + +if t.TYPE_CHECKING: + from _typeshed import SupportsRichComparisonT # pragma: no cover + +__all__ = ( + "apply", + "apply_catch", + "apply_if", + "apply_if_not_none", + "assign", + "assign_with", + "callables", + "clone", + "clone_deep", + "clone_deep_with", + "clone_with", + "defaults", + "defaults_deep", + "find_key", + "find_last_key", + "for_in", + "for_in_right", + "get", + "has", + "invert", + "invert_by", + "invoke", + "keys", + "map_keys", + "map_values", + "map_values_deep", + "merge", + "merge_with", + "omit", + "omit_by", + "parse_int", + "pick", + "pick_by", + "rename_keys", + "set_", + "set_with", + "to_boolean", + "to_dict", + "to_integer", + "to_list", + "to_number", + "to_pairs", + "to_string", + "transform", + "unset", + "update", + "update_with", + "values", +) + +T = t.TypeVar("T") +T2 = t.TypeVar("T2") +T3 = t.TypeVar("T3") +T4 = t.TypeVar("T4") +T5 = t.TypeVar("T5") + + +@t.overload +def assign( + obj: t.Mapping[T, T2], *sources: t.Mapping[T3, T4] +) -> t.Dict[t.Union[T, T3], t.Union[T2, T4]]: ... + + +@t.overload +def assign( + obj: t.Union[t.Tuple[T, ...], t.List[T]], *sources: t.Mapping[int, T2] +) -> t.List[t.Union[T, T2]]: ... + + +def assign(obj, *sources) -> t.Union[t.List[t.Any], t.Dict[t.Any, t.Any]]: + """ + Assigns properties of source object(s) to the destination object. + + Args: + obj: Destination object whose properties will be modified. + sources: Source objects to assign to `obj`. + + Returns: + Modified `obj`. + + Warning: + `obj` is modified in place. + + Example: + + >>> obj = {} + >>> obj2 = assign(obj, {"a": 1}, {"b": 2}, {"c": 3}) + >>> obj == {"a": 1, "b": 2, "c": 3} + True + >>> obj is obj2 + True + + .. versionadded:: 1.0.0 + + .. versionchanged:: 2.3.2 + Apply :func:`clone_deep` to each `source` before assigning to `obj`. + + .. versionchanged:: 3.0.0 + Allow iteratees to accept partial arguments. + + .. versionchanged:: 3.4.4 + Shallow copy each `source` instead of deep copying. + + .. versionchanged:: 4.0.0 + + - Moved `iteratee` argument to :func:`assign_with`. + - Removed alias ``extend``. + """ + return assign_with(obj, *sources) # type: ignore + + +@t.overload +def assign_with( + obj: t.Mapping[T, T2], + *sources: t.Mapping[T3, t.Any], + customizer: t.Callable[[t.Union[T2, None]], T5], +) -> t.Dict[t.Union[T, T3], t.Union[T2, T5]]: ... + + +@t.overload +def assign_with( + obj: t.Mapping[T, T2], + *sources: t.Mapping[T3, T4], + customizer: t.Callable[[t.Union[T2, None], T4], T5], +) -> t.Dict[t.Union[T, T3], t.Union[T2, T5]]: ... + + +@t.overload +def assign_with( + obj: t.Mapping[T, T2], + *sources: t.Mapping[T3, T4], + customizer: t.Callable[[t.Union[T2, None], T4, T3], T5], +) -> t.Dict[t.Union[T, T3], t.Union[T2, T5]]: ... + + +@t.overload +def assign_with( + obj: t.Mapping[T, T2], + *sources: t.Mapping[T3, T4], + customizer: t.Callable[[t.Union[T2, None], T4, T3, t.Dict[T, T2]], T5], +) -> t.Dict[t.Union[T, T3], t.Union[T2, T5]]: ... + + +@t.overload +def assign_with( + obj: t.Mapping[T, T2], + *sources: t.Mapping[T3, T4], + customizer: t.Callable[[t.Union[T2, None], T4, T3, t.Dict[T, T2], t.Dict[T3, T4]], T5], +) -> t.Dict[t.Union[T, T3], t.Union[T2, T5]]: ... + + +@t.overload +def assign_with( + obj: t.Mapping[T, T2], *sources: t.Mapping[T3, T4], customizer: None = None +) -> t.Dict[t.Union[T, T3], t.Union[T2, T4]]: ... + + +def assign_with(obj, *sources, customizer=None): + """ + This method is like :func:`assign` except that it accepts customizer which is invoked to produce + the assigned values. If customizer returns ``None``, assignment is handled by the method + instead. The customizer is invoked with five arguments: ``(obj_value, src_value, key, obj, + source)``. + + Args: + obj: Destination object whose properties will be modified. + sources: Source objects to assign to `obj`. + + Keyword Args: + customizer: Customizer applied per iteration. + + Returns: + Modified `obj`. + + Warning: + `obj` is modified in place. + + Example: + + >>> customizer = lambda o, s: s if o is None else o + >>> results = assign_with({"a": 1}, {"b": 2}, {"a": 3}, customizer) + >>> results == {"a": 1, "b": 2} + True + + .. versionadded:: 4.0.0 + """ + sources = list(sources) + + if customizer is None and callable(sources[-1]): + customizer = sources.pop() + + if customizer is not None: + argcount = getargcount(customizer, maxargs=5) + else: + argcount = None + + for source in sources: + source = source.copy() + + for key, value in source.items(): + if customizer: + val = callit(customizer, obj.get(key), value, key, obj, source, argcount=argcount) + if val is not None: + value = val + + obj[key] = value + + return obj + + +@t.overload +def callables( + obj: t.Mapping["SupportsRichComparisonT", t.Any], +) -> t.List["SupportsRichComparisonT"]: ... + + +@t.overload +def callables(obj: t.Iterable[T]) -> t.List[T]: ... + + +def callables(obj): + """ + Creates a sorted list of keys of an object that are callable. + + Args: + obj: Object to inspect. + + Returns: + All keys whose values are callable. + + Example: + + >>> callables({"a": 1, "b": lambda: 2, "c": lambda: 3}) + ['b', 'c'] + + .. versionadded:: 1.0.0 + + .. versionchanged:: 2.0.0 + Renamed ``functions`` to ``callables``. + + .. versionchanged:: 4.0.0 + Removed alias ``methods``. + """ + return sorted(key for key, value in iterator(obj) if callable(value)) + + +def clone(value: T) -> T: + """ + Creates a clone of `value`. + + Args: + value: Object to clone. + + Example: + + >>> x = {"a": 1, "b": 2, "c": {"d": 3}} + >>> y = clone(x) + >>> y == y + True + >>> x is y + False + >>> x["c"] is y["c"] + True + + Returns: + Cloned object. + + .. versionadded:: 1.0.0 + + .. versionchanged:: 4.0.0 + Moved 'iteratee' parameter to :func:`clone_with`. + """ + return base_clone(value) + + +@t.overload +def clone_with( + value: t.Mapping[T, T2], customizer: t.Callable[[T2, T, t.Mapping[T, T2]], T3] +) -> t.Dict[T, t.Union[T2, T3]]: ... + + +@t.overload +def clone_with( + value: t.Mapping[T, T2], customizer: t.Callable[[T2, T], T3] +) -> t.Dict[T, t.Union[T2, T3]]: ... + + +@t.overload +def clone_with( + value: t.Mapping[T, T2], customizer: t.Callable[[T2], T3] +) -> t.Dict[T, t.Union[T2, T3]]: ... + + +@t.overload +def clone_with( + value: t.List[T], customizer: t.Callable[[T, int, t.List[T]], T2] +) -> t.List[t.Union[T, T2]]: ... + + +@t.overload +def clone_with( + value: t.List[T], customizer: t.Callable[[T, int], T2] +) -> t.List[t.Union[T, T2]]: ... + + +@t.overload +def clone_with(value: t.List[T], customizer: t.Callable[[T], T2]) -> t.List[t.Union[T, T2]]: ... + + +@t.overload +def clone_with(value: T, customizer: None = None) -> T: ... + + +@t.overload +def clone_with(value: t.Any, customizer: t.Callable[..., t.Any]) -> t.Any: ... + + +def clone_with(value, customizer=None): + """ + This method is like :func:`clone` except that it accepts customizer which is invoked to produce + the cloned value. If customizer returns ``None``, cloning is handled by the method instead. The + customizer is invoked with up to three arguments: ``(value, index|key, object)``. + + Args: + value: Object to clone. + customizer: Function to customize cloning. + + Returns: + Cloned object. + + Example: + + >>> x = {"a": 1, "b": 2, "c": {"d": 3}} + >>> cbk = lambda v, k: v + 2 if isinstance(v, int) and k else None + >>> y = clone_with(x, cbk) + >>> y == {"a": 3, "b": 4, "c": {"d": 3}} + True + """ + return base_clone(value, customizer=customizer) + + +def clone_deep(value: T) -> T: + """ + Creates a deep clone of `value`. If an iteratee is provided it will be executed to produce the + cloned values. + + Args: + value: Object to clone. + + Returns: + Cloned object. + + Example: + + >>> x = {"a": 1, "b": 2, "c": {"d": 3}} + >>> y = clone_deep(x) + >>> y == y + True + >>> x is y + False + >>> x["c"] is y["c"] + False + + .. versionadded:: 1.0.0 + + .. versionchanged:: 4.0.0 + Moved 'iteratee' parameter to :func:`clone_deep_with`. + """ + return base_clone(value, is_deep=True) + + +@t.overload +def clone_deep_with( + value: t.Mapping[T, T2], customizer: t.Callable[[T2, T, t.Mapping[T, T2]], T3] +) -> t.Dict[T, t.Union[T2, T3]]: ... + + +@t.overload +def clone_deep_with( + value: t.Mapping[T, T2], customizer: t.Callable[[T2, T], T3] +) -> t.Dict[T, t.Union[T2, T3]]: ... + + +@t.overload +def clone_deep_with( + value: t.Mapping[T, T2], customizer: t.Callable[[T2], T3] +) -> t.Dict[T, t.Union[T2, T3]]: ... + + +@t.overload +def clone_deep_with( + value: t.List[T], customizer: t.Callable[[T, int, t.List[T]], T2] +) -> t.List[t.Union[T, T2]]: ... + + +@t.overload +def clone_deep_with( + value: t.List[T], customizer: t.Callable[[T, int], T2] +) -> t.List[t.Union[T, T2]]: ... + + +@t.overload +def clone_deep_with( + value: t.List[T], customizer: t.Callable[[T], T2] +) -> t.List[t.Union[T, T2]]: ... + + +@t.overload +def clone_deep_with(value: T, customizer: None = None) -> T: ... + + +@t.overload +def clone_deep_with(value: t.Any, customizer: t.Callable[..., t.Any]) -> t.Any: ... + + +def clone_deep_with(value, customizer=None): + """ + This method is like :func:`clone_with` except that it recursively clones `value`. + + Args: + value: Object to clone. + customizer: Function to customize cloning. + + Returns: + Cloned object. + """ + return base_clone(value, is_deep=True, customizer=customizer) + + +def defaults( + obj: t.Dict[T, T2], *sources: t.Dict[T3, T4] +) -> t.Dict[t.Union[T, T3], t.Union[T2, T4]]: + """ + Assigns properties of source object(s) to the destination object for all destination properties + that resolve to undefined. + + Args: + obj: Destination object whose properties will be modified. + sources: Source objects to assign to `obj`. + + Returns: + Modified `obj`. + + Warning: + `obj` is modified in place. + + Example: + + >>> obj = {"a": 1} + >>> obj2 = defaults(obj, {"b": 2}, {"c": 3}, {"a": 4}) + >>> obj is obj2 + True + >>> obj == {"a": 1, "b": 2, "c": 3} + True + + .. versionadded:: 1.0.0 + """ + for source in sources: + for key, value in source.items(): + obj.setdefault(key, value) # type: ignore + + return obj # type: ignore + + +def defaults_deep( + obj: t.Dict[T, T2], *sources: t.Dict[T3, T4] +) -> t.Dict[t.Union[T, T3], t.Union[T2, T4]]: + """ + This method is like :func:`defaults` except that it recursively assigns default properties. + + Args: + obj: Destination object whose properties will be modified. + sources: Source objects to assign to `obj`. + + Returns: + Modified `obj`. + + Warning: + `obj` is modified in place. + + Example: + + >>> obj = {"a": {"b": 1}} + >>> obj2 = defaults_deep(obj, {"a": {"b": 2, "c": 3}}) + >>> obj is obj2 + True + >>> obj == {"a": {"b": 1, "c": 3}} + True + + .. versionadded:: 3.3.0 + """ + + def setter(obj, key, value): + if hasattr(obj, "setdefault"): + obj.setdefault(key, value) + + return merge_with(obj, *sources, _setter=setter) + + +@t.overload +def find_key( + obj: t.Mapping[T, T2], predicate: t.Callable[[T2, T, t.Dict[T, T2]], t.Any] +) -> t.Union[T, None]: ... + + +@t.overload +def find_key(obj: t.Mapping[T, T2], predicate: t.Callable[[T2, T], t.Any]) -> t.Union[T, None]: ... + + +@t.overload +def find_key(obj: t.Mapping[T, T2], predicate: t.Callable[[T2], t.Any]) -> t.Union[T, None]: ... + + +@t.overload +def find_key(obj: t.Mapping[T, t.Any], predicate: None = None) -> t.Union[T, None]: ... + + +@t.overload +def find_key( + collection: t.Iterable[T], iteratee: t.Callable[[T, int, t.List[T]], t.Any] +) -> t.Union[int, None]: ... + + +@t.overload +def find_key( + collection: t.Iterable[T], iteratee: t.Callable[[T, int], t.Any] +) -> t.Union[int, None]: ... + + +@t.overload +def find_key(collection: t.Iterable[T], iteratee: t.Callable[[T], t.Any]) -> t.Union[int, None]: ... + + +@t.overload +def find_key(collection: t.Iterable[t.Any], iteratee: None = None) -> t.Union[int, None]: ... + + +def find_key(obj, predicate=None): + """ + This method is like :func:`pydash.arrays.find_index` except that it returns the key of the first + element that passes the predicate check, instead of the element itself. + + Args: + obj: Object to search. + predicate: Predicate applied per iteration. + + Returns: + Found key or ``None``. + + Example: + + >>> find_key({"a": 1, "b": 2, "c": 3}, lambda x: x == 1) + 'a' + >>> find_key([1, 2, 3, 4], lambda x: x == 1) + 0 + + .. versionadded:: 1.0.0 + """ + for result, _, key, _ in iteriteratee(obj, predicate): + if result: + return key + + +@t.overload +def find_last_key( + obj: t.Mapping[T, T2], predicate: t.Callable[[T2, T, t.Dict[T, T2]], t.Any] +) -> t.Union[T, None]: ... + + +@t.overload +def find_last_key( + obj: t.Mapping[T, T2], predicate: t.Callable[[T2, T], t.Any] +) -> t.Union[T, None]: ... + + +@t.overload +def find_last_key( + obj: t.Mapping[T, T2], predicate: t.Callable[[T2], t.Any] +) -> t.Union[T, None]: ... + + +@t.overload +def find_last_key(obj: t.Mapping[T, t.Any], predicate: None = None) -> t.Union[T, None]: ... + + +@t.overload +def find_last_key( + collection: t.Iterable[T], iteratee: t.Callable[[T, int, t.List[T]], t.Any] +) -> t.Union[int, None]: ... + + +@t.overload +def find_last_key( + collection: t.Iterable[T], iteratee: t.Callable[[T, int], t.Any] +) -> t.Union[int, None]: ... + + +@t.overload +def find_last_key( + collection: t.Iterable[T], iteratee: t.Callable[[T], t.Any] +) -> t.Union[int, None]: ... + + +@t.overload +def find_last_key(collection: t.Iterable[t.Any], iteratee: None = None) -> t.Union[int, None]: ... + + +def find_last_key(obj, predicate=None): + """ + This method is like :func:`find_key` except that it iterates over elements of a collection in + the opposite order. + + Args: + obj: Object to search. + predicate: Predicate applied per iteration. + + Returns: + Found key or ``None``. + + Example: + + >>> find_last_key({"a": 1, "b": 2, "c": 3}, lambda x: x == 1) + 'a' + >>> find_last_key([1, 2, 3, 1], lambda x: x == 1) + 3 + + .. versionchanged:: 4.0.0 + Made into its own function (instead of an alias of ``find_key``) with + proper reverse find implementation. + """ + reversed_obj = reversed(list(iteriteratee(obj, predicate))) + for result, _, key, _ in reversed_obj: + if result: + return key + + +@t.overload +def for_in( + obj: t.Mapping[T, T2], iteratee: t.Callable[[T2, T, t.Dict[T, T2]], t.Any] +) -> t.Dict[T, T2]: ... + + +@t.overload +def for_in(obj: t.Mapping[T, T2], iteratee: t.Callable[[T2, T], t.Any]) -> t.Dict[T, T2]: ... + + +@t.overload +def for_in(obj: t.Mapping[T, T2], iteratee: t.Callable[[T2], t.Any]) -> t.Dict[T, T2]: ... + + +@t.overload +def for_in(obj: t.Mapping[T, T2], iteratee: None = None) -> t.Dict[T, T2]: ... + + +@t.overload +def for_in(obj: t.Sequence[T], iteratee: t.Callable[[T, int, t.List[T]], t.Any]) -> t.List[T]: ... + + +@t.overload +def for_in(obj: t.Sequence[T], iteratee: t.Callable[[T, int], t.Any]) -> t.List[T]: ... + + +@t.overload +def for_in(obj: t.Sequence[T], iteratee: t.Callable[[T], t.Any]) -> t.List[T]: ... + + +@t.overload +def for_in(obj: t.Sequence[T], iteratee: None = None) -> t.List[T]: ... + + +def for_in(obj, iteratee=None): + """ + Iterates over own and inherited enumerable properties of `obj`, executing `iteratee` for each + property. + + Args: + obj: Object to process. + iteratee: Iteratee applied per iteration. + + Returns: + `obj`. + + Example: + + >>> obj = {} + >>> def cb(v, k): + ... obj[k] = v + >>> results = for_in({"a": 1, "b": 2, "c": 3}, cb) + >>> results == {"a": 1, "b": 2, "c": 3} + True + >>> obj == {"a": 1, "b": 2, "c": 3} + True + + .. versionadded:: 1.0.0 + + .. versionchanged:: 4.0.0 + Removed alias ``for_own``. + """ + walk = (None for ret, _, _, _ in iteriteratee(obj, iteratee) if ret is False) + next(walk, None) + return obj + + +@t.overload +def for_in_right( + obj: t.Mapping[T, T2], iteratee: t.Callable[[T2, T, t.Dict[T, T2]], t.Any] +) -> t.Dict[T, T2]: ... + + +@t.overload +def for_in_right(obj: t.Mapping[T, T2], iteratee: t.Callable[[T2, T], t.Any]) -> t.Dict[T, T2]: ... + + +@t.overload +def for_in_right(obj: t.Mapping[T, T2], iteratee: t.Callable[[T2], t.Any]) -> t.Dict[T, T2]: ... + + +@t.overload +def for_in_right(obj: t.Mapping[T, T2], iteratee: None = None) -> t.Dict[T, T2]: ... + + +@t.overload +def for_in_right( + obj: t.Sequence[T], iteratee: t.Callable[[T, int, t.List[T]], t.Any] +) -> t.List[T]: ... + + +@t.overload +def for_in_right(obj: t.Sequence[T], iteratee: t.Callable[[T, int], t.Any]) -> t.List[T]: ... + + +@t.overload +def for_in_right(obj: t.Sequence[T], iteratee: t.Callable[[T], t.Any]) -> t.List[T]: ... + + +@t.overload +def for_in_right(obj: t.Sequence[T], iteratee: None = None) -> t.List[T]: ... + + +def for_in_right(obj, iteratee=None): + """ + This function is like :func:`for_in` except it iterates over the properties in reverse order. + + Args: + obj: Object to process. + iteratee: Iteratee applied per iteration. + + Returns: + `obj`. + + Example: + + >>> data = {"product": 1} + >>> def cb(v): + ... data["product"] *= v + >>> for_in_right([1, 2, 3, 4], cb) + [1, 2, 3, 4] + >>> data["product"] == 24 + True + + .. versionadded:: 1.0.0 + + .. versionchanged:: 4.0.0 + Removed alias ``for_own_right``. + """ + walk = (None for ret, _, _, _ in iteriteratee(obj, iteratee, reverse=True) if ret is False) + next(walk, None) + return obj + + +@t.overload +def get(obj: t.List[T], path: int, default: T2) -> t.Union[T, T2]: ... + + +@t.overload +def get(obj: t.List[T], path: int, default: None = None) -> t.Union[T, None]: ... + + +@t.overload +def get(obj: t.Any, path: PathT, default: t.Any = None) -> t.Any: ... + + +def get(obj: t.Any, path: PathT, default: t.Any = None) -> t.Any: + """ + Get the value at any depth of a nested object based on the path described by `path`. If path + doesn't exist, `default` is returned. + + Args: + obj: Object to process. + path: List or ``.`` delimited string of path describing path. + default: Default value to return if path doesn't exist. Defaults to ``None``. + + Returns: + Value of `obj` at path. + + Example: + + >>> get({}, "a.b.c") is None + True + >>> get({"a": {"b": {"c": [1, 2, 3, 4]}}}, "a.b.c[1]") + 2 + >>> get({"a": {"b": {"c": [1, 2, 3, 4]}}}, "a.b.c.1") + 2 + >>> get({"a": {"b": [0, {"c": [1, 2]}]}}, "a.b.1.c.1") + 2 + >>> get({"a": {"b": [0, {"c": [1, 2]}]}}, ["a", "b", 1, "c", 1]) + 2 + >>> get({"a": {"b": [0, {"c": [1, 2]}]}}, "a.b.1.c.2") is None + True + + .. versionadded:: 2.0.0 + + .. versionchanged:: 2.2.0 + Support escaping "." delimiter in single string path key. + + .. versionchanged:: 3.3.0 + + - Added :func:`get` as main definition and :func:`get_path` as alias. + - Made :func:`deep_get` an alias. + + .. versionchanged:: 3.4.7 + Fixed bug where an iterable default was iterated over instead of being returned when an + object path wasn't found. + + .. versionchanged:: 4.0.0 + + - Support attribute access on `obj` if item access fails. + - Removed aliases ``get_path`` and ``deep_get``. + + .. versionchanged:: 4.7.6 + Fixed bug where getattr is used on Mappings and Sequence in Python 3.5+ + """ + if default is UNSET: + # When NoValue given for default, then this method will raise if path is not present in obj. + sentinel = default + else: + # When a returnable default is given, use a sentinel value to detect when base_get() returns + # a default value for a missing path, so we can exit early from the loop and not mistakenly + # iterate over the default. + sentinel = object() + + for key in to_path(path): + obj = base_get(obj, key, default=sentinel) + + if obj is sentinel: + # Path doesn't exist so set return obj to the default. + obj = default + break + + return obj + + +def has(obj: t.Any, path: PathT) -> bool: + """ + Checks if `path` exists as a key of `obj`. + + Args: + obj: Object to test. + path: Path to test for. Can be a list of nested keys or a ``.`` delimited string of + path describing the path. + + Returns: + Whether `obj` has `path`. + + Example: + + >>> has([1, 2, 3], 1) + True + >>> has({"a": 1, "b": 2}, "b") + True + >>> has({"a": 1, "b": 2}, "c") + False + >>> has({"a": {"b": [0, {"c": [1, 2]}]}}, "a.b.1.c.1") + True + >>> has({"a": {"b": [0, {"c": [1, 2]}]}}, "a.b.1.c.2") + False + + .. versionadded:: 1.0.0 + + .. versionchanged:: 3.0.0 + Return ``False`` on ``ValueError`` when checking path. + + .. versionchanged:: 3.3.0 + + - Added :func:`deep_has` as alias. + - Added :func:`has_path` as alias. + + .. versionchanged:: 4.0.0 + Removed aliases ``deep_has`` and ``has_path``. + """ + try: + get(obj, path, default=UNSET) + exists = True + except (KeyError, IndexError, TypeError, ValueError): + exists = False + + return exists + + +@t.overload +def invert(obj: t.Mapping[T, T2]) -> t.Dict[T2, T]: ... + + +@t.overload +def invert(obj: t.Union[t.Iterator[T], t.Sequence[T]]) -> t.Dict[T, int]: ... + + +def invert(obj): + """ + Creates an object composed of the inverted keys and values of the given object. + + Args: + obj: Dict to invert. + + Returns: + Inverted dict. + + Example: + + >>> results = invert({"a": 1, "b": 2, "c": 3}) + >>> results == {1: "a", 2: "b", 3: "c"} + True + + Note: + Assumes `obj` values are hashable as ``dict`` keys. + + .. versionadded:: 1.0.0 + + .. versionchanged:: 2.0.0 + Added ``multivalue`` argument. + + .. versionchanged:: 4.0.0 + Moved ``multivalue=True`` functionality to :func:`invert_by`. + """ + return {value: key for key, value in iterator(obj)} + + +@t.overload +def invert_by(obj: t.Mapping[T, T2], iteratee: t.Callable[[T2], T3]) -> t.Dict[T3, t.List[T]]: ... + + +@t.overload +def invert_by(obj: t.Mapping[T, T2], iteratee: None = None) -> t.Dict[T2, t.List[T]]: ... + + +@t.overload +def invert_by( + obj: t.Union[t.Iterator[T], t.Sequence[T]], iteratee: t.Callable[[T], T2] +) -> t.Dict[T2, t.List[int]]: ... + + +@t.overload +def invert_by( + obj: t.Union[t.Iterator[T], t.Sequence[T]], iteratee: None = None +) -> t.Dict[T, t.List[int]]: ... + + +def invert_by(obj, iteratee=None): + """ + This method is like :func:`invert` except that the inverted object is generated from the results + of running each element of object through iteratee. The corresponding inverted value of each + inverted key is a list of keys responsible for generating the inverted value. The iteratee is + invoked with one argument: ``(value)``. + + Args: + obj: Object to invert. + iteratee: Iteratee applied per iteration. + + Returns: + Inverted dict. + + Example: + + >>> obj = {"a": 1, "b": 2, "c": 1} + >>> results = invert_by(obj) # {1: ['a', 'c'], 2: ['b']} + >>> set(results[1]) == set(["a", "c"]) + True + >>> set(results[2]) == set(["b"]) + True + >>> results2 = invert_by(obj, lambda value: "group" + str(value)) + >>> results2["group1"] == results[1] + True + >>> results2["group2"] == results[2] + True + + Note: + Assumes `obj` values are hashable as ``dict`` keys. + + .. versionadded:: 4.0.0 + """ + callback = pyd.iteratee(iteratee) + result = {} + + for key, value in iterator(obj): + result.setdefault(callback(value), []).append(key) + + return result + + +def invoke(obj: t.Any, path: PathT, *args: t.Any, **kwargs: t.Any) -> t.Any: + """ + Invokes the method at path of object. + + Args: + obj: The object to query. + path: The path of the method to invoke. + args: Arguments to pass to method call. + kwargs: Keyword arguments to pass to method call. + + Returns: + Result of the invoked method. + + Example: + + >>> obj = {"a": [{"b": {"c": [1, 2, 3, 4]}}]} + >>> invoke(obj, "a[0].b.c.pop", 1) + 2 + >>> obj + {'a': [{'b': {'c': [1, 3, 4]}}]} + + .. versionadded:: 1.0.0 + """ + paths = to_path(path) + target_path = pyd.initial(paths) + method_name = pyd.last(paths) + + try: + # potential error is caught + method = getattr(get(obj, target_path), method_name) # type: ignore + except AttributeError: + ret = None + else: + ret = method(*args, **kwargs) + + return ret + + +@t.overload +def keys(obj: t.Iterable[T]) -> t.List[T]: ... + + +@t.overload +def keys(obj: t.Any) -> t.List[t.Any]: ... + + +def keys(obj): + """ + Creates a list composed of the keys of `obj`. + + Args: + obj: Object to extract keys from. + + Returns: + List of keys. + + Example: + + >>> keys([1, 2, 3]) + [0, 1, 2] + >>> set(keys({"a": 1, "b": 2, "c": 3})) == set(["a", "b", "c"]) + True + + .. versionadded:: 1.0.0 + + .. versionchanged:: 1.1.0 + Added ``keys_in`` as alias. + + .. versionchanged:: 4.0.0 + Removed alias ``keys_in``. + """ + return [key for key, _ in iterator(obj)] + + +@t.overload +def map_keys( + obj: t.Mapping[T, T2], iteratee: t.Callable[[T2, T, t.Dict[T, T2]], T3] +) -> t.Dict[T3, T2]: ... + + +@t.overload +def map_keys(obj: t.Mapping[T, T2], iteratee: t.Callable[[T2, T], T3]) -> t.Dict[T3, T2]: ... + + +@t.overload +def map_keys(obj: t.Mapping[t.Any, T2], iteratee: t.Callable[[T2], T3]) -> t.Dict[T3, T2]: ... + + +@t.overload +def map_keys( + obj: t.Iterable[T], iteratee: t.Callable[[T, int, t.List[T]], T2] +) -> t.Dict[T2, T]: ... + + +@t.overload +def map_keys(obj: t.Iterable[T], iteratee: t.Callable[[T, int], T2]) -> t.Dict[T2, T]: ... + + +@t.overload +def map_keys(obj: t.Iterable[T], iteratee: t.Callable[[T], T2]) -> t.Dict[T2, T]: ... + + +@t.overload +def map_keys( + obj: t.Iterable[t.Any], iteratee: t.Union[IterateeObjT, None] = None +) -> t.Dict[t.Any, t.Any]: ... + + +def map_keys(obj, iteratee=None): + """ + The opposite of :func:`map_values`, this method creates an object with the same values as object + and keys generated by running each own enumerable string keyed property of object through + iteratee. The iteratee is invoked with three arguments: ``(value, key, object)``. + + Args: + obj: Object to map. + iteratee: Iteratee applied per iteration. + + Returns: + Results of running `obj` through `iteratee`. + + Example: + + >>> callback = lambda value, key: key * 2 + >>> results = map_keys({"a": 1, "b": 2, "c": 3}, callback) + >>> results == {"aa": 1, "bb": 2, "cc": 3} + True + + .. versionadded:: 3.3.0 + """ + return {result: value for result, value, _, _ in iteriteratee(obj, iteratee)} + + +@t.overload +def map_values( + obj: t.Mapping[T, T2], iteratee: t.Callable[[T2, T, t.Dict[T, T2]], T3] +) -> t.Dict[T, T3]: ... + + +@t.overload +def map_values(obj: t.Mapping[T, T2], iteratee: t.Callable[[T2, T], T3]) -> t.Dict[T, T3]: ... + + +@t.overload +def map_values(obj: t.Mapping[T, T2], iteratee: t.Callable[[T2], T3]) -> t.Dict[T, T3]: ... + + +@t.overload +def map_values( + obj: t.Iterable[T], iteratee: t.Callable[[T, int, t.List[T]], T2] +) -> t.Dict[T, T2]: ... + + +@t.overload +def map_values(obj: t.Iterable[T], iteratee: t.Callable[[T, int], T2]) -> t.Dict[T, T2]: ... + + +@t.overload +def map_values(obj: t.Iterable[T], iteratee: t.Callable[[T], T2]) -> t.Dict[T, T2]: ... + + +@t.overload +def map_values( + obj: t.Iterable[t.Any], iteratee: t.Union[IterateeObjT, None] = None +) -> t.Dict[t.Any, t.Any]: ... + + +def map_values(obj, iteratee=None): + """ + Creates an object with the same keys as object and values generated by running each string keyed + property of object through iteratee. The iteratee is invoked with three arguments: ``(value, + key, object)``. + + Args: + obj: Object to map. + iteratee: Iteratee applied per iteration. + + Returns: + Results of running `obj` through `iteratee`. + + Example: + + >>> results = map_values({"a": 1, "b": 2, "c": 3}, lambda x: x * 2) + >>> results == {"a": 2, "b": 4, "c": 6} + True + >>> results = map_values({"a": 1, "b": {"d": 4}, "c": 3}, {"d": 4}) + >>> results == {"a": False, "b": True, "c": False} + True + + .. versionadded:: 1.0.0 + """ + return {key: result for result, _, key, _ in iteriteratee(obj, iteratee)} + + +def map_values_deep( + obj: t.Iterable[t.Any], + iteratee: t.Union[t.Callable[..., t.Any], None] = None, + property_path: t.Any = UNSET, +) -> t.Any: + """ + Map all non-object values in `obj` with return values from `iteratee`. The iteratee is invoked + with two arguments: ``(obj_value, property_path)`` where ``property_path`` contains the list of + path keys corresponding to the path of ``obj_value``. + + Args: + obj: Object to map. + iteratee: Iteratee applied to each value. + property_path: Path key(s) to access. + + Returns: + The modified object. + + Warning: + `obj` is modified in place. + + Example: + + >>> x = {"a": 1, "b": {"c": 2}} + >>> y = map_values_deep(x, lambda val: val * 2) + >>> y == {"a": 2, "b": {"c": 4}} + True + >>> z = map_values_deep(x, lambda val, props: props) + >>> z == {"a": ["a"], "b": {"c": ["b", "c"]}} + True + + .. versionadded: 2.2.0 + + .. versionchanged:: 3.0.0 + Allow iteratees to accept partial arguments. + + .. versionchanged:: 4.0.0 + Renamed from ``deep_map_values`` to ``map_values_deep``. + """ + properties = to_path(property_path) + + if pyd.is_object(obj): + + def deep_iteratee(value, key): + return map_values_deep(value, iteratee, pyd.flatten([properties, key])) + + return assign(obj, map_values(obj, deep_iteratee)) # type: ignore + else: + return callit(iteratee, obj, properties) + + +def apply(obj: T, func: t.Callable[[T], T2]) -> T2: + """ + Returns the result of calling `func` on `obj`. Particularly useful to pass + `obj` through a function during a method chain. + + Args: + obj: Object to apply function to + func: Function called with `obj`. + + Returns: + Results of ``func(value)``. + + Example: + + >>> apply(5, lambda x: x * 2) + 10 + + .. versionadded:: 8.0.0 + """ + return func(obj) + + +def apply_if(obj: T, func: t.Callable[[T], T2], predicate: t.Callable[[T], bool]) -> t.Union[T, T2]: + """ + Apply `func` to `obj` if `predicate` returns `True`. + + Args: + obj: Object to apply `func` to. + func: Function to apply to `obj`. + predicate: Predicate applied to `obj`. + + Returns: + Result of applying `func` to `obj` or `obj`. + + Example: + + >>> apply_if(2, lambda x: x * 2, lambda x: x > 1) + 4 + >>> apply_if(2, lambda x: x * 2, lambda x: x < 1) + 2 + + .. versionadded:: 8.0.0 + """ + return func(obj) if predicate(obj) else obj + + +def apply_if_not_none(obj: t.Optional[T], func: t.Callable[[T], T2]) -> t.Optional[T2]: + """ + Apply `func` to `obj` if `obj` is not ``None``. + + Args: + obj: Object to apply `func` to. + func: Function to apply to `obj`. + + Returns: + Result of applying `func` to `obj` or ``None``. + + Example: + + >>> apply_if_not_none(2, lambda x: x * 2) + 4 + >>> apply_if_not_none(None, lambda x: x * 2) is None + True + + .. versionadded:: 8.0.0 + """ + return apply_if(obj, func, lambda x: x is not None) # type: ignore + + +@t.overload +def apply_catch( + obj: T, func: t.Callable[[T], T2], exceptions: t.Iterable[t.Type[Exception]], default: T3 +) -> t.Union[T2, T3]: ... + + +@t.overload +def apply_catch( + obj: T, + func: t.Callable[[T], T2], + exceptions: t.Iterable[t.Type[Exception]], + default: Unset = UNSET, +) -> t.Union[T, T2]: ... + + +def apply_catch(obj, func, exceptions, default=UNSET): + """ + Tries to apply `func` to `obj` if any of the exceptions in `excs` are raised, return `default` + or `obj` if not set. + + Args: + obj: Object to apply `func` to. + func: Function to apply to `obj`. + excs: Exceptions to catch. + default: Value to return if exception is raised. + + Returns: + Result of applying `func` to `obj` or ``default``. + + Example: + + >>> apply_catch(2, lambda x: x * 2, [ValueError]) + 4 + >>> apply_catch(2, lambda x: x / 0, [ZeroDivisionError], "error") + 'error' + >>> apply_catch(2, lambda x: x / 0, [ZeroDivisionError]) + 2 + + .. versionadded:: 8.0.0 + """ + try: + return func(obj) + except tuple(exceptions): + return obj if default is UNSET else default + + +@t.overload +def merge( + obj: t.Mapping[T, T2], *sources: t.Mapping[T3, T4] +) -> t.Dict[t.Union[T, T3], t.Union[T2, T4]]: ... + + +@t.overload +def merge(obj: t.Sequence[T], *sources: t.Sequence[T2]) -> t.List[t.Union[T, T2]]: ... + + +def merge(obj, *sources): + """ + Recursively merges properties of the source object(s) into the destination object. Subsequent + sources will overwrite property assignments of previous sources. + + Args: + obj: Destination object to merge source(s) into. + sources: Source objects to merge from. subsequent sources overwrite previous ones. + + Returns: + Merged object. + + Warning: + `obj` is modified in place. + + Example: + + >>> obj = {"a": 2} + >>> obj2 = merge(obj, {"a": 1}, {"b": 2, "c": 3}, {"d": 4}) + >>> obj2 == {"a": 1, "b": 2, "c": 3, "d": 4} + True + >>> obj is obj2 + True + + .. versionadded:: 1.0.0 + + .. versionchanged:: 2.3.2 + Apply :func:`clone_deep` to each `source` before assigning to `obj`. + + .. versionchanged:: 2.3.2 + Allow `iteratee` to be passed by reference if it is the last positional argument. + + .. versionchanged:: 4.0.0 + Moved iteratee argument to :func:`merge_with`. + + .. versionchanged:: 4.9.3 + Fixed regression in v4.8.0 that caused exception when `obj` was ``None``. + """ + return merge_with(obj, *sources) + + +def merge_with(obj: t.Any, *sources: t.Any, **kwargs: t.Any) -> t.Any: + """ + This method is like :func:`merge` except that it accepts customizer which is invoked to produce + the merged values of the destination and source properties. If customizer returns ``None``, + merging is handled by this method instead. The customizer is invoked with five arguments: + ``(obj_value, src_value, key, obj, source)``. + + Args: + obj: Destination object to merge source(s) into. + sources: Source objects to merge from. subsequent sources + overwrite previous ones. + + Keyword Args: + iteratee: Iteratee function to handle merging + (must be passed in as keyword argument). + + Returns: + Merged object. + + Warning: + `obj` is modified in place. + + Example: + + >>> cbk = lambda obj_val, src_val: obj_val + src_val + >>> obj1 = {"a": [1], "b": [2]} + >>> obj2 = {"a": [3], "b": [4]} + >>> res = merge_with(obj1, obj2, cbk) + >>> obj1 == {"a": [1, 3], "b": [2, 4]} + True + + .. versionadded:: 4.0.0 + + .. versionchanged:: 4.9.3 + Fixed regression in v4.8.0 that caused exception when `obj` was ``None``. + """ + if obj is None: + return None + + list_sources = list(sources) + iteratee = kwargs.pop("iteratee", None) + + if iteratee is None and list_sources and callable(list_sources[-1]): + iteratee = list_sources.pop() + + list_sources = [copy.deepcopy(source) for source in list_sources] + + if callable(iteratee): + iteratee = partial(callit, iteratee, argcount=getargcount(iteratee, maxargs=5)) + else: + iteratee = None + + return _merge_with(obj, *list_sources, iteratee=iteratee, **kwargs) + + +def _merge_with(obj, *sources, **kwargs): + iteratee = kwargs.get("iteratee") + setter = kwargs.get("_setter") + + if setter is None: + setter = base_set + + for source in sources: + for key, src_value in iterator(source): + obj_value = base_get(obj, key, default=None) + all_sequences = isinstance(src_value, list) and isinstance(obj_value, list) + all_mappings = isinstance(src_value, dict) and isinstance(obj_value, dict) + + _result = None + if iteratee: + _result = iteratee(obj_value, src_value, key, obj, source) + + if _result is not None: + result = _result + elif all_sequences or all_mappings: + result = _merge_with(obj_value, src_value, iteratee=iteratee, _setter=setter) + else: + result = src_value + + setter(obj, key, result) + + return obj + + +@t.overload +def omit(obj: t.Mapping[T, T2], *properties: PathT) -> t.Dict[T, T2]: ... + + +@t.overload +def omit(obj: t.Union[t.Iterator[T], t.Sequence[T]], *properties: PathT) -> t.Dict[int, T]: ... + + +@t.overload +def omit(obj: t.Any, *properties: PathT) -> t.Dict[t.Any, t.Any]: ... + + +def omit(obj, *properties): + """ + The opposite of :func:`pick`. This method creates an object composed of the property paths of + `obj` that are not omitted. + + Args: + obj: Object to process. + *properties: Property values to omit. + + Returns: + Results of omitting properties. + + Example: + + >>> omit({"a": 1, "b": 2, "c": 3}, "b", "c") == {"a": 1} + True + >>> omit({"a": 1, "b": 2, "c": 3}, ["a", "c"]) == {"b": 2} + True + >>> omit([1, 2, 3, 4], 0, 3) == {1: 2, 2: 3} + True + >>> omit({"a": {"b": {"c": "d"}}}, "a.b.c") == {"a": {"b": {}}} + True + + .. versionadded:: 1.0.0 + + .. versionchanged:: 4.0.0 + Moved iteratee argument to :func:`omit_by`. + + .. versionchanged:: 4.2.0 + Support deep paths. + """ + return omit_by(obj, pyd.flatten(properties)) + + +@t.overload +def omit_by(obj: t.Mapping[T, T2], iteratee: t.Callable[[T2, T], t.Any]) -> t.Dict[T, T2]: ... + + +@t.overload +def omit_by(obj: t.Mapping[T, T2], iteratee: t.Callable[[T2], t.Any]) -> t.Dict[T, T2]: ... + + +@t.overload +def omit_by(obj: t.Dict[T, T2], iteratee: None = None) -> t.Dict[T, T2]: ... + + +@t.overload +def omit_by( + obj: t.Union[t.Iterator[T], t.Sequence[T]], iteratee: t.Callable[[T, int], t.Any] +) -> t.Dict[int, T]: ... + + +@t.overload +def omit_by( + obj: t.Union[t.Iterator[T], t.Sequence[T]], iteratee: t.Callable[[T], t.Any] +) -> t.Dict[int, T]: ... + + +@t.overload +def omit_by(obj: t.List[T], iteratee: None = None) -> t.Dict[int, T]: ... + + +@t.overload +def omit_by( + obj: t.Any, iteratee: t.Union[t.Callable[..., t.Any], None] = None +) -> t.Dict[t.Any, t.Any]: ... + + +def omit_by(obj, iteratee=None): + """ + The opposite of :func:`pick_by`. This method creates an object composed of the string keyed + properties of object that predicate doesn't return truthy for. The predicate is invoked with two + arguments: ``(value, key)``. + + Args: + obj: Object to process. + iteratee: Iteratee used to determine which properties to omit. + + Returns: + Results of omitting properties. + + Example: + + >>> omit_by({"a": 1, "b": "2", "c": 3}, lambda v: isinstance(v, int)) + {'b': '2'} + + .. versionadded:: 4.0.0 + + .. versionchanged:: 4.2.0 + Support deep paths for `iteratee`. + """ + if not callable(iteratee): + paths = pyd.map_(iteratee, to_path) + + if any(len(path) > 1 for path in paths): + cloned = clone_deep(obj) + else: + cloned = to_dict(obj) + + def _unset(obj, path): + pyd.unset(obj, path) + return obj + + ret = pyd.reduce_(paths, _unset, cloned) + else: + argcount = getargcount(iteratee, maxargs=2) + + ret = { + key: value + for key, value in iterator(obj) + if not callit(iteratee, value, key, argcount=argcount) + } + + return ret + + +def parse_int(value: t.Any, radix: t.Union[int, None] = None) -> t.Union[int, None]: + """ + Converts the given `value` into an integer of the specified `radix`. If `radix` is falsey, a + radix of ``10`` is used unless the `value` is a hexadecimal, in which case a radix of 16 is + used. + + Args: + value: Value to parse. + radix: Base to convert to. + + Returns: + Integer if parsable else ``None``. + + Example: + + >>> parse_int("5") + 5 + >>> parse_int("12", 8) + 10 + >>> parse_int("x") is None + True + + .. versionadded:: 1.0.0 + """ + if not radix and pyd.is_string(value): + try: + # Check if value is hexadecimal and if so use base-16 conversion. + int(value, 16) + except ValueError: + pass + else: + radix = 16 + + if not radix: + radix = 10 + + try: + # NOTE: Must convert value to string when supplying radix to int(). Dropping radix arg when + # 10 is needed to allow floats to parse correctly. + args = (value,) if radix == 10 else (to_string(value), radix) + parsed = int(*args) + except (ValueError, TypeError): + parsed = None + + return parsed + + +@t.overload +def pick(obj: t.Mapping[T, T2], *properties: PathT) -> t.Dict[T, T2]: ... + + +@t.overload +def pick(obj: t.Union[t.Tuple[T, ...], t.List[T]], *properties: PathT) -> t.Dict[int, T]: ... + + +@t.overload +def pick(obj: t.Any, *properties: PathT) -> t.Dict[t.Any, t.Any]: ... + + +def pick(obj, *properties): + """ + Creates an object composed of the picked object properties. + + Args: + obj: Object to pick from. + properties: Property values to pick. + + Returns: + Dict containing picked properties. + + Example: + + >>> pick({"a": 1, "b": 2, "c": 3}, "a", "b") == {"a": 1, "b": 2} + True + + .. versionadded:: 1.0.0 + + .. versionchanged:: 4.0.0 + Moved iteratee argument to :func:`pick_by`. + """ + return pick_by(obj, pyd.flatten(properties)) + + +@t.overload +def pick_by(obj: t.Mapping[T, T2], iteratee: t.Callable[[T2], t.Any]) -> t.Dict[T, T2]: ... + + +@t.overload +def pick_by(obj: t.Mapping[T, T2], iteratee: t.Callable[[T2, T], t.Any]) -> t.Dict[T, T2]: ... + + +@t.overload +def pick_by(obj: t.Dict[T, T2], iteratee: None = None) -> t.Dict[T, T2]: ... + + +@t.overload +def pick_by( + obj: t.Union[t.Tuple[T, ...], t.List[T]], iteratee: t.Callable[[T, int], t.Any] +) -> t.Dict[int, T]: ... + + +@t.overload +def pick_by( + obj: t.Union[t.Tuple[T, ...], t.List[T]], iteratee: t.Callable[[T], t.Any] +) -> t.Dict[int, T]: ... + + +@t.overload +def pick_by(obj: t.Union[t.Tuple[T, ...], t.List[T]], iteratee: None = None) -> t.Dict[int, T]: ... + + +@t.overload +def pick_by( + obj: t.Any, iteratee: t.Union[t.Callable[..., t.Any], None] = None +) -> t.Dict[t.Any, t.Any]: ... + + +def pick_by(obj, iteratee=None): + """ + Creates an object composed of the object properties predicate returns truthy for. The predicate + is invoked with two arguments: ``(value, key)``. + + Args: + obj: Object to pick from. + iteratee: Iteratee used to determine which properties to pick. + + Returns: + Dict containing picked properties. + + Example: + + >>> obj = {"a": 1, "b": "2", "c": 3} + >>> pick_by(obj, lambda v: isinstance(v, int)) == {"a": 1, "c": 3} + True + + .. versionadded:: 4.0.0 + """ + obj = to_dict(obj) + + if iteratee is None or callable(iteratee): + paths = keys(obj) + if iteratee is None: + iteratee = pyd.identity + argcount = 1 + else: + argcount = getargcount(iteratee, maxargs=2) + else: + paths = iteratee if iteratee is not None else [] + + def iteratee(value, path): # pylint: disable=function-redefined + return has(obj, path) + + argcount = 2 + + result = {} + + for path in paths: + value = get(obj, path) + + if callit(iteratee, value, path, argcount=argcount): + set_(result, path, value) + + return result + + +def rename_keys(obj: t.Dict[T, T2], key_map: t.Dict[t.Any, T3]) -> t.Dict[t.Union[T, T3], T2]: + """ + Rename the keys of `obj` using `key_map` and return new object. + + Args: + obj: Object to rename. + key_map: Renaming map whose keys correspond to existing keys in `obj` and whose + values are the new key name. + + Returns: + Renamed `obj`. + + Example: + + >>> obj = rename_keys({"a": 1, "b": 2, "c": 3}, {"a": "A", "b": "B"}) + >>> obj == {"A": 1, "B": 2, "c": 3} + True + + .. versionadded:: 2.0.0 + """ + return {key_map.get(key, key): value for key, value in obj.items()} + + +def set_(obj: T, path: PathT, value: t.Any) -> T: + """ + Sets the value of an object described by `path`. If any part of the object path doesn't exist, + it will be created. + + Args: + obj: Object to modify. + path: Target path to set value to. + value: Value to set. + + Returns: + Modified `obj`. + + Warning: + `obj` is modified in place. + + Example: + + >>> set_({}, "a.b.c", 1) + {'a': {'b': {'c': 1}}} + >>> set_({}, "a.0.c", 1) + {'a': {'0': {'c': 1}}} + >>> set_([1, 2], "[2][0]", 1) + [1, 2, [1]] + >>> set_({}, "a.b[0].c", 1) + {'a': {'b': [{'c': 1}]}} + + .. versionadded:: 2.2.0 + + .. versionchanged:: 3.3.0 + Added :func:`set_` as main definition and :func:`deep_set` as alias. + + .. versionchanged:: 4.0.0 + + - Modify `obj` in place. + - Support creating default path values as ``list`` or ``dict`` based on whether key or index + substrings are used. + - Remove alias ``deep_set``. + """ + return set_with(obj, path, value) + + +def set_with( + obj: T, path: PathT, value: t.Any, customizer: t.Union[t.Callable[..., t.Any], None] = None +) -> T: + """ + This method is like :func:`set_` except that it accepts customizer which is invoked to produce + the objects of path. If customizer returns undefined path creation is handled by the method + instead. The customizer is invoked with three arguments: ``(nested_value, key, nested_object)``. + + Args: + obj: Object to modify. + path: Target path to set value to. + value: Value to set. + customizer: The function to customize assigned values. + + Returns: + Modified `obj`. + + Warning: + `obj` is modified in place. + + Example: + + >>> set_with({}, "[0][1]", "a", lambda: {}) + {0: {1: 'a'}} + + .. versionadded:: 4.0.0 + + .. versionchanged:: 4.3.1 + Fixed bug where a callable `value` was called when being set. + """ + return update_with(obj, path, pyd.constant(value), customizer=customizer) + + +def to_boolean( + obj: t.Any, + true_values: t.Tuple[str, ...] = ("true", "1"), + false_values: t.Tuple[str, ...] = ("false", "0"), +) -> t.Union[bool, None]: + """ + Convert `obj` to boolean. This is not like the builtin ``bool`` function. By default, commonly + considered strings values are converted to their boolean equivalent, i.e., ``'0'`` and + ``'false'`` are converted to ``False`` while ``'1'`` and ``'true'`` are converted to ``True``. + If a string value is provided that isn't recognized as having a common boolean conversion, then + the returned value is ``None``. Non-string values of `obj` are converted using ``bool``. + Optionally, `true_values` and `false_values` can be overridden but each value must be a string. + + Args: + obj: Object to convert. + true_values: Values to consider ``True``. Each value must be a string. + Comparision is case-insensitive. Defaults to ``('true', '1')``. + false_values: Values to consider ``False``. Each value must be a string. + Comparision is case-insensitive. Defaults to ``('false', '0')``. + + Returns: + Boolean value of `obj`. + + Example: + + >>> to_boolean("true") + True + >>> to_boolean("1") + True + >>> to_boolean("false") + False + >>> to_boolean("0") + False + >>> assert to_boolean("a") is None + + .. versionadded:: 3.0.0 + """ + if pyd.is_string(obj): + obj = obj.strip() + + def boolean_match(text, vals): + if text.lower() in [val.lower() for val in vals]: + return True + else: + return re.match("|".join(vals), text) + + if true_values and boolean_match(obj, true_values): + value = True + elif false_values and boolean_match(obj, false_values): + value = False + else: + value = None + else: + value = bool(obj) + + return value + + +@t.overload +def to_dict(obj: t.Mapping[T, T2]) -> t.Dict[T, T2]: ... + + +@t.overload +def to_dict(obj: t.Union[t.Iterator[T], t.Sequence[T]]) -> t.Dict[int, T]: ... + + +@t.overload +def to_dict(obj: t.Any) -> t.Dict[t.Any, t.Any]: ... + + +def to_dict(obj): + """ + Convert `obj` to ``dict`` by creating a new ``dict`` using `obj` keys and values. + + Args: + obj: Object to convert. + + Returns: + Object converted to ``dict``. + + Example: + + >>> obj = {"a": 1, "b": 2} + >>> obj2 = to_dict(obj) + >>> obj2 == obj + True + >>> obj2 is not obj + True + + .. versionadded:: 3.0.0 + + .. versionchanged:: 4.0.0 + Removed alias ``to_plain_object``. + + .. versionchanged:: 4.2.0 + Use ``pydash.helpers.iterator`` to generate key/value pairs. + + .. versionchanged:: 4.7.1 + Try to convert to ``dict`` using ``dict()`` first, then fallback to using + ``pydash.helpers.iterator``. + """ + return dict(iterator(obj)) + + +def to_integer(obj: t.Any) -> int: + """ + Converts `obj` to an integer. + + Args: + obj: Object to convert. + + Returns: + Converted integer or ``0`` if it can't be converted. + + Example: + + >>> to_integer(3.2) + 3 + >>> to_integer("3.2") + 3 + >>> to_integer("3.9") + 3 + >>> to_integer("invalid") + 0 + + .. versionadded:: 4.0.0 + """ + try: + # Convert to float first to handle converting floats as string since int('1.1') would fail + # but this won't. + num = int(float(obj)) + except (ValueError, TypeError): + num = 0 + + return num + + +@t.overload +def to_list(obj: t.Dict[t.Any, T], split_strings: bool = True) -> t.List[T]: ... + + +@t.overload +def to_list(obj: t.Iterable[T], split_strings: bool = True) -> t.List[T]: ... + + +@t.overload +def to_list(obj: T, split_strings: bool = True) -> t.List[T]: ... + + +def to_list(obj, split_strings=True): + """ + Converts an obj, an iterable or a single item to a list. + + Args: + obj: Object to convert item or wrap. + split_strings: Whether to split strings into single chars. Defaults to + ``True``. + + Returns: + Converted obj or wrapped item. + + Example: + + >>> results = to_list({"a": 1, "b": 2, "c": 3}) + >>> assert set(results) == set([1, 2, 3]) + + >>> to_list((1, 2, 3, 4)) + [1, 2, 3, 4] + + >>> to_list(1) + [1] + + >>> to_list([1]) + [1] + + >>> to_list(a for a in [1, 2, 3]) + [1, 2, 3] + + >>> to_list("cat") + ['c', 'a', 't'] + + >>> to_list("cat", split_strings=False) + ['cat'] + + .. versionadded:: 1.0.0 + + .. versionchanged:: 4.3.0 + + - Wrap non-iterable items in a list. + - Convert other iterables to list. + - Byte objects are returned as single character strings in Python 3. + """ + if isinstance(obj, list): + return obj[:] + elif isinstance(obj, dict): + return obj.values() + elif not split_strings and isinstance(obj, (str, bytes)): + return [obj] + elif split_strings and isinstance(obj, bytes): + # in python3 iterating over bytes gives integers instead of strings + return list(chr(c) if isinstance(c, int) else c for c in obj) + else: + try: + return list(obj) + except TypeError: + return [obj] + + +def to_number(obj: t.Any, precision: int = 0) -> t.Union[float, None]: + """ + Convert `obj` to a number. All numbers are retuned as ``float``. If precision is negative, round + `obj` to the nearest positive integer place. If `obj` can't be converted to a number, ``None`` + is returned. + + Args: + obj: Object to convert. + precision: Precision to round number to. Defaults to ``0``. + + Returns: + Converted number or ``None`` if it can't be converted. + + Example: + + >>> to_number("1234.5678") + 1235.0 + >>> to_number("1234.5678", 4) + 1234.5678 + >>> to_number(1, 2) + 1.0 + + .. versionadded:: 3.0.0 + """ + try: + factor = pow(10, precision) + + if precision < 0: + # Round down since negative `precision` means we are going to the nearest positive + # integer place. + rounder: t.Callable[..., t.Any] = math.floor + else: + rounder = round + + num = rounder(float(obj) * factor) / factor + except Exception: + num = None + + return num + + +@t.overload +def to_pairs(obj: t.Mapping[T, T2]) -> t.List[t.Tuple[T, T2]]: ... + + +@t.overload +def to_pairs(obj: t.Union[t.Iterator[T], t.Sequence[T]]) -> t.List[t.Tuple[int, T]]: ... + + +@t.overload +def to_pairs(obj: t.Any) -> t.List[t.Any]: ... + + +def to_pairs(obj): + """ + Creates a list of tuples of an object's key-value pairs, i.e., + ``[(key1, value1), (key2, value2)]``. + + Args: + obj: Object to process. + + Returns: + List of tuples of the object's key-value pairs. + + Example: + + >>> to_pairs([1, 2, 3, 4]) + [(0, 1), (1, 2), (2, 3), (3, 4)] + >>> to_pairs({"a": 1}) + [('a', 1)] + + .. versionadded:: 1.0.0 + + .. versionchanged:: 4.0.0 + Renamed from ``pairs`` to ``to_pairs``. + + .. versionchanged:: 8.0.0 + Returning list of tuples instead of list of lists. + """ + return [(key, value) for key, value in iterator(obj)] + + +def to_string(obj: t.Any) -> str: + """ + Converts an object to string. + + Args: + obj: Object to convert. + + Returns: + String representation of `obj`. + + Example: + + >>> to_string(1) == "1" + True + >>> to_string(None) == "" + True + >>> to_string([1, 2, 3]) == "[1, 2, 3]" + True + >>> to_string("a") == "a" + True + + .. versionadded:: 2.0.0 + + .. versionchanged:: 3.0.0 + Convert ``None`` to empty string. + """ + if pyd.is_string(obj): + res = obj + elif obj is None: + res = "" + else: + res = str(obj) + return res + + +@t.overload +def transform( + obj: t.Mapping[T, T2], iteratee: t.Callable[[T3, T2, T, t.Dict[T, T2]], t.Any], accumulator: T3 +) -> T3: ... + + +@t.overload +def transform( + obj: t.Mapping[T, T2], iteratee: t.Callable[[T3, T2, T], t.Any], accumulator: T3 +) -> T3: ... + + +@t.overload +def transform( + obj: t.Mapping[t.Any, T2], iteratee: t.Callable[[T3, T2], t.Any], accumulator: T3 +) -> T3: ... + + +@t.overload +def transform( + obj: t.Mapping[t.Any, t.Any], iteratee: t.Callable[[T3], t.Any], accumulator: T3 +) -> T3: ... + + +@t.overload +def transform( + obj: t.Iterable[T], iteratee: t.Callable[[T3, T, int, t.List[T]], t.Any], accumulator: T3 +) -> T3: ... + + +@t.overload +def transform( + obj: t.Iterable[T], iteratee: t.Callable[[T3, T, int], t.Any], accumulator: T3 +) -> T3: ... + + +@t.overload +def transform(obj: t.Iterable[T], iteratee: t.Callable[[T3, T], t.Any], accumulator: T3) -> T3: ... + + +@t.overload +def transform(obj: t.Iterable[t.Any], iteratee: t.Callable[[T3], t.Any], accumulator: T3) -> T3: ... + + +@t.overload +def transform(obj: t.Any, iteratee: t.Any = None, accumulator: t.Any = None) -> t.Any: ... + + +def transform(obj, iteratee=None, accumulator=None): + """ + An alernative to :func:`pydash.collections.reduce`, this method transforms `obj` to a new + accumulator object which is the result of running each of its properties through an iteratee, + with each iteratee execution potentially mutating the accumulator object. The iteratee is + invoked with four arguments: ``(accumulator, value, key, object)``. Iteratees may exit iteration + early by explicitly returning ``False``. + + Args: + obj: Object to process. + iteratee: Iteratee applied per iteration. + accumulator: Accumulated object. Defaults to ``list``. + + Returns: + Accumulated object. + + Example: + + >>> transform([1, 2, 3, 4], lambda acc, v, k: acc.append((k, v))) + [(0, 1), (1, 2), (2, 3), (3, 4)] + + .. versionadded:: 1.0.0 + """ + if iteratee is None: + iteratee = pyd.identity + argcount = 1 + else: + argcount = getargcount(iteratee, maxargs=4) + + if accumulator is None: + accumulator = [] + + walk = ( + None + for key, item in iterator(obj) + if callit(iteratee, accumulator, item, key, obj, argcount=argcount) is False + ) + next(walk, None) + + return accumulator + + +@t.overload +def update( + obj: t.Dict[t.Any, T2], + path: PathT, + updater: t.Callable[[T2], t.Any], +) -> t.Dict[t.Any, t.Any]: ... + + +@t.overload +def update( + obj: t.List[T], + path: PathT, + updater: t.Callable[[T], t.Any], +) -> t.List[t.Any]: ... + + +@t.overload +def update( + obj: T, + path: PathT, + updater: t.Callable[..., t.Any], +) -> T: ... + + +def update(obj, path, updater): + """ + This method is like :func:`set_` except that accepts updater to produce the value to set. Use + :func:`update_with` to customize path creation. The updater is invoked with one argument: + ``(value)``. + + Args: + obj: Object to modify. + path: A string or list of keys that describe the object path to modify. + updater: Function that returns updated value. + + Returns: + Updated `obj`. + + Warning: + `obj` is modified in place. + + Example: + + >>> update({}, ["a", "b"], lambda value: value) + {'a': {'b': None}} + >>> update([], [0, 0], lambda value: 1) + [[1]] + + .. versionadded:: 4.0.0 + """ + return update_with(obj, path, updater) + + +@t.overload +def update_with( + obj: t.Dict[t.Any, T2], + path: PathT, + updater: t.Callable[[T2], t.Any], + customizer: t.Union[t.Callable[..., t.Any], None], +) -> t.Dict[t.Any, t.Any]: ... + + +@t.overload +def update_with( + obj: t.List[T], + path: PathT, + updater: t.Callable[[T], t.Any], + customizer: t.Union[t.Callable[..., t.Any], None] = None, +) -> t.List[t.Any]: ... + + +@t.overload +def update_with( + obj: T, + path: PathT, + updater: t.Callable[..., t.Any], + customizer: t.Union[t.Callable[..., t.Any], None] = None, +) -> T: ... + + +def update_with(obj, path, updater, customizer=None): # noqa: PLR0912 + """ + This method is like :func:`update` except that it accepts customizer which is invoked to produce + the objects of path. If customizer returns ``None``, path creation is handled by the method + instead. The customizer is invoked with three arguments: ``(nested_value, key, nested_object)``. + + Args: + obj: Object to modify. + path: A string or list of keys that describe the object path to modify. + updater: Function that returns updated value. + customizer: The function to customize assigned values. + + Returns: + Updated `obj`. + + Warning: + `obj` is modified in place. + + Example: + + >>> update_with({}, "[0][1]", lambda: "a", lambda: {}) + {0: {1: 'a'}} + + .. versionadded:: 4.0.0 + """ + if not callable(updater): + updater = pyd.constant(updater) + + if customizer is not None and not callable(customizer): + call_customizer = partial(callit, clone, customizer, argcount=1) + elif customizer: + call_customizer = partial(callit, customizer, argcount=getargcount(customizer, maxargs=3)) + else: + call_customizer = None + + default_type = dict if isinstance(obj, dict) else list + tokens = to_path_tokens(path) + + last_key = pyd.last(tokens) + + if isinstance(last_key, PathToken): + last_key = last_key.key + + target = obj + + for idx, token in enumerate(pyd.initial(tokens)): + key = token.key + default_factory = pyd.get(tokens, [idx + 1, "default_factory"], default=default_type) + + obj_val = base_get(target, key, default=None) + path_obj = None + + if call_customizer: + path_obj = call_customizer(obj_val, key, target) + + if path_obj is None: + path_obj = default_factory() + + base_set(target, key, path_obj, allow_override=False) + + try: + target = base_get(target, key, default=None) + except TypeError as exc: # pragma: no cover + try: + target = target[int(key)] + _failed = False + except Exception: + _failed = True + + if _failed: + raise TypeError(f"Unable to update object at index {key!r}. {exc}") from exc + + value = base_get(target, last_key, default=None) + base_set(target, last_key, callit(updater, value)) + + return obj + + +def unset(obj: t.Union[t.List[t.Any], t.Dict[t.Any, t.Any]], path: PathT) -> bool: # noqa: C901 + """ + Removes the property at `path` of `obj`. + + Note: + Only ``list``, ``dict``, or objects with a ``pop()`` method can be unset by this function. + + Args: + obj: The object to modify. + path: The path of the property to unset. + + Returns: + Whether the property was deleted. + + Warning: + `obj` is modified in place. + + Example: + + >>> obj = {"a": [{"b": {"c": 7}}]} + >>> unset(obj, "a[0].b.c") + True + >>> obj + {'a': [{'b': {}}]} + >>> unset(obj, "a[0].b.c") + False + """ + tokens = to_path_tokens(path) + + last_key = pyd.last(tokens) + + if isinstance(last_key, PathToken): + last_key = last_key.key + + target = obj + + for token in pyd.initial(tokens): + key = token.key + + try: + try: + target = target[key] + except TypeError: + target = target[int(key)] + except Exception: + # Allow different types reassignment + target = UNSET # type: ignore + + if target is UNSET: + break + + did_unset = False + + if target is not UNSET: + try: + try: + # last_key can be a lot of things + # safe as everything wrapped in try/except + target.pop(last_key) # type: ignore + did_unset = True + except TypeError: + target.pop(int(last_key)) # type: ignore + did_unset = True + except Exception: + pass + + return did_unset + + +@t.overload +def values(obj: t.Mapping[t.Any, T2]) -> t.List[T2]: ... + + +@t.overload +def values(obj: t.Iterable[T]) -> t.List[T]: ... + + +@t.overload +def values(obj: t.Any) -> t.List[t.Any]: ... + + +def values(obj): + """ + Creates a list composed of the values of `obj`. + + Args: + obj: Object to extract values from. + + Returns: + List of values. + + Example: + + >>> results = values({"a": 1, "b": 2, "c": 3}) + >>> set(results) == set([1, 2, 3]) + True + >>> values([2, 4, 6, 8]) + [2, 4, 6, 8] + + .. versionadded:: 1.0.0 + + .. versionchanged:: 1.1.0 + Added ``values_in`` as alias. + + .. versionchanged:: 4.0.0 + Removed alias ``values_in``. + """ + return [value for _, value in iterator(obj)] + + +# +# Utility methods not a part of the main API +# + + +def base_clone(value, is_deep=False, customizer=None, key=None, _cloned=False): + """Base clone function that supports deep clone and customizer callback.""" + clone_by = copy.deepcopy if is_deep else copy.copy + result = None + + if callable(customizer) and not _cloned: + argcount = getargcount(customizer, maxargs=4) + cbk = partial(callit, customizer, argcount=argcount) + elif _cloned: + cbk = customizer + else: + cbk = None + + if cbk: + result = cbk(value, key, value) + + if result is not None: + return result + + if not _cloned: + result = clone_by(value) + else: + result = value + + if cbk and not pyd.is_string(value) and not isinstance(value, bytes): + for key, subvalue in iterator(value): # noqa: PLR1704 + if is_deep: + val = base_clone(subvalue, is_deep, cbk, key, _cloned=True) + else: + val = cbk(subvalue, key, value) + + if val is not None: + result[key] = val + + return result |