diff options
Diffstat (limited to '.venv/lib/python3.12/site-packages/pydash')
17 files changed, 21020 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/pydash/__init__.py b/.venv/lib/python3.12/site-packages/pydash/__init__.py new file mode 100644 index 00000000..74eafd4f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydash/__init__.py @@ -0,0 +1,756 @@ +"""Python port of Lo-Dash.""" + +__version__ = "8.0.5" + +from .arrays import ( + chunk, + compact, + concat, + difference, + difference_by, + difference_with, + drop, + drop_right, + drop_right_while, + drop_while, + duplicates, + fill, + find_index, + find_last_index, + flatten, + flatten_deep, + flatten_depth, + from_pairs, + head, + index_of, + initial, + intercalate, + interleave, + intersection, + intersection_by, + intersection_with, + intersperse, + last, + last_index_of, + mapcat, + nth, + pop, + pull, + pull_all, + pull_all_by, + pull_all_with, + pull_at, + push, + remove, + reverse, + shift, + slice_, + sort, + sorted_index, + sorted_index_by, + sorted_index_of, + sorted_last_index, + sorted_last_index_by, + sorted_last_index_of, + sorted_uniq, + sorted_uniq_by, + splice, + split_at, + tail, + take, + take_right, + take_right_while, + take_while, + union, + union_by, + union_with, + uniq, + uniq_by, + uniq_with, + unshift, + unzip, + unzip_with, + without, + xor, + xor_by, + xor_with, + zip_, + zip_object, + zip_object_deep, + zip_with, +) +from .chaining import _Dash, chain, tap +from .collections import ( + at, + count_by, + every, + filter_, + find, + find_last, + flat_map, + flat_map_deep, + flat_map_depth, + for_each, + for_each_right, + group_by, + includes, + invoke_map, + key_by, + map_, + nest, + order_by, + partition, + pluck, + reduce_, + reduce_right, + reductions, + reductions_right, + reject, + sample, + sample_size, + shuffle, + size, + some, + sort_by, +) +from .exceptions import InvalidMethod +from .functions import ( + after, + ary, + before, + conjoin, + curry, + curry_right, + debounce, + delay, + disjoin, + flip, + flow, + flow_right, + iterated, + juxtapose, + negate, + once, + over_args, + partial, + partial_right, + rearg, + spread, + throttle, + unary, + wrap, +) +from .numerical import ( + add, + ceil, + clamp, + divide, + floor, + max_, + max_by, + mean, + mean_by, + median, + min_, + min_by, + moving_mean, + multiply, + power, + round_, + scale, + slope, + std_deviation, + subtract, + sum_, + sum_by, + transpose, + variance, + zscore, +) +from .objects import ( + 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, +) +from .predicates import ( + eq, + eq_cmp, + gt, + gt_cmp, + gte, + gte_cmp, + in_range, + in_range_cmp, + is_associative, + is_blank, + is_boolean, + is_builtin, + is_date, + is_decreasing, + is_dict, + is_empty, + is_equal, + is_equal_cmp, + is_equal_with, + is_equal_with_cmp, + is_error, + is_even, + is_float, + is_function, + is_increasing, + is_indexed, + is_instance_of, + is_instance_of_cmp, + is_integer, + is_iterable, + is_json, + is_list, + is_match, + is_match_cmp, + is_match_with, + is_match_with_cmp, + is_monotone, + is_monotone_cmp, + is_nan, + is_negative, + is_none, + is_number, + is_object, + is_odd, + is_positive, + is_reg_exp, + is_set, + is_strictly_decreasing, + is_strictly_increasing, + is_string, + is_tuple, + is_zero, + lt, + lt_cmp, + lte, + lte_cmp, +) +from .strings import ( + camel_case, + capitalize, + chars, + chop, + chop_right, + clean, + count_substr, + deburr, + decapitalize, + ends_with, + ensure_ends_with, + ensure_starts_with, + escape, + escape_reg_exp, + has_substr, + human_case, + insert_substr, + join, + kebab_case, + lines, + lower_case, + lower_first, + number_format, + pad, + pad_end, + pad_start, + pascal_case, + predecessor, + prune, + quote, + reg_exp_js_match, + reg_exp_js_replace, + reg_exp_replace, + repeat, + replace, + replace_end, + replace_start, + separator_case, + series_phrase, + series_phrase_serial, + slugify, + snake_case, + split, + start_case, + starts_with, + strip_tags, + substr_left, + substr_left_end, + substr_right, + substr_right_end, + successor, + surround, + swap_case, + title_case, + to_lower, + to_upper, + trim, + trim_end, + trim_start, + truncate, + unescape, + unquote, + upper_case, + upper_first, + url, + words, +) +from .utilities import ( + attempt, + cond, + conforms, + conforms_to, + constant, + default_to, + default_to_any, + identity, + iteratee, + matches, + matches_property, + memoize, + method, + method_of, + noop, + now, + nth_arg, + over, + over_every, + over_some, + properties, + property_, + property_of, + random, + range_, + range_right, + result, + retry, + stub_dict, + stub_false, + stub_list, + stub_string, + stub_true, + times, + to_path, + unique_id, +) + + +py_ = _Dash() +_ = py_ + + +__all__ = ( + "chunk", + "compact", + "concat", + "difference", + "difference_by", + "difference_with", + "drop", + "drop_right", + "drop_right_while", + "drop_while", + "duplicates", + "fill", + "find_index", + "find_last_index", + "flatten", + "flatten_deep", + "flatten_depth", + "from_pairs", + "head", + "index_of", + "initial", + "intercalate", + "interleave", + "intersection", + "intersection_by", + "intersection_with", + "intersperse", + "last", + "last_index_of", + "mapcat", + "nth", + "pop", + "pull", + "pull_all", + "pull_all_by", + "pull_all_with", + "pull_at", + "push", + "remove", + "reverse", + "shift", + "slice_", + "sort", + "sorted_index", + "sorted_index_by", + "sorted_index_of", + "sorted_last_index", + "sorted_last_index_by", + "sorted_last_index_of", + "sorted_uniq", + "sorted_uniq_by", + "splice", + "split_at", + "tail", + "take", + "take_right", + "take_right_while", + "take_while", + "union", + "union_by", + "union_with", + "uniq", + "uniq_by", + "uniq_with", + "unshift", + "unzip", + "unzip_with", + "without", + "xor", + "xor_by", + "xor_with", + "zip_", + "zip_object", + "zip_object_deep", + "zip_with", + "_Dash", + "chain", + "tap", + "at", + "count_by", + "every", + "filter_", + "find", + "find_last", + "flat_map", + "flat_map_deep", + "flat_map_depth", + "for_each", + "for_each_right", + "group_by", + "includes", + "invoke_map", + "key_by", + "map_", + "nest", + "order_by", + "partition", + "pluck", + "reduce_", + "reduce_right", + "reductions", + "reductions_right", + "reject", + "sample", + "sample_size", + "shuffle", + "size", + "some", + "sort_by", + "InvalidMethod", + "after", + "ary", + "before", + "conjoin", + "curry", + "curry_right", + "debounce", + "delay", + "disjoin", + "flip", + "flow", + "flow_right", + "iterated", + "juxtapose", + "negate", + "once", + "over_args", + "partial", + "partial_right", + "rearg", + "spread", + "throttle", + "unary", + "wrap", + "add", + "ceil", + "clamp", + "divide", + "floor", + "max_", + "max_by", + "mean", + "mean_by", + "median", + "min_", + "min_by", + "moving_mean", + "multiply", + "power", + "round_", + "scale", + "slope", + "std_deviation", + "subtract", + "sum_", + "sum_by", + "transpose", + "variance", + "zscore", + "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", + "eq", + "eq_cmp", + "gt", + "gt_cmp", + "gte", + "gte_cmp", + "in_range", + "in_range_cmp", + "is_associative", + "is_blank", + "is_boolean", + "is_builtin", + "is_date", + "is_decreasing", + "is_dict", + "is_empty", + "is_equal", + "is_equal_cmp", + "is_equal_with", + "is_equal_with_cmp", + "is_error", + "is_even", + "is_float", + "is_function", + "is_increasing", + "is_indexed", + "is_instance_of", + "is_instance_of_cmp", + "is_integer", + "is_iterable", + "is_json", + "is_list", + "is_match", + "is_match_cmp", + "is_match_with", + "is_match_with_cmp", + "is_monotone", + "is_monotone_cmp", + "is_nan", + "is_negative", + "is_none", + "is_number", + "is_object", + "is_odd", + "is_positive", + "is_reg_exp", + "is_set", + "is_strictly_decreasing", + "is_strictly_increasing", + "is_string", + "is_tuple", + "is_zero", + "lt", + "lt_cmp", + "lte", + "lte_cmp", + "camel_case", + "capitalize", + "chars", + "chop", + "chop_right", + "clean", + "count_substr", + "deburr", + "decapitalize", + "ends_with", + "ensure_ends_with", + "ensure_starts_with", + "escape", + "escape_reg_exp", + "has_substr", + "human_case", + "insert_substr", + "join", + "kebab_case", + "lines", + "lower_case", + "lower_first", + "number_format", + "pad", + "pad_end", + "pad_start", + "pascal_case", + "predecessor", + "prune", + "quote", + "reg_exp_js_match", + "reg_exp_js_replace", + "reg_exp_replace", + "repeat", + "replace", + "replace_end", + "replace_start", + "separator_case", + "series_phrase", + "series_phrase_serial", + "slugify", + "snake_case", + "split", + "start_case", + "starts_with", + "strip_tags", + "substr_left", + "substr_left_end", + "substr_right", + "substr_right_end", + "successor", + "surround", + "swap_case", + "title_case", + "to_lower", + "to_upper", + "trim", + "trim_end", + "trim_start", + "truncate", + "unescape", + "unquote", + "upper_case", + "upper_first", + "url", + "words", + "attempt", + "cond", + "conforms", + "conforms_to", + "constant", + "default_to", + "default_to_any", + "identity", + "iteratee", + "matches", + "matches_property", + "memoize", + "method", + "method_of", + "noop", + "now", + "nth_arg", + "over", + "over_every", + "over_some", + "properties", + "property_", + "property_of", + "random", + "range_", + "range_right", + "result", + "retry", + "stub_dict", + "stub_false", + "stub_list", + "stub_string", + "stub_true", + "times", + "to_path", + "unique_id", +) diff --git a/.venv/lib/python3.12/site-packages/pydash/arrays.py b/.venv/lib/python3.12/site-packages/pydash/arrays.py new file mode 100644 index 00000000..2bc3bc94 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydash/arrays.py @@ -0,0 +1,2964 @@ +""" +Functions that operate on lists. + +.. versionadded:: 1.0.0 +""" + +from __future__ import annotations + +from bisect import bisect_left, bisect_right +from functools import cmp_to_key +from math import ceil +import typing as t + +import pydash as pyd + +from .helpers import base_get, iteriteratee, parse_iteratee +from .types import IterateeObjT + + +if t.TYPE_CHECKING: + from _typeshed import SupportsRichComparisonT # pragma: no cover + + +__all__ = ( + "chunk", + "compact", + "concat", + "difference", + "difference_by", + "difference_with", + "drop", + "drop_right", + "drop_right_while", + "drop_while", + "duplicates", + "fill", + "find_index", + "find_last_index", + "flatten", + "flatten_deep", + "flatten_depth", + "from_pairs", + "head", + "index_of", + "initial", + "intercalate", + "interleave", + "intersection", + "intersection_by", + "intersection_with", + "intersperse", + "last", + "last_index_of", + "mapcat", + "nth", + "pull", + "pull_all", + "pull_all_by", + "pull_all_with", + "pull_at", + "push", + "remove", + "reverse", + "shift", + "slice_", + "sort", + "sorted_index", + "sorted_index_by", + "sorted_index_of", + "sorted_last_index", + "sorted_last_index_by", + "sorted_last_index_of", + "sorted_uniq", + "sorted_uniq_by", + "splice", + "split_at", + "tail", + "take", + "take_right", + "take_right_while", + "take_while", + "union", + "union_by", + "union_with", + "uniq", + "uniq_by", + "uniq_with", + "unshift", + "unzip", + "unzip_with", + "without", + "xor", + "xor_by", + "xor_with", + "zip_", + "zip_object", + "zip_object_deep", + "zip_with", +) + +T = t.TypeVar("T") +T2 = t.TypeVar("T2") +T3 = t.TypeVar("T3") +T4 = t.TypeVar("T4") +T5 = t.TypeVar("T5") +SequenceT = t.TypeVar("SequenceT", bound=t.Sequence[t.Any]) +MutableSequenceT = t.TypeVar("MutableSequenceT", bound=t.MutableSequence[t.Any]) + + +def chunk(array: t.Sequence[T], size: int = 1) -> t.List[t.Sequence[T]]: + """ + Creates a list of elements split into groups the length of `size`. If `array` can't be split + evenly, the final chunk will be the remaining elements. + + Args: + array: List to chunk. + size: Chunk size. Defaults to ``1``. + + Returns: + New list containing chunks of `array`. + + Example: + + >>> chunk([1, 2, 3, 4, 5], 2) + [[1, 2], [3, 4], [5]] + + .. versionadded:: 1.1.0 + """ + chunks = int(ceil(len(array) / float(size))) + return [array[i * size : (i + 1) * size] for i in range(chunks)] + + +def compact(array: t.Iterable[t.Union[T, None]]) -> t.List[T]: + """ + Creates a list with all falsey values of array removed. + + Args: + array: List to compact. + + Returns: + Compacted list. + + Example: + + >>> compact(["", 1, 0, True, False, None]) + [1, True] + + .. versionadded:: 1.0.0 + """ + return [item for item in array if item] + + +def concat(*arrays: t.Iterable[T]) -> t.List[T]: + """ + Concatenates zero or more lists into one. + + Args: + arrays: Lists to concatenate. + + Returns: + Concatenated list. + + Example: + + >>> concat([1, 2], [3, 4], [[5], [6]]) + [1, 2, 3, 4, [5], [6]] + + .. versionadded:: 2.0.0 + + .. versionchanged:: 4.0.0 + Renamed from ``cat`` to ``concat``. + """ + return flatten(arrays) + + +def difference(array: t.Iterable[T], *others: t.Iterable[T]) -> t.List[T]: + """ + Creates a list of list elements not present in others. + + Args: + array: List to process. + others: Lists to check. + + Returns: + Difference between `others`. + + Example: + + >>> difference([1, 2, 3], [1], [2]) + [3] + + .. versionadded:: 1.0.0 + """ + return difference_with(array, *others) + + +@t.overload +def difference_by( + array: t.Iterable[T], + *others: t.Iterable[T], + iteratee: t.Union[IterateeObjT, t.Callable[[T], t.Any], None], +) -> t.List[T]: ... + + +@t.overload +def difference_by( + array: t.Iterable[T], *others: t.Union[IterateeObjT, t.Iterable[T], t.Callable[[T], t.Any]] +) -> t.List[T]: ... + + +def difference_by(array, *others, **kwargs): + """ + This method is like :func:`difference` except that it accepts an iteratee which is invoked for + each element of each array to generate the criterion by which they're compared. The order and + references of result values are determined by `array`. The iteratee is invoked with one + argument: ``(value)``. + + Args: + array: The array to find the difference of. + others: Lists to check for difference with `array`. + + Keyword Args: + iteratee: Function to transform the elements of the arrays. Defaults to + :func:`.identity`. + + Returns: + Difference between `others`. + + Example: + + >>> difference_by([1.2, 1.5, 1.7, 2.8], [0.9, 3.2], round) + [1.5, 1.7] + + .. versionadded:: 4.0.0 + """ + array = array[:] + + if not others: + return array + + # Check if last other is a potential iteratee. + iteratee, others = parse_iteratee("iteratee", *others, **kwargs) + + for other in others: + if not other: + continue + array = list(iterdifference(array, other, iteratee=iteratee)) + + return array + + +@t.overload +def difference_with( + array: t.Iterable[T], + *others: t.Iterable[T2], + comparator: t.Union[t.Callable[[T, T2], t.Any], None], +) -> t.List[T]: ... + + +@t.overload +def difference_with( + array: t.Iterable[T], *others: t.Union[t.Iterable[T2], t.Callable[[T, T2], t.Any]] +) -> t.List[T]: ... + + +def difference_with(array, *others, **kwargs): + """ + This method is like :func:`difference` except that it accepts a comparator which is invoked to + compare the elements of all arrays. The order and references of result values are determined by + the first array. The comparator is invoked with two arguments: ``(arr_val, oth_val)``. + + Args: + array: The array to find the difference of. + others: Lists to check for difference with `array`. + + Keyword Args: + comparator: Function to compare the elements of the arrays. Defaults to + :func:`.is_equal`. + + Returns: + Difference between `others`. + + Example: + + >>> array = ["apple", "banana", "pear"] + >>> others = (["avocado", "pumpkin"], ["peach"]) + >>> comparator = lambda a, b: a[0] == b[0] + >>> difference_with(array, *others, comparator=comparator) + ['banana'] + + .. versionadded:: 4.0.0 + """ + array = array[:] + + if not others: + return array + + comparator = kwargs.get("comparator") + last_other = others[-1] + + # Check if last other is a comparator. + if comparator is None and (callable(last_other) or last_other is None): + comparator = last_other + others = others[:-1] + + for other in others: + if not other: + continue + array = list(iterdifference(array, other, comparator=comparator)) + + return array + + +def drop(array: t.Sequence[T], n: int = 1) -> t.List[T]: + """ + Creates a slice of `array` with `n` elements dropped from the beginning. + + Args: + array: List to process. + n: Number of elements to drop. Defaults to ``1``. + + Returns: + Dropped list. + + Example: + + >>> drop([1, 2, 3, 4], 2) + [3, 4] + + .. versionadded:: 1.0.0 + + .. versionchanged:: 1.1.0 + Added ``n`` argument and removed as alias of :func:`rest`. + + .. versionchanged:: 3.0.0 + Made ``n`` default to ``1``. + """ + return drop_while(array, lambda _, index: index < n) + + +def drop_right(array: t.Sequence[T], n: int = 1) -> t.List[T]: + """ + Creates a slice of `array` with `n` elements dropped from the end. + + Args: + array: List to process. + n: Number of elements to drop. Defaults to ``1``. + + Returns: + Dropped list. + + Example: + + >>> drop_right([1, 2, 3, 4], 2) + [1, 2] + + .. versionadded:: 1.1.0 + + .. versionchanged:: 3.0.0 + Made ``n`` default to ``1``. + """ + length = len(array) + return drop_right_while(array, lambda _, index: (length - index) <= n) + + +@t.overload +def drop_right_while( + array: t.Sequence[T], predicate: t.Callable[[T, int, t.List[T]], t.Any] +) -> t.List[T]: ... + + +@t.overload +def drop_right_while(array: t.Sequence[T], predicate: t.Callable[[T, int], t.Any]) -> t.List[T]: ... + + +@t.overload +def drop_right_while(array: t.Sequence[T], predicate: t.Callable[[T], t.Any]) -> t.List[T]: ... + + +@t.overload +def drop_right_while(array: t.Sequence[T], predicate: None = None) -> t.List[T]: ... + + +def drop_right_while(array, predicate=None): + """ + Creates a slice of `array` excluding elements dropped from the end. Elements are dropped until + the `predicate` returns falsey. The `predicate` is invoked with three arguments: ``(value, + index, array)``. + + Args: + array: List to process. + predicate: Predicate called per iteration + + Returns: + Dropped list. + + Example: + + >>> drop_right_while([1, 2, 3, 4], lambda x: x >= 3) + [1, 2] + + .. versionadded:: 1.1.0 + """ + n = len(array) + for is_true, _, _, _ in iteriteratee(array, predicate, reverse=True): + if is_true: + n -= 1 + else: + break + + return array[:n] + + +@t.overload +def drop_while( + array: t.Sequence[T], predicate: t.Callable[[T, int, t.List[T]], t.Any] +) -> t.List[T]: ... + + +@t.overload +def drop_while(array: t.Sequence[T], predicate: t.Callable[[T, int], t.Any]) -> t.List[T]: ... + + +@t.overload +def drop_while(array: t.Sequence[T], predicate: t.Callable[[T], t.Any]) -> t.List[T]: ... + + +@t.overload +def drop_while(array: t.Sequence[T], predicate: None = None) -> t.List[T]: ... + + +def drop_while(array, predicate=None): + """ + Creates a slice of `array` excluding elements dropped from the beginning. Elements are dropped + until the `predicate` returns falsey. The `predicate` is invoked with three arguments: ``(value, + index, array)``. + + Args: + array: List to process. + predicate: Predicate called per iteration + + Returns: + Dropped list. + + Example: + + >>> drop_while([1, 2, 3, 4], lambda x: x < 3) + [3, 4] + + .. versionadded:: 1.1.0 + """ + n = 0 + for is_true, _, _, _ in iteriteratee(array, predicate): + if is_true: + n += 1 + else: + break + + return array[n:] + + +def duplicates( + array: t.Sequence[T], iteratee: t.Union[t.Callable[[T], t.Any], IterateeObjT, None] = None +) -> t.List[T]: + """ + Creates a unique list of duplicate values from `array`. If iteratee is passed, each element of + array is passed through an iteratee before duplicates are computed. The iteratee is invoked with + three arguments: ``(value, index, array)``. If an object path is passed for iteratee, the + created iteratee will return the path value of the given element. If an object is passed for + iteratee, the created filter style iteratee will return ``True`` for elements that have the + properties of the given object, else ``False``. + + Args: + array: List to process. + iteratee: Iteratee applied per iteration. + + Returns: + List of duplicates. + + Example: + + >>> duplicates([0, 1, 3, 2, 3, 1]) + [3, 1] + + .. versionadded:: 3.0.0 + """ + if iteratee: + cbk = pyd.iteratee(iteratee) + computed = [cbk(item) for item in array] + else: + computed = array # type: ignore + + # NOTE: Using array[i] instead of item since iteratee could have modified + # returned item values. + lst = uniq(array[i] for i, _ in iterduplicates(computed)) + + return lst + + +def fill( + array: t.Sequence[T], value: T2, start: int = 0, end: t.Union[int, None] = None +) -> t.List[t.Union[T, T2]]: + """ + Fills elements of array with value from `start` up to, but not including, `end`. + + Args: + array: List to fill. + value: Value to fill with. + start: Index to start filling. Defaults to ``0``. + end: Index to end filling. Defaults to ``len(array)``. + + Returns: + Filled `array`. + + Example: + + >>> fill([1, 2, 3, 4, 5], 0) + [0, 0, 0, 0, 0] + >>> fill([1, 2, 3, 4, 5], 0, 1, 3) + [1, 0, 0, 4, 5] + >>> fill([1, 2, 3, 4, 5], 0, 0, 100) + [0, 0, 0, 0, 0] + + Warning: + `array` is modified in place. + + .. versionadded:: 3.1.0 + """ + if end is None: + end = len(array) + else: + end = min(end, len(array)) + + # Use this style of assignment so that `array` is mutated. + array[:] = array[:start] + [value] * len(array[start:end]) + array[end:] # type: ignore + return array # type: ignore + + +@t.overload +def find_index(array: t.Iterable[T], predicate: t.Callable[[T, int, t.List[T]], t.Any]) -> int: ... + + +@t.overload +def find_index(array: t.Iterable[T], predicate: t.Callable[[T, int], t.Any]) -> int: ... + + +@t.overload +def find_index(array: t.Iterable[T], predicate: t.Callable[[T], t.Any]) -> int: ... + + +@t.overload +def find_index(array: t.Iterable[t.Any], predicate: IterateeObjT) -> int: ... + + +@t.overload +def find_index(array: t.Iterable[t.Any], predicate: None = None) -> int: ... + + +def find_index(array, predicate=None): + """ + This method is similar to :func:`pydash.collections.find`, except that it returns the index of + the element that passes the predicate check, instead of the element itself. + + Args: + array: List to process. + predicate: Predicate applied per iteration. + + Returns: + Index of found item or ``-1`` if not found. + + Example: + + >>> find_index([1, 2, 3, 4], lambda x: x >= 3) + 2 + >>> find_index([1, 2, 3, 4], lambda x: x > 4) + -1 + + .. versionadded:: 1.0.0 + """ + search = (i for is_true, _, i, _ in iteriteratee(array, predicate) if is_true) + return next(search, -1) + + +@t.overload +def find_last_index( + array: t.Iterable[T], predicate: t.Callable[[T, int, t.List[T]], t.Any] +) -> int: ... + + +@t.overload +def find_last_index(array: t.Iterable[T], predicate: t.Callable[[T, int], t.Any]) -> int: ... + + +@t.overload +def find_last_index(array: t.Iterable[T], predicate: t.Callable[[T], t.Any]) -> int: ... + + +@t.overload +def find_last_index(array: t.Iterable[t.Any], predicate: IterateeObjT) -> int: ... + + +@t.overload +def find_last_index(array: t.Iterable[t.Any], predicate: None = None) -> int: ... + + +def find_last_index(array, predicate=None): + """ + This method is similar to :func:`find_index`, except that it iterates over elements from right + to left. + + Args: + array: List to process. + predicate: Predicate applied per iteration. + + Returns: + Index of found item or ``-1`` if not found. + + Example: + + >>> find_last_index([1, 2, 3, 4], lambda x: x >= 3) + 3 + >>> find_last_index([1, 2, 3, 4], lambda x: x > 4) + -1 + + .. versionadded:: 1.0.0 + """ + search = (i for is_true, _, i, _ in iteriteratee(array, predicate, reverse=True) if is_true) + return next(search, -1) + + +@t.overload +def flatten(array: t.Iterable[t.Iterable[T]]) -> t.List[T]: ... + + +@t.overload +def flatten(array: t.Iterable[T]) -> t.List[T]: ... + + +def flatten(array): + """ + Flattens array a single level deep. + + Args: + array: List to flatten. + + Returns: + Flattened list. + + Example: + + >>> flatten([[1], [2, [3]], [[4]]]) + [1, 2, [3], [4]] + + + .. versionadded:: 1.0.0 + + .. versionchanged:: 2.0.0 + Removed `callback` option. Added ``is_deep`` option. Made it shallow + by default. + + .. versionchanged:: 4.0.0 + Removed ``is_deep`` option. Use :func:`flatten_deep` instead. + """ + return flatten_depth(array, depth=1) + + +def flatten_deep(array: t.Iterable[t.Any]) -> t.List[t.Any]: + """ + Flattens an array recursively. + + Args: + array: List to flatten. + + Returns: + Flattened list. + + Example: + + >>> flatten_deep([[1], [2, [3]], [[4]]]) + [1, 2, 3, 4] + + .. versionadded:: 2.0.0 + """ + return flatten_depth(array, depth=-1) + + +def flatten_depth(array: t.Iterable[t.Any], depth: int = 1) -> t.List[t.Any]: + """ + Recursively flatten `array` up to `depth` times. + + Args: + array: List to flatten. + depth: Depth to flatten to. Defaults to ``1``. + + Returns: + Flattened list. + + Example: + + >>> flatten_depth([[[1], [2, [3]], [[4]]]], 1) + [[1], [2, [3]], [[4]]] + >>> flatten_depth([[[1], [2, [3]], [[4]]]], 2) + [1, 2, [3], [4]] + >>> flatten_depth([[[1], [2, [3]], [[4]]]], 3) + [1, 2, 3, 4] + >>> flatten_depth([[[1], [2, [3]], [[4]]]], 4) + [1, 2, 3, 4] + + .. versionadded:: 4.0.0 + """ + return list(iterflatten(array, depth=depth)) + + +@t.overload +def from_pairs(pairs: t.Iterable[t.Tuple[T, T2]]) -> t.Dict[T, T2]: ... + + +@t.overload +def from_pairs( + pairs: t.Iterable[t.List[t.Union[T, T2]]], +) -> t.Dict[t.Union[T, T2], t.Union[T, T2]]: ... + + +def from_pairs(pairs): + """ + Returns a dict from the given list of pairs. + + Args: + pairs: List of key-value pairs. + + Returns: + dict + + Example: + + >>> from_pairs([["a", 1], ["b", 2]]) == {"a": 1, "b": 2} + True + + .. versionadded:: 4.0.0 + """ + return dict(pairs) + + +def head(array: t.Sequence[T]) -> t.Union[T, None]: + """ + Return the first element of `array`. + + Args: + array: List to process. + + Returns: + First element of list. + + Example: + + >>> head([1, 2, 3, 4]) + 1 + + .. versionadded:: 1.0.0 + + .. versionchanged:: + Renamed from ``first`` to ``head``. + """ + return base_get(array, 0, default=None) + + +def index_of(array: t.Sequence[T], value: T, from_index: int = 0) -> int: + """ + Gets the index at which the first occurrence of value is found. + + Args: + array: List to search. + value: Value to search for. + from_index: Index to search from. + + Returns: + Index of found item or ``-1`` if not found. + + Example: + + >>> index_of([1, 2, 3, 4], 2) + 1 + >>> index_of([2, 1, 2, 3], 2, from_index=1) + 2 + + .. versionadded:: 1.0.0 + """ + try: + return array.index(value, from_index) + except ValueError: + return -1 + + +def initial(array: t.Sequence[T]) -> t.Sequence[T]: + """ + Return all but the last element of `array`. + + Args: + array: List to process. + + Returns: + Initial part of `array`. + + Example: + + >>> initial([1, 2, 3, 4]) + [1, 2, 3] + + .. versionadded:: 1.0.0 + """ + return array[:-1] + + +@t.overload +def intercalate(array: t.Iterable[t.Iterable[T]], separator: T2) -> t.List[t.Union[T, T2]]: ... + + +@t.overload +def intercalate(array: t.Iterable[T], separator: T2) -> t.List[t.Union[T, T2]]: ... + + +def intercalate(array, separator): + """ + Like :func:`intersperse` for lists of lists but shallowly flattening the result. + + Args: + array: List to intercalate. + separator: Element to insert. + + Returns: + Intercalated list. + + Example: + + >>> intercalate([1, [2], [3], 4], "x") + [1, 'x', 2, 'x', 3, 'x', 4] + + + .. versionadded:: 2.0.0 + """ + return flatten(intersperse(array, separator)) + + +def interleave(*arrays: t.Iterable[T]) -> t.List[T]: + """ + Merge multiple lists into a single list by inserting the next element of each list by sequential + round-robin into the new list. + + Args: + arrays: Lists to interleave. + + Returns: + Interleaved list. + + Example: + + >>> interleave([1, 2, 3], [4, 5, 6], [7, 8, 9]) + [1, 4, 7, 2, 5, 8, 3, 6, 9] + + .. versionadded:: 2.0.0 + """ + return list(iterinterleave(*arrays)) + + +def intersection(array: t.Sequence[T], *others: t.Iterable[t.Any]) -> t.List[T]: + """ + Computes the intersection of all the passed-in arrays. + + Args: + array: The array to find the intersection of. + others: Lists to check for intersection with `array`. + + Returns: + Intersection of provided lists. + + Example: + + >>> intersection([1, 2, 3], [1, 2, 3, 4, 5], [2, 3]) + [2, 3] + + >>> intersection([1, 2, 3]) + [1, 2, 3] + + .. versionadded:: 1.0.0 + + .. versionchanged:: 4.0.0 + Support finding intersection of unhashable types. + """ + return intersection_with(array, *others) + + +@t.overload +def intersection_by( + array: t.Sequence[T], + *others: t.Iterable[t.Any], + iteratee: t.Union[t.Callable[[T], t.Any], IterateeObjT], +) -> t.List[T]: ... + + +@t.overload +def intersection_by( + array: t.Sequence[T], *others: t.Union[t.Iterable[t.Any], t.Callable[[T], t.Any], IterateeObjT] +) -> t.List[T]: ... + + +def intersection_by(array, *others, **kwargs): + """ + This method is like :func:`intersection` except that it accepts an iteratee which is invoked for + each element of each array to generate the criterion by which they're compared. The order and + references of result values are determined by `array`. The iteratee is invoked with one + argument: ``(value)``. + + Args: + array: The array to find the intersection of. + others: Lists to check for intersection with `array`. + + Keyword Args: + iteratee: Function to transform the elements of the arrays. Defaults to + :func:`.identity`. + + Returns: + Intersection of provided lists. + + Example: + + >>> intersection_by([1.2, 1.5, 1.7, 2.8], [0.9, 3.2], round) + [1.2, 2.8] + + .. versionadded:: 4.0.0 + """ + array = array[:] + + if not others: + return array + + iteratee, others = parse_iteratee("iteratee", *others, **kwargs) + + # Sort by smallest list length to make intersection faster. + others = sorted(others, key=lambda other: len(other)) + + for other in others: + array = list(iterintersection(array, other, iteratee=iteratee)) + if not array: + break + + return array + + +@t.overload +def intersection_with( + array: t.Sequence[T], *others: t.Iterable[T2], comparator: t.Callable[[T, T2], t.Any] +) -> t.List[T]: ... + + +@t.overload +def intersection_with( + array: t.Sequence[T], *others: t.Union[t.Iterable[T2], t.Callable[[T, T2], t.Any]] +) -> t.List[T]: ... + + +def intersection_with(array, *others, **kwargs): + """ + This method is like :func:`intersection` except that it accepts a comparator which is invoked to + compare the elements of all arrays. The order and references of result values are determined by + the first array. The comparator is invoked with two arguments: ``(arr_val, oth_val)``. + + Args: + array: The array to find the intersection of. + others: Lists to check for intersection with `array`. + + Keyword Args: + comparator: Function to compare the elements of the arrays. Defaults to + :func:`.is_equal`. + + Returns: + Intersection of provided lists. + + Example: + + >>> array = ["apple", "banana", "pear"] + >>> others = (["avocado", "pumpkin"], ["peach"]) + >>> comparator = lambda a, b: a[0] == b[0] + >>> intersection_with(array, *others, comparator=comparator) + ['pear'] + + .. versionadded:: 4.0.0 + """ + array = array[:] + + if not others: + return array + + comparator, others = parse_iteratee("comparator", *others, **kwargs) + + # Sort by smallest list length to reduce to intersection faster. + others = sorted(others, key=lambda other: len(other)) + + for other in others: + array = list(iterintersection(array, other, comparator=comparator)) + if not array: + break + + return array + + +def intersperse(array: t.Iterable[T], separator: T2) -> t.List[t.Union[T, T2]]: + """ + Insert a separating element between the elements of `array`. + + Args: + array: List to intersperse. + separator: Element to insert. + + Returns: + Interspersed list. + + Example: + + >>> intersperse([1, [2], [3], 4], "x") + [1, 'x', [2], 'x', [3], 'x', 4] + + .. versionadded:: 2.0.0 + """ + return list(iterintersperse(array, separator)) + + +def last(array: t.Sequence[T]) -> t.Union[T, None]: + """ + Return the last element of `array`. + + Args: + array: List to process. + + Returns: + Last part of `array`. + + Example: + + >>> last([1, 2, 3, 4]) + 4 + + .. versionadded:: 1.0.0 + """ + return base_get(array, -1, default=None) + + +def last_index_of( + array: t.Sequence[t.Any], value: t.Any, from_index: t.Union[int, None] = None +) -> int: + """ + Gets the index at which the last occurrence of value is found. + + Args: + array: List to search. + value: Value to search for. + from_index: Index to search from. + + Returns: + Index of found item or ``-1`` if not found. + + Example: + + >>> last_index_of([1, 2, 2, 4], 2) + 2 + >>> last_index_of([1, 2, 2, 4], 2, from_index=1) + 1 + + .. versionadded:: 1.0.0 + """ + index = array_len = len(array) + + try: + # safe as we are catching any type errors + from_index = int(from_index) # type: ignore + except (TypeError, ValueError): + pass + else: + # Set starting index base on from_index offset. + index = max(0, index + from_index) if from_index < 0 else min(from_index, index - 1) + + while index: + if index < array_len and array[index] == value: + return index + index -= 1 + return -1 + + +@t.overload +def mapcat( + array: t.Iterable[T], + iteratee: t.Callable[[T, int, t.List[T]], t.Union[t.List[T2], t.List[t.List[T2]]]], +) -> t.List[T2]: ... + + +@t.overload +def mapcat(array: t.Iterable[T], iteratee: t.Callable[[T, int, t.List[T]], T2]) -> t.List[T2]: ... + + +@t.overload +def mapcat( + array: t.Iterable[T], iteratee: t.Callable[[T, int], t.Union[t.List[T2], t.List[t.List[T2]]]] +) -> t.List[T2]: ... + + +@t.overload +def mapcat(array: t.Iterable[T], iteratee: t.Callable[[T, int], T2]) -> t.List[T2]: ... + + +@t.overload +def mapcat( + array: t.Iterable[T], iteratee: t.Callable[[T], t.Union[t.List[T2], t.List[t.List[T2]]]] +) -> t.List[T2]: ... + + +@t.overload +def mapcat(array: t.Iterable[T], iteratee: t.Callable[[T], T2]) -> t.List[T2]: ... + + +@t.overload +def mapcat( + array: t.Iterable[t.Union[t.List[T], t.List[t.List[T]]]], iteratee: None = None +) -> t.List[t.Union[T, t.List[T]]]: ... + + +def mapcat(array, iteratee=None): + """ + Map an iteratee to each element of a list and concatenate the results into a single list using + :func:`concat`. + + Args: + array: List to map and concatenate. + iteratee: Iteratee to apply to each element. + + Returns: + Mapped and concatenated list. + + Example: + + >>> mapcat(range(4), lambda x: list(range(x))) + [0, 0, 1, 0, 1, 2] + + .. versionadded:: 2.0.0 + """ + return concat(*pyd.map_(array, iteratee)) + + +def nth(array: t.Iterable[T], pos: int = 0) -> t.Union[T, None]: + """ + Gets the element at index n of array. + + Args: + array: List passed in by the user. + pos: Index of element to return. + + Returns: + Returns the element at :attr:`pos`. + + Example: + + >>> nth([1, 2, 3], 0) + 1 + >>> nth([3, 4, 5, 6], 2) + 5 + >>> nth([11, 22, 33], -1) + 33 + >>> nth([11, 22, 33]) + 11 + + .. versionadded:: 4.0.0 + """ + return pyd.get(array, pos) + + +def pop(array: t.List[T], index: int = -1) -> T: + """ + Remove element of array at `index` and return element. + + Args: + array: List to pop from. + index: Index to remove element from. Defaults to ``-1``. + + Returns: + Value at `index`. + + Warning: + `array` is modified in place. + + Example: + + >>> array = [1, 2, 3, 4] + >>> item = pop(array) + >>> item + 4 + >>> array + [1, 2, 3] + >>> item = pop(array, index=0) + >>> item + 1 + >>> array + [2, 3] + + .. versionadded:: 2.2.0 + """ + return array.pop(index) + + +def pull(array: t.List[T], *values: T) -> t.List[T]: + """ + Removes all provided values from the given array. + + Args: + array: List to pull from. + values: Values to remove. + + Returns: + Modified `array`. + + Warning: + `array` is modified in place. + + Example: + + >>> pull([1, 2, 2, 3, 3, 4], 2, 3) + [1, 4] + + .. versionadded:: 1.0.0 + + .. versionchanged:: 4.0.0 + :func:`pull` method now calls :func:`pull_all` method for the desired + functionality. + """ + return pull_all(array, values) + + +def pull_all(array: t.List[T], values: t.Iterable[T]) -> t.List[T]: + """ + Removes all provided values from the given array. + + Args: + array: Array to modify. + values: Values to remove. + + Returns: + Modified `array`. + + Example: + + >>> pull_all([1, 2, 2, 3, 3, 4], [2, 3]) + [1, 4] + + .. versionadded:: 4.0.0 + """ + # Use this style of assignment so that `array` is mutated. + array[:] = without(array, *values) + return array + + +def pull_all_by( + array: t.List[T], + values: t.Iterable[T], + iteratee: t.Union[IterateeObjT, t.Callable[[T], t.Any], None] = None, +) -> t.List[T]: + """ + This method is like :func:`pull_all` except that it accepts iteratee which is invoked for each + element of array and values to generate the criterion by which they're compared. The iteratee is + invoked with one argument: ``(value)``. + + Args: + array: Array to modify. + values: Values to remove. + iteratee: Function to transform the elements of the arrays. Defaults to + :func:`.identity`. + + Returns: + Modified `array`. + + Example: + + >>> array = [{"x": 1}, {"x": 2}, {"x": 3}, {"x": 1}] + >>> pull_all_by(array, [{"x": 1}, {"x": 3}], "x") + [{'x': 2}] + + .. versionadded:: 4.0.0 + """ + values = difference(array, difference_by(array, values, iteratee=iteratee)) + return pull_all(array, values) + + +def pull_all_with( + array: t.List[T], + values: t.Iterable[T], + comparator: t.Union[t.Callable[[T, T], t.Any], None] = None, +) -> t.List[T]: + """ + This method is like :func:`pull_all` except that it accepts comparator which is invoked to + compare elements of array to values. The comparator is invoked with two arguments: ``(arr_val, + oth_val)``. + + Args: + array: Array to modify. + values: Values to remove. + comparator: Function to compare the elements of the arrays. Defaults to + :func:`.is_equal`. + + Returns: + Modified `array`. + + Example: + + >>> array = [{"x": 1, "y": 2}, {"x": 3, "y": 4}, {"x": 5, "y": 6}] + >>> res = pull_all_with(array, [{"x": 3, "y": 4}], lambda a, b: a == b) + >>> res == [{"x": 1, "y": 2}, {"x": 5, "y": 6}] + True + >>> array = [{"x": 1, "y": 2}, {"x": 3, "y": 4}, {"x": 5, "y": 6}] + >>> res = pull_all_with(array, [{"x": 3, "y": 4}], lambda a, b: a != b) + >>> res == [{"x": 3, "y": 4}] + True + + .. versionadded:: 4.0.0 + """ + values = difference(array, difference_with(array, values, comparator=comparator)) + return pull_all(array, values) + + +def pull_at(array: t.List[T], *indexes: int) -> t.List[T]: + """ + Removes elements from `array` corresponding to the specified indexes and returns a list of the + removed elements. Indexes may be specified as a list of indexes or as individual arguments. + + Args: + array: List to pull from. + indexes: Indexes to pull. + + Returns: + Modified `array`. + + Warning: + `array` is modified in place. + + Example: + + >>> pull_at([1, 2, 3, 4], 0, 2) + [2, 4] + + .. versionadded:: 1.1.0 + """ + flat_indexes = flatten(indexes) + for index in sorted(flat_indexes, reverse=True): + del array[index] + + return array + + +def push(array: t.List[T], *items: T2) -> t.List[t.Union[T, T2]]: + """ + Push items onto the end of `array` and return modified `array`. + + Args: + array: List to push to. + items: Items to append. + + Returns: + Modified `array`. + + Warning: + `array` is modified in place. + + Example: + + >>> array = [1, 2, 3] + >>> push(array, 4, 5, [6]) + [1, 2, 3, 4, 5, [6]] + + .. versionadded:: 2.2.0 + + .. versionchanged:: 4.0.0 + Removed alias ``append``. + """ + for item in items: + array.append(item) # type: ignore + return array # type: ignore + + +def remove( + array: t.List[T], + predicate: t.Union[ + t.Callable[[T, int, t.List[T]], t.Any], + t.Callable[[T, int], t.Any], + t.Callable[[T], t.Any], + None, + ] = None, +) -> t.List[T]: + """ + Removes all elements from a list that the predicate returns truthy for and returns an array of + removed elements. + + Args: + array: List to remove elements from. + predicate: Predicate applied per iteration. + + Returns: + Removed elements of `array`. + + Warning: + `array` is modified in place. + + Example: + + >>> array = [1, 2, 3, 4] + >>> items = remove(array, lambda x: x >= 3) + >>> items + [3, 4] + >>> array + [1, 2] + + .. versionadded:: 1.0.0 + """ + removed = [] + kept = [] + + for is_true, _, i, _ in iteriteratee(array, predicate): + if is_true: + removed.append(array[i]) + else: + kept.append(array[i]) + + # Modify array in place. + array[:] = kept + + return removed + + +def reverse(array: SequenceT) -> SequenceT: + """ + Return `array` in reverse order. + + Args: + array: Object to process. + + Returns: + Reverse of object. + + Example: + + >>> reverse([1, 2, 3, 4]) + [4, 3, 2, 1] + + .. versionadded:: 2.2.0 + """ + # NOTE: Using this method to reverse object since it works for both lists and strings. + return array[::-1] # type: ignore + + +def shift(array: t.List[T]) -> T: + """ + Remove the first element of `array` and return it. + + Args: + array: List to shift. + + Returns: + First element of `array`. + + Warning: + `array` is modified in place. + + Example: + + >>> array = [1, 2, 3, 4] + >>> item = shift(array) + >>> item + 1 + >>> array + [2, 3, 4] + + .. versionadded:: 2.2.0 + """ + return pop(array, 0) + + +def slice_(array: SequenceT, start: int = 0, end: t.Union[int, None] = None) -> SequenceT: + """ + Slices `array` from the `start` index up to, but not including, the `end` index. + + Args: + array: Array to slice. + start: Start index. Defaults to ``0``. + end: End index. Defaults to selecting the value at ``start`` index. + + Returns: + Sliced list. + + Example: + + >>> slice_([1, 2, 3, 4]) + [1] + >>> slice_([1, 2, 3, 4], 1) + [2] + >>> slice_([1, 2, 3, 4], 1, 3) + [2, 3] + + .. versionadded:: 1.1.0 + """ + if end is None: + end = (start + 1) if start >= 0 else (len(array) + start + 1) + + return array[start:end] # type: ignore + + +@t.overload +def sort( + array: t.List["SupportsRichComparisonT"], + comparator: None = None, + key: None = None, + reverse: bool = False, +) -> t.List["SupportsRichComparisonT"]: ... + + +@t.overload +def sort( + array: t.List[T], comparator: t.Callable[[T, T], int], *, reverse: bool = False +) -> t.List[T]: ... + + +@t.overload +def sort( + array: t.List[T], *, key: t.Callable[[T], "SupportsRichComparisonT"], reverse: bool = False +) -> t.List[T]: ... + + +def sort(array, comparator=None, key=None, reverse=False): + """ + Sort `array` using optional `comparator`, `key`, and `reverse` options and return sorted + `array`. + + Note: + Python 3 removed the option to pass a custom comparator function and instead only allows a + key function. Therefore, if a comparator function is passed in, it will be converted to a + key function automatically using ``functools.cmp_to_key``. + + Args: + array: List to sort. + comparator: A custom comparator function used to sort the list. + Function should accept two arguments and return a negative, zero, or position number + depending on whether the first argument is considered smaller than, equal to, or larger + than the second argument. Defaults to ``None``. This argument is mutually exclusive with + `key`. + key: A function of one argument used to extract a comparator key from each list element. + Defaults to ``None``. This argument is mutually exclusive with `comparator`. + reverse: Whether to reverse the sort. Defaults to ``False``. + + Returns: + Sorted list. + + Warning: + `array` is modified in place. + + Example: + + >>> sort([2, 1, 4, 3]) + [1, 2, 3, 4] + >>> sort([2, 1, 4, 3], reverse=True) + [4, 3, 2, 1] + >>> results = sort([{'a': 2, 'b': 1},\ + {'a': 3, 'b': 2},\ + {'a': 0, 'b': 3}],\ + key=lambda item: item['a']) + >>> assert results == [{'a': 0, 'b': 3},\ + {'a': 2, 'b': 1},\ + {'a': 3, 'b': 2}] + + .. versionadded:: 2.2.0 + """ + if comparator and key: + raise ValueError('The "comparator" and "key" arguments are mutually exclusive') + + if comparator: + key = cmp_to_key(comparator) + + array.sort(key=key, reverse=reverse) + return array + + +def sorted_index( + array: t.Sequence["SupportsRichComparisonT"], value: "SupportsRichComparisonT" +) -> int: + """ + Uses a binary search to determine the lowest index at which `value` should be inserted into + `array` in order to maintain its sort order. + + Args: + array: List to inspect. + value: Value to evaluate. + + Returns: + Returns the index at which `value` should be inserted into `array`. + + Example: + + >>> sorted_index([1, 2, 2, 3, 4], 2) + 1 + + .. versionadded:: 1.0.0 + + .. versionchanged:: 4.0.0 + Move iteratee support to :func:`sorted_index_by`. + """ + return sorted_index_by(array, value) + + +@t.overload +def sorted_index_by( + array: t.Sequence[T], + value: T, + iteratee: t.Union[IterateeObjT, t.Callable[[T], "SupportsRichComparisonT"]], +) -> int: ... + + +@t.overload +def sorted_index_by( + array: t.Sequence["SupportsRichComparisonT"], + value: "SupportsRichComparisonT", + iteratee: None = None, +) -> int: ... + + +def sorted_index_by(array, value, iteratee=None): + """ + This method is like :func:`sorted_index` except that it accepts iteratee which is invoked for + `value` and each element of `array` to compute their sort ranking. The iteratee is invoked with + one argument: ``(value)``. + + Args: + array: List to inspect. + value: Value to evaluate. + iteratee: The iteratee invoked per element. Defaults to :func:`.identity`. + + Returns: + Returns the index at which `value` should be inserted into `array`. + + Example: + + >>> array = [{"x": 4}, {"x": 5}] + >>> sorted_index_by(array, {"x": 4}, lambda o: o["x"]) + 0 + >>> sorted_index_by(array, {"x": 4}, "x") + 0 + + .. versionadded:: 4.0.0 + """ + if iteratee: + # Generate array of sorted keys computed using iteratee. + iteratee = pyd.iteratee(iteratee) + array = sorted(iteratee(item) for item in array) + value = iteratee(value) + + return bisect_left(array, value) + + +def sorted_index_of( + array: t.Sequence["SupportsRichComparisonT"], value: "SupportsRichComparisonT" +) -> int: + """ + Returns the index of the matched `value` from the sorted `array`, else ``-1``. + + Args: + array: Array to inspect. + value: Value to search for. + + Returns: + Returns the index of the first matched value, else ``-1``. + + Example: + + >>> sorted_index_of([3, 5, 7, 10], 3) + 0 + >>> sorted_index_of([10, 10, 5, 7, 3], 10) + -1 + + .. versionadded:: 4.0.0 + """ + index = sorted_index(array, value) + + if index < len(array) and array[index] == value: + return index + else: + return -1 + + +def sorted_last_index( + array: t.Sequence["SupportsRichComparisonT"], value: "SupportsRichComparisonT" +) -> int: + """ + This method is like :func:`sorted_index` except that it returns the highest index at which + `value` should be inserted into `array` in order to maintain its sort order. + + Args: + array: List to inspect. + value: Value to evaluate. + + Returns: + Returns the index at which `value` should be inserted into `array`. + + Example: + + >>> sorted_last_index([1, 2, 2, 3, 4], 2) + 3 + + .. versionadded:: 1.1.0 + + .. versionchanged:: 4.0.0 + Move iteratee support to :func:`sorted_last_index_by`. + """ + return sorted_last_index_by(array, value) + + +@t.overload +def sorted_last_index_by( + array: t.Sequence[T], + value: T, + iteratee: t.Union[IterateeObjT, t.Callable[[T], "SupportsRichComparisonT"]], +) -> int: ... + + +@t.overload +def sorted_last_index_by( + array: t.Sequence["SupportsRichComparisonT"], + value: "SupportsRichComparisonT", + iteratee: None = None, +) -> int: ... + + +def sorted_last_index_by(array, value, iteratee=None): + """ + This method is like :func:`sorted_last_index` except that it accepts iteratee which is invoked + for `value` and each element of `array` to compute their sort ranking. The iteratee is invoked + with one argument: ``(value)``. + + Args: + array: List to inspect. + value: Value to evaluate. + iteratee: The iteratee invoked per element. Defaults to :func:`.identity`. + + Returns: + Returns the index at which `value` should be inserted into `array`. + + Example: + + >>> array = [{"x": 4}, {"x": 5}] + >>> sorted_last_index_by(array, {"x": 4}, lambda o: o["x"]) + 1 + >>> sorted_last_index_by(array, {"x": 4}, "x") + 1 + """ + if iteratee: + # Generate array of sorted keys computed using iteratee. + iteratee = pyd.iteratee(iteratee) + array = sorted(iteratee(item) for item in array) + value = iteratee(value) + + return bisect_right(array, value) + + +def sorted_last_index_of( + array: t.Sequence["SupportsRichComparisonT"], value: "SupportsRichComparisonT" +) -> int: + """ + This method is like :func:`last_index_of` except that it performs a binary search on a sorted + `array`. + + Args: + array: Array to inspect. + value: Value to search for. + + Returns: + Returns the index of the matched value, else ``-1``. + + Example: + + >>> sorted_last_index_of([4, 5, 5, 5, 6], 5) + 3 + >>> sorted_last_index_of([6, 5, 5, 5, 4], 6) + -1 + + .. versionadded:: 4.0.0 + """ + index = sorted_last_index(array, value) - 1 + + if index < len(array) and array[index] == value: + return index + else: + return -1 + + +def sorted_uniq(array: t.Iterable["SupportsRichComparisonT"]) -> t.List["SupportsRichComparisonT"]: + """ + Return sorted array with unique elements. + + Args: + array: List of values to be sorted. + + Returns: + List of unique elements in a sorted fashion. + + Example: + + >>> sorted_uniq([4, 2, 2, 5]) + [2, 4, 5] + >>> sorted_uniq([-2, -2, 4, 1]) + [-2, 1, 4] + + .. versionadded:: 4.0.0 + """ + return sorted(uniq(array)) + + +def sorted_uniq_by( + array: t.Iterable["SupportsRichComparisonT"], + iteratee: t.Union[ + t.Callable[["SupportsRichComparisonT"], "SupportsRichComparisonT"], None + ] = None, +) -> t.List["SupportsRichComparisonT"]: + """ + This method is like :func:`sorted_uniq` except that it accepts iteratee which is invoked for + each element in array to generate the criterion by which uniqueness is computed. The order of + result values is determined by the order they occur in the array. The iteratee is invoked with + one argument: ``(value)``. + + Args: + array: List of values to be sorted. + iteratee: Function to transform the elements of the arrays. Defaults to + :func:`.identity`. + + Returns: + Unique list. + + Example: + + >>> sorted_uniq_by([3, 2, 1, 3, 2, 1], lambda val: val % 2) + [2, 3] + + .. versionadded:: 4.0.0 + """ + return sorted(uniq_by(array, iteratee=iteratee)) + + +def splice( + array: MutableSequenceT, start: int, count: t.Union[int, None] = None, *items: t.Any +) -> MutableSequenceT: + """ + Modify the contents of `array` by inserting elements starting at index `start` and removing + `count` number of elements after. + + Args: + array: List to splice. + start: Start to splice at. + count: Number of items to remove starting at `start`. If ``None`` then all + items after `start` are removed. Defaults to ``None``. + items: Elements to insert starting at `start`. Each item is inserted in the order + given. + + Returns: + The removed elements of `array` or the spliced string. + + Warning: + `array` is modified in place if ``list``. + + Example: + + >>> array = [1, 2, 3, 4] + >>> splice(array, 1) + [2, 3, 4] + >>> array + [1] + >>> array = [1, 2, 3, 4] + >>> splice(array, 1, 2) + [2, 3] + >>> array + [1, 4] + >>> array = [1, 2, 3, 4] + >>> splice(array, 1, 2, 0, 0) + [2, 3] + >>> array + [1, 0, 0, 4] + + .. versionadded:: 2.2.0 + + .. versionchanged:: 3.0.0 + Support string splicing. + """ + if count is None: + count = len(array) - start + + is_string = pyd.is_string(array) + + if is_string: + # allow reassignment with different type + array = list(array) # type: ignore + + removed = array[start : start + count] + del array[start : start + count] + + for item in reverse(items): + array.insert(start, item) + + if is_string: + return "".join(array) # type: ignore + else: + return removed # type: ignore + + +def split_at(array: t.Sequence[T], index: int) -> t.List[t.Sequence[T]]: + """ + Returns a list of two lists composed of the split of `array` at `index`. + + Args: + array: List to split. + index: Index to split at. + + Returns: + Split list. + + Example: + + >>> split_at([1, 2, 3, 4], 2) + [[1, 2], [3, 4]] + + .. versionadded:: 2.0.0 + """ + return [array[:index], array[index:]] + + +def tail(array: t.Sequence[T]) -> t.Sequence[T]: + """ + Return all but the first element of `array`. + + Args: + array: List to process. + + Returns: + Rest of the list. + + Example: + + >>> tail([1, 2, 3, 4]) + [2, 3, 4] + + .. versionadded:: 1.0.0 + + .. versionchanged:: 4.0.0 + Renamed from ``rest`` to ``tail``. + """ + return array[1:] + + +def take(array: t.Sequence[T], n: int = 1) -> t.Sequence[T]: + """ + Creates a slice of `array` with `n` elements taken from the beginning. + + Args: + array: List to process. + n: Number of elements to take. Defaults to ``1``. + + Returns: + Taken list. + + Example: + + >>> take([1, 2, 3, 4], 2) + [1, 2] + + .. versionadded:: 1.0.0 + + .. versionchanged:: 1.1.0 + Added ``n`` argument and removed as alias of :func:`first`. + + .. versionchanged:: 3.0.0 + Made ``n`` default to ``1``. + """ + return take_while(array, lambda _, index: index < n) + + +def take_right(array: t.Sequence[T], n: int = 1) -> t.Sequence[T]: + """ + Creates a slice of `array` with `n` elements taken from the end. + + Args: + array: List to process. + n: Number of elements to take. Defaults to ``1``. + + Returns: + Taken list. + + Example: + + >>> take_right([1, 2, 3, 4], 2) + [3, 4] + + .. versionadded:: 1.1.0 + + .. versionchanged:: 3.0.0 + Made ``n`` default to ``1``. + """ + length = len(array) + return take_right_while(array, lambda _, index: (length - index) <= n) + + +@t.overload +def take_right_while( + array: t.Sequence[T], predicate: t.Callable[[T, int, t.List[T]], t.Any] +) -> t.Sequence[T]: ... + + +@t.overload +def take_right_while( + array: t.Sequence[T], predicate: t.Callable[[T, int], t.Any] +) -> t.Sequence[T]: ... + + +@t.overload +def take_right_while(array: t.Sequence[T], predicate: t.Callable[[T], t.Any]) -> t.Sequence[T]: ... + + +@t.overload +def take_right_while(array: t.Sequence[T], predicate: None = None) -> t.Sequence[T]: ... + + +def take_right_while(array, predicate=None): + """ + Creates a slice of `array` with elements taken from the end. Elements are taken until the + `predicate` returns falsey. The `predicate` is invoked with three arguments: ``(value, index, + array)``. + + Args: + array: List to process. + predicate: Predicate called per iteration + + Returns: + Dropped list. + + Example: + + >>> take_right_while([1, 2, 3, 4], lambda x: x >= 3) + [3, 4] + + .. versionadded:: 1.1.0 + """ + n = len(array) + for is_true, _, _, _ in iteriteratee(array, predicate, reverse=True): + if is_true: + n -= 1 + else: + break + + return array[n:] + + +@t.overload +def take_while( + array: t.Sequence[T], predicate: t.Callable[[T, int, t.List[T]], t.Any] +) -> t.List[T]: ... + + +@t.overload +def take_while(array: t.Sequence[T], predicate: t.Callable[[T, int], t.Any]) -> t.List[T]: ... + + +@t.overload +def take_while(array: t.Sequence[T], predicate: t.Callable[[T], t.Any]) -> t.List[T]: ... + + +@t.overload +def take_while(array: t.Sequence[T], predicate: None = None) -> t.List[T]: ... + + +def take_while(array, predicate=None): + """ + Creates a slice of `array` with elements taken from the beginning. Elements are taken until the + `predicate` returns falsey. The `predicate` is invoked with three arguments: ``(value, index, + array)``. + + Args: + array: List to process. + predicate: Predicate called per iteration + + Returns: + Taken list. + + Example: + + >>> take_while([1, 2, 3, 4], lambda x: x < 3) + [1, 2] + + .. versionadded:: 1.1.0 + """ + n = 0 + for is_true, _, _, _ in iteriteratee(array, predicate): + if is_true: + n += 1 + else: + break + + return array[:n] + + +@t.overload +def union(array: t.Sequence[T]) -> t.List[T]: ... + + +@t.overload +def union(array: t.Sequence[T], *others: t.Sequence[T2]) -> t.List[t.Union[T, T2]]: ... + + +def union(array, *others): + """ + Computes the union of the passed-in arrays. + + Args: + array: List to union with. + others: Lists to unionize with `array`. + + Returns: + Unionized list. + + Example: + + >>> union([1, 2, 3], [2, 3, 4], [3, 4, 5]) + [1, 2, 3, 4, 5] + + .. versionadded:: 1.0.0 + """ + if not others: + return array[:] + + return uniq(flatten([array] + list(others))) + + +@t.overload +def union_by( + array: t.Sequence[T], *others: t.Iterable[T], iteratee: t.Callable[[T], t.Any] +) -> t.List[T]: ... + + +@t.overload +def union_by( + array: t.Sequence[T], *others: t.Union[t.Iterable[T], t.Callable[[T], t.Any]] +) -> t.List[T]: ... + + +def union_by(array, *others, **kwargs): + """ + This method is similar to :func:`union` except that it accepts iteratee which is invoked for + each element of each array to generate the criterion by which uniqueness is computed. + + Args: + array: List to unionize with. + others: Lists to unionize with `array`. + + Keyword Args: + iteratee: Function to invoke on each element. + + Returns: + Unionized list. + + Example: + + >>> union_by([1, 2, 3], [2, 3, 4], iteratee=lambda x: x % 2) + [1, 2] + >>> union_by([1, 2, 3], [2, 3, 4], iteratee=lambda x: x % 9) + [1, 2, 3, 4] + + .. versionadded:: 4.0.0 + """ + if not others: + return array[:] + + iteratee, others = parse_iteratee("iteratee", *others, **kwargs) + + return uniq_by(flatten([array] + list(others)), iteratee=iteratee) + + +@t.overload +def union_with( + array: t.Sequence[T], *others: t.Iterable[T2], comparator: t.Callable[[T, T2], t.Any] +) -> t.List[T]: ... + + +@t.overload +def union_with( + array: t.Sequence[T], *others: t.Union[t.Iterable[T2], t.Callable[[T, T2], t.Any]] +) -> t.List[T]: ... + + +def union_with(array, *others, **kwargs): + """ + This method is like :func:`union` except that it accepts comparator which is invoked to compare + elements of arrays. Result values are chosen from the first array in which the value occurs. + + Args: + array: List to unionize with. + others: Lists to unionize with `array`. + + Keyword Args: + comparator: Function to compare the elements of the arrays. Defaults to + :func:`.is_equal`. + + Returns: + Unionized list. + + Example: + + >>> comparator = lambda a, b: (a % 2) == (b % 2) + >>> union_with([1, 2, 3], [2, 3, 4], comparator=comparator) + [1, 2] + >>> union_with([1, 2, 3], [2, 3, 4]) + [1, 2, 3, 4] + + .. versionadded:: 4.0.0 + """ + if not others: + return array[:] + + comparator, others = parse_iteratee("comparator", *others, **kwargs) + + return uniq_with(flatten([array] + list(others)), comparator=comparator) + + +def uniq(array: t.Iterable[T]) -> t.List[T]: + """ + Creates a duplicate-value-free version of the array. If iteratee is passed, each element of + array is passed through an iteratee before uniqueness is computed. The iteratee is invoked with + three arguments: ``(value, index, array)``. If an object path is passed for iteratee, the + created iteratee will return the path value of the given element. If an object is passed for + iteratee, the created filter style iteratee will return ``True`` for elements that have the + properties of the given object, else ``False``. + + Args: + array: List to process. + + Returns: + Unique list. + + Example: + + >>> uniq([1, 2, 3, 1, 2, 3]) + [1, 2, 3] + + .. versionadded:: 1.0.0 + + .. versionchanged:: 4.0.0 + + - Moved `iteratee` argument to :func:`uniq_by`. + - Removed alias ``unique``. + """ + return uniq_by(array) + + +def uniq_by( + array: t.Iterable[T], iteratee: t.Union[t.Callable[[T], t.Any], None] = None +) -> t.List[T]: + """ + This method is like :func:`uniq` except that it accepts iteratee which is invoked for each + element in array to generate the criterion by which uniqueness is computed. The order of result + values is determined by the order they occur in the array. The iteratee is invoked with one + argument: ``(value)``. + + Args: + array: List to process. + iteratee: Function to transform the elements of the arrays. Defaults to + :func:`.identity`. + + Returns: + Unique list. + + Example: + + >>> uniq_by([1, 2, 3, 1, 2, 3], lambda val: val % 2) + [1, 2] + + .. versionadded:: 4.0.0 + """ + return list(iterunique(array, iteratee=iteratee)) + + +def uniq_with( + array: t.Sequence[T], comparator: t.Union[t.Callable[[T, T], t.Any], None] = None +) -> t.List[T]: + """ + This method is like :func:`uniq` except that it accepts comparator which is invoked to compare + elements of array. The order of result values is determined by the order they occur in the + array.The comparator is invoked with two arguments: ``(value, other)``. + + Args: + array: List to process. + comparator: Function to compare the elements of the arrays. Defaults to + :func:`.is_equal`. + + Returns: + Unique list. + + Example: + + >>> uniq_with([1, 2, 3, 4, 5], lambda a, b: (a % 2) == (b % 2)) + [1, 2] + + .. versionadded:: 4.0.0 + """ + return list(iterunique(array, comparator=comparator)) + + +def unshift(array: t.List[T], *items: T2) -> t.List[t.Union[T, T2]]: + """ + Insert the given elements at the beginning of `array` and return the modified list. + + Args: + array: List to modify. + items: Items to insert. + + Returns: + Modified list. + + Warning: + `array` is modified in place. + + Example: + + >>> array = [1, 2, 3, 4] + >>> unshift(array, -1, -2) + [-1, -2, 1, 2, 3, 4] + >>> array + [-1, -2, 1, 2, 3, 4] + + .. versionadded:: 2.2.0 + """ + for item in reverse(items): + array.insert(0, item) # type: ignore + + return array # type: ignore + + +@t.overload +def unzip(array: t.Iterable[t.Tuple[T, T2]]) -> t.List[t.Tuple[T, T2]]: ... + + +@t.overload +def unzip(array: t.Iterable[t.Tuple[T, T2, T3]]) -> t.List[t.Tuple[T, T2, T3]]: ... + + +@t.overload +def unzip(array: t.Iterable[t.Tuple[T, T2, T3, T4]]) -> t.List[t.Tuple[T, T2, T3, T4]]: ... + + +@t.overload +def unzip(array: t.Iterable[t.Tuple[T, T2, T3, T4, T5]]) -> t.List[t.Tuple[T, T2, T3, T4, T5]]: ... + + +@t.overload +def unzip(array: t.Iterable[t.Iterable[t.Any]]) -> t.List[t.Tuple[t.Any, ...]]: ... + + +def unzip(array): + """ + The inverse of :func:`zip_`, this method splits groups of elements into tuples composed of + elements from each group at their corresponding indexes. + + Args: + array: List to process. + + Returns: + Unzipped list. + + Example: + + >>> unzip([(1, 4, 7), (2, 5, 8), (3, 6, 9)]) + [(1, 2, 3), (4, 5, 6), (7, 8, 9)] + + .. versionadded:: 1.0.0 + + .. versionchanged:: 8.0.0 + Support list of tuples instead. + """ + return zip_(*array) + + +@t.overload +def unzip_with( + array: t.Iterable[t.Tuple[T, T2]], + iteratee: t.Union[ + t.Callable[[t.Union[T, T2, T3], t.Union[T, T2], int], T3], + t.Callable[[t.Union[T, T2, T3], t.Union[T, T2]], T3], + t.Callable[[t.Union[T, T2, T3]], T3], + ], +) -> t.List[T3]: ... + + +@t.overload +def unzip_with( + array: t.Iterable[t.Iterable[t.Any]], + iteratee: t.Union[ + t.Callable[[t.Any, t.Any, int], T3], + t.Callable[[t.Any, t.Any], T3], + t.Callable[[t.Any], T3], + ], +) -> t.List[T3]: ... + + +@t.overload +def unzip_with( + array: t.Iterable[t.Iterable[T]], + iteratee: None = None, +) -> t.List[t.Tuple[T]]: ... + + +def unzip_with(array, iteratee=None): + """ + This method is like :func:`unzip` except that it accepts an iteratee to specify how regrouped + values should be combined. The iteratee is invoked with three arguments: ``(accumulator, value, + index)``. + + Args: + array: List to process. + iteratee: Function to combine regrouped values. + + Returns: + Unzipped list. + + Example: + + >>> from pydash import add + >>> unzip_with([(1, 10, 100), (2, 20, 200)], add) + [3, 30, 300] + + .. versionadded:: 3.3.0 + """ + if not array: + return [] + + result = unzip(array) + + if iteratee is None: + return result + + def cbk(group): + return pyd.reduce_(group, iteratee) + + return pyd.map_(result, cbk) + + +def without(array: t.Iterable[T], *values: T) -> t.List[T]: + """ + Creates an array with all occurrences of the passed values removed. + + Args: + array: List to filter. + values: Values to remove. + + Returns: + Filtered list. + + Example: + + >>> without([1, 2, 3, 2, 4, 4], 2, 4) + [1, 3] + + .. versionadded:: 1.0.0 + """ + return [item for item in array if item not in values] + + +def xor(array: t.Iterable[T], *lists: t.Iterable[T]) -> t.List[T]: + """ + Creates a list that is the symmetric difference of the provided lists. + + Args: + array: List to process. + *lists: Lists to xor with. + + Returns: + XOR'd list. + + Example: + + >>> xor([1, 3, 4], [1, 2, 4], [2]) + [3] + + .. versionadded:: 1.0.0 + """ + return xor_by(array, *lists) + + +@t.overload +def xor_by( + array: t.Iterable[T], + *lists: t.Iterable[T], + iteratee: t.Union[t.Callable[[T], t.Any], IterateeObjT], +) -> t.List[T]: ... + + +@t.overload +def xor_by( + array: t.Iterable[T], *lists: t.Union[t.Iterable[T], t.Callable[[T], t.Any]] +) -> t.List[T]: ... + + +def xor_by(array, *lists, **kwargs): + """ + This method is like :func:`xor` except that it accepts iteratee which is invoked for each + element of each arras to generate the criterion by which they're compared. The order of result + values is determined by the order they occur in the arrays. The iteratee is invoked with one + argument: ``(value)``. + + Args: + array: List to process. + *lists: Lists to xor with. + + Keyword Args: + iteratee: Function to transform the elements of the arrays. Defaults to + :func:`.identity`. + + Returns: + XOR'd list. + + Example: + + >>> xor_by([2.1, 1.2], [2.3, 3.4], round) + [1.2, 3.4] + >>> xor_by([{"x": 1}], [{"x": 2}, {"x": 1}], "x") + [{'x': 2}] + + .. versionadded:: 4.0.0 + """ + if not lists: + return array[:] + + iteratee, lists = parse_iteratee("iteratee", *lists, **kwargs) + + return xor( + uniq( + difference_by( + array + lists[0], + intersection_by(array, lists[0], iteratee=iteratee), + iteratee=iteratee, + ) + ), + *lists[1:], + ) + + +@t.overload +def xor_with( + array: t.Sequence[T], *lists: t.Iterable[T2], comparator: t.Callable[[T, T2], t.Any] +) -> t.List[T]: ... + + +@t.overload +def xor_with( + array: t.Sequence[T], *lists: t.Union[t.Iterable[T2], t.Callable[[T, T2], t.Any]] +) -> t.List[T]: ... + + +def xor_with(array, *lists, **kwargs): + """ + This method is like :func:`xor` except that it accepts comparator which is invoked to compare + elements of arrays. The order of result values is determined by the order they occur in the + arrays. The comparator is invoked with two arguments: ``(arr_val, oth_val)``. + + Args: + array: List to process. + *lists: Lists to xor with. + + Keyword Args: + comparator: Function to compare the elements of the arrays. Defaults to + :func:`.is_equal`. + + Returns: + XOR'd list. + + Example: + + >>> objects = [{"x": 1, "y": 2}, {"x": 2, "y": 1}] + >>> others = [{"x": 1, "y": 1}, {"x": 1, "y": 2}] + >>> expected = [{"y": 1, "x": 2}, {"y": 1, "x": 1}] + >>> xor_with(objects, others, lambda a, b: a == b) == expected + True + + .. versionadded:: 4.0.0 + """ + if not lists: + return array[:] + + comp, lists = parse_iteratee("comparator", *lists, **kwargs) + + return xor_with( + uniq( + difference_with( + array + lists[0], + intersection_with(array, lists[0], comparator=comp), + comparator=comp, + ) + ), + *lists[1:], + ) + + +@t.overload +def zip_(array1: t.Iterable[T], array2: t.Iterable[T2], /) -> t.List[t.Tuple[T, T2]]: ... + + +@t.overload +def zip_( + array1: t.Iterable[T], array2: t.Iterable[T2], array3: t.Iterable[T3], / +) -> t.List[t.Tuple[T, T2, T3]]: ... + + +@t.overload +def zip_( + array1: t.Iterable[T], array2: t.Iterable[T2], array3: t.Iterable[T3], array4: t.Iterable[T4], / +) -> t.List[t.Tuple[T, T2, T3, T4]]: ... + + +@t.overload +def zip_( + array1: t.Iterable[T], + array2: t.Iterable[T2], + array3: t.Iterable[T3], + array4: t.Iterable[T4], + array5: t.Iterable[T5], + /, +) -> t.List[t.Tuple[T, T2, T3, T4, T5]]: ... + + +@t.overload +def zip_(*arrays: t.Iterable[t.Any]) -> t.List[t.Tuple[t.Any, ...]]: ... + + +def zip_(*arrays): + """ + Groups the elements of each array at their corresponding indexes. Useful for separate data + sources that are coordinated through matching array indexes. + + Args: + arrays: Lists to process. + + Returns: + Zipped list. + + Example: + + >>> zip_([1, 2, 3], [4, 5, 6], [7, 8, 9]) + [(1, 4, 7), (2, 5, 8), (3, 6, 9)] + + .. versionadded:: 1.0.0 + + .. versionchanged:: 8.0.0 + Return list of tuples instead of list of lists. + """ + return list(zip(*arrays)) + + +@t.overload +def zip_object(keys: t.Iterable[t.Tuple[T, T2]], values: None = None) -> t.Dict[T, T2]: ... + + +@t.overload +def zip_object( + keys: t.Iterable[t.List[t.Union[T, T2]]], values: None = None +) -> t.Dict[t.Union[T, T2], t.Union[T, T2]]: ... + + +@t.overload +def zip_object(keys: t.Iterable[T], values: t.List[T2]) -> t.Dict[T, T2]: ... + + +def zip_object(keys, values=None): + """ + Creates a dict composed of lists of keys and values. Pass either a single two-dimensional list, + i.e. ``[[key1, value1], [key2, value2]]``, or two lists, one of keys and one of corresponding + values. + + Args: + keys: Either a list of keys or a list of ``[key, value]`` pairs. + values: List of values to zip. + + Returns: + Zipped dict. + + Example: + + >>> zip_object([1, 2, 3], [4, 5, 6]) + {1: 4, 2: 5, 3: 6} + + .. versionadded:: 1.0.0 + + .. versionchanged:: 4.0.0 + Removed alias ``object_``. + """ + if values is None: + keys_values = unzip(keys) + if len(keys_values) == 0: + keys, values = [], [] + else: + keys, values = keys_values + + return dict(zip(keys, values)) + + +def zip_object_deep( + keys: t.Iterable[t.Any], values: t.Union[t.List[t.Any], None] = None +) -> t.Dict[t.Any, t.Any]: + """ + This method is like :func:`zip_object` except that it supports property paths. + + Args: + keys: Either a list of keys or a list of ``[key, value]`` pairs. + values: List of values to zip. + + Returns: + Zipped dict. + + Example: + + >>> expected = {"a": {"b": {"c": 1, "d": 2}}} + >>> zip_object_deep(["a.b.c", "a.b.d"], [1, 2]) == expected + True + + .. versionadded:: 4.0.0 + """ + if values is None: + keys_values = unzip(keys) + if len(keys_values) == 0: + keys, values = [], [] + else: + keys, values = keys_values + + obj: t.Dict[t.Any, t.Any] = {} + for idx, key in enumerate(keys): + obj = pyd.set_(obj, key, pyd.get(values, idx)) + + return obj + + +@t.overload +def zip_with( + array1: t.Iterable[T], + array2: t.Iterable[T2], + *, + iteratee: t.Union[ + t.Callable[[T, T2, int], T3], + t.Callable[[T, T2], T3], + t.Callable[[T], T3], + ], +) -> t.List[T3]: ... + + +@t.overload +def zip_with( + *arrays: t.Iterable[t.Any], + iteratee: t.Union[ + t.Callable[[t.Any, t.Any, int], T2], + t.Callable[[t.Any, t.Any], T2], + t.Callable[[t.Any], T2], + ], +) -> t.List[T2]: ... + + +@t.overload +def zip_with( + *arrays: t.Union[ + t.Iterable[t.Any], + t.Callable[[t.Any, t.Any, int], T2], + t.Callable[[t.Any, t.Any], T2], + t.Callable[[t.Any], T2], + ], +) -> t.List[T2]: ... + + +def zip_with(*arrays, **kwargs): + """ + This method is like :func:`zip` except that it accepts an iteratee to specify how grouped values + should be combined. The iteratee is invoked with three arguments: + ``(accumulator, value, index)``. + + Args: + *arrays: Lists to process. + + Keyword Args: + iteratee (callable): Function to combine grouped values. + + Returns: + Zipped list of grouped elements. + + Example: + + >>> from pydash import add + >>> zip_with([1, 2], [10, 20], [100, 200], add) + [111, 222] + >>> zip_with([1, 2], [10, 20], [100, 200], iteratee=add) + [111, 222] + + .. versionadded:: 3.3.0 + """ + if "iteratee" in kwargs: + iteratee = kwargs["iteratee"] + elif len(arrays) > 1: + iteratee = arrays[-1] + arrays = arrays[:-1] + else: + iteratee = None + + return unzip_with(arrays, iteratee) + + +# +# Utility methods not a part of the main API +# + + +def iterflatten(array, depth=-1): + """Iteratively flatten a list shallowly or deeply.""" + for item in array: + if isinstance(item, (list, tuple)) and depth != 0: + for subitem in iterflatten(item, depth - 1): + yield subitem + else: + yield item + + +def iterinterleave(*arrays): + """Interleave multiple lists.""" + iters = [iter(arr) for arr in arrays] + + while iters: + nextiters = [] + for itr in iters: + try: + yield next(itr) + nextiters.append(itr) + except StopIteration: + pass + + iters = nextiters + + +def iterintersperse(iterable, separator): + """Iteratively intersperse iterable.""" + iterable = iter(iterable) + yield next(iterable) + for item in iterable: + yield separator + yield item + + +def iterunique(array, comparator=None, iteratee=None): # noqa: PLR0912 + """Yield each unique item in array.""" + if not array: # pragma: no cover + return + + if iteratee is not None: + iteratee = pyd.iteratee(iteratee) + + seen_hashable = set() + seen_unhashable = [] + + for item in array: + if iteratee is None: + cmp_item = item + else: + cmp_item = iteratee(item) + + if comparator is None: + try: + if cmp_item not in seen_hashable: + yield item + seen_hashable.add(cmp_item) + except TypeError: + if cmp_item not in seen_unhashable: + yield item + seen_unhashable.append(cmp_item) + else: + unseen = True + for seen_item in seen_unhashable: + if comparator(cmp_item, seen_item): + unseen = False + break + if unseen: + yield item + seen_unhashable.append(cmp_item) + + +def iterduplicates(array): + """Yield duplictes found in `array`.""" + seen = [] + for i, item in enumerate(array): + if item in seen: + yield i, item + else: + seen.append(item) + + +def iterintersection(array, other, comparator=None, iteratee=None): + """Yield intersecting values between `array` and `other` using `comparator` to determine if they + intersect.""" + if not array or not other: # pragma: no cover + return + + if comparator is None: + comparator = pyd.is_equal + + iteratee = pyd.iteratee(iteratee) + + # NOTE: Maintain ordering of yielded values based on `array` ordering. + seen = [] + for item in array: + cmp_item = iteratee(item) + + if cmp_item in seen: + continue + + seen.append(cmp_item) + seen_others = [] + + for value in other: + cmp_value = iteratee(value) + + if cmp_value in seen_others: + continue + + seen_others.append(cmp_value) + + if comparator(cmp_item, cmp_value): + yield item + break + + +def iterdifference(array, other, comparator=None, iteratee=None): + """Yield different values in `array` as compared to `other` using `comparator` to determine if + they are different.""" + if not array or not other: # pragma: no cover + return + + if comparator is None: + comparator = pyd.is_equal + + iteratee = pyd.iteratee(iteratee) + + def is_different(item, seen): + is_diff = True + + if item not in seen: + for value in other: + if comparator(iteratee(item), iteratee(value)): + is_diff = False + break + + if is_diff: + seen.append(item) + return is_diff + + seen = [] + not_seen = [] + + for item in array: + if item in not_seen or is_different(item, seen): + yield item diff --git a/.venv/lib/python3.12/site-packages/pydash/chaining/__init__.py b/.venv/lib/python3.12/site-packages/pydash/chaining/__init__.py new file mode 100644 index 00000000..0c37c37a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydash/chaining/__init__.py @@ -0,0 +1,8 @@ +from .chaining import _Dash, chain, tap + + +__all__ = ( + "_Dash", + "chain", + "tap", +) diff --git a/.venv/lib/python3.12/site-packages/pydash/chaining/all_funcs.py b/.venv/lib/python3.12/site-packages/pydash/chaining/all_funcs.py new file mode 100644 index 00000000..bfe63c4d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydash/chaining/all_funcs.py @@ -0,0 +1,44 @@ +from abc import ABC, abstractmethod +import typing as t + + +class AllFuncs(ABC): + """Exposing all of the exposed functions of a module through an class.""" + + module: t.Any + invalid_method_exception: t.Type[Exception] + + @abstractmethod + def _wrap(self, func) -> t.Callable: + """Proxy attribute access to :attr:`module`.""" + raise NotImplementedError() # pragma: no cover + + @classmethod + def get_method(cls, name: str) -> t.Callable: + """ + Return valid :attr:`module` method. + + Args: + name: Name of pydash method to get. + + Returns: + :attr:`module` callable. + + Raises: + InvalidMethod: Raised if `name` is not a valid :attr:`module` method. + """ + method = getattr(cls.module, name, None) + + if not callable(method) and not name.endswith("_"): + # Alias method names not ending in underscore to their underscore + # counterpart. This allows chaining of functions like "map_()" + # using "map()" instead. + method = getattr(cls.module, name + "_", None) + + if not callable(method): + raise cls.invalid_method_exception(f"Invalid {cls.module.__name__} method: {name}") + + return method + + def __getattr__(self, name: str) -> t.Callable: + return self._wrap(self.get_method(name)) diff --git a/.venv/lib/python3.12/site-packages/pydash/chaining/all_funcs.pyi b/.venv/lib/python3.12/site-packages/pydash/chaining/all_funcs.pyi new file mode 100644 index 00000000..7c0efa7f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydash/chaining/all_funcs.pyi @@ -0,0 +1,3540 @@ +# mypy: disable-error-code=misc +"""Generated from the `scripts/chaining_type_generator.py` script.""" + +import re +import typing as t + +from _typeshed import ( + SupportsAdd, + SupportsDunderGE, + SupportsDunderGT, + SupportsDunderLE, + SupportsDunderLT, + SupportsRichComparison, + SupportsRichComparisonT, + SupportsSub, +) +from typing_extensions import Concatenate, Literal, ParamSpec, Type + +import pydash as pyd +from pydash.chaining.chaining import Chain +from pydash.functions import ( + After, + Ary, + Before, + CurryFive, + CurryFour, + CurryOne, + CurryRightFive, + CurryRightFour, + CurryRightOne, + CurryRightThree, + CurryRightTwo, + CurryThree, + CurryTwo, + Debounce, + Disjoin, + Flow, + Iterated, + Juxtapose, + Negate, + Once, + Partial, + Rearg, + Spread, + Throttle, +) +from pydash.helpers import UNSET, Unset +from pydash.types import * +from pydash.utilities import MemoizedFunc + +ValueT_co = t.TypeVar("ValueT_co", covariant=True) +T = t.TypeVar("T") +T1 = t.TypeVar("T1") +T2 = t.TypeVar("T2") +T3 = t.TypeVar("T3") +T4 = t.TypeVar("T4") +T5 = t.TypeVar("T5") +NumT = t.TypeVar("NumT", int, float, "Decimal") +NumT2 = t.TypeVar("NumT2", int, float, "Decimal") +NumT3 = t.TypeVar("NumT3", int, float, "Decimal") +CallableT = t.TypeVar("CallableT", bound=t.Callable[..., t.Any]) +SequenceT = t.TypeVar("SequenceT", bound=t.Sequence[t.Any]) +MutableSequenceT = t.TypeVar("MutableSequenceT", bound=t.MutableSequence[t.Any]) +P = ParamSpec("P") + +class AllFuncs: + def chunk(self: "Chain[t.Sequence[T]]", size: int = 1) -> "Chain[t.List[t.Sequence[T]]]": + return self._wrap(pyd.chunk)(size) + + def compact(self: "Chain[t.Iterable[t.Union[T, None]]]") -> "Chain[t.List[T]]": + return self._wrap(pyd.compact)() + + def concat(self: "Chain[t.Iterable[T]]", *arrays: t.Iterable[T]) -> "Chain[t.List[T]]": + return self._wrap(pyd.concat)(*arrays) + + def difference(self: "Chain[t.Iterable[T]]", *others: t.Iterable[T]) -> "Chain[t.List[T]]": + return self._wrap(pyd.difference)(*others) + + @t.overload + def difference_by( + self: "Chain[t.Iterable[T]]", + *others: t.Iterable[T], + iteratee: t.Union[IterateeObjT, t.Callable[[T], t.Any], None], + ) -> "Chain[t.List[T]]": ... + @t.overload + def difference_by( + self: "Chain[t.Iterable[T]]", + *others: t.Union[IterateeObjT, t.Iterable[T], t.Callable[[T], t.Any]], + ) -> "Chain[t.List[T]]": ... + def difference_by(self, *others, **kwargs): + return self._wrap(pyd.difference_by)(*others, **kwargs) + + @t.overload + def difference_with( + self: "Chain[t.Iterable[T]]", + *others: t.Iterable[T2], + comparator: t.Union[t.Callable[[T, T2], t.Any], None], + ) -> "Chain[t.List[T]]": ... + @t.overload + def difference_with( + self: "Chain[t.Iterable[T]]", *others: t.Union[t.Iterable[T2], t.Callable[[T, T2], t.Any]] + ) -> "Chain[t.List[T]]": ... + def difference_with(self, *others, **kwargs): + return self._wrap(pyd.difference_with)(*others, **kwargs) + + def drop(self: "Chain[t.Sequence[T]]", n: int = 1) -> "Chain[t.List[T]]": + return self._wrap(pyd.drop)(n) + + def drop_right(self: "Chain[t.Sequence[T]]", n: int = 1) -> "Chain[t.List[T]]": + return self._wrap(pyd.drop_right)(n) + + @t.overload + def drop_right_while( + self: "Chain[t.Sequence[T]]", predicate: t.Callable[[T, int, t.List[T]], t.Any] + ) -> "Chain[t.List[T]]": ... + @t.overload + def drop_right_while( + self: "Chain[t.Sequence[T]]", predicate: t.Callable[[T, int], t.Any] + ) -> "Chain[t.List[T]]": ... + @t.overload + def drop_right_while( + self: "Chain[t.Sequence[T]]", predicate: t.Callable[[T], t.Any] + ) -> "Chain[t.List[T]]": ... + @t.overload + def drop_right_while( + self: "Chain[t.Sequence[T]]", predicate: None = None + ) -> "Chain[t.List[T]]": ... + def drop_right_while(self, predicate=None): + return self._wrap(pyd.drop_right_while)(predicate) + + @t.overload + def drop_while( + self: "Chain[t.Sequence[T]]", predicate: t.Callable[[T, int, t.List[T]], t.Any] + ) -> "Chain[t.List[T]]": ... + @t.overload + def drop_while( + self: "Chain[t.Sequence[T]]", predicate: t.Callable[[T, int], t.Any] + ) -> "Chain[t.List[T]]": ... + @t.overload + def drop_while( + self: "Chain[t.Sequence[T]]", predicate: t.Callable[[T], t.Any] + ) -> "Chain[t.List[T]]": ... + @t.overload + def drop_while(self: "Chain[t.Sequence[T]]", predicate: None = None) -> "Chain[t.List[T]]": ... + def drop_while(self, predicate=None): + return self._wrap(pyd.drop_while)(predicate) + + def duplicates( + self: "Chain[t.Sequence[T]]", + iteratee: t.Union[t.Callable[[T], t.Any], IterateeObjT, None] = None, + ) -> "Chain[t.List[T]]": + return self._wrap(pyd.duplicates)(iteratee) + + def fill( + self: "Chain[t.Sequence[T]]", value: T2, start: int = 0, end: t.Union[int, None] = None + ) -> "Chain[t.List[t.Union[T, T2]]]": + return self._wrap(pyd.fill)(value, start, end) + + @t.overload + def find_index( + self: "Chain[t.Iterable[T]]", predicate: t.Callable[[T, int, t.List[T]], t.Any] + ) -> "Chain[int]": ... + @t.overload + def find_index( + self: "Chain[t.Iterable[T]]", predicate: t.Callable[[T, int], t.Any] + ) -> "Chain[int]": ... + @t.overload + def find_index( + self: "Chain[t.Iterable[T]]", predicate: t.Callable[[T], t.Any] + ) -> "Chain[int]": ... + @t.overload + def find_index(self: "Chain[t.Iterable[t.Any]]", predicate: IterateeObjT) -> "Chain[int]": ... + @t.overload + def find_index(self: "Chain[t.Iterable[t.Any]]", predicate: None = None) -> "Chain[int]": ... + def find_index(self, predicate=None): + return self._wrap(pyd.find_index)(predicate) + + @t.overload + def find_last_index( + self: "Chain[t.Iterable[T]]", predicate: t.Callable[[T, int, t.List[T]], t.Any] + ) -> "Chain[int]": ... + @t.overload + def find_last_index( + self: "Chain[t.Iterable[T]]", predicate: t.Callable[[T, int], t.Any] + ) -> "Chain[int]": ... + @t.overload + def find_last_index( + self: "Chain[t.Iterable[T]]", predicate: t.Callable[[T], t.Any] + ) -> "Chain[int]": ... + @t.overload + def find_last_index( + self: "Chain[t.Iterable[t.Any]]", predicate: IterateeObjT + ) -> "Chain[int]": ... + @t.overload + def find_last_index( + self: "Chain[t.Iterable[t.Any]]", predicate: None = None + ) -> "Chain[int]": ... + def find_last_index(self, predicate=None): + return self._wrap(pyd.find_last_index)(predicate) + + @t.overload + def flatten(self: "Chain[t.Iterable[t.Iterable[T]]]") -> "Chain[t.List[T]]": ... + @t.overload + def flatten(self: "Chain[t.Iterable[T]]") -> "Chain[t.List[T]]": ... + def flatten(self): + return self._wrap(pyd.flatten)() + + def flatten_deep(self: "Chain[t.Iterable[t.Any]]") -> "Chain[t.List[t.Any]]": + return self._wrap(pyd.flatten_deep)() + + def flatten_depth(self: "Chain[t.Iterable[t.Any]]", depth: int = 1) -> "Chain[t.List[t.Any]]": + return self._wrap(pyd.flatten_depth)(depth) + + @t.overload + def from_pairs(self: "Chain[t.Iterable[t.Tuple[T, T2]]]") -> "Chain[t.Dict[T, T2]]": ... + @t.overload + def from_pairs( + self: "Chain[t.Iterable[t.List[t.Union[T, T2]]]]", + ) -> "Chain[t.Dict[t.Union[T, T2], t.Union[T, T2]]]": ... + def from_pairs(self): + return self._wrap(pyd.from_pairs)() + + def head(self: "Chain[t.Sequence[T]]") -> "Chain[t.Union[T, None]]": + return self._wrap(pyd.head)() + + def index_of(self: "Chain[t.Sequence[T]]", value: T, from_index: int = 0) -> "Chain[int]": + return self._wrap(pyd.index_of)(value, from_index) + + def initial(self: "Chain[t.Sequence[T]]") -> "Chain[t.Sequence[T]]": + return self._wrap(pyd.initial)() + + @t.overload + def intercalate( + self: "Chain[t.Iterable[t.Iterable[T]]]", separator: T2 + ) -> "Chain[t.List[t.Union[T, T2]]]": ... + @t.overload + def intercalate( + self: "Chain[t.Iterable[T]]", separator: T2 + ) -> "Chain[t.List[t.Union[T, T2]]]": ... + def intercalate(self, separator): + return self._wrap(pyd.intercalate)(separator) + + def interleave(self: "Chain[t.Iterable[T]]", *arrays: t.Iterable[T]) -> "Chain[t.List[T]]": + return self._wrap(pyd.interleave)(*arrays) + + def intersection( + self: "Chain[t.Sequence[T]]", *others: t.Iterable[t.Any] + ) -> "Chain[t.List[T]]": + return self._wrap(pyd.intersection)(*others) + + @t.overload + def intersection_by( + self: "Chain[t.Sequence[T]]", + *others: t.Iterable[t.Any], + iteratee: t.Union[t.Callable[[T], t.Any], IterateeObjT], + ) -> "Chain[t.List[T]]": ... + @t.overload + def intersection_by( + self: "Chain[t.Sequence[T]]", + *others: t.Union[t.Iterable[t.Any], t.Callable[[T], t.Any], IterateeObjT], + ) -> "Chain[t.List[T]]": ... + def intersection_by(self, *others, **kwargs): + return self._wrap(pyd.intersection_by)(*others, **kwargs) + + @t.overload + def intersection_with( + self: "Chain[t.Sequence[T]]", + *others: t.Iterable[T2], + comparator: t.Callable[[T, T2], t.Any], + ) -> "Chain[t.List[T]]": ... + @t.overload + def intersection_with( + self: "Chain[t.Sequence[T]]", *others: t.Union[t.Iterable[T2], t.Callable[[T, T2], t.Any]] + ) -> "Chain[t.List[T]]": ... + def intersection_with(self, *others, **kwargs): + return self._wrap(pyd.intersection_with)(*others, **kwargs) + + def intersperse(self: "Chain[t.Iterable[T]]", separator: T2) -> "Chain[t.List[t.Union[T, T2]]]": + return self._wrap(pyd.intersperse)(separator) + + def last(self: "Chain[t.Sequence[T]]") -> "Chain[t.Union[T, None]]": + return self._wrap(pyd.last)() + + def last_index_of( + self: "Chain[t.Sequence[t.Any]]", value: t.Any, from_index: t.Union[int, None] = None + ) -> "Chain[int]": + return self._wrap(pyd.last_index_of)(value, from_index) + + @t.overload + def mapcat( + self: "Chain[t.Iterable[T]]", + iteratee: t.Callable[[T, int, t.List[T]], t.Union[t.List[T2], t.List[t.List[T2]]]], + ) -> "Chain[t.List[T2]]": ... + @t.overload + def mapcat( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T, int, t.List[T]], T2] + ) -> "Chain[t.List[T2]]": ... + @t.overload + def mapcat( + self: "Chain[t.Iterable[T]]", + iteratee: t.Callable[[T, int], t.Union[t.List[T2], t.List[t.List[T2]]]], + ) -> "Chain[t.List[T2]]": ... + @t.overload + def mapcat( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T, int], T2] + ) -> "Chain[t.List[T2]]": ... + @t.overload + def mapcat( + self: "Chain[t.Iterable[T]]", + iteratee: t.Callable[[T], t.Union[t.List[T2], t.List[t.List[T2]]]], + ) -> "Chain[t.List[T2]]": ... + @t.overload + def mapcat( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T], T2] + ) -> "Chain[t.List[T2]]": ... + @t.overload + def mapcat( + self: "Chain[t.Iterable[t.Union[t.List[T], t.List[t.List[T]]]]]", iteratee: None = None + ) -> "Chain[t.List[t.Union[T, t.List[T]]]]": ... + def mapcat(self, iteratee=None): + return self._wrap(pyd.mapcat)(iteratee) + + def nth(self: "Chain[t.Iterable[T]]", pos: int = 0) -> "Chain[t.Union[T, None]]": + return self._wrap(pyd.nth)(pos) + + def pop(self: "Chain[t.List[T]]", index: int = -1) -> "Chain[T]": + return self._wrap(pyd.pop)(index) + + def pull(self: "Chain[t.List[T]]", *values: T) -> "Chain[t.List[T]]": + return self._wrap(pyd.pull)(*values) + + def pull_all(self: "Chain[t.List[T]]", values: t.Iterable[T]) -> "Chain[t.List[T]]": + return self._wrap(pyd.pull_all)(values) + + def pull_all_by( + self: "Chain[t.List[T]]", + values: t.Iterable[T], + iteratee: t.Union[IterateeObjT, t.Callable[[T], t.Any], None] = None, + ) -> "Chain[t.List[T]]": + return self._wrap(pyd.pull_all_by)(values, iteratee) + + def pull_all_with( + self: "Chain[t.List[T]]", + values: t.Iterable[T], + comparator: t.Union[t.Callable[[T, T], t.Any], None] = None, + ) -> "Chain[t.List[T]]": + return self._wrap(pyd.pull_all_with)(values, comparator) + + def pull_at(self: "Chain[t.List[T]]", *indexes: int) -> "Chain[t.List[T]]": + return self._wrap(pyd.pull_at)(*indexes) + + def push(self: "Chain[t.List[T]]", *items: T2) -> "Chain[t.List[t.Union[T, T2]]]": + return self._wrap(pyd.push)(*items) + + def remove( + self: "Chain[t.List[T]]", + predicate: t.Union[ + t.Callable[[T, int, t.List[T]], t.Any], + t.Callable[[T, int], t.Any], + t.Callable[[T], t.Any], + None, + ] = None, + ) -> "Chain[t.List[T]]": + return self._wrap(pyd.remove)(predicate) + + def reverse(self: "Chain[SequenceT]") -> "Chain[SequenceT]": + return self._wrap(pyd.reverse)() + + def shift(self: "Chain[t.List[T]]") -> "Chain[T]": + return self._wrap(pyd.shift)() + + def slice_( + self: "Chain[SequenceT]", start: int = 0, end: t.Union[int, None] = None + ) -> "Chain[SequenceT]": + return self._wrap(pyd.slice_)(start, end) + + slice = slice_ + + @t.overload + def sort( + self: "Chain[t.List['SupportsRichComparisonT']]", + comparator: None = None, + key: None = None, + reverse: bool = False, + ) -> "Chain[t.List['SupportsRichComparisonT']]": ... + @t.overload + def sort( + self: "Chain[t.List[T]]", comparator: t.Callable[[T, T], int], *, reverse: bool = False + ) -> "Chain[t.List[T]]": ... + @t.overload + def sort( + self: "Chain[t.List[T]]", + *, + key: t.Callable[[T], "SupportsRichComparisonT"], + reverse: bool = False, + ) -> "Chain[t.List[T]]": ... + def sort(self, comparator=None, key=None, reverse=False): + return self._wrap(pyd.sort)(comparator, key, reverse) + + def sorted_index( + self: "Chain[t.Sequence['SupportsRichComparisonT']]", value: "SupportsRichComparisonT" + ) -> "Chain[int]": + return self._wrap(pyd.sorted_index)(value) + + @t.overload + def sorted_index_by( + self: "Chain[t.Sequence[T]]", + value: T, + iteratee: t.Union[IterateeObjT, t.Callable[[T], "SupportsRichComparisonT"]], + ) -> "Chain[int]": ... + @t.overload + def sorted_index_by( + self: "Chain[t.Sequence['SupportsRichComparisonT']]", + value: "SupportsRichComparisonT", + iteratee: None = None, + ) -> "Chain[int]": ... + def sorted_index_by(self, value, iteratee=None): + return self._wrap(pyd.sorted_index_by)(value, iteratee) + + def sorted_index_of( + self: "Chain[t.Sequence['SupportsRichComparisonT']]", value: "SupportsRichComparisonT" + ) -> "Chain[int]": + return self._wrap(pyd.sorted_index_of)(value) + + def sorted_last_index( + self: "Chain[t.Sequence['SupportsRichComparisonT']]", value: "SupportsRichComparisonT" + ) -> "Chain[int]": + return self._wrap(pyd.sorted_last_index)(value) + + @t.overload + def sorted_last_index_by( + self: "Chain[t.Sequence[T]]", + value: T, + iteratee: t.Union[IterateeObjT, t.Callable[[T], "SupportsRichComparisonT"]], + ) -> "Chain[int]": ... + @t.overload + def sorted_last_index_by( + self: "Chain[t.Sequence['SupportsRichComparisonT']]", + value: "SupportsRichComparisonT", + iteratee: None = None, + ) -> "Chain[int]": ... + def sorted_last_index_by(self, value, iteratee=None): + return self._wrap(pyd.sorted_last_index_by)(value, iteratee) + + def sorted_last_index_of( + self: "Chain[t.Sequence['SupportsRichComparisonT']]", value: "SupportsRichComparisonT" + ) -> "Chain[int]": + return self._wrap(pyd.sorted_last_index_of)(value) + + def sorted_uniq( + self: "Chain[t.Iterable['SupportsRichComparisonT']]", + ) -> "Chain[t.List['SupportsRichComparisonT']]": + return self._wrap(pyd.sorted_uniq)() + + def sorted_uniq_by( + self: "Chain[t.Iterable['SupportsRichComparisonT']]", + iteratee: t.Union[ + t.Callable[["SupportsRichComparisonT"], "SupportsRichComparisonT"], None + ] = None, + ) -> "Chain[t.List['SupportsRichComparisonT']]": + return self._wrap(pyd.sorted_uniq_by)(iteratee) + + def splice( + self: "Chain[MutableSequenceT]", start: int, count: t.Union[int, None] = None, *items: t.Any + ) -> "Chain[MutableSequenceT]": + return self._wrap(pyd.splice)(start, count, *items) + + def split_at(self: "Chain[t.Sequence[T]]", index: int) -> "Chain[t.List[t.Sequence[T]]]": + return self._wrap(pyd.split_at)(index) + + def tail(self: "Chain[t.Sequence[T]]") -> "Chain[t.Sequence[T]]": + return self._wrap(pyd.tail)() + + def take(self: "Chain[t.Sequence[T]]", n: int = 1) -> "Chain[t.Sequence[T]]": + return self._wrap(pyd.take)(n) + + def take_right(self: "Chain[t.Sequence[T]]", n: int = 1) -> "Chain[t.Sequence[T]]": + return self._wrap(pyd.take_right)(n) + + @t.overload + def take_right_while( + self: "Chain[t.Sequence[T]]", predicate: t.Callable[[T, int, t.List[T]], t.Any] + ) -> "Chain[t.Sequence[T]]": ... + @t.overload + def take_right_while( + self: "Chain[t.Sequence[T]]", predicate: t.Callable[[T, int], t.Any] + ) -> "Chain[t.Sequence[T]]": ... + @t.overload + def take_right_while( + self: "Chain[t.Sequence[T]]", predicate: t.Callable[[T], t.Any] + ) -> "Chain[t.Sequence[T]]": ... + @t.overload + def take_right_while( + self: "Chain[t.Sequence[T]]", predicate: None = None + ) -> "Chain[t.Sequence[T]]": ... + def take_right_while(self, predicate=None): + return self._wrap(pyd.take_right_while)(predicate) + + @t.overload + def take_while( + self: "Chain[t.Sequence[T]]", predicate: t.Callable[[T, int, t.List[T]], t.Any] + ) -> "Chain[t.List[T]]": ... + @t.overload + def take_while( + self: "Chain[t.Sequence[T]]", predicate: t.Callable[[T, int], t.Any] + ) -> "Chain[t.List[T]]": ... + @t.overload + def take_while( + self: "Chain[t.Sequence[T]]", predicate: t.Callable[[T], t.Any] + ) -> "Chain[t.List[T]]": ... + @t.overload + def take_while(self: "Chain[t.Sequence[T]]", predicate: None = None) -> "Chain[t.List[T]]": ... + def take_while(self, predicate=None): + return self._wrap(pyd.take_while)(predicate) + + @t.overload + def union(self: "Chain[t.Sequence[T]]") -> "Chain[t.List[T]]": ... + @t.overload + def union( + self: "Chain[t.Sequence[T]]", *others: t.Sequence[T2] + ) -> "Chain[t.List[t.Union[T, T2]]]": ... + def union(self, *others): + return self._wrap(pyd.union)(*others) + + @t.overload + def union_by( + self: "Chain[t.Sequence[T]]", *others: t.Iterable[T], iteratee: t.Callable[[T], t.Any] + ) -> "Chain[t.List[T]]": ... + @t.overload + def union_by( + self: "Chain[t.Sequence[T]]", *others: t.Union[t.Iterable[T], t.Callable[[T], t.Any]] + ) -> "Chain[t.List[T]]": ... + def union_by(self, *others, **kwargs): + return self._wrap(pyd.union_by)(*others, **kwargs) + + @t.overload + def union_with( + self: "Chain[t.Sequence[T]]", + *others: t.Iterable[T2], + comparator: t.Callable[[T, T2], t.Any], + ) -> "Chain[t.List[T]]": ... + @t.overload + def union_with( + self: "Chain[t.Sequence[T]]", *others: t.Union[t.Iterable[T2], t.Callable[[T, T2], t.Any]] + ) -> "Chain[t.List[T]]": ... + def union_with(self, *others, **kwargs): + return self._wrap(pyd.union_with)(*others, **kwargs) + + def uniq(self: "Chain[t.Iterable[T]]") -> "Chain[t.List[T]]": + return self._wrap(pyd.uniq)() + + def uniq_by( + self: "Chain[t.Iterable[T]]", iteratee: t.Union[t.Callable[[T], t.Any], None] = None + ) -> "Chain[t.List[T]]": + return self._wrap(pyd.uniq_by)(iteratee) + + def uniq_with( + self: "Chain[t.Sequence[T]]", comparator: t.Union[t.Callable[[T, T], t.Any], None] = None + ) -> "Chain[t.List[T]]": + return self._wrap(pyd.uniq_with)(comparator) + + def unshift(self: "Chain[t.List[T]]", *items: T2) -> "Chain[t.List[t.Union[T, T2]]]": + return self._wrap(pyd.unshift)(*items) + + @t.overload + def unzip(self: "Chain[t.Iterable[t.Tuple[T, T2]]]") -> "Chain[t.List[t.Tuple[T, T2]]]": ... + @t.overload + def unzip( + self: "Chain[t.Iterable[t.Tuple[T, T2, T3]]]", + ) -> "Chain[t.List[t.Tuple[T, T2, T3]]]": ... + @t.overload + def unzip( + self: "Chain[t.Iterable[t.Tuple[T, T2, T3, T4]]]", + ) -> "Chain[t.List[t.Tuple[T, T2, T3, T4]]]": ... + @t.overload + def unzip( + self: "Chain[t.Iterable[t.Tuple[T, T2, T3, T4, T5]]]", + ) -> "Chain[t.List[t.Tuple[T, T2, T3, T4, T5]]]": ... + @t.overload + def unzip( + self: "Chain[t.Iterable[t.Iterable[t.Any]]]", + ) -> "Chain[t.List[t.Tuple[t.Any, ...]]]": ... + def unzip(self): + return self._wrap(pyd.unzip)() + + @t.overload + def unzip_with( + self: "Chain[t.Iterable[t.Tuple[T, T2]]]", + iteratee: t.Union[ + t.Callable[[t.Union[T, T2, T3], t.Union[T, T2], int], T3], + t.Callable[[t.Union[T, T2, T3], t.Union[T, T2]], T3], + t.Callable[[t.Union[T, T2, T3]], T3], + ], + ) -> "Chain[t.List[T3]]": ... + @t.overload + def unzip_with( + self: "Chain[t.Iterable[t.Iterable[t.Any]]]", + iteratee: t.Union[ + t.Callable[[t.Any, t.Any, int], T3], + t.Callable[[t.Any, t.Any], T3], + t.Callable[[t.Any], T3], + ], + ) -> "Chain[t.List[T3]]": ... + @t.overload + def unzip_with( + self: "Chain[t.Iterable[t.Iterable[T]]]", iteratee: None = None + ) -> "Chain[t.List[t.Tuple[T]]]": ... + def unzip_with(self, iteratee=None): + return self._wrap(pyd.unzip_with)(iteratee) + + def without(self: "Chain[t.Iterable[T]]", *values: T) -> "Chain[t.List[T]]": + return self._wrap(pyd.without)(*values) + + def xor(self: "Chain[t.Iterable[T]]", *lists: t.Iterable[T]) -> "Chain[t.List[T]]": + return self._wrap(pyd.xor)(*lists) + + @t.overload + def xor_by( + self: "Chain[t.Iterable[T]]", + *lists: t.Iterable[T], + iteratee: t.Union[t.Callable[[T], t.Any], IterateeObjT], + ) -> "Chain[t.List[T]]": ... + @t.overload + def xor_by( + self: "Chain[t.Iterable[T]]", *lists: t.Union[t.Iterable[T], t.Callable[[T], t.Any]] + ) -> "Chain[t.List[T]]": ... + def xor_by(self, *lists, **kwargs): + return self._wrap(pyd.xor_by)(*lists, **kwargs) + + @t.overload + def xor_with( + self: "Chain[t.Sequence[T]]", *lists: t.Iterable[T2], comparator: t.Callable[[T, T2], t.Any] + ) -> "Chain[t.List[T]]": ... + @t.overload + def xor_with( + self: "Chain[t.Sequence[T]]", *lists: t.Union[t.Iterable[T2], t.Callable[[T, T2], t.Any]] + ) -> "Chain[t.List[T]]": ... + def xor_with(self, *lists, **kwargs): + return self._wrap(pyd.xor_with)(*lists, **kwargs) + + @t.overload + def zip_( + self: "Chain[t.Iterable[t.Any]]", *arrays: t.Iterable[t.Any] + ) -> "Chain[t.List[t.Tuple[t.Any, ...]]]": ... + def zip_(self, *arrays): + return self._wrap(pyd.zip_)(*arrays) + + zip = zip_ + + @t.overload + def zip_object( + self: "Chain[t.Iterable[t.Tuple[T, T2]]]", values: None = None + ) -> "Chain[t.Dict[T, T2]]": ... + @t.overload + def zip_object( + self: "Chain[t.Iterable[t.List[t.Union[T, T2]]]]", values: None = None + ) -> "Chain[t.Dict[t.Union[T, T2], t.Union[T, T2]]]": ... + @t.overload + def zip_object(self: "Chain[t.Iterable[T]]", values: t.List[T2]) -> "Chain[t.Dict[T, T2]]": ... + def zip_object(self, values=None): + return self._wrap(pyd.zip_object)(values) + + def zip_object_deep( + self: "Chain[t.Iterable[t.Any]]", values: t.Union[t.List[t.Any], None] = None + ) -> "Chain[t.Dict[t.Any, t.Any]]": + return self._wrap(pyd.zip_object_deep)(values) + + @t.overload + def zip_with( + self: "Chain[t.Iterable[T]]", + array2: t.Iterable[T2], + *, + iteratee: t.Union[ + t.Callable[[T, T2, int], T3], t.Callable[[T, T2], T3], t.Callable[[T], T3] + ], + ) -> "Chain[t.List[T3]]": ... + @t.overload + def zip_with( + self: "Chain[t.Iterable[t.Any]]", + *arrays: t.Iterable[t.Any], + iteratee: t.Union[ + t.Callable[[t.Any, t.Any, int], T2], + t.Callable[[t.Any, t.Any], T2], + t.Callable[[t.Any], T2], + ], + ) -> "Chain[t.List[T2]]": ... + @t.overload + def zip_with( + self: "Chain[t.Union[t.Iterable[t.Any], t.Callable[[t.Any, t.Any, int], T2], t.Callable[[t.Any, t.Any], T2], t.Callable[[t.Any], T2]]]", + *arrays: t.Union[ + t.Iterable[t.Any], + t.Callable[[t.Any, t.Any, int], T2], + t.Callable[[t.Any, t.Any], T2], + t.Callable[[t.Any], T2], + ], + ) -> "Chain[t.List[T2]]": ... + def zip_with(self, *arrays, **kwargs): + return self._wrap(pyd.zip_with)(*arrays, **kwargs) + + def tap(self: "Chain[T]", interceptor: t.Callable[[T], t.Any]) -> "Chain[T]": + return self._wrap(pyd.tap)(interceptor) + + @t.overload + def at(self: "Chain[t.Mapping[T, T2]]", *paths: T) -> "Chain[t.List[t.Union[T2, None]]]": ... + @t.overload + def at( + self: "Chain[t.Mapping[T, t.Any]]", *paths: t.Union[T, t.Iterable[T]] + ) -> "Chain[t.List[t.Any]]": ... + @t.overload + def at(self: "Chain[t.Iterable[T]]", *paths: int) -> "Chain[t.List[t.Union[T, None]]]": ... + @t.overload + def at( + self: "Chain[t.Iterable[t.Any]]", *paths: t.Union[int, t.Iterable[int]] + ) -> "Chain[t.List[t.Any]]": ... + def at(self, *paths): + return self._wrap(pyd.at)(*paths) + + @t.overload + def count_by( + self: "Chain[t.Mapping[t.Any, T2]]", iteratee: None = None + ) -> "Chain[t.Dict[T2, int]]": ... + @t.overload + def count_by( + self: "Chain[t.Mapping[T, T2]]", iteratee: t.Callable[[T2, T, t.Dict[T, T2]], T3] + ) -> "Chain[t.Dict[T3, int]]": ... + @t.overload + def count_by( + self: "Chain[t.Mapping[T, T2]]", iteratee: t.Callable[[T2, T], T3] + ) -> "Chain[t.Dict[T3, int]]": ... + @t.overload + def count_by( + self: "Chain[t.Mapping[t.Any, T2]]", iteratee: t.Callable[[T2], T3] + ) -> "Chain[t.Dict[T3, int]]": ... + @t.overload + def count_by( + self: "Chain[t.Iterable[T]]", iteratee: None = None + ) -> "Chain[t.Dict[T, int]]": ... + @t.overload + def count_by( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T, int, t.List[T]], T2] + ) -> "Chain[t.Dict[T2, int]]": ... + @t.overload + def count_by( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T, int], T2] + ) -> "Chain[t.Dict[T2, int]]": ... + @t.overload + def count_by( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T], T2] + ) -> "Chain[t.Dict[T2, int]]": ... + def count_by(self, iteratee=None): + return self._wrap(pyd.count_by)(iteratee) + + def every( + self: "Chain[t.Iterable[T]]", + predicate: t.Union[t.Callable[[T], t.Any], IterateeObjT, None] = None, + ) -> "Chain[bool]": + return self._wrap(pyd.every)(predicate) + + @t.overload + def filter_( + self: "Chain[t.Mapping[T, T2]]", + predicate: t.Union[t.Callable[[T2, T, t.Dict[T, T2]], t.Any], IterateeObjT, None] = None, + ) -> "Chain[t.List[T2]]": ... + @t.overload + def filter_( + self: "Chain[t.Mapping[T, T2]]", + predicate: t.Union[t.Callable[[T2, T], t.Any], IterateeObjT, None] = None, + ) -> "Chain[t.List[T2]]": ... + @t.overload + def filter_( + self: "Chain[t.Mapping[t.Any, T2]]", + predicate: t.Union[t.Callable[[T2], t.Any], IterateeObjT, None] = None, + ) -> "Chain[t.List[T2]]": ... + @t.overload + def filter_( + self: "Chain[t.Iterable[T]]", + predicate: t.Union[t.Callable[[T, int, t.List[T]], t.Any], IterateeObjT, None] = None, + ) -> "Chain[t.List[T]]": ... + @t.overload + def filter_( + self: "Chain[t.Iterable[T]]", + predicate: t.Union[t.Callable[[T, int], t.Any], IterateeObjT, None] = None, + ) -> "Chain[t.List[T]]": ... + @t.overload + def filter_( + self: "Chain[t.Iterable[T]]", + predicate: t.Union[t.Callable[[T], t.Any], IterateeObjT, None] = None, + ) -> "Chain[t.List[T]]": ... + def filter_(self, predicate=None): + return self._wrap(pyd.filter_)(predicate) + + filter = filter_ + + @t.overload + def find( + self: "Chain[t.Dict[T, T2]]", + predicate: t.Union[t.Callable[[T2, T, t.Dict[T, T2]], t.Any], IterateeObjT, None] = None, + ) -> "Chain[t.Union[T2, None]]": ... + @t.overload + def find( + self: "Chain[t.Dict[T, T2]]", + predicate: t.Union[t.Callable[[T2, T], t.Any], IterateeObjT, None] = None, + ) -> "Chain[t.Union[T2, None]]": ... + @t.overload + def find( + self: "Chain[t.Dict[T, T2]]", + predicate: t.Union[t.Callable[[T2], t.Any], IterateeObjT, None] = None, + ) -> "Chain[t.Union[T2, None]]": ... + @t.overload + def find( + self: "Chain[t.List[T]]", + predicate: t.Union[t.Callable[[T, int, t.List[T]], t.Any], IterateeObjT, None] = None, + ) -> "Chain[t.Union[T, None]]": ... + @t.overload + def find( + self: "Chain[t.List[T]]", + predicate: t.Union[t.Callable[[T, int], t.Any], IterateeObjT, None] = None, + ) -> "Chain[t.Union[T, None]]": ... + @t.overload + def find( + self: "Chain[t.List[T]]", + predicate: t.Union[t.Callable[[T], t.Any], IterateeObjT, None] = None, + ) -> "Chain[t.Union[T, None]]": ... + def find(self, predicate=None): + return self._wrap(pyd.find)(predicate) + + @t.overload + def find_last( + self: "Chain[t.Dict[T, T2]]", + predicate: t.Union[t.Callable[[T2, T, t.Dict[T, T2]], t.Any], IterateeObjT, None] = None, + ) -> "Chain[t.Union[T2, None]]": ... + @t.overload + def find_last( + self: "Chain[t.Dict[T, T2]]", + predicate: t.Union[t.Callable[[T2, T], t.Any], IterateeObjT, None] = None, + ) -> "Chain[t.Union[T2, None]]": ... + @t.overload + def find_last( + self: "Chain[t.Dict[t.Any, T2]]", + predicate: t.Union[t.Callable[[T2], t.Any], IterateeObjT, None] = None, + ) -> "Chain[t.Union[T2, None]]": ... + @t.overload + def find_last( + self: "Chain[t.List[T]]", + predicate: t.Union[t.Callable[[T, int, t.List[T]], t.Any], IterateeObjT, None] = None, + ) -> "Chain[t.Union[T, None]]": ... + @t.overload + def find_last( + self: "Chain[t.List[T]]", + predicate: t.Union[t.Callable[[T, int], t.Any], IterateeObjT, None] = None, + ) -> "Chain[t.Union[T, None]]": ... + @t.overload + def find_last( + self: "Chain[t.List[T]]", + predicate: t.Union[t.Callable[[T], t.Any], IterateeObjT, None] = None, + ) -> "Chain[t.Union[T, None]]": ... + def find_last(self, predicate=None): + return self._wrap(pyd.find_last)(predicate) + + @t.overload + def flat_map( + self: "Chain[t.Mapping[T, T2]]", + iteratee: t.Callable[[T2, T, t.Dict[T, T2]], t.Iterable[T3]], + ) -> "Chain[t.List[T3]]": ... + @t.overload + def flat_map( + self: "Chain[t.Mapping[T, T2]]", iteratee: t.Callable[[T2, T], t.Iterable[T3]] + ) -> "Chain[t.List[T3]]": ... + @t.overload + def flat_map( + self: "Chain[t.Mapping[t.Any, T2]]", iteratee: t.Callable[[T2], t.Iterable[T3]] + ) -> "Chain[t.List[T3]]": ... + @t.overload + def flat_map( + self: "Chain[t.Mapping[T, T2]]", iteratee: t.Callable[[T2, T, t.Dict[T, T2]], T3] + ) -> "Chain[t.List[T3]]": ... + @t.overload + def flat_map( + self: "Chain[t.Mapping[T, T2]]", iteratee: t.Callable[[T2, T], T3] + ) -> "Chain[t.List[T3]]": ... + @t.overload + def flat_map( + self: "Chain[t.Mapping[t.Any, T2]]", iteratee: t.Callable[[T2], T3] + ) -> "Chain[t.List[T3]]": ... + @t.overload + def flat_map( + self: "Chain[t.Mapping[t.Any, t.Iterable[T2]]]", iteratee: None = None + ) -> "Chain[t.List[T2]]": ... + @t.overload + def flat_map( + self: "Chain[t.Mapping[t.Any, T2]]", iteratee: None = None + ) -> "Chain[t.List[T2]]": ... + @t.overload + def flat_map( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T, int, t.List[T]], t.Iterable[T2]] + ) -> "Chain[t.List[T2]]": ... + @t.overload + def flat_map( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T, int], t.Iterable[T2]] + ) -> "Chain[t.List[T2]]": ... + @t.overload + def flat_map( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T], t.Iterable[T2]] + ) -> "Chain[t.List[T2]]": ... + @t.overload + def flat_map( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T, int, t.List[T]], T2] + ) -> "Chain[t.List[T2]]": ... + @t.overload + def flat_map( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T, int], T2] + ) -> "Chain[t.List[T2]]": ... + @t.overload + def flat_map( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T], T2] + ) -> "Chain[t.List[T2]]": ... + @t.overload + def flat_map( + self: "Chain[t.Iterable[t.Iterable[T]]]", iteratee: None = None + ) -> "Chain[t.List[T]]": ... + @t.overload + def flat_map(self: "Chain[t.Iterable[T]]", iteratee: None = None) -> "Chain[t.List[T]]": ... + def flat_map(self, iteratee=None): + return self._wrap(pyd.flat_map)(iteratee) + + @t.overload + def flat_map_deep( + self: "Chain[t.Mapping[T, T2]]", + iteratee: t.Union[t.Callable[[T2, T, t.Dict[T, T2]], t.Any], None] = None, + ) -> "Chain[t.List[t.Any]]": ... + @t.overload + def flat_map_deep( + self: "Chain[t.Mapping[T, T2]]", iteratee: t.Union[t.Callable[[T2, T], t.Any], None] = None + ) -> "Chain[t.List[t.Any]]": ... + @t.overload + def flat_map_deep( + self: "Chain[t.Mapping[t.Any, T2]]", iteratee: t.Union[t.Callable[[T2], t.Any], None] = None + ) -> "Chain[t.List[t.Any]]": ... + @t.overload + def flat_map_deep( + self: "Chain[t.Iterable[T]]", + iteratee: t.Union[t.Callable[[T, int, t.List[T]], t.Any], None] = None, + ) -> "Chain[t.List[t.Any]]": ... + @t.overload + def flat_map_deep( + self: "Chain[t.Iterable[T]]", iteratee: t.Union[t.Callable[[T, int], t.Any], None] = None + ) -> "Chain[t.List[t.Any]]": ... + @t.overload + def flat_map_deep( + self: "Chain[t.Iterable[T]]", iteratee: t.Union[t.Callable[[T], t.Any], None] = None + ) -> "Chain[t.List[t.Any]]": ... + def flat_map_deep(self, iteratee=None): + return self._wrap(pyd.flat_map_deep)(iteratee) + + @t.overload + def flat_map_depth( + self: "Chain[t.Mapping[T, T2]]", + iteratee: t.Union[t.Callable[[T2, T, t.Dict[T, T2]], t.Any], None] = None, + depth: int = 1, + ) -> "Chain[t.List[t.Any]]": ... + @t.overload + def flat_map_depth( + self: "Chain[t.Mapping[T, T2]]", + iteratee: t.Union[t.Callable[[T2, T], t.Any], None] = None, + depth: int = 1, + ) -> "Chain[t.List[t.Any]]": ... + @t.overload + def flat_map_depth( + self: "Chain[t.Mapping[t.Any, T2]]", + iteratee: t.Union[t.Callable[[T2], t.Any], None] = None, + depth: int = 1, + ) -> "Chain[t.List[t.Any]]": ... + @t.overload + def flat_map_depth( + self: "Chain[t.Iterable[T]]", + iteratee: t.Union[t.Callable[[T, int, t.List[T]], t.Any], None] = None, + depth: int = 1, + ) -> "Chain[t.List[t.Any]]": ... + @t.overload + def flat_map_depth( + self: "Chain[t.Iterable[T]]", + iteratee: t.Union[t.Callable[[T, int], t.Any], None] = None, + depth: int = 1, + ) -> "Chain[t.List[t.Any]]": ... + @t.overload + def flat_map_depth( + self: "Chain[t.Iterable[T]]", + iteratee: t.Union[t.Callable[[T], t.Any], None] = None, + depth: int = 1, + ) -> "Chain[t.List[t.Any]]": ... + def flat_map_depth(self, iteratee=None, depth=1): + return self._wrap(pyd.flat_map_depth)(iteratee, depth) + + @t.overload + def for_each( + self: "Chain[t.Dict[T, T2]]", + iteratee: t.Union[t.Callable[[T2, T, t.Dict[T, T2]], t.Any], IterateeObjT, None] = None, + ) -> "Chain[t.Dict[T, T2]]": ... + @t.overload + def for_each( + self: "Chain[t.Dict[T, T2]]", + iteratee: t.Union[t.Callable[[T2, T], t.Any], IterateeObjT, None] = None, + ) -> "Chain[t.Dict[T, T2]]": ... + @t.overload + def for_each( + self: "Chain[t.Dict[T, T2]]", + iteratee: t.Union[t.Callable[[T2], t.Any], IterateeObjT, None] = None, + ) -> "Chain[t.Dict[T, T2]]": ... + @t.overload + def for_each( + self: "Chain[t.List[T]]", + iteratee: t.Union[t.Callable[[T, int, t.List[T]], t.Any], IterateeObjT, None] = None, + ) -> "Chain[t.List[T]]": ... + @t.overload + def for_each( + self: "Chain[t.List[T]]", + iteratee: t.Union[t.Callable[[T, int], t.Any], IterateeObjT, None] = None, + ) -> "Chain[t.List[T]]": ... + @t.overload + def for_each( + self: "Chain[t.List[T]]", + iteratee: t.Union[t.Callable[[T], t.Any], IterateeObjT, None] = None, + ) -> "Chain[t.List[T]]": ... + def for_each(self, iteratee=None): + return self._wrap(pyd.for_each)(iteratee) + + @t.overload + def for_each_right( + self: "Chain[t.Dict[T, T2]]", + iteratee: t.Union[t.Callable[[T2, T, t.Dict[T, T2]], t.Any], IterateeObjT], + ) -> "Chain[t.Dict[T, T2]]": ... + @t.overload + def for_each_right( + self: "Chain[t.Dict[T, T2]]", iteratee: t.Union[t.Callable[[T2, T], t.Any], IterateeObjT] + ) -> "Chain[t.Dict[T, T2]]": ... + @t.overload + def for_each_right( + self: "Chain[t.Dict[T, T2]]", iteratee: t.Union[t.Callable[[T2], t.Any], IterateeObjT] + ) -> "Chain[t.Dict[T, T2]]": ... + @t.overload + def for_each_right( + self: "Chain[t.List[T]]", + iteratee: t.Union[t.Callable[[T, int, t.List[T]], t.Any], IterateeObjT], + ) -> "Chain[t.List[T]]": ... + @t.overload + def for_each_right( + self: "Chain[t.List[T]]", iteratee: t.Union[t.Callable[[T, int], t.Any], IterateeObjT] + ) -> "Chain[t.List[T]]": ... + @t.overload + def for_each_right( + self: "Chain[t.List[T]]", iteratee: t.Union[t.Callable[[T], t.Any], IterateeObjT] + ) -> "Chain[t.List[T]]": ... + def for_each_right(self, iteratee): + return self._wrap(pyd.for_each_right)(iteratee) + + @t.overload + def group_by( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T], T2] + ) -> "Chain[t.Dict[T2, t.List[T]]]": ... + @t.overload + def group_by( + self: "Chain[t.Iterable[T]]", iteratee: t.Union[IterateeObjT, None] = None + ) -> "Chain[t.Dict[t.Any, t.List[T]]]": ... + def group_by(self, iteratee=None): + return self._wrap(pyd.group_by)(iteratee) + + def includes( + self: "Chain[t.Union[t.Sequence[t.Any], t.Dict[t.Any, t.Any]]]", + target: t.Any, + from_index: int = 0, + ) -> "Chain[bool]": + return self._wrap(pyd.includes)(target, from_index) + + def invoke_map( + self: "Chain[t.Iterable[t.Any]]", path: PathT, *args: t.Any, **kwargs: t.Any + ) -> "Chain[t.List[t.Any]]": + return self._wrap(pyd.invoke_map)(path, *args, **kwargs) + + @t.overload + def key_by( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T], T2] + ) -> "Chain[t.Dict[T2, T]]": ... + @t.overload + def key_by( + self: "Chain[t.Iterable[t.Any]]", iteratee: t.Union[IterateeObjT, None] = None + ) -> "Chain[t.Dict[t.Any, t.Any]]": ... + def key_by(self, iteratee=None): + return self._wrap(pyd.key_by)(iteratee) + + @t.overload + def map_( + self: "Chain[t.Mapping[t.Any, T2]]", iteratee: t.Callable[[T2], T3] + ) -> "Chain[t.List[T3]]": ... + @t.overload + def map_( + self: "Chain[t.Mapping[T, T2]]", iteratee: t.Callable[[T2, T], T3] + ) -> "Chain[t.List[T3]]": ... + @t.overload + def map_( + self: "Chain[t.Mapping[T, T2]]", iteratee: t.Callable[[T2, T, t.Dict[T, T2]], T3] + ) -> "Chain[t.List[T3]]": ... + @t.overload + def map_( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T], T2] + ) -> "Chain[t.List[T2]]": ... + @t.overload + def map_( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T, int], T2] + ) -> "Chain[t.List[T2]]": ... + @t.overload + def map_( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T, int, t.List[T]], T2] + ) -> "Chain[t.List[T2]]": ... + @t.overload + def map_( + self: "Chain[t.Iterable[t.Any]]", iteratee: t.Union[IterateeObjT, None] = None + ) -> "Chain[t.List[t.Any]]": ... + def map_(self, iteratee=None): + return self._wrap(pyd.map_)(iteratee) + + map = map_ + + def nest(self: "Chain[t.Iterable[t.Any]]", *properties: t.Any) -> "Chain[t.Any]": + return self._wrap(pyd.nest)(*properties) + + @t.overload + def order_by( + self: "Chain[t.Mapping[t.Any, T2]]", + keys: t.Iterable[t.Union[str, int]], + orders: t.Union[t.Iterable[bool], bool], + reverse: bool = False, + ) -> "Chain[t.List[T2]]": ... + @t.overload + def order_by( + self: "Chain[t.Mapping[t.Any, T2]]", + keys: t.Iterable[str], + orders: None = None, + reverse: bool = False, + ) -> "Chain[t.List[T2]]": ... + @t.overload + def order_by( + self: "Chain[t.Iterable[T]]", + keys: t.Iterable[t.Union[str, int]], + orders: t.Union[t.Iterable[bool], bool], + reverse: bool = False, + ) -> "Chain[t.List[T]]": ... + @t.overload + def order_by( + self: "Chain[t.Iterable[T]]", + keys: t.Iterable[str], + orders: None = None, + reverse: bool = False, + ) -> "Chain[t.List[T]]": ... + def order_by(self, keys, orders=None, reverse=False): + return self._wrap(pyd.order_by)(keys, orders, reverse) + + @t.overload + def partition( + self: "Chain[t.Mapping[T, T2]]", predicate: t.Callable[[T2, T, t.Dict[T, T2]], t.Any] + ) -> "Chain[t.List[t.List[T2]]]": ... + @t.overload + def partition( + self: "Chain[t.Mapping[T, T2]]", predicate: t.Callable[[T2, T], t.Any] + ) -> "Chain[t.List[t.List[T2]]]": ... + @t.overload + def partition( + self: "Chain[t.Mapping[t.Any, T2]]", predicate: t.Callable[[T2], t.Any] + ) -> "Chain[t.List[t.List[T2]]]": ... + @t.overload + def partition( + self: "Chain[t.Mapping[t.Any, T2]]", predicate: t.Union[IterateeObjT, None] = None + ) -> "Chain[t.List[t.List[T2]]]": ... + @t.overload + def partition( + self: "Chain[t.Iterable[T]]", predicate: t.Callable[[T, int, t.List[T]], t.Any] + ) -> "Chain[t.List[t.List[T]]]": ... + @t.overload + def partition( + self: "Chain[t.Iterable[T]]", predicate: t.Callable[[T, int], t.Any] + ) -> "Chain[t.List[t.List[T]]]": ... + @t.overload + def partition( + self: "Chain[t.Iterable[T]]", predicate: t.Callable[[T], t.Any] + ) -> "Chain[t.List[t.List[T]]]": ... + @t.overload + def partition( + self: "Chain[t.Iterable[T]]", predicate: t.Union[IterateeObjT, None] = None + ) -> "Chain[t.List[t.List[T]]]": ... + def partition(self, predicate=None): + return self._wrap(pyd.partition)(predicate) + + def pluck(self: "Chain[t.Iterable[t.Any]]", path: PathT) -> "Chain[t.List[t.Any]]": + return self._wrap(pyd.pluck)(path) + + @t.overload + def reduce_( + self: "Chain[t.Mapping[T, T2]]", iteratee: t.Callable[[T3, T2, T], T3], accumulator: T3 + ) -> "Chain[T3]": ... + @t.overload + def reduce_( + self: "Chain[t.Mapping[t.Any, T2]]", iteratee: t.Callable[[T3, T2], T3], accumulator: T3 + ) -> "Chain[T3]": ... + @t.overload + def reduce_( + self: "Chain[t.Mapping[t.Any, t.Any]]", iteratee: t.Callable[[T3], T3], accumulator: T3 + ) -> "Chain[T3]": ... + @t.overload + def reduce_( + self: "Chain[t.Mapping[T, T2]]", + iteratee: t.Callable[[T2, T2, T], T2], + accumulator: None = None, + ) -> "Chain[T2]": ... + @t.overload + def reduce_( + self: "Chain[t.Mapping[t.Any, T2]]", + iteratee: t.Callable[[T2, T2], T2], + accumulator: None = None, + ) -> "Chain[T2]": ... + @t.overload + def reduce_( + self: "Chain[t.Mapping[t.Any, t.Any]]", + iteratee: t.Callable[[T], T], + accumulator: None = None, + ) -> "Chain[T]": ... + @t.overload + def reduce_( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T2, T, int], T2], accumulator: T2 + ) -> "Chain[T2]": ... + @t.overload + def reduce_( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T2, T], T2], accumulator: T2 + ) -> "Chain[T2]": ... + @t.overload + def reduce_( + self: "Chain[t.Iterable[t.Any]]", iteratee: t.Callable[[T2], T2], accumulator: T2 + ) -> "Chain[T2]": ... + @t.overload + def reduce_( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T, T, int], T], accumulator: None = None + ) -> "Chain[T]": ... + @t.overload + def reduce_( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T, T], T], accumulator: None = None + ) -> "Chain[T]": ... + @t.overload + def reduce_( + self: "Chain[t.Iterable[t.Any]]", iteratee: t.Callable[[T], T], accumulator: None = None + ) -> "Chain[T]": ... + @t.overload + def reduce_( + self: "Chain[t.Iterable[T]]", iteratee: None = None, accumulator: t.Union[T, None] = None + ) -> "Chain[T]": ... + def reduce_(self, iteratee=None, accumulator=None): + return self._wrap(pyd.reduce_)(iteratee, accumulator) + + reduce = reduce_ + + @t.overload + def reduce_right( + self: "Chain[t.Mapping[T, T2]]", iteratee: t.Callable[[T3, T2, T], T3], accumulator: T3 + ) -> "Chain[T3]": ... + @t.overload + def reduce_right( + self: "Chain[t.Mapping[t.Any, T2]]", iteratee: t.Callable[[T3, T2], T3], accumulator: T3 + ) -> "Chain[T3]": ... + @t.overload + def reduce_right( + self: "Chain[t.Mapping[t.Any, t.Any]]", iteratee: t.Callable[[T3], T3], accumulator: T3 + ) -> "Chain[T3]": ... + @t.overload + def reduce_right( + self: "Chain[t.Mapping[T, T2]]", + iteratee: t.Callable[[T2, T2, T], T2], + accumulator: None = None, + ) -> "Chain[T2]": ... + @t.overload + def reduce_right( + self: "Chain[t.Mapping[t.Any, T2]]", + iteratee: t.Callable[[T2, T2], T2], + accumulator: None = None, + ) -> "Chain[T2]": ... + @t.overload + def reduce_right( + self: "Chain[t.Mapping[t.Any, t.Any]]", + iteratee: t.Callable[[T], T], + accumulator: None = None, + ) -> "Chain[T]": ... + @t.overload + def reduce_right( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T2, T, int], T2], accumulator: T2 + ) -> "Chain[T2]": ... + @t.overload + def reduce_right( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T2, T], T2], accumulator: T2 + ) -> "Chain[T2]": ... + @t.overload + def reduce_right( + self: "Chain[t.Iterable[t.Any]]", iteratee: t.Callable[[T2], T2], accumulator: T2 + ) -> "Chain[T2]": ... + @t.overload + def reduce_right( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T, T, int], T], accumulator: None = None + ) -> "Chain[T]": ... + @t.overload + def reduce_right( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T, T], T], accumulator: None = None + ) -> "Chain[T]": ... + @t.overload + def reduce_right( + self: "Chain[t.Iterable[t.Any]]", iteratee: t.Callable[[T], T], accumulator: None = None + ) -> "Chain[T]": ... + @t.overload + def reduce_right( + self: "Chain[t.Iterable[T]]", iteratee: None = None, accumulator: t.Union[T, None] = None + ) -> "Chain[T]": ... + def reduce_right(self, iteratee=None, accumulator=None): + return self._wrap(pyd.reduce_right)(iteratee, accumulator) + + @t.overload + def reductions( + self: "Chain[t.Mapping[T, T2]]", + iteratee: t.Callable[[T3, T2, T], T3], + accumulator: T3, + from_right: bool = False, + ) -> "Chain[t.List[T3]]": ... + @t.overload + def reductions( + self: "Chain[t.Mapping[t.Any, T2]]", + iteratee: t.Callable[[T3, T2], T3], + accumulator: T3, + from_right: bool = False, + ) -> "Chain[t.List[T3]]": ... + @t.overload + def reductions( + self: "Chain[t.Mapping[t.Any, t.Any]]", + iteratee: t.Callable[[T3], T3], + accumulator: T3, + from_right: bool = False, + ) -> "Chain[t.List[T3]]": ... + @t.overload + def reductions( + self: "Chain[t.Mapping[T, T2]]", + iteratee: t.Callable[[T2, T2, T], T2], + accumulator: None = None, + from_right: bool = False, + ) -> "Chain[t.List[T2]]": ... + @t.overload + def reductions( + self: "Chain[t.Mapping[t.Any, T2]]", + iteratee: t.Callable[[T2, T2], T2], + accumulator: None = None, + from_right: bool = False, + ) -> "Chain[t.List[T2]]": ... + @t.overload + def reductions( + self: "Chain[t.Mapping[t.Any, t.Any]]", + iteratee: t.Callable[[T], T], + accumulator: None = None, + from_right: bool = False, + ) -> "Chain[t.List[T]]": ... + @t.overload + def reductions( + self: "Chain[t.Iterable[T]]", + iteratee: t.Callable[[T2, T, int], T2], + accumulator: T2, + from_right: bool = False, + ) -> "Chain[t.List[T2]]": ... + @t.overload + def reductions( + self: "Chain[t.Iterable[T]]", + iteratee: t.Callable[[T2, T], T2], + accumulator: T2, + from_right: bool = False, + ) -> "Chain[t.List[T2]]": ... + @t.overload + def reductions( + self: "Chain[t.Iterable[t.Any]]", + iteratee: t.Callable[[T2], T2], + accumulator: T2, + from_right: bool = False, + ) -> "Chain[t.List[T2]]": ... + @t.overload + def reductions( + self: "Chain[t.Iterable[T]]", + iteratee: t.Callable[[T, T, int], T], + accumulator: None = None, + from_right: bool = False, + ) -> "Chain[t.List[T]]": ... + @t.overload + def reductions( + self: "Chain[t.Iterable[T]]", + iteratee: t.Callable[[T, T], T], + accumulator: None = None, + from_right: bool = False, + ) -> "Chain[t.List[T]]": ... + @t.overload + def reductions( + self: "Chain[t.Iterable[t.Any]]", + iteratee: t.Callable[[T], T], + accumulator: None = None, + from_right: bool = False, + ) -> "Chain[t.List[T]]": ... + @t.overload + def reductions( + self: "Chain[t.Iterable[T]]", + iteratee: None = None, + accumulator: t.Union[T, None] = None, + from_right: bool = False, + ) -> "Chain[t.List[T]]": ... + def reductions(self, iteratee=None, accumulator=None, from_right=False): + return self._wrap(pyd.reductions)(iteratee, accumulator, from_right) + + @t.overload + def reductions_right( + self: "Chain[t.Mapping[T, T2]]", iteratee: t.Callable[[T3, T2, T], T3], accumulator: T3 + ) -> "Chain[t.List[T3]]": ... + @t.overload + def reductions_right( + self: "Chain[t.Mapping[t.Any, T2]]", iteratee: t.Callable[[T3, T2], T3], accumulator: T3 + ) -> "Chain[t.List[T3]]": ... + @t.overload + def reductions_right( + self: "Chain[t.Mapping[t.Any, t.Any]]", iteratee: t.Callable[[T3], T3], accumulator: T3 + ) -> "Chain[t.List[T3]]": ... + @t.overload + def reductions_right( + self: "Chain[t.Mapping[T, T2]]", + iteratee: t.Callable[[T2, T2, T], T2], + accumulator: None = None, + ) -> "Chain[t.List[T2]]": ... + @t.overload + def reductions_right( + self: "Chain[t.Mapping[t.Any, T2]]", + iteratee: t.Callable[[T2, T2], T2], + accumulator: None = None, + ) -> "Chain[t.List[T2]]": ... + @t.overload + def reductions_right( + self: "Chain[t.Mapping[t.Any, t.Any]]", + iteratee: t.Callable[[T], T], + accumulator: None = None, + ) -> "Chain[t.List[T]]": ... + @t.overload + def reductions_right( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T2, T, int], T2], accumulator: T2 + ) -> "Chain[t.List[T2]]": ... + @t.overload + def reductions_right( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T2, T], T2], accumulator: T2 + ) -> "Chain[t.List[T2]]": ... + @t.overload + def reductions_right( + self: "Chain[t.Iterable[t.Any]]", iteratee: t.Callable[[T2], T2], accumulator: T2 + ) -> "Chain[t.List[T2]]": ... + @t.overload + def reductions_right( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T, T, int], T], accumulator: None = None + ) -> "Chain[t.List[T]]": ... + @t.overload + def reductions_right( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T, T], T], accumulator: None = None + ) -> "Chain[t.List[T]]": ... + @t.overload + def reductions_right( + self: "Chain[t.Iterable[t.Any]]", iteratee: t.Callable[[T], T], accumulator: None = None + ) -> "Chain[t.List[T]]": ... + @t.overload + def reductions_right( + self: "Chain[t.Iterable[T]]", iteratee: None = None, accumulator: t.Union[T, None] = None + ) -> "Chain[t.List[T]]": ... + def reductions_right(self, iteratee=None, accumulator=None): + return self._wrap(pyd.reductions_right)(iteratee, accumulator) + + @t.overload + def reject( + self: "Chain[t.Mapping[T, T2]]", + predicate: t.Union[t.Callable[[T2, T, t.Dict[T, T2]], t.Any], IterateeObjT, None] = None, + ) -> "Chain[t.List[T2]]": ... + @t.overload + def reject( + self: "Chain[t.Mapping[T, T2]]", + predicate: t.Union[t.Callable[[T2, T], t.Any], IterateeObjT, None] = None, + ) -> "Chain[t.List[T2]]": ... + @t.overload + def reject( + self: "Chain[t.Mapping[t.Any, T2]]", + predicate: t.Union[t.Callable[[T2], t.Any], IterateeObjT, None] = None, + ) -> "Chain[t.List[T2]]": ... + @t.overload + def reject( + self: "Chain[t.Iterable[T]]", + predicate: t.Union[t.Callable[[T, int, t.List[T]], t.Any], IterateeObjT, None] = None, + ) -> "Chain[t.List[T]]": ... + @t.overload + def reject( + self: "Chain[t.Iterable[T]]", + predicate: t.Union[t.Callable[[T, int], t.Any], IterateeObjT, None] = None, + ) -> "Chain[t.List[T]]": ... + @t.overload + def reject( + self: "Chain[t.Iterable[T]]", + predicate: t.Union[t.Callable[[T], t.Any], IterateeObjT, None] = None, + ) -> "Chain[t.List[T]]": ... + def reject(self, predicate=None): + return self._wrap(pyd.reject)(predicate) + + def sample(self: "Chain[t.Sequence[T]]") -> "Chain[T]": + return self._wrap(pyd.sample)() + + def sample_size( + self: "Chain[t.Sequence[T]]", n: t.Union[int, None] = None + ) -> "Chain[t.List[T]]": + return self._wrap(pyd.sample_size)(n) + + @t.overload + def shuffle(self: "Chain[t.Mapping[t.Any, T]]") -> "Chain[t.List[T]]": ... + @t.overload + def shuffle(self: "Chain[t.Iterable[T]]") -> "Chain[t.List[T]]": ... + def shuffle(self): + return self._wrap(pyd.shuffle)() + + def size(self: "Chain[t.Sized]") -> "Chain[int]": + return self._wrap(pyd.size)() + + def some( + self: "Chain[t.Iterable[T]]", predicate: t.Union[t.Callable[[T], t.Any], None] = None + ) -> "Chain[bool]": + return self._wrap(pyd.some)(predicate) + + @t.overload + def sort_by( + self: "Chain[t.Mapping[t.Any, T2]]", + iteratee: t.Union[t.Callable[[T2], t.Any], IterateeObjT, None] = None, + reverse: bool = False, + ) -> "Chain[t.List[T2]]": ... + @t.overload + def sort_by( + self: "Chain[t.Iterable[T]]", + iteratee: t.Union[t.Callable[[T], t.Any], IterateeObjT, None] = None, + reverse: bool = False, + ) -> "Chain[t.List[T]]": ... + def sort_by(self, iteratee=None, reverse=False): + return self._wrap(pyd.sort_by)(iteratee, reverse) + + def after(self: "Chain[t.Callable[P, T]]", n: t.SupportsInt) -> "Chain[After[P, T]]": + return self._wrap(pyd.after)(n) + + def ary(self: "Chain[t.Callable[..., T]]", n: t.Union[t.SupportsInt, None]) -> "Chain[Ary[T]]": + return self._wrap(pyd.ary)(n) + + def before(self: "Chain[t.Callable[P, T]]", n: t.SupportsInt) -> "Chain[Before[P, T]]": + return self._wrap(pyd.before)(n) + + def conjoin( + self: "Chain[t.Callable[[T], t.Any]]", *funcs: t.Callable[[T], t.Any] + ) -> "Chain[t.Callable[[t.Iterable[T]], bool]]": + return self._wrap(pyd.conjoin)(*funcs) + + @t.overload + def curry( + self: "Chain[t.Callable[[T1], T]]", arity: t.Union[int, None] = None + ) -> "Chain[CurryOne[T1, T]]": ... + @t.overload + def curry( + self: "Chain[t.Callable[[T1, T2], T]]", arity: t.Union[int, None] = None + ) -> "Chain[CurryTwo[T1, T2, T]]": ... + @t.overload + def curry( + self: "Chain[t.Callable[[T1, T2, T3], T]]", arity: t.Union[int, None] = None + ) -> "Chain[CurryThree[T1, T2, T3, T]]": ... + @t.overload + def curry( + self: "Chain[t.Callable[[T1, T2, T3, T4], T]]", arity: t.Union[int, None] = None + ) -> "Chain[CurryFour[T1, T2, T3, T4, T]]": ... + @t.overload + def curry( + self: "Chain[t.Callable[[T1, T2, T3, T4, T5], T]]", arity: t.Union[int, None] = None + ) -> "Chain[CurryFive[T1, T2, T3, T4, T5, T]]": ... + def curry(self, arity=None): + return self._wrap(pyd.curry)(arity) + + @t.overload + def curry_right( + self: "Chain[t.Callable[[T1], T]]", arity: t.Union[int, None] = None + ) -> "Chain[CurryRightOne[T1, T]]": ... + @t.overload + def curry_right( + self: "Chain[t.Callable[[T1, T2], T]]", arity: t.Union[int, None] = None + ) -> "Chain[CurryRightTwo[T2, T1, T]]": ... + @t.overload + def curry_right( + self: "Chain[t.Callable[[T1, T2, T3], T]]", arity: t.Union[int, None] = None + ) -> "Chain[CurryRightThree[T3, T2, T1, T]]": ... + @t.overload + def curry_right( + self: "Chain[t.Callable[[T1, T2, T3, T4], T]]", arity: t.Union[int, None] = None + ) -> "Chain[CurryRightFour[T4, T3, T2, T1, T]]": ... + @t.overload + def curry_right( + self: "Chain[t.Callable[[T1, T2, T3, T4, T5], T]]", + ) -> "Chain[CurryRightFive[T5, T4, T3, T2, T1, T]]": ... + def curry_right(self, arity=None): + return self._wrap(pyd.curry_right)(arity) + + def debounce( + self: "Chain[t.Callable[P, T]]", wait: int, max_wait: t.Union[int, Literal[False]] = False + ) -> "Chain[Debounce[P, T]]": + return self._wrap(pyd.debounce)(wait, max_wait) + + def delay( + self: "Chain[t.Callable[P, T]]", wait: int, *args: "P.args", **kwargs: "P.kwargs" + ) -> "Chain[T]": + return self._wrap(pyd.delay)(wait, *args, **kwargs) + + def disjoin( + self: "Chain[t.Callable[[T], t.Any]]", *funcs: t.Callable[[T], t.Any] + ) -> "Chain[Disjoin[T]]": + return self._wrap(pyd.disjoin)(*funcs) + + @t.overload + def flip( + self: "Chain[t.Callable[[T1, T2, T3, T4, T5], T]]", + ) -> "Chain[t.Callable[[T5, T4, T3, T2, T1], T]]": ... + @t.overload + def flip( + self: "Chain[t.Callable[[T1, T2, T3, T4], T]]", + ) -> "Chain[t.Callable[[T4, T3, T2, T1], T]]": ... + @t.overload + def flip( + self: "Chain[t.Callable[[T1, T2, T3], T]]", + ) -> "Chain[t.Callable[[T3, T2, T1], T]]": ... + @t.overload + def flip(self: "Chain[t.Callable[[T1, T2], T]]") -> "Chain[t.Callable[[T2, T1], T]]": ... + @t.overload + def flip(self: "Chain[t.Callable[[T1], T]]") -> "Chain[t.Callable[[T1], T]]": ... + def flip(self: "Chain[t.Callable[..., t.Any]]") -> "Chain[t.Callable[..., t.Any]]": + return self._wrap(pyd.flip)() + + @t.overload + def flow( + self: "Chain[t.Callable[P, T2]]", + func2: t.Callable[[T2], T3], + func3: t.Callable[[T3], T4], + func4: t.Callable[[T4], T5], + func5: t.Callable[[T5], T], + ) -> "Chain[Flow[P, T]]": ... + @t.overload + def flow( + self: "Chain[t.Callable[P, T2]]", + func2: t.Callable[[T2], T3], + func3: t.Callable[[T3], T4], + func4: t.Callable[[T4], T], + ) -> "Chain[Flow[P, T]]": ... + @t.overload + def flow( + self: "Chain[t.Callable[P, T2]]", func2: t.Callable[[T2], T3], func3: t.Callable[[T3], T] + ) -> "Chain[Flow[P, T]]": ... + @t.overload + def flow( + self: "Chain[t.Callable[P, T2]]", func2: t.Callable[[T2], T] + ) -> "Chain[Flow[P, T]]": ... + @t.overload + def flow(self: "Chain[t.Callable[P, T]]") -> "Chain[Flow[P, T]]": ... + def flow(self, *funcs): + return self._wrap(pyd.flow)(*funcs) + + @t.overload + def flow_right( + self: "Chain[t.Callable[[T4], T]]", + func4: t.Callable[[T3], T4], + func3: t.Callable[[T2], T3], + func2: t.Callable[[T1], T2], + func1: t.Callable[P, T1], + ) -> "Chain[Flow[P, T]]": ... + @t.overload + def flow_right( + self: "Chain[t.Callable[[T3], T]]", + func3: t.Callable[[T2], T3], + func2: t.Callable[[T1], T2], + func1: t.Callable[P, T1], + ) -> "Chain[Flow[P, T]]": ... + @t.overload + def flow_right( + self: "Chain[t.Callable[[T2], T]]", func2: t.Callable[[T1], T2], func1: t.Callable[P, T1] + ) -> "Chain[Flow[P, T]]": ... + @t.overload + def flow_right( + self: "Chain[t.Callable[[T1], T]]", func1: t.Callable[P, T1] + ) -> "Chain[Flow[P, T]]": ... + @t.overload + def flow_right(self: "Chain[t.Callable[P, T]]") -> "Chain[Flow[P, T]]": ... + def flow_right(self, *funcs): + return self._wrap(pyd.flow_right)(*funcs) + + def iterated(self: "Chain[t.Callable[[T], T]]") -> "Chain[Iterated[T]]": + return self._wrap(pyd.iterated)() + + def juxtapose( + self: "Chain[t.Callable[P, T]]", *funcs: t.Callable[P, T] + ) -> "Chain[Juxtapose[P, T]]": + return self._wrap(pyd.juxtapose)(*funcs) + + def negate(self: "Chain[t.Callable[P, t.Any]]") -> "Chain[Negate[P]]": + return self._wrap(pyd.negate)() + + def once(self: "Chain[t.Callable[P, T]]") -> "Chain[Once[P, T]]": + return self._wrap(pyd.once)() + + @t.overload + def over_args( + self: "Chain[t.Callable[[T1, T2, T3, T4, T5], T]]", + transform_one: t.Callable[[T1], T1], + transform_two: t.Callable[[T2], T2], + transform_three: t.Callable[[T3], T3], + transform_four: t.Callable[[T4], T4], + transform_five: t.Callable[[T5], T5], + ) -> "Chain[t.Callable[[T1, T2, T3, T4, T5], T]]": ... + @t.overload + def over_args( + self: "Chain[t.Callable[[T1, T2, T3, T4], T]]", + transform_one: t.Callable[[T1], T1], + transform_two: t.Callable[[T2], T2], + transform_three: t.Callable[[T3], T3], + transform_four: t.Callable[[T4], T4], + ) -> "Chain[t.Callable[[T1, T2, T3, T4], T]]": ... + @t.overload + def over_args( + self: "Chain[t.Callable[[T1, T2, T3], T]]", + transform_one: t.Callable[[T1], T1], + transform_two: t.Callable[[T2], T2], + transform_three: t.Callable[[T3], T3], + ) -> "Chain[t.Callable[[T1, T2, T3], T]]": ... + @t.overload + def over_args( + self: "Chain[t.Callable[[T1, T2], T]]", + transform_one: t.Callable[[T1], T1], + transform_two: t.Callable[[T2], T2], + ) -> "Chain[t.Callable[[T1, T2], T]]": ... + @t.overload + def over_args( + self: "Chain[t.Callable[[T1], T]]", transform_one: t.Callable[[T1], T1] + ) -> "Chain[t.Callable[[T1], T]]": ... + def over_args(self, *transforms): + return self._wrap(pyd.over_args)(*transforms) + + def partial( + self: "Chain[t.Callable[..., T]]", *args: t.Any, **kwargs: t.Any + ) -> "Chain[Partial[T]]": + return self._wrap(pyd.partial)(*args, **kwargs) + + def partial_right( + self: "Chain[t.Callable[..., T]]", *args: t.Any, **kwargs: t.Any + ) -> "Chain[Partial[T]]": + return self._wrap(pyd.partial_right)(*args, **kwargs) + + def rearg(self: "Chain[t.Callable[P, T]]", *indexes: int) -> "Chain[Rearg[P, T]]": + return self._wrap(pyd.rearg)(*indexes) + + def spread(self: "Chain[t.Callable[..., T]]") -> "Chain[Spread[T]]": + return self._wrap(pyd.spread)() + + def throttle(self: "Chain[t.Callable[P, T]]", wait: int) -> "Chain[Throttle[P, T]]": + return self._wrap(pyd.throttle)(wait) + + def unary(self: "Chain[t.Callable[..., T]]") -> "Chain[Ary[T]]": + return self._wrap(pyd.unary)() + + def wrap(self: "Chain[T1]", func: t.Callable[Concatenate[T1, P], T]) -> "Chain[Partial[T]]": + return self._wrap(pyd.wrap)(func) + + @t.overload + def add(self: "Chain['SupportsAdd[T, T2]']", b: T) -> "Chain[T2]": ... + @t.overload + def add(self: "Chain[T]", b: "SupportsAdd[T, T2]") -> "Chain[T2]": ... + def add(self, b): + return self._wrap(pyd.add)(b) + + @t.overload + def sum_(self: "Chain[t.Mapping[t.Any, 'SupportsAdd[int, T]']]") -> "Chain[T]": ... + @t.overload + def sum_(self: "Chain[t.Iterable['SupportsAdd[int, T]']]") -> "Chain[T]": ... + def sum_(self): + return self._wrap(pyd.sum_)() + + sum = sum_ + + @t.overload + def sum_by( + self: "Chain[t.Mapping[T, T2]]", + iteratee: t.Callable[[T2, T, t.Dict[T, T2]], "SupportsAdd[int, T3]"], + ) -> "Chain[T3]": ... + @t.overload + def sum_by( + self: "Chain[t.Mapping[T, T2]]", iteratee: t.Callable[[T2, T], "SupportsAdd[int, T3]"] + ) -> "Chain[T3]": ... + @t.overload + def sum_by( + self: "Chain[t.Mapping[t.Any, T2]]", iteratee: t.Callable[[T2], "SupportsAdd[int, T3]"] + ) -> "Chain[T3]": ... + @t.overload + def sum_by( + self: "Chain[t.Iterable[T]]", + iteratee: t.Callable[[T, int, t.List[T]], "SupportsAdd[int, T2]"], + ) -> "Chain[T2]": ... + @t.overload + def sum_by( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T, int], "SupportsAdd[int, T2]"] + ) -> "Chain[T2]": ... + @t.overload + def sum_by( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T], "SupportsAdd[int, T2]"] + ) -> "Chain[T2]": ... + @t.overload + def sum_by( + self: "Chain[t.Mapping[t.Any, 'SupportsAdd[int, T]']]", iteratee: None = None + ) -> "Chain[T]": ... + @t.overload + def sum_by( + self: "Chain[t.Iterable['SupportsAdd[int, T]']]", iteratee: None = None + ) -> "Chain[T]": ... + def sum_by(self, iteratee=None): + return self._wrap(pyd.sum_by)(iteratee) + + @t.overload + def mean(self: "Chain[t.Mapping[t.Any, 'SupportsAdd[int, t.Any]']]") -> "Chain[float]": ... + @t.overload + def mean(self: "Chain[t.Iterable['SupportsAdd[int, t.Any]']]") -> "Chain[float]": ... + def mean(self): + return self._wrap(pyd.mean)() + + @t.overload + def mean_by( + self: "Chain[t.Mapping[T, T2]]", + iteratee: t.Callable[[T2, T, t.Dict[T, T2]], "SupportsAdd[int, t.Any]"], + ) -> "Chain[float]": ... + @t.overload + def mean_by( + self: "Chain[t.Mapping[T, T2]]", iteratee: t.Callable[[T2, T], "SupportsAdd[int, t.Any]"] + ) -> "Chain[float]": ... + @t.overload + def mean_by( + self: "Chain[t.Mapping[t.Any, T2]]", iteratee: t.Callable[[T2], "SupportsAdd[int, t.Any]"] + ) -> "Chain[float]": ... + @t.overload + def mean_by( + self: "Chain[t.Iterable[T]]", + iteratee: t.Callable[[T, int, t.List[T]], "SupportsAdd[int, t.Any]"], + ) -> "Chain[float]": ... + @t.overload + def mean_by( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T, int], "SupportsAdd[int, t.Any]"] + ) -> "Chain[float]": ... + @t.overload + def mean_by( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T], "SupportsAdd[int, t.Any]"] + ) -> "Chain[float]": ... + @t.overload + def mean_by( + self: "Chain[t.Mapping[t.Any, 'SupportsAdd[int, t.Any]']]", iteratee: None = None + ) -> "Chain[float]": ... + @t.overload + def mean_by( + self: "Chain[t.Iterable['SupportsAdd[int, t.Any]']]", iteratee: None = None + ) -> "Chain[float]": ... + def mean_by(self, iteratee=None): + return self._wrap(pyd.mean_by)(iteratee) + + def ceil(self: "Chain[NumberT]", precision: int = 0) -> "Chain[float]": + return self._wrap(pyd.ceil)(precision) + + def clamp( + self: "Chain[NumT]", lower: NumT2, upper: t.Union[NumT3, None] = None + ) -> "Chain[t.Union[NumT, NumT2, NumT3]]": + return self._wrap(pyd.clamp)(lower, upper) + + def divide( + self: "Chain[t.Union[NumberT, None]]", divisor: t.Union[NumberT, None] + ) -> "Chain[float]": + return self._wrap(pyd.divide)(divisor) + + def floor(self: "Chain[NumberT]", precision: int = 0) -> "Chain[float]": + return self._wrap(pyd.floor)(precision) + + @t.overload + def max_( + self: "Chain[t.Mapping[t.Any, 'SupportsRichComparisonT']]", default: Unset = UNSET + ) -> "Chain['SupportsRichComparisonT']": ... + @t.overload + def max_( + self: "Chain[t.Mapping[t.Any, 'SupportsRichComparisonT']]", default: T + ) -> "Chain[t.Union['SupportsRichComparisonT', T]]": ... + @t.overload + def max_( + self: "Chain[t.Iterable['SupportsRichComparisonT']]", default: Unset = UNSET + ) -> "Chain['SupportsRichComparisonT']": ... + @t.overload + def max_( + self: "Chain[t.Iterable['SupportsRichComparisonT']]", default: T + ) -> "Chain[t.Union['SupportsRichComparisonT', T]]": ... + def max_(self, default=UNSET): + return self._wrap(pyd.max_)(default) + + max = max_ + + @t.overload + def max_by( + self: "Chain[t.Mapping[t.Any, 'SupportsRichComparisonT']]", + iteratee: None = None, + default: Unset = UNSET, + ) -> "Chain['SupportsRichComparisonT']": ... + @t.overload + def max_by( + self: "Chain[t.Mapping[t.Any, T2]]", + iteratee: t.Callable[[T2], "SupportsRichComparisonT"], + default: Unset = UNSET, + ) -> "Chain[T2]": ... + @t.overload + def max_by( + self: "Chain[t.Mapping[t.Any, T2]]", + iteratee: t.Callable[[T2], "SupportsRichComparisonT"], + *, + default: T, + ) -> "Chain[t.Union[T2, T]]": ... + @t.overload + def max_by( + self: "Chain[t.Mapping[t.Any, 'SupportsRichComparisonT']]", + iteratee: None = None, + *, + default: T, + ) -> "Chain[t.Union['SupportsRichComparisonT', T]]": ... + @t.overload + def max_by( + self: "Chain[t.Iterable['SupportsRichComparisonT']]", + iteratee: None = None, + default: Unset = UNSET, + ) -> "Chain['SupportsRichComparisonT']": ... + @t.overload + def max_by( + self: "Chain[t.Iterable[T2]]", + iteratee: t.Callable[[T2], "SupportsRichComparisonT"], + default: Unset = UNSET, + ) -> "Chain[T2]": ... + @t.overload + def max_by( + self: "Chain[t.Iterable[T2]]", + iteratee: t.Callable[[T2], "SupportsRichComparisonT"], + *, + default: T, + ) -> "Chain[t.Union[T2, T]]": ... + @t.overload + def max_by( + self: "Chain[t.Iterable['SupportsRichComparisonT']]", iteratee: None = None, *, default: T + ) -> "Chain[t.Union['SupportsRichComparisonT', T]]": ... + @t.overload + def max_by( + self: "Chain[t.Iterable[T]]", iteratee: IterateeObjT, default: Unset = UNSET + ) -> "Chain[T]": ... + @t.overload + def max_by( + self: "Chain[t.Iterable[T]]", iteratee: IterateeObjT, default: T2 + ) -> "Chain[t.Union[T, T2]]": ... + def max_by(self, iteratee=None, default=UNSET): + return self._wrap(pyd.max_by)(iteratee, default) + + @t.overload + def median( + self: "Chain[t.Mapping[T, T2]]", iteratee: t.Callable[[T2, T, t.Dict[T, T2]], NumberT] + ) -> "Chain[t.Union[float, int]]": ... + @t.overload + def median( + self: "Chain[t.Mapping[T, T2]]", iteratee: t.Callable[[T2, T], NumberT] + ) -> "Chain[t.Union[float, int]]": ... + @t.overload + def median( + self: "Chain[t.Mapping[t.Any, T2]]", iteratee: t.Callable[[T2], NumberT] + ) -> "Chain[t.Union[float, int]]": ... + @t.overload + def median( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T, int, t.List[T]], NumberT] + ) -> "Chain[t.Union[float, int]]": ... + @t.overload + def median( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T, int], NumberT] + ) -> "Chain[t.Union[float, int]]": ... + @t.overload + def median( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T], NumberT] + ) -> "Chain[t.Union[float, int]]": ... + @t.overload + def median( + self: "Chain[t.Iterable[NumberT]]", iteratee: None = None + ) -> "Chain[t.Union[float, int]]": ... + def median(self, iteratee=None): + return self._wrap(pyd.median)(iteratee) + + @t.overload + def min_( + self: "Chain[t.Mapping[t.Any, 'SupportsRichComparisonT']]", default: Unset = UNSET + ) -> "Chain['SupportsRichComparisonT']": ... + @t.overload + def min_( + self: "Chain[t.Mapping[t.Any, 'SupportsRichComparisonT']]", default: T + ) -> "Chain[t.Union['SupportsRichComparisonT', T]]": ... + @t.overload + def min_( + self: "Chain[t.Iterable['SupportsRichComparisonT']]", default: Unset = UNSET + ) -> "Chain['SupportsRichComparisonT']": ... + @t.overload + def min_( + self: "Chain[t.Iterable['SupportsRichComparisonT']]", default: T + ) -> "Chain[t.Union['SupportsRichComparisonT', T]]": ... + def min_(self, default=UNSET): + return self._wrap(pyd.min_)(default) + + min = min_ + + @t.overload + def min_by( + self: "Chain[t.Mapping[t.Any, 'SupportsRichComparisonT']]", + iteratee: None = None, + default: Unset = UNSET, + ) -> "Chain['SupportsRichComparisonT']": ... + @t.overload + def min_by( + self: "Chain[t.Mapping[t.Any, T2]]", + iteratee: t.Callable[[T2], "SupportsRichComparisonT"], + default: Unset = UNSET, + ) -> "Chain[T2]": ... + @t.overload + def min_by( + self: "Chain[t.Mapping[t.Any, T2]]", + iteratee: t.Callable[[T2], "SupportsRichComparisonT"], + *, + default: T, + ) -> "Chain[t.Union[T2, T]]": ... + @t.overload + def min_by( + self: "Chain[t.Mapping[t.Any, 'SupportsRichComparisonT']]", + iteratee: None = None, + *, + default: T, + ) -> "Chain[t.Union['SupportsRichComparisonT', T]]": ... + @t.overload + def min_by( + self: "Chain[t.Iterable['SupportsRichComparisonT']]", + iteratee: None = None, + default: Unset = UNSET, + ) -> "Chain['SupportsRichComparisonT']": ... + @t.overload + def min_by( + self: "Chain[t.Iterable[T2]]", + iteratee: t.Callable[[T2], "SupportsRichComparisonT"], + default: Unset = UNSET, + ) -> "Chain[T2]": ... + @t.overload + def min_by( + self: "Chain[t.Iterable[T2]]", + iteratee: t.Callable[[T2], "SupportsRichComparisonT"], + *, + default: T, + ) -> "Chain[t.Union[T2, T]]": ... + @t.overload + def min_by( + self: "Chain[t.Iterable['SupportsRichComparisonT']]", iteratee: None = None, *, default: T + ) -> "Chain[t.Union['SupportsRichComparisonT', T]]": ... + @t.overload + def min_by( + self: "Chain[t.Iterable[T]]", iteratee: IterateeObjT, default: Unset = UNSET + ) -> "Chain[T]": ... + @t.overload + def min_by( + self: "Chain[t.Iterable[T]]", iteratee: IterateeObjT, default: T2 + ) -> "Chain[t.Union[T, T2]]": ... + def min_by(self, iteratee=None, default=UNSET): + return self._wrap(pyd.min_by)(iteratee, default) + + def moving_mean( + self: "Chain[t.Sequence['SupportsAdd[int, t.Any]']]", size: t.SupportsInt + ) -> "Chain[t.List[float]]": + return self._wrap(pyd.moving_mean)(size) + + @t.overload + def multiply(self: "Chain[SupportsMul[int, T2]]", multiplicand: None) -> "Chain[T2]": ... + @t.overload + def multiply(self: "Chain[None]", multiplicand: SupportsMul[int, T2]) -> "Chain[T2]": ... + @t.overload + def multiply(self: "Chain[None]", multiplicand: None) -> "Chain[int]": ... + @t.overload + def multiply(self: "Chain[SupportsMul[T, T2]]", multiplicand: T) -> "Chain[T2]": ... + @t.overload + def multiply(self: "Chain[T]", multiplicand: SupportsMul[T, T2]) -> "Chain[T2]": ... + def multiply(self, multiplicand): + return self._wrap(pyd.multiply)(multiplicand) + + @t.overload + def power(self: "Chain[int]", n: int) -> "Chain[t.Union[int, float]]": ... + @t.overload + def power(self: "Chain[float]", n: t.Union[int, float]) -> "Chain[float]": ... + @t.overload + def power(self: "Chain[t.List[int]]", n: int) -> "Chain[t.List[t.Union[int, float]]]": ... + @t.overload + def power( + self: "Chain[t.List[float]]", n: t.List[t.Union[int, float]] + ) -> "Chain[t.List[float]]": ... + def power(self, n): + return self._wrap(pyd.power)(n) + + @t.overload + def round_( + self: "Chain[t.List[SupportsRound[NumberT]]]", precision: int = 0 + ) -> "Chain[t.List[float]]": ... + @t.overload + def round_(self: "Chain[SupportsRound[NumberT]]", precision: int = 0) -> "Chain[float]": ... + def round_(self, precision=0): + return self._wrap(pyd.round_)(precision) + + round = round_ + + @t.overload + def scale( + self: "Chain[t.Iterable['Decimal']]", maximum: "Decimal" + ) -> "Chain[t.List['Decimal']]": ... + @t.overload + def scale( + self: "Chain[t.Iterable[NumberNoDecimalT]]", maximum: NumberNoDecimalT + ) -> "Chain[t.List[float]]": ... + @t.overload + def scale(self: "Chain[t.Iterable[NumberT]]", maximum: int = 1) -> "Chain[t.List[float]]": ... + def scale(self, maximum: NumberT = 1): + return self._wrap(pyd.scale)(maximum) + + @t.overload + def slope( + self: "Chain[t.Union[t.Tuple['Decimal', 'Decimal'], t.List['Decimal']]]", + point2: t.Union[t.Tuple["Decimal", "Decimal"], t.List["Decimal"]], + ) -> "Chain['Decimal']": ... + @t.overload + def slope( + self: "Chain[t.Union[t.Tuple[NumberNoDecimalT, NumberNoDecimalT], t.List[NumberNoDecimalT]]]", + point2: t.Union[t.Tuple[NumberNoDecimalT, NumberNoDecimalT], t.List[NumberNoDecimalT]], + ) -> "Chain[float]": ... + def slope(self, point2): + return self._wrap(pyd.slope)(point2) + + def std_deviation(self: "Chain[t.List[NumberT]]") -> "Chain[float]": + return self._wrap(pyd.std_deviation)() + + @t.overload + def subtract(self: "Chain['SupportsSub[T, T2]']", subtrahend: T) -> "Chain[T2]": ... + @t.overload + def subtract(self: "Chain[T]", subtrahend: "SupportsSub[T, T2]") -> "Chain[T2]": ... + def subtract(self, subtrahend): + return self._wrap(pyd.subtract)(subtrahend) + + def transpose(self: "Chain[t.Iterable[t.Iterable[T]]]") -> "Chain[t.List[t.List[T]]]": + return self._wrap(pyd.transpose)() + + @t.overload + def variance(self: "Chain[t.Mapping[t.Any, 'SupportsAdd[int, t.Any]']]") -> "Chain[float]": ... + @t.overload + def variance(self: "Chain[t.Iterable['SupportsAdd[int, t.Any]']]") -> "Chain[float]": ... + def variance(self): + return self._wrap(pyd.variance)() + + @t.overload + def zscore( + self: "Chain[t.Mapping[T, T2]]", iteratee: t.Callable[[T2, T, t.Dict[T, T2]], NumberT] + ) -> "Chain[t.List[float]]": ... + @t.overload + def zscore( + self: "Chain[t.Mapping[T, T2]]", iteratee: t.Callable[[T2, T], NumberT] + ) -> "Chain[t.List[float]]": ... + @t.overload + def zscore( + self: "Chain[t.Mapping[t.Any, T2]]", iteratee: t.Callable[[T2], NumberT] + ) -> "Chain[t.List[float]]": ... + @t.overload + def zscore( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T, int, t.List[T]], NumberT] + ) -> "Chain[t.List[float]]": ... + @t.overload + def zscore( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T, int], NumberT] + ) -> "Chain[t.List[float]]": ... + @t.overload + def zscore( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T], NumberT] + ) -> "Chain[t.List[float]]": ... + @t.overload + def zscore( + self: "Chain[t.Iterable[NumberT]]", iteratee: None = None + ) -> "Chain[t.List[float]]": ... + def zscore(self, iteratee=None): + return self._wrap(pyd.zscore)(iteratee) + + @t.overload + def assign( + self: "Chain[t.Mapping[T, T2]]", *sources: t.Mapping[T3, T4] + ) -> "Chain[t.Dict[t.Union[T, T3], t.Union[T2, T4]]]": ... + @t.overload + def assign( + self: "Chain[t.Union[t.Tuple[T, ...], t.List[T]]]", *sources: t.Mapping[int, T2] + ) -> "Chain[t.List[t.Union[T, T2]]]": ... + def assign(self, *sources) -> "Chain[t.Union[t.List[t.Any], t.Dict[t.Any, t.Any]]]": + return self._wrap(pyd.assign)(*sources) + + @t.overload + def assign_with( + self: "Chain[t.Mapping[T, T2]]", + *sources: t.Mapping[T3, t.Any], + customizer: t.Callable[[t.Union[T2, None]], T5], + ) -> "Chain[t.Dict[t.Union[T, T3], t.Union[T2, T5]]]": ... + @t.overload + def assign_with( + self: "Chain[t.Mapping[T, T2]]", + *sources: t.Mapping[T3, T4], + customizer: t.Callable[[t.Union[T2, None], T4], T5], + ) -> "Chain[t.Dict[t.Union[T, T3], t.Union[T2, T5]]]": ... + @t.overload + def assign_with( + self: "Chain[t.Mapping[T, T2]]", + *sources: t.Mapping[T3, T4], + customizer: t.Callable[[t.Union[T2, None], T4, T3], T5], + ) -> "Chain[t.Dict[t.Union[T, T3], t.Union[T2, T5]]]": ... + @t.overload + def assign_with( + self: "Chain[t.Mapping[T, T2]]", + *sources: t.Mapping[T3, T4], + customizer: t.Callable[[t.Union[T2, None], T4, T3, t.Dict[T, T2]], T5], + ) -> "Chain[t.Dict[t.Union[T, T3], t.Union[T2, T5]]]": ... + @t.overload + def assign_with( + self: "Chain[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], + ) -> "Chain[t.Dict[t.Union[T, T3], t.Union[T2, T5]]]": ... + @t.overload + def assign_with( + self: "Chain[t.Mapping[T, T2]]", *sources: t.Mapping[T3, T4], customizer: None = None + ) -> "Chain[t.Dict[t.Union[T, T3], t.Union[T2, T4]]]": ... + def assign_with(self, *sources, customizer=None): + return self._wrap(pyd.assign_with)(*sources, customizer=customizer) + + @t.overload + def callables( + self: "Chain[t.Mapping['SupportsRichComparisonT', t.Any]]", + ) -> "Chain[t.List['SupportsRichComparisonT']]": ... + @t.overload + def callables(self: "Chain[t.Iterable[T]]") -> "Chain[t.List[T]]": ... + def callables(self): + return self._wrap(pyd.callables)() + + def clone(self: "Chain[T]") -> "Chain[T]": + return self._wrap(pyd.clone)() + + @t.overload + def clone_with( + self: "Chain[t.Mapping[T, T2]]", customizer: t.Callable[[T2, T, t.Mapping[T, T2]], T3] + ) -> "Chain[t.Dict[T, t.Union[T2, T3]]]": ... + @t.overload + def clone_with( + self: "Chain[t.Mapping[T, T2]]", customizer: t.Callable[[T2, T], T3] + ) -> "Chain[t.Dict[T, t.Union[T2, T3]]]": ... + @t.overload + def clone_with( + self: "Chain[t.Mapping[T, T2]]", customizer: t.Callable[[T2], T3] + ) -> "Chain[t.Dict[T, t.Union[T2, T3]]]": ... + @t.overload + def clone_with( + self: "Chain[t.List[T]]", customizer: t.Callable[[T, int, t.List[T]], T2] + ) -> "Chain[t.List[t.Union[T, T2]]]": ... + @t.overload + def clone_with( + self: "Chain[t.List[T]]", customizer: t.Callable[[T, int], T2] + ) -> "Chain[t.List[t.Union[T, T2]]]": ... + @t.overload + def clone_with( + self: "Chain[t.List[T]]", customizer: t.Callable[[T], T2] + ) -> "Chain[t.List[t.Union[T, T2]]]": ... + @t.overload + def clone_with(self: "Chain[T]", customizer: None = None) -> "Chain[T]": ... + @t.overload + def clone_with(self: "Chain[t.Any]", customizer: t.Callable[..., t.Any]) -> "Chain[t.Any]": ... + def clone_with(self, customizer=None): + return self._wrap(pyd.clone_with)(customizer) + + def clone_deep(self: "Chain[T]") -> "Chain[T]": + return self._wrap(pyd.clone_deep)() + + @t.overload + def clone_deep_with( + self: "Chain[t.Mapping[T, T2]]", customizer: t.Callable[[T2, T, t.Mapping[T, T2]], T3] + ) -> "Chain[t.Dict[T, t.Union[T2, T3]]]": ... + @t.overload + def clone_deep_with( + self: "Chain[t.Mapping[T, T2]]", customizer: t.Callable[[T2, T], T3] + ) -> "Chain[t.Dict[T, t.Union[T2, T3]]]": ... + @t.overload + def clone_deep_with( + self: "Chain[t.Mapping[T, T2]]", customizer: t.Callable[[T2], T3] + ) -> "Chain[t.Dict[T, t.Union[T2, T3]]]": ... + @t.overload + def clone_deep_with( + self: "Chain[t.List[T]]", customizer: t.Callable[[T, int, t.List[T]], T2] + ) -> "Chain[t.List[t.Union[T, T2]]]": ... + @t.overload + def clone_deep_with( + self: "Chain[t.List[T]]", customizer: t.Callable[[T, int], T2] + ) -> "Chain[t.List[t.Union[T, T2]]]": ... + @t.overload + def clone_deep_with( + self: "Chain[t.List[T]]", customizer: t.Callable[[T], T2] + ) -> "Chain[t.List[t.Union[T, T2]]]": ... + @t.overload + def clone_deep_with(self: "Chain[T]", customizer: None = None) -> "Chain[T]": ... + @t.overload + def clone_deep_with( + self: "Chain[t.Any]", customizer: t.Callable[..., t.Any] + ) -> "Chain[t.Any]": ... + def clone_deep_with(self, customizer=None): + return self._wrap(pyd.clone_deep_with)(customizer) + + def defaults( + self: "Chain[t.Dict[T, T2]]", *sources: t.Dict[T3, T4] + ) -> "Chain[t.Dict[t.Union[T, T3], t.Union[T2, T4]]]": + return self._wrap(pyd.defaults)(*sources) + + def defaults_deep( + self: "Chain[t.Dict[T, T2]]", *sources: t.Dict[T3, T4] + ) -> "Chain[t.Dict[t.Union[T, T3], t.Union[T2, T4]]]": + return self._wrap(pyd.defaults_deep)(*sources) + + @t.overload + def find_key( + self: "Chain[t.Mapping[T, T2]]", predicate: t.Callable[[T2, T, t.Dict[T, T2]], t.Any] + ) -> "Chain[t.Union[T, None]]": ... + @t.overload + def find_key( + self: "Chain[t.Mapping[T, T2]]", predicate: t.Callable[[T2, T], t.Any] + ) -> "Chain[t.Union[T, None]]": ... + @t.overload + def find_key( + self: "Chain[t.Mapping[T, T2]]", predicate: t.Callable[[T2], t.Any] + ) -> "Chain[t.Union[T, None]]": ... + @t.overload + def find_key( + self: "Chain[t.Mapping[T, t.Any]]", predicate: None = None + ) -> "Chain[t.Union[T, None]]": ... + @t.overload + def find_key( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T, int, t.List[T]], t.Any] + ) -> "Chain[t.Union[int, None]]": ... + @t.overload + def find_key( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T, int], t.Any] + ) -> "Chain[t.Union[int, None]]": ... + @t.overload + def find_key( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T], t.Any] + ) -> "Chain[t.Union[int, None]]": ... + @t.overload + def find_key( + self: "Chain[t.Iterable[t.Any]]", iteratee: None = None + ) -> "Chain[t.Union[int, None]]": ... + def find_key(self, predicate=None): + return self._wrap(pyd.find_key)(predicate) + + @t.overload + def find_last_key( + self: "Chain[t.Mapping[T, T2]]", predicate: t.Callable[[T2, T, t.Dict[T, T2]], t.Any] + ) -> "Chain[t.Union[T, None]]": ... + @t.overload + def find_last_key( + self: "Chain[t.Mapping[T, T2]]", predicate: t.Callable[[T2, T], t.Any] + ) -> "Chain[t.Union[T, None]]": ... + @t.overload + def find_last_key( + self: "Chain[t.Mapping[T, T2]]", predicate: t.Callable[[T2], t.Any] + ) -> "Chain[t.Union[T, None]]": ... + @t.overload + def find_last_key( + self: "Chain[t.Mapping[T, t.Any]]", predicate: None = None + ) -> "Chain[t.Union[T, None]]": ... + @t.overload + def find_last_key( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T, int, t.List[T]], t.Any] + ) -> "Chain[t.Union[int, None]]": ... + @t.overload + def find_last_key( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T, int], t.Any] + ) -> "Chain[t.Union[int, None]]": ... + @t.overload + def find_last_key( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T], t.Any] + ) -> "Chain[t.Union[int, None]]": ... + @t.overload + def find_last_key( + self: "Chain[t.Iterable[t.Any]]", iteratee: None = None + ) -> "Chain[t.Union[int, None]]": ... + def find_last_key(self, predicate=None): + return self._wrap(pyd.find_last_key)(predicate) + + @t.overload + def for_in( + self: "Chain[t.Mapping[T, T2]]", iteratee: t.Callable[[T2, T, t.Dict[T, T2]], t.Any] + ) -> "Chain[t.Dict[T, T2]]": ... + @t.overload + def for_in( + self: "Chain[t.Mapping[T, T2]]", iteratee: t.Callable[[T2, T], t.Any] + ) -> "Chain[t.Dict[T, T2]]": ... + @t.overload + def for_in( + self: "Chain[t.Mapping[T, T2]]", iteratee: t.Callable[[T2], t.Any] + ) -> "Chain[t.Dict[T, T2]]": ... + @t.overload + def for_in( + self: "Chain[t.Mapping[T, T2]]", iteratee: None = None + ) -> "Chain[t.Dict[T, T2]]": ... + @t.overload + def for_in( + self: "Chain[t.Sequence[T]]", iteratee: t.Callable[[T, int, t.List[T]], t.Any] + ) -> "Chain[t.List[T]]": ... + @t.overload + def for_in( + self: "Chain[t.Sequence[T]]", iteratee: t.Callable[[T, int], t.Any] + ) -> "Chain[t.List[T]]": ... + @t.overload + def for_in( + self: "Chain[t.Sequence[T]]", iteratee: t.Callable[[T], t.Any] + ) -> "Chain[t.List[T]]": ... + @t.overload + def for_in(self: "Chain[t.Sequence[T]]", iteratee: None = None) -> "Chain[t.List[T]]": ... + def for_in(self, iteratee=None): + return self._wrap(pyd.for_in)(iteratee) + + @t.overload + def for_in_right( + self: "Chain[t.Mapping[T, T2]]", iteratee: t.Callable[[T2, T, t.Dict[T, T2]], t.Any] + ) -> "Chain[t.Dict[T, T2]]": ... + @t.overload + def for_in_right( + self: "Chain[t.Mapping[T, T2]]", iteratee: t.Callable[[T2, T], t.Any] + ) -> "Chain[t.Dict[T, T2]]": ... + @t.overload + def for_in_right( + self: "Chain[t.Mapping[T, T2]]", iteratee: t.Callable[[T2], t.Any] + ) -> "Chain[t.Dict[T, T2]]": ... + @t.overload + def for_in_right( + self: "Chain[t.Mapping[T, T2]]", iteratee: None = None + ) -> "Chain[t.Dict[T, T2]]": ... + @t.overload + def for_in_right( + self: "Chain[t.Sequence[T]]", iteratee: t.Callable[[T, int, t.List[T]], t.Any] + ) -> "Chain[t.List[T]]": ... + @t.overload + def for_in_right( + self: "Chain[t.Sequence[T]]", iteratee: t.Callable[[T, int], t.Any] + ) -> "Chain[t.List[T]]": ... + @t.overload + def for_in_right( + self: "Chain[t.Sequence[T]]", iteratee: t.Callable[[T], t.Any] + ) -> "Chain[t.List[T]]": ... + @t.overload + def for_in_right(self: "Chain[t.Sequence[T]]", iteratee: None = None) -> "Chain[t.List[T]]": ... + def for_in_right(self, iteratee=None): + return self._wrap(pyd.for_in_right)(iteratee) + + @t.overload + def get(self: "Chain[t.List[T]]", path: int, default: T2) -> "Chain[t.Union[T, T2]]": ... + @t.overload + def get( + self: "Chain[t.List[T]]", path: int, default: None = None + ) -> "Chain[t.Union[T, None]]": ... + @t.overload + def get(self: "Chain[t.Any]", path: PathT, default: t.Any = None) -> "Chain[t.Any]": ... + def get(self: "Chain[t.Any]", path: PathT, default: t.Any = None) -> "Chain[t.Any]": + return self._wrap(pyd.get)(path, default) + + def has(self: "Chain[t.Any]", path: PathT) -> "Chain[bool]": + return self._wrap(pyd.has)(path) + + @t.overload + def invert(self: "Chain[t.Mapping[T, T2]]") -> "Chain[t.Dict[T2, T]]": ... + @t.overload + def invert(self: "Chain[t.Union[t.Iterator[T], t.Sequence[T]]]") -> "Chain[t.Dict[T, int]]": ... + def invert(self): + return self._wrap(pyd.invert)() + + @t.overload + def invert_by( + self: "Chain[t.Mapping[T, T2]]", iteratee: t.Callable[[T2], T3] + ) -> "Chain[t.Dict[T3, t.List[T]]]": ... + @t.overload + def invert_by( + self: "Chain[t.Mapping[T, T2]]", iteratee: None = None + ) -> "Chain[t.Dict[T2, t.List[T]]]": ... + @t.overload + def invert_by( + self: "Chain[t.Union[t.Iterator[T], t.Sequence[T]]]", iteratee: t.Callable[[T], T2] + ) -> "Chain[t.Dict[T2, t.List[int]]]": ... + @t.overload + def invert_by( + self: "Chain[t.Union[t.Iterator[T], t.Sequence[T]]]", iteratee: None = None + ) -> "Chain[t.Dict[T, t.List[int]]]": ... + def invert_by(self, iteratee=None): + return self._wrap(pyd.invert_by)(iteratee) + + def invoke(self: "Chain[t.Any]", path: PathT, *args: t.Any, **kwargs: t.Any) -> "Chain[t.Any]": + return self._wrap(pyd.invoke)(path, *args, **kwargs) + + @t.overload + def keys(self: "Chain[t.Iterable[T]]") -> "Chain[t.List[T]]": ... + @t.overload + def keys(self: "Chain[t.Any]") -> "Chain[t.List[t.Any]]": ... + def keys(self): + return self._wrap(pyd.keys)() + + @t.overload + def map_keys( + self: "Chain[t.Mapping[T, T2]]", iteratee: t.Callable[[T2, T, t.Dict[T, T2]], T3] + ) -> "Chain[t.Dict[T3, T2]]": ... + @t.overload + def map_keys( + self: "Chain[t.Mapping[T, T2]]", iteratee: t.Callable[[T2, T], T3] + ) -> "Chain[t.Dict[T3, T2]]": ... + @t.overload + def map_keys( + self: "Chain[t.Mapping[t.Any, T2]]", iteratee: t.Callable[[T2], T3] + ) -> "Chain[t.Dict[T3, T2]]": ... + @t.overload + def map_keys( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T, int, t.List[T]], T2] + ) -> "Chain[t.Dict[T2, T]]": ... + @t.overload + def map_keys( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T, int], T2] + ) -> "Chain[t.Dict[T2, T]]": ... + @t.overload + def map_keys( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T], T2] + ) -> "Chain[t.Dict[T2, T]]": ... + @t.overload + def map_keys( + self: "Chain[t.Iterable[t.Any]]", iteratee: t.Union[IterateeObjT, None] = None + ) -> "Chain[t.Dict[t.Any, t.Any]]": ... + def map_keys(self, iteratee=None): + return self._wrap(pyd.map_keys)(iteratee) + + @t.overload + def map_values( + self: "Chain[t.Mapping[T, T2]]", iteratee: t.Callable[[T2, T, t.Dict[T, T2]], T3] + ) -> "Chain[t.Dict[T, T3]]": ... + @t.overload + def map_values( + self: "Chain[t.Mapping[T, T2]]", iteratee: t.Callable[[T2, T], T3] + ) -> "Chain[t.Dict[T, T3]]": ... + @t.overload + def map_values( + self: "Chain[t.Mapping[T, T2]]", iteratee: t.Callable[[T2], T3] + ) -> "Chain[t.Dict[T, T3]]": ... + @t.overload + def map_values( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T, int, t.List[T]], T2] + ) -> "Chain[t.Dict[T, T2]]": ... + @t.overload + def map_values( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T, int], T2] + ) -> "Chain[t.Dict[T, T2]]": ... + @t.overload + def map_values( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T], T2] + ) -> "Chain[t.Dict[T, T2]]": ... + @t.overload + def map_values( + self: "Chain[t.Iterable[t.Any]]", iteratee: t.Union[IterateeObjT, None] = None + ) -> "Chain[t.Dict[t.Any, t.Any]]": ... + def map_values(self, iteratee=None): + return self._wrap(pyd.map_values)(iteratee) + + def map_values_deep( + self: "Chain[t.Iterable[t.Any]]", + iteratee: t.Union[t.Callable[..., t.Any], None] = None, + property_path: t.Any = UNSET, + ) -> "Chain[t.Any]": + return self._wrap(pyd.map_values_deep)(iteratee, property_path) + + def apply(self: "Chain[T]", func: t.Callable[[T], T2]) -> "Chain[T2]": + return self._wrap(pyd.apply)(func) + + def apply_if( + self: "Chain[T]", func: t.Callable[[T], T2], predicate: t.Callable[[T], bool] + ) -> "Chain[t.Union[T, T2]]": + return self._wrap(pyd.apply_if)(func, predicate) + + def apply_if_not_none( + self: "Chain[t.Optional[T]]", func: t.Callable[[T], T2] + ) -> "Chain[t.Optional[T2]]": + return self._wrap(pyd.apply_if_not_none)(func) + + @t.overload + def apply_catch( + self: "Chain[T]", + func: t.Callable[[T], T2], + exceptions: t.Iterable[t.Type[Exception]], + default: T3, + ) -> "Chain[t.Union[T2, T3]]": ... + @t.overload + def apply_catch( + self: "Chain[T]", + func: t.Callable[[T], T2], + exceptions: t.Iterable[t.Type[Exception]], + default: Unset = UNSET, + ) -> "Chain[t.Union[T, T2]]": ... + def apply_catch(self, func, exceptions, default=UNSET): + return self._wrap(pyd.apply_catch)(func, exceptions, default) + + @t.overload + def merge( + self: "Chain[t.Mapping[T, T2]]", *sources: t.Mapping[T3, T4] + ) -> "Chain[t.Dict[t.Union[T, T3], t.Union[T2, T4]]]": ... + @t.overload + def merge( + self: "Chain[t.Sequence[T]]", *sources: t.Sequence[T2] + ) -> "Chain[t.List[t.Union[T, T2]]]": ... + def merge(self, *sources): + return self._wrap(pyd.merge)(*sources) + + def merge_with(self: "Chain[t.Any]", *sources: t.Any, **kwargs: t.Any) -> "Chain[t.Any]": + return self._wrap(pyd.merge_with)(*sources, **kwargs) + + @t.overload + def omit(self: "Chain[t.Mapping[T, T2]]", *properties: PathT) -> "Chain[t.Dict[T, T2]]": ... + @t.overload + def omit( + self: "Chain[t.Union[t.Iterator[T], t.Sequence[T]]]", *properties: PathT + ) -> "Chain[t.Dict[int, T]]": ... + @t.overload + def omit(self: "Chain[t.Any]", *properties: PathT) -> "Chain[t.Dict[t.Any, t.Any]]": ... + def omit(self, *properties): + return self._wrap(pyd.omit)(*properties) + + @t.overload + def omit_by( + self: "Chain[t.Mapping[T, T2]]", iteratee: t.Callable[[T2, T], t.Any] + ) -> "Chain[t.Dict[T, T2]]": ... + @t.overload + def omit_by( + self: "Chain[t.Mapping[T, T2]]", iteratee: t.Callable[[T2], t.Any] + ) -> "Chain[t.Dict[T, T2]]": ... + @t.overload + def omit_by(self: "Chain[t.Dict[T, T2]]", iteratee: None = None) -> "Chain[t.Dict[T, T2]]": ... + @t.overload + def omit_by( + self: "Chain[t.Union[t.Iterator[T], t.Sequence[T]]]", iteratee: t.Callable[[T, int], t.Any] + ) -> "Chain[t.Dict[int, T]]": ... + @t.overload + def omit_by( + self: "Chain[t.Union[t.Iterator[T], t.Sequence[T]]]", iteratee: t.Callable[[T], t.Any] + ) -> "Chain[t.Dict[int, T]]": ... + @t.overload + def omit_by(self: "Chain[t.List[T]]", iteratee: None = None) -> "Chain[t.Dict[int, T]]": ... + @t.overload + def omit_by( + self: "Chain[t.Any]", iteratee: t.Union[t.Callable[..., t.Any], None] = None + ) -> "Chain[t.Dict[t.Any, t.Any]]": ... + def omit_by(self, iteratee=None): + return self._wrap(pyd.omit_by)(iteratee) + + def parse_int( + self: "Chain[t.Any]", radix: t.Union[int, None] = None + ) -> "Chain[t.Union[int, None]]": + return self._wrap(pyd.parse_int)(radix) + + @t.overload + def pick(self: "Chain[t.Mapping[T, T2]]", *properties: PathT) -> "Chain[t.Dict[T, T2]]": ... + @t.overload + def pick( + self: "Chain[t.Union[t.Tuple[T, ...], t.List[T]]]", *properties: PathT + ) -> "Chain[t.Dict[int, T]]": ... + @t.overload + def pick(self: "Chain[t.Any]", *properties: PathT) -> "Chain[t.Dict[t.Any, t.Any]]": ... + def pick(self, *properties): + return self._wrap(pyd.pick)(*properties) + + @t.overload + def pick_by( + self: "Chain[t.Mapping[T, T2]]", iteratee: t.Callable[[T2], t.Any] + ) -> "Chain[t.Dict[T, T2]]": ... + @t.overload + def pick_by( + self: "Chain[t.Mapping[T, T2]]", iteratee: t.Callable[[T2, T], t.Any] + ) -> "Chain[t.Dict[T, T2]]": ... + @t.overload + def pick_by(self: "Chain[t.Dict[T, T2]]", iteratee: None = None) -> "Chain[t.Dict[T, T2]]": ... + @t.overload + def pick_by( + self: "Chain[t.Union[t.Tuple[T, ...], t.List[T]]]", iteratee: t.Callable[[T, int], t.Any] + ) -> "Chain[t.Dict[int, T]]": ... + @t.overload + def pick_by( + self: "Chain[t.Union[t.Tuple[T, ...], t.List[T]]]", iteratee: t.Callable[[T], t.Any] + ) -> "Chain[t.Dict[int, T]]": ... + @t.overload + def pick_by( + self: "Chain[t.Union[t.Tuple[T, ...], t.List[T]]]", iteratee: None = None + ) -> "Chain[t.Dict[int, T]]": ... + @t.overload + def pick_by( + self: "Chain[t.Any]", iteratee: t.Union[t.Callable[..., t.Any], None] = None + ) -> "Chain[t.Dict[t.Any, t.Any]]": ... + def pick_by(self, iteratee=None): + return self._wrap(pyd.pick_by)(iteratee) + + def rename_keys( + self: "Chain[t.Dict[T, T2]]", key_map: t.Dict[t.Any, T3] + ) -> "Chain[t.Dict[t.Union[T, T3], T2]]": + return self._wrap(pyd.rename_keys)(key_map) + + def set_(self: "Chain[T]", path: PathT, value: t.Any) -> "Chain[T]": + return self._wrap(pyd.set_)(path, value) + + set = set_ + + def set_with( + self: "Chain[T]", + path: PathT, + value: t.Any, + customizer: t.Union[t.Callable[..., t.Any], None] = None, + ) -> "Chain[T]": + return self._wrap(pyd.set_with)(path, value, customizer) + + def to_boolean( + self: "Chain[t.Any]", + true_values: t.Tuple[str, ...] = ("true", "1"), + false_values: t.Tuple[str, ...] = ("false", "0"), + ) -> "Chain[t.Union[bool, None]]": + return self._wrap(pyd.to_boolean)(true_values, false_values) + + @t.overload + def to_dict(self: "Chain[t.Mapping[T, T2]]") -> "Chain[t.Dict[T, T2]]": ... + @t.overload + def to_dict( + self: "Chain[t.Union[t.Iterator[T], t.Sequence[T]]]", + ) -> "Chain[t.Dict[int, T]]": ... + @t.overload + def to_dict(self: "Chain[t.Any]") -> "Chain[t.Dict[t.Any, t.Any]]": ... + def to_dict(self): + return self._wrap(pyd.to_dict)() + + def to_integer(self: "Chain[t.Any]") -> "Chain[int]": + return self._wrap(pyd.to_integer)() + + @t.overload + def to_list( + self: "Chain[t.Dict[t.Any, T]]", split_strings: bool = True + ) -> "Chain[t.List[T]]": ... + @t.overload + def to_list(self: "Chain[t.Iterable[T]]", split_strings: bool = True) -> "Chain[t.List[T]]": ... + @t.overload + def to_list(self: "Chain[T]", split_strings: bool = True) -> "Chain[t.List[T]]": ... + def to_list(self, split_strings=True): + return self._wrap(pyd.to_list)(split_strings) + + def to_number(self: "Chain[t.Any]", precision: int = 0) -> "Chain[t.Union[float, None]]": + return self._wrap(pyd.to_number)(precision) + + @t.overload + def to_pairs(self: "Chain[t.Mapping[T, T2]]") -> "Chain[t.List[t.Tuple[T, T2]]]": ... + @t.overload + def to_pairs( + self: "Chain[t.Union[t.Iterator[T], t.Sequence[T]]]", + ) -> "Chain[t.List[t.Tuple[int, T]]]": ... + @t.overload + def to_pairs(self: "Chain[t.Any]") -> "Chain[t.List[t.Any]]": ... + def to_pairs(self): + return self._wrap(pyd.to_pairs)() + + @t.overload + def transform( + self: "Chain[t.Mapping[T, T2]]", + iteratee: t.Callable[[T3, T2, T, t.Dict[T, T2]], t.Any], + accumulator: T3, + ) -> "Chain[T3]": ... + @t.overload + def transform( + self: "Chain[t.Mapping[T, T2]]", iteratee: t.Callable[[T3, T2, T], t.Any], accumulator: T3 + ) -> "Chain[T3]": ... + @t.overload + def transform( + self: "Chain[t.Mapping[t.Any, T2]]", iteratee: t.Callable[[T3, T2], t.Any], accumulator: T3 + ) -> "Chain[T3]": ... + @t.overload + def transform( + self: "Chain[t.Mapping[t.Any, t.Any]]", iteratee: t.Callable[[T3], t.Any], accumulator: T3 + ) -> "Chain[T3]": ... + @t.overload + def transform( + self: "Chain[t.Iterable[T]]", + iteratee: t.Callable[[T3, T, int, t.List[T]], t.Any], + accumulator: T3, + ) -> "Chain[T3]": ... + @t.overload + def transform( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T3, T, int], t.Any], accumulator: T3 + ) -> "Chain[T3]": ... + @t.overload + def transform( + self: "Chain[t.Iterable[T]]", iteratee: t.Callable[[T3, T], t.Any], accumulator: T3 + ) -> "Chain[T3]": ... + @t.overload + def transform( + self: "Chain[t.Iterable[t.Any]]", iteratee: t.Callable[[T3], t.Any], accumulator: T3 + ) -> "Chain[T3]": ... + @t.overload + def transform( + self: "Chain[t.Any]", iteratee: t.Any = None, accumulator: t.Any = None + ) -> "Chain[t.Any]": ... + def transform(self, iteratee=None, accumulator=None): + return self._wrap(pyd.transform)(iteratee, accumulator) + + @t.overload + def update( + self: "Chain[t.Dict[t.Any, T2]]", path: PathT, updater: t.Callable[[T2], t.Any] + ) -> "Chain[t.Dict[t.Any, t.Any]]": ... + @t.overload + def update( + self: "Chain[t.List[T]]", path: PathT, updater: t.Callable[[T], t.Any] + ) -> "Chain[t.List[t.Any]]": ... + @t.overload + def update(self: "Chain[T]", path: PathT, updater: t.Callable[..., t.Any]) -> "Chain[T]": ... + def update(self, path, updater): + return self._wrap(pyd.update)(path, updater) + + @t.overload + def update_with( + self: "Chain[t.Dict[t.Any, T2]]", + path: PathT, + updater: t.Callable[[T2], t.Any], + customizer: t.Union[t.Callable[..., t.Any], None], + ) -> "Chain[t.Dict[t.Any, t.Any]]": ... + @t.overload + def update_with( + self: "Chain[t.List[T]]", + path: PathT, + updater: t.Callable[[T], t.Any], + customizer: t.Union[t.Callable[..., t.Any], None] = None, + ) -> "Chain[t.List[t.Any]]": ... + @t.overload + def update_with( + self: "Chain[T]", + path: PathT, + updater: t.Callable[..., t.Any], + customizer: t.Union[t.Callable[..., t.Any], None] = None, + ) -> "Chain[T]": ... + def update_with(self, path, updater, customizer=None): + return self._wrap(pyd.update_with)(path, updater, customizer) + + def unset( + self: "Chain[t.Union[t.List[t.Any], t.Dict[t.Any, t.Any]]]", path: PathT + ) -> "Chain[bool]": + return self._wrap(pyd.unset)(path) + + @t.overload + def values(self: "Chain[t.Mapping[t.Any, T2]]") -> "Chain[t.List[T2]]": ... + @t.overload + def values(self: "Chain[t.Iterable[T]]") -> "Chain[t.List[T]]": ... + @t.overload + def values(self: "Chain[t.Any]") -> "Chain[t.List[t.Any]]": ... + def values(self): + return self._wrap(pyd.values)() + + def eq(self: "Chain[t.Any]", other: t.Any) -> "Chain[bool]": + return self._wrap(pyd.eq)(other) + + def eq_cmp(self: "Chain[T]") -> "Chain[t.Callable[[T], bool]]": + return self._wrap(pyd.eq_cmp)() + + def gt(self: "Chain['SupportsDunderGT[T]']", other: T) -> "Chain[bool]": + return self._wrap(pyd.gt)(other) + + def gt_cmp(self: "Chain[T]") -> "Chain[t.Callable[['SupportsDunderGT[T]'], bool]]": + return self._wrap(pyd.gt_cmp)() + + def gte(self: "Chain['SupportsDunderGE[T]']", other: T) -> "Chain[bool]": + return self._wrap(pyd.gte)(other) + + def gte_cmp(self: "Chain[T]") -> "Chain[t.Callable[['SupportsDunderGE[T]'], bool]]": + return self._wrap(pyd.gte_cmp)() + + def lt(self: "Chain['SupportsDunderLT[T]']", other: T) -> "Chain[bool]": + return self._wrap(pyd.lt)(other) + + def lt_cmp(self: "Chain[T]") -> "Chain[t.Callable[['SupportsDunderLT[T]'], bool]]": + return self._wrap(pyd.lt_cmp)() + + def lte(self: "Chain['SupportsDunderLE[T]']", other: T) -> "Chain[bool]": + return self._wrap(pyd.lte)(other) + + def lte_cmp(self: "Chain[T]") -> "Chain[t.Callable[['SupportsDunderLE[T]'], bool]]": + return self._wrap(pyd.lte_cmp)() + + def in_range(self: "Chain[t.Any]", start: t.Any = 0, end: t.Any = None) -> "Chain[bool]": + return self._wrap(pyd.in_range)(start, end) + + def in_range_cmp(self: "Chain[t.Any]", end: t.Any = None) -> "Chain[t.Callable[[t.Any], bool]]": + return self._wrap(pyd.in_range_cmp)(end) + + def is_associative(self: "Chain[t.Any]") -> "Chain[bool]": + return self._wrap(pyd.is_associative)() + + def is_blank(self: "Chain[t.Any]") -> "Chain[bool]": + return self._wrap(pyd.is_blank)() + + def is_boolean(self: "Chain[t.Any]") -> "Chain[bool]": + return self._wrap(pyd.is_boolean)() + + def is_builtin(self: "Chain[t.Any]") -> "Chain[bool]": + return self._wrap(pyd.is_builtin)() + + def is_date(self: "Chain[t.Any]") -> "Chain[bool]": + return self._wrap(pyd.is_date)() + + def is_decreasing( + self: "Chain[t.Union['SupportsRichComparison', t.List['SupportsRichComparison']]]", + ) -> "Chain[bool]": + return self._wrap(pyd.is_decreasing)() + + def is_dict(self: "Chain[t.Any]") -> "Chain[bool]": + return self._wrap(pyd.is_dict)() + + def is_empty(self: "Chain[t.Any]") -> "Chain[bool]": + return self._wrap(pyd.is_empty)() + + def is_equal(self: "Chain[t.Any]", other: t.Any) -> "Chain[bool]": + return self._wrap(pyd.is_equal)(other) + + def is_equal_cmp(self: "Chain[T]") -> "Chain[t.Callable[[T], bool]]": + return self._wrap(pyd.is_equal_cmp)() + + @t.overload + def is_equal_with( + self: "Chain[T]", other: T2, customizer: t.Callable[[T, T2], T3] + ) -> "Chain[T3]": ... + @t.overload + def is_equal_with( + self: "Chain[t.Any]", other: t.Any, customizer: t.Callable[..., t.Any] + ) -> "Chain[bool]": ... + @t.overload + def is_equal_with(self: "Chain[t.Any]", other: t.Any, customizer: None) -> "Chain[bool]": ... + def is_equal_with(self, other, customizer): + return self._wrap(pyd.is_equal_with)(other, customizer) + + def is_equal_with_cmp( + self: "Chain[T]", customizer: t.Callable[[T, T], T3] + ) -> "Chain[t.Callable[[T], T3]]": + return self._wrap(pyd.is_equal_with_cmp)(customizer) + + def is_error(self: "Chain[t.Any]") -> "Chain[bool]": + return self._wrap(pyd.is_error)() + + def is_even(self: "Chain[t.Any]") -> "Chain[bool]": + return self._wrap(pyd.is_even)() + + def is_float(self: "Chain[t.Any]") -> "Chain[bool]": + return self._wrap(pyd.is_float)() + + def is_function(self: "Chain[t.Any]") -> "Chain[bool]": + return self._wrap(pyd.is_function)() + + def is_increasing( + self: "Chain[t.Union['SupportsRichComparison', t.List['SupportsRichComparison']]]", + ) -> "Chain[bool]": + return self._wrap(pyd.is_increasing)() + + def is_indexed(self: "Chain[t.Any]") -> "Chain[bool]": + return self._wrap(pyd.is_indexed)() + + def is_instance_of( + self: "Chain[t.Any]", types: t.Union[type, t.Tuple[type, ...]] + ) -> "Chain[bool]": + return self._wrap(pyd.is_instance_of)(types) + + def is_instance_of_cmp( + self: "Chain[t.Union[type, t.Tuple[type, ...]]]", + ) -> "Chain[t.Callable[[t.Any], bool]]": + return self._wrap(pyd.is_instance_of_cmp)() + + def is_integer(self: "Chain[t.Any]") -> "Chain[bool]": + return self._wrap(pyd.is_integer)() + + def is_iterable(self: "Chain[t.Any]") -> "Chain[bool]": + return self._wrap(pyd.is_iterable)() + + def is_json(self: "Chain[t.Any]") -> "Chain[bool]": + return self._wrap(pyd.is_json)() + + def is_list(self: "Chain[t.Any]") -> "Chain[bool]": + return self._wrap(pyd.is_list)() + + def is_match(self: "Chain[t.Any]", source: t.Any) -> "Chain[bool]": + return self._wrap(pyd.is_match)(source) + + def is_match_cmp(self: "Chain[t.Any]") -> "Chain[t.Callable[[t.Any], bool]]": + return self._wrap(pyd.is_match_cmp)() + + def is_match_with( + self: "Chain[t.Any]", + source: t.Any, + customizer: t.Any = None, + _key: t.Any = UNSET, + _obj: t.Any = UNSET, + _source: t.Any = UNSET, + ) -> "Chain[bool]": + return self._wrap(pyd.is_match_with)(source, customizer, _key, _obj, _source) + + def is_match_with_cmp( + self: "Chain[t.Any]", customizer: t.Any = None + ) -> "Chain[t.Callable[[t.Any], bool]]": + return self._wrap(pyd.is_match_with_cmp)(customizer) + + def is_monotone( + self: "Chain[t.Union[T, t.List[T]]]", op: t.Callable[[T, T], t.Any] + ) -> "Chain[bool]": + return self._wrap(pyd.is_monotone)(op) + + def is_monotone_cmp( + self: "Chain[t.Callable[[T, T], t.Any]]", + ) -> "Chain[t.Callable[[t.Union[T, t.List[T]]], bool]]": + return self._wrap(pyd.is_monotone_cmp)() + + def is_nan(self: "Chain[t.Any]") -> "Chain[bool]": + return self._wrap(pyd.is_nan)() + + def is_negative(self: "Chain[t.Any]") -> "Chain[bool]": + return self._wrap(pyd.is_negative)() + + def is_none(self: "Chain[t.Any]") -> "Chain[bool]": + return self._wrap(pyd.is_none)() + + def is_number(self: "Chain[t.Any]") -> "Chain[bool]": + return self._wrap(pyd.is_number)() + + def is_object(self: "Chain[t.Any]") -> "Chain[bool]": + return self._wrap(pyd.is_object)() + + def is_odd(self: "Chain[t.Any]") -> "Chain[bool]": + return self._wrap(pyd.is_odd)() + + def is_positive(self: "Chain[t.Any]") -> "Chain[bool]": + return self._wrap(pyd.is_positive)() + + def is_reg_exp(self: "Chain[t.Any]") -> "Chain[bool]": + return self._wrap(pyd.is_reg_exp)() + + def is_set(self: "Chain[t.Any]") -> "Chain[bool]": + return self._wrap(pyd.is_set)() + + def is_strictly_decreasing( + self: "Chain[t.Union['SupportsRichComparison', t.List['SupportsRichComparison']]]", + ) -> "Chain[bool]": + return self._wrap(pyd.is_strictly_decreasing)() + + def is_strictly_increasing( + self: "Chain[t.Union['SupportsRichComparison', t.List['SupportsRichComparison']]]", + ) -> "Chain[bool]": + return self._wrap(pyd.is_strictly_increasing)() + + def is_string(self: "Chain[t.Any]") -> "Chain[bool]": + return self._wrap(pyd.is_string)() + + def is_tuple(self: "Chain[t.Any]") -> "Chain[bool]": + return self._wrap(pyd.is_tuple)() + + def is_zero(self: "Chain[t.Any]") -> "Chain[bool]": + return self._wrap(pyd.is_zero)() + + def camel_case(self: "Chain[t.Any]") -> "Chain[str]": + return self._wrap(pyd.camel_case)() + + def capitalize(self: "Chain[t.Any]", strict: bool = True) -> "Chain[str]": + return self._wrap(pyd.capitalize)(strict) + + def chars(self: "Chain[t.Any]") -> "Chain[t.List[str]]": + return self._wrap(pyd.chars)() + + def chop(self: "Chain[t.Any]", step: int) -> "Chain[t.List[str]]": + return self._wrap(pyd.chop)(step) + + def chop_right(self: "Chain[t.Any]", step: int) -> "Chain[t.List[str]]": + return self._wrap(pyd.chop_right)(step) + + def clean(self: "Chain[t.Any]") -> "Chain[str]": + return self._wrap(pyd.clean)() + + def count_substr(self: "Chain[t.Any]", subtext: t.Any) -> "Chain[int]": + return self._wrap(pyd.count_substr)(subtext) + + def deburr(self: "Chain[t.Any]") -> "Chain[str]": + return self._wrap(pyd.deburr)() + + def decapitalize(self: "Chain[t.Any]") -> "Chain[str]": + return self._wrap(pyd.decapitalize)() + + def ends_with( + self: "Chain[t.Any]", target: t.Any, position: t.Union[int, None] = None + ) -> "Chain[bool]": + return self._wrap(pyd.ends_with)(target, position) + + def ensure_ends_with(self: "Chain[t.Any]", suffix: t.Any) -> "Chain[str]": + return self._wrap(pyd.ensure_ends_with)(suffix) + + def ensure_starts_with(self: "Chain[t.Any]", prefix: t.Any) -> "Chain[str]": + return self._wrap(pyd.ensure_starts_with)(prefix) + + def escape(self: "Chain[t.Any]") -> "Chain[str]": + return self._wrap(pyd.escape)() + + def escape_reg_exp(self: "Chain[t.Any]") -> "Chain[str]": + return self._wrap(pyd.escape_reg_exp)() + + def has_substr(self: "Chain[t.Any]", subtext: t.Any) -> "Chain[bool]": + return self._wrap(pyd.has_substr)(subtext) + + def human_case(self: "Chain[t.Any]") -> "Chain[str]": + return self._wrap(pyd.human_case)() + + def insert_substr(self: "Chain[t.Any]", index: int, subtext: t.Any) -> "Chain[str]": + return self._wrap(pyd.insert_substr)(index, subtext) + + def join(self: "Chain[t.Iterable[t.Any]]", separator: t.Any = "") -> "Chain[str]": + return self._wrap(pyd.join)(separator) + + def kebab_case(self: "Chain[t.Any]") -> "Chain[str]": + return self._wrap(pyd.kebab_case)() + + def lines(self: "Chain[t.Any]") -> "Chain[t.List[str]]": + return self._wrap(pyd.lines)() + + def lower_case(self: "Chain[t.Any]") -> "Chain[str]": + return self._wrap(pyd.lower_case)() + + def lower_first(self: "Chain[str]") -> "Chain[str]": + return self._wrap(pyd.lower_first)() + + def number_format( + self: "Chain[NumberT]", + scale: int = 0, + decimal_separator: str = ".", + order_separator: str = ",", + ) -> "Chain[str]": + return self._wrap(pyd.number_format)(scale, decimal_separator, order_separator) + + def pad(self: "Chain[t.Any]", length: int, chars: t.Any = " ") -> "Chain[str]": + return self._wrap(pyd.pad)(length, chars) + + def pad_end(self: "Chain[t.Any]", length: int, chars: t.Any = " ") -> "Chain[str]": + return self._wrap(pyd.pad_end)(length, chars) + + def pad_start(self: "Chain[t.Any]", length: int, chars: t.Any = " ") -> "Chain[str]": + return self._wrap(pyd.pad_start)(length, chars) + + def pascal_case(self: "Chain[t.Any]", strict: bool = True) -> "Chain[str]": + return self._wrap(pyd.pascal_case)(strict) + + def predecessor(self: "Chain[t.Any]") -> "Chain[str]": + return self._wrap(pyd.predecessor)() + + def prune(self: "Chain[t.Any]", length: int = 0, omission: str = "...") -> "Chain[str]": + return self._wrap(pyd.prune)(length, omission) + + def quote(self: "Chain[t.Any]", quote_char: t.Any = '"') -> "Chain[str]": + return self._wrap(pyd.quote)(quote_char) + + def reg_exp_js_match(self: "Chain[t.Any]", reg_exp: str) -> "Chain[t.List[str]]": + return self._wrap(pyd.reg_exp_js_match)(reg_exp) + + def reg_exp_js_replace( + self: "Chain[t.Any]", reg_exp: str, repl: t.Union[str, t.Callable[[re.Match[str]], str]] + ) -> "Chain[str]": + return self._wrap(pyd.reg_exp_js_replace)(reg_exp, repl) + + def reg_exp_replace( + self: "Chain[t.Any]", + pattern: t.Any, + repl: t.Union[str, t.Callable[[re.Match[str]], str]], + ignore_case: bool = False, + count: int = 0, + ) -> "Chain[str]": + return self._wrap(pyd.reg_exp_replace)(pattern, repl, ignore_case, count) + + def repeat(self: "Chain[t.Any]", n: t.SupportsInt = 0) -> "Chain[str]": + return self._wrap(pyd.repeat)(n) + + def replace( + self: "Chain[t.Any]", + pattern: t.Any, + repl: t.Union[str, t.Callable[[re.Match[str]], str]], + ignore_case: bool = False, + count: int = 0, + escape: bool = True, + from_start: bool = False, + from_end: bool = False, + ) -> "Chain[str]": + return self._wrap(pyd.replace)( + pattern, repl, ignore_case, count, escape, from_start, from_end + ) + + def replace_end( + self: "Chain[t.Any]", + pattern: t.Any, + repl: t.Union[str, t.Callable[[re.Match[str]], str]], + ignore_case: bool = False, + escape: bool = True, + ) -> "Chain[str]": + return self._wrap(pyd.replace_end)(pattern, repl, ignore_case, escape) + + def replace_start( + self: "Chain[t.Any]", + pattern: t.Any, + repl: t.Union[str, t.Callable[[re.Match[str]], str]], + ignore_case: bool = False, + escape: bool = True, + ) -> "Chain[str]": + return self._wrap(pyd.replace_start)(pattern, repl, ignore_case, escape) + + def separator_case(self: "Chain[t.Any]", separator: str) -> "Chain[str]": + return self._wrap(pyd.separator_case)(separator) + + def series_phrase( + self: "Chain[t.List[t.Any]]", + separator: t.Any = ", ", + last_separator: t.Any = " and ", + serial: bool = False, + ) -> "Chain[str]": + return self._wrap(pyd.series_phrase)(separator, last_separator, serial) + + def series_phrase_serial( + self: "Chain[t.List[t.Any]]", separator: t.Any = ", ", last_separator: t.Any = " and " + ) -> "Chain[str]": + return self._wrap(pyd.series_phrase_serial)(separator, last_separator) + + def slugify(self: "Chain[t.Any]", separator: str = "-") -> "Chain[str]": + return self._wrap(pyd.slugify)(separator) + + def snake_case(self: "Chain[t.Any]") -> "Chain[str]": + return self._wrap(pyd.snake_case)() + + def split( + self: "Chain[t.Any]", separator: t.Union[str, Unset, None] = UNSET + ) -> "Chain[t.List[str]]": + return self._wrap(pyd.split)(separator) + + def start_case(self: "Chain[t.Any]") -> "Chain[str]": + return self._wrap(pyd.start_case)() + + def starts_with(self: "Chain[t.Any]", target: t.Any, position: int = 0) -> "Chain[bool]": + return self._wrap(pyd.starts_with)(target, position) + + def strip_tags(self: "Chain[t.Any]") -> "Chain[str]": + return self._wrap(pyd.strip_tags)() + + def substr_left(self: "Chain[t.Any]", subtext: str) -> "Chain[str]": + return self._wrap(pyd.substr_left)(subtext) + + def substr_left_end(self: "Chain[t.Any]", subtext: str) -> "Chain[str]": + return self._wrap(pyd.substr_left_end)(subtext) + + def substr_right(self: "Chain[t.Any]", subtext: str) -> "Chain[str]": + return self._wrap(pyd.substr_right)(subtext) + + def substr_right_end(self: "Chain[t.Any]", subtext: str) -> "Chain[str]": + return self._wrap(pyd.substr_right_end)(subtext) + + def successor(self: "Chain[t.Any]") -> "Chain[str]": + return self._wrap(pyd.successor)() + + def surround(self: "Chain[t.Any]", wrapper: t.Any) -> "Chain[str]": + return self._wrap(pyd.surround)(wrapper) + + def swap_case(self: "Chain[t.Any]") -> "Chain[str]": + return self._wrap(pyd.swap_case)() + + def title_case(self: "Chain[t.Any]") -> "Chain[str]": + return self._wrap(pyd.title_case)() + + def to_lower(self: "Chain[t.Any]") -> "Chain[str]": + return self._wrap(pyd.to_lower)() + + def to_upper(self: "Chain[t.Any]") -> "Chain[str]": + return self._wrap(pyd.to_upper)() + + def trim(self: "Chain[t.Any]", chars: t.Union[str, None] = None) -> "Chain[str]": + return self._wrap(pyd.trim)(chars) + + def trim_end(self: "Chain[t.Any]", chars: t.Union[str, None] = None) -> "Chain[str]": + return self._wrap(pyd.trim_end)(chars) + + def trim_start(self: "Chain[t.Any]", chars: t.Union[str, None] = None) -> "Chain[str]": + return self._wrap(pyd.trim_start)(chars) + + def truncate( + self: "Chain[t.Any]", + length: int = 30, + omission: str = "...", + separator: t.Union[str, re.Pattern[str], None] = None, + ) -> "Chain[str]": + return self._wrap(pyd.truncate)(length, omission, separator) + + def unescape(self: "Chain[t.Any]") -> "Chain[str]": + return self._wrap(pyd.unescape)() + + def upper_case(self: "Chain[t.Any]") -> "Chain[str]": + return self._wrap(pyd.upper_case)() + + def upper_first(self: "Chain[str]") -> "Chain[str]": + return self._wrap(pyd.upper_first)() + + def unquote(self: "Chain[t.Any]", quote_char: t.Any = '"') -> "Chain[str]": + return self._wrap(pyd.unquote)(quote_char) + + def url(self: "Chain[t.Any]", *paths: t.Any, **params: t.Any) -> "Chain[str]": + return self._wrap(pyd.url)(*paths, **params) + + def words(self: "Chain[t.Any]", pattern: t.Union[str, None] = None) -> "Chain[t.List[str]]": + return self._wrap(pyd.words)(pattern) + + def attempt( + self: "Chain[t.Callable[P, T]]", *args: "P.args", **kwargs: "P.kwargs" + ) -> "Chain[t.Union[T, Exception]]": + return self._wrap(pyd.attempt)(*args, **kwargs) + + @t.overload + def cond( + self: "Chain[t.List[t.Tuple[t.Callable[P, t.Any], t.Callable[P, T]]]]", + *extra_pairs: t.Tuple[t.Callable[P, t.Any], t.Callable[P, T]], + ) -> "Chain[t.Callable[P, T]]": ... + @t.overload + def cond( + self: "Chain[t.List[t.List[t.Callable[P, t.Any]]]]", + *extra_pairs: t.List[t.Callable[P, t.Any]], + ) -> "Chain[t.Callable[P, t.Any]]": ... + def cond(self, *extra_pairs): + return self._wrap(pyd.cond)(*extra_pairs) + + @t.overload + def conforms( + self: "Chain[t.Dict[T, t.Callable[[T2], t.Any]]]", + ) -> "Chain[t.Callable[[t.Dict[T, T2]], bool]]": ... + @t.overload + def conforms( + self: "Chain[t.List[t.Callable[[T], t.Any]]]", + ) -> "Chain[t.Callable[[t.List[T]], bool]]": ... + def conforms( + self: "Chain[t.Union[t.List[t.Any], t.Dict[t.Any, t.Any]]]", + ) -> "Chain[t.Callable[..., t.Any]]": + return self._wrap(pyd.conforms)() + + @t.overload + def conforms_to( + self: "Chain[t.Dict[T, T2]]", source: t.Dict[T, t.Callable[[T2], t.Any]] + ) -> "Chain[bool]": ... + @t.overload + def conforms_to( + self: "Chain[t.List[T]]", source: t.List[t.Callable[[T], t.Any]] + ) -> "Chain[bool]": ... + def conforms_to(self, source): + return self._wrap(pyd.conforms_to)(source) + + def constant(self: "Chain[T]") -> "Chain[t.Callable[..., T]]": + return self._wrap(pyd.constant)() + + def default_to(self: "Chain[t.Union[T, None]]", default_value: T2) -> "Chain[t.Union[T, T2]]": + return self._wrap(pyd.default_to)(default_value) + + @t.overload + def default_to_any(self: "Chain[None]", *default_values: None) -> "Chain[None]": ... + @t.overload + def default_to_any( + self: "Chain[t.Union[T, None]]", default_value1: None, default_value2: T2 + ) -> "Chain[t.Union[T, T2]]": ... + @t.overload + def default_to_any( + self: "Chain[t.Union[T, None]]", + default_value1: None, + default_value2: None, + default_value3: T2, + ) -> "Chain[t.Union[T, T2]]": ... + @t.overload + def default_to_any( + self: "Chain[t.Union[T, None]]", + default_value1: None, + default_value2: None, + default_value3: None, + default_value4: T2, + ) -> "Chain[t.Union[T, T2]]": ... + @t.overload + def default_to_any( + self: "Chain[t.Union[T, None]]", + default_value1: None, + default_value2: None, + default_value3: None, + default_value4: None, + default_value5: T2, + ) -> "Chain[t.Union[T, T2]]": ... + @t.overload + def default_to_any( + self: "Chain[t.Union[T, None]]", *default_values: T2 + ) -> "Chain[t.Union[T, T2]]": ... + def default_to_any(self, *default_values): + return self._wrap(pyd.default_to_any)(*default_values) + + @t.overload + def identity(self: "Chain[T]", *args: t.Any) -> "Chain[T]": ... + @t.overload + def iteratee(self: "Chain[t.Callable[P, T]]") -> "Chain[t.Callable[P, T]]": ... + @t.overload + def iteratee(self: "Chain[t.Any]") -> "Chain[t.Callable[..., t.Any]]": ... + def iteratee(self): + return self._wrap(pyd.iteratee)() + + def matches(self: "Chain[t.Any]") -> "Chain[t.Callable[[t.Any], bool]]": + return self._wrap(pyd.matches)() + + def matches_property(self: "Chain[t.Any]", value: t.Any) -> "Chain[t.Callable[[t.Any], bool]]": + return self._wrap(pyd.matches_property)(value) + + @t.overload + def memoize( + self: "Chain[t.Callable[P, T]]", resolver: None = None + ) -> "Chain[MemoizedFunc[P, T, str]]": ... + @t.overload + def memoize( + self: "Chain[t.Callable[P, T]]", resolver: t.Union[t.Callable[P, T2], None] = None + ) -> "Chain[MemoizedFunc[P, T, T2]]": ... + def memoize(self, resolver=None): + return self._wrap(pyd.memoize)(resolver) + + def method( + self: "Chain[PathT]", *args: t.Any, **kwargs: t.Any + ) -> "Chain[t.Callable[..., t.Any]]": + return self._wrap(pyd.method)(*args, **kwargs) + + def method_of( + self: "Chain[t.Any]", *args: t.Any, **kwargs: t.Any + ) -> "Chain[t.Callable[..., t.Any]]": + return self._wrap(pyd.method_of)(*args, **kwargs) + + def noop(self: "Chain[t.Any]", *args: t.Any, **kwargs: t.Any) -> "Chain[None]": + return self._wrap(pyd.noop)(*args, **kwargs) + + def over(self: "Chain[t.Iterable[t.Callable[P, T]]]") -> "Chain[t.Callable[P, t.List[T]]]": + return self._wrap(pyd.over)() + + def over_every(self: "Chain[t.Iterable[t.Callable[P, t.Any]]]") -> "Chain[t.Callable[P, bool]]": + return self._wrap(pyd.over_every)() + + def over_some(self: "Chain[t.Iterable[t.Callable[P, t.Any]]]") -> "Chain[t.Callable[P, bool]]": + return self._wrap(pyd.over_some)() + + def property_(self: "Chain[PathT]") -> "Chain[t.Callable[[t.Any], t.Any]]": + return self._wrap(pyd.property_)() + + property = property_ + + def properties(self: "Chain[t.Any]", *paths: t.Any) -> "Chain[t.Callable[[t.Any], t.Any]]": + return self._wrap(pyd.properties)(*paths) + + def property_of(self: "Chain[t.Any]") -> "Chain[t.Callable[[PathT], t.Any]]": + return self._wrap(pyd.property_of)() + + @t.overload + def random( + self: "Chain[int]", stop: int = 1, *, floating: Literal[False] = False + ) -> "Chain[int]": ... + @t.overload + def random(self: "Chain[float]", stop: int = 1, floating: bool = False) -> "Chain[float]": ... + @t.overload + def random(self: "Chain[float]", stop: float, floating: bool = False) -> "Chain[float]": ... + @t.overload + def random( + self: "Chain[t.Union[float, int]]", + stop: t.Union[float, int] = 1, + *, + floating: Literal[True], + ) -> "Chain[float]": ... + def random( + self: "Chain[t.Union[float, int]]", stop: t.Union[float, int] = 1, floating: bool = False + ): + return self._wrap(pyd.random)(stop, floating) + + @t.overload + def range_(self: "Chain[int]") -> "Chain[t.Generator[int, None, None]]": ... + @t.overload + def range_( + self: "Chain[int]", stop: int, step: int = 1 + ) -> "Chain[t.Generator[int, None, None]]": ... + def range_(self, *args): + return self._wrap(pyd.range_)(*args) + + range = range_ + + @t.overload + def range_right(self: "Chain[int]") -> "Chain[t.Generator[int, None, None]]": ... + @t.overload + def range_right( + self: "Chain[int]", stop: int, step: int = 1 + ) -> "Chain[t.Generator[int, None, None]]": ... + def range_right(self, *args): + return self._wrap(pyd.range_right)(*args) + + @t.overload + def result(self: "Chain[None]", key: t.Any, default: None = None) -> "Chain[None]": ... + @t.overload + def result(self: "Chain[None]", key: t.Any, default: T) -> "Chain[T]": ... + @t.overload + def result(self: "Chain[t.Any]", key: t.Any, default: t.Any = None) -> "Chain[t.Any]": ... + def result(self, key, default=None): + return self._wrap(pyd.result)(key, default) + + def retry( + self: "Chain[int]", + delay: t.Union[int, float] = 0.5, + max_delay: t.Union[int, float] = 150.0, + scale: t.Union[int, float] = 2.0, + jitter: t.Union[int, float, t.Tuple[t.Union[int, float], t.Union[int, float]]] = 0, + exceptions: t.Iterable[Type[Exception]] = (Exception,), + on_exception: t.Union[t.Callable[[Exception, int], t.Any], None] = None, + ) -> "Chain[t.Callable[[CallableT], CallableT]]": + return self._wrap(pyd.retry)(delay, max_delay, scale, jitter, exceptions, on_exception) + + @t.overload + def times(self: "Chain[int]", iteratee: t.Callable[..., T]) -> "Chain[t.List[T]]": ... + @t.overload + def times(self: "Chain[int]", iteratee: None = None) -> "Chain[t.List[int]]": ... + def times(self: "Chain[int]", iteratee=None): + return self._wrap(pyd.times)(iteratee) + + def to_path(self: "Chain[PathT]") -> "Chain[t.List[t.Hashable]]": + return self._wrap(pyd.to_path)() 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 diff --git a/.venv/lib/python3.12/site-packages/pydash/collections.py b/.venv/lib/python3.12/site-packages/pydash/collections.py new file mode 100644 index 00000000..22c498d8 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydash/collections.py @@ -0,0 +1,2181 @@ +""" +Functions that operate on lists and dicts. + +.. versionadded:: 1.0.0 +""" + +from __future__ import annotations + +from functools import cmp_to_key +import random +import typing as t + +import pydash as pyd + +from .helpers import callit, cmp, getargcount, iterator, iteriteratee +from .types import IterateeObjT, PathT + + +__all__ = ( + "at", + "count_by", + "every", + "filter_", + "find", + "find_last", + "flat_map", + "flat_map_deep", + "flat_map_depth", + "for_each", + "for_each_right", + "group_by", + "includes", + "invoke_map", + "key_by", + "map_", + "nest", + "order_by", + "partition", + "pluck", + "reduce_", + "reduce_right", + "reductions", + "reductions_right", + "reject", + "sample", + "sample_size", + "shuffle", + "size", + "some", + "sort_by", +) + +T = t.TypeVar("T") +T2 = t.TypeVar("T2") +T3 = t.TypeVar("T3") +T4 = t.TypeVar("T4") + + +@t.overload +def at(collection: t.Mapping[T, T2], *paths: T) -> t.List[t.Union[T2, None]]: ... + + +@t.overload +def at(collection: t.Mapping[T, t.Any], *paths: t.Union[T, t.Iterable[T]]) -> t.List[t.Any]: ... + + +@t.overload +def at(collection: t.Iterable[T], *paths: int) -> t.List[t.Union[T, None]]: ... + + +@t.overload +def at(collection: t.Iterable[t.Any], *paths: t.Union[int, t.Iterable[int]]) -> t.List[t.Any]: ... + + +def at(collection, *paths): + """ + Creates a list of elements from the specified indexes, or keys, of the collection. Indexes may + be specified as individual arguments or as arrays of indexes. + + Args: + collection: Collection to iterate over. + *paths: The indexes of `collection` to retrieve, specified as individual indexes or + arrays of indexes. + + Returns: + filtered list + + Example: + + >>> at([1, 2, 3, 4], 0, 2) + [1, 3] + >>> at({"a": 1, "b": 2, "c": 3, "d": 4}, "a", "c") + [1, 3] + >>> at({"a": 1, "b": 2, "c": {"d": {"e": 3}}}, "a", ["c", "d", "e"]) + [1, 3] + + .. versionadded:: 1.0.0 + + .. versionchanged:: 4.1.0 + Support deep path access. + """ + return pyd.properties(*paths)(collection) + + +@t.overload +def count_by(collection: t.Mapping[t.Any, T2], iteratee: None = None) -> t.Dict[T2, int]: ... + + +@t.overload +def count_by( + collection: t.Mapping[T, T2], iteratee: t.Callable[[T2, T, t.Dict[T, T2]], T3] +) -> t.Dict[T3, int]: ... + + +@t.overload +def count_by( + collection: t.Mapping[T, T2], iteratee: t.Callable[[T2, T], T3] +) -> t.Dict[T3, int]: ... + + +@t.overload +def count_by( + collection: t.Mapping[t.Any, T2], iteratee: t.Callable[[T2], T3] +) -> t.Dict[T3, int]: ... + + +@t.overload +def count_by(collection: t.Iterable[T], iteratee: None = None) -> t.Dict[T, int]: ... + + +@t.overload +def count_by( + collection: t.Iterable[T], iteratee: t.Callable[[T, int, t.List[T]], T2] +) -> t.Dict[T2, int]: ... + + +@t.overload +def count_by(collection: t.Iterable[T], iteratee: t.Callable[[T, int], T2]) -> t.Dict[T2, int]: ... + + +@t.overload +def count_by(collection: t.Iterable[T], iteratee: t.Callable[[T], T2]) -> t.Dict[T2, int]: ... + + +def count_by(collection, iteratee=None): + """ + Creates an object composed of keys generated from the results of running each element of + `collection` through the iteratee. + + Args: + collection: Collection to iterate over. + iteratee: Iteratee applied per iteration. + + Returns: + Dict containing counts by key. + + Example: + + >>> results = count_by([1, 2, 1, 2, 3, 4]) + >>> assert results == {1: 2, 2: 2, 3: 1, 4: 1} + >>> results = count_by(["a", "A", "B", "b"], lambda x: x.lower()) + >>> assert results == {"a": 2, "b": 2} + >>> results = count_by({"a": 1, "b": 1, "c": 3, "d": 3}) + >>> assert results == {1: 2, 3: 2} + + .. versionadded:: 1.0.0 + """ + ret = {} + + for result in iteriteratee(collection, iteratee): + ret.setdefault(result[0], 0) + ret[result[0]] += 1 + + return ret + + +def every( + collection: t.Iterable[T], predicate: t.Union[t.Callable[[T], t.Any], IterateeObjT, None] = None +) -> bool: + """ + Checks if the predicate returns a truthy value for all elements of a collection. The predicate + is invoked with three arguments: ``(value, index|key, collection)``. If a property name is + passed for predicate, the created :func:`pluck` style predicate will return the property value + of the given element. If an object is passed for predicate, the created :func:`.matches` style + predicate will return ``True`` for elements that have the properties of the given object, else + ``False``. + + Args: + collection: Collection to iterate over. + predicate: Predicate applied per iteration. + + Returns: + Whether all elements are truthy. + + Example: + + >>> every([1, True, "hello"]) + True + >>> every([1, False, "hello"]) + False + >>> every([{"a": 1}, {"a": True}, {"a": "hello"}], "a") + True + >>> every([{"a": 1}, {"a": False}, {"a": "hello"}], "a") + False + >>> every([{"a": 1}, {"a": 1}], {"a": 1}) + True + >>> every([{"a": 1}, {"a": 2}], {"a": 1}) + False + + .. versionadded:: 1.0.0 + + .. versionchanged: 4.0.0 + Removed alias ``all_``. + """ + if predicate: + cbk = pyd.iteratee(predicate) + collection = (cbk(item) for item in collection) + + return all(collection) + + +@t.overload +def filter_( + collection: t.Mapping[T, T2], + predicate: t.Union[t.Callable[[T2, T, t.Dict[T, T2]], t.Any], IterateeObjT, None] = None, +) -> t.List[T2]: ... + + +@t.overload +def filter_( + collection: t.Mapping[T, T2], + predicate: t.Union[t.Callable[[T2, T], t.Any], IterateeObjT, None] = None, +) -> t.List[T2]: ... + + +@t.overload +def filter_( + collection: t.Mapping[t.Any, T2], + predicate: t.Union[t.Callable[[T2], t.Any], IterateeObjT, None] = None, +) -> t.List[T2]: ... + + +@t.overload +def filter_( + collection: t.Iterable[T], + predicate: t.Union[t.Callable[[T, int, t.List[T]], t.Any], IterateeObjT, None] = None, +) -> t.List[T]: ... + + +@t.overload +def filter_( + collection: t.Iterable[T], + predicate: t.Union[t.Callable[[T, int], t.Any], IterateeObjT, None] = None, +) -> t.List[T]: ... + + +@t.overload +def filter_( + collection: t.Iterable[T], + predicate: t.Union[t.Callable[[T], t.Any], IterateeObjT, None] = None, +) -> t.List[T]: ... + + +def filter_(collection, predicate=None): + """ + Iterates over elements of a collection, returning a list of all elements the predicate returns + truthy for. + + Args: + collection: Collection to iterate over. + predicate: Predicate applied per iteration. + + Returns: + Filtered list. + + Example: + + >>> results = filter_([{"a": 1}, {"b": 2}, {"a": 1, "b": 3}], {"a": 1}) + >>> assert results == [{"a": 1}, {"a": 1, "b": 3}] + >>> filter_([1, 2, 3, 4], lambda x: x >= 3) + [3, 4] + + .. versionadded:: 1.0.0 + + .. versionchanged:: 4.0.0 + Removed alias ``select``. + """ + return [value for is_true, value, _, _ in iteriteratee(collection, predicate) if is_true] + + +@t.overload +def find( + collection: t.Dict[T, T2], + predicate: t.Union[t.Callable[[T2, T, t.Dict[T, T2]], t.Any], IterateeObjT, None] = None, +) -> t.Union[T2, None]: ... + + +@t.overload +def find( + collection: t.Dict[T, T2], + predicate: t.Union[t.Callable[[T2, T], t.Any], IterateeObjT, None] = None, +) -> t.Union[T2, None]: ... + + +@t.overload +def find( + collection: t.Dict[T, T2], + predicate: t.Union[t.Callable[[T2], t.Any], IterateeObjT, None] = None, +) -> t.Union[T2, None]: ... + + +@t.overload +def find( + collection: t.List[T], + predicate: t.Union[t.Callable[[T, int, t.List[T]], t.Any], IterateeObjT, None] = None, +) -> t.Union[T, None]: ... + + +@t.overload +def find( + collection: t.List[T], + predicate: t.Union[t.Callable[[T, int], t.Any], IterateeObjT, None] = None, +) -> t.Union[T, None]: ... + + +@t.overload +def find( + collection: t.List[T], + predicate: t.Union[t.Callable[[T], t.Any], IterateeObjT, None] = None, +) -> t.Union[T, None]: ... + + +def find(collection, predicate=None): + """ + Iterates over elements of a collection, returning the first element that the predicate returns + truthy for. + + Args: + collection: Collection to iterate over. + predicate: Predicate applied per iteration. + + Returns: + First element found or ``None``. + + Example: + + >>> find([1, 2, 3, 4], lambda x: x >= 3) + 3 + >>> find([{"a": 1}, {"b": 2}, {"a": 1, "b": 2}], {"a": 1}) + {'a': 1} + + .. versionadded:: 1.0.0 + + .. versionchanged:: 4.0.0 + Removed aliases ``detect`` and ``find_where``. + """ + search = (value for is_true, value, _, _ in iteriteratee(collection, predicate) if is_true) + return next(search, None) + + +@t.overload +def find_last( + collection: t.Dict[T, T2], + predicate: t.Union[t.Callable[[T2, T, t.Dict[T, T2]], t.Any], IterateeObjT, None] = None, +) -> t.Union[T2, None]: ... + + +@t.overload +def find_last( + collection: t.Dict[T, T2], + predicate: t.Union[t.Callable[[T2, T], t.Any], IterateeObjT, None] = None, +) -> t.Union[T2, None]: ... + + +@t.overload +def find_last( + collection: t.Dict[t.Any, T2], + predicate: t.Union[t.Callable[[T2], t.Any], IterateeObjT, None] = None, +) -> t.Union[T2, None]: ... + + +@t.overload +def find_last( + collection: t.List[T], + predicate: t.Union[t.Callable[[T, int, t.List[T]], t.Any], IterateeObjT, None] = None, +) -> t.Union[T, None]: ... + + +@t.overload +def find_last( + collection: t.List[T], + predicate: t.Union[t.Callable[[T, int], t.Any], IterateeObjT, None] = None, +) -> t.Union[T, None]: ... + + +@t.overload +def find_last( + collection: t.List[T], + predicate: t.Union[t.Callable[[T], t.Any], IterateeObjT, None] = None, +) -> t.Union[T, None]: ... + + +def find_last(collection, predicate=None): + """ + This method is like :func:`find` except that it iterates over elements of a `collection` from + right to left. + + Args: + collection: Collection to iterate over. + predicate: Predicate applied per iteration. + + Returns: + Last element found or ``None``. + + Example: + + >>> find_last([1, 2, 3, 4], lambda x: x >= 3) + 4 + >>> results = find_last([{'a': 1}, {'b': 2}, {'a': 1, 'b': 2}],\ + {'a': 1}) + >>> assert results == {'a': 1, 'b': 2} + + .. versionadded:: 1.0.0 + """ + search = ( + value + for is_true, value, _, _ in iteriteratee(collection, predicate, reverse=True) + if is_true + ) + return next(search, None) + + +@t.overload +def flat_map( + collection: t.Mapping[T, T2], iteratee: t.Callable[[T2, T, t.Dict[T, T2]], t.Iterable[T3]] +) -> t.List[T3]: ... + + +@t.overload +def flat_map( + collection: t.Mapping[T, T2], iteratee: t.Callable[[T2, T], t.Iterable[T3]] +) -> t.List[T3]: ... + + +@t.overload +def flat_map( + collection: t.Mapping[t.Any, T2], iteratee: t.Callable[[T2], t.Iterable[T3]] +) -> t.List[T3]: ... + + +@t.overload +def flat_map( + collection: t.Mapping[T, T2], iteratee: t.Callable[[T2, T, t.Dict[T, T2]], T3] +) -> t.List[T3]: ... + + +@t.overload +def flat_map(collection: t.Mapping[T, T2], iteratee: t.Callable[[T2, T], T3]) -> t.List[T3]: ... + + +@t.overload +def flat_map(collection: t.Mapping[t.Any, T2], iteratee: t.Callable[[T2], T3]) -> t.List[T3]: ... + + +@t.overload +def flat_map(collection: t.Mapping[t.Any, t.Iterable[T2]], iteratee: None = None) -> t.List[T2]: ... + + +@t.overload +def flat_map(collection: t.Mapping[t.Any, T2], iteratee: None = None) -> t.List[T2]: ... + + +@t.overload +def flat_map( + collection: t.Iterable[T], iteratee: t.Callable[[T, int, t.List[T]], t.Iterable[T2]] +) -> t.List[T2]: ... + + +@t.overload +def flat_map( + collection: t.Iterable[T], iteratee: t.Callable[[T, int], t.Iterable[T2]] +) -> t.List[T2]: ... + + +@t.overload +def flat_map( + collection: t.Iterable[T], iteratee: t.Callable[[T], t.Iterable[T2]] +) -> t.List[T2]: ... + + +@t.overload +def flat_map( + collection: t.Iterable[T], iteratee: t.Callable[[T, int, t.List[T]], T2] +) -> t.List[T2]: ... + + +@t.overload +def flat_map(collection: t.Iterable[T], iteratee: t.Callable[[T, int], T2]) -> t.List[T2]: ... + + +@t.overload +def flat_map(collection: t.Iterable[T], iteratee: t.Callable[[T], T2]) -> t.List[T2]: ... + + +@t.overload +def flat_map(collection: t.Iterable[t.Iterable[T]], iteratee: None = None) -> t.List[T]: ... + + +@t.overload +def flat_map(collection: t.Iterable[T], iteratee: None = None) -> t.List[T]: ... + + +def flat_map(collection, iteratee=None): + """ + Creates a flattened list of values by running each element in collection through `iteratee` and + flattening the mapped results. The `iteratee` is invoked with three arguments: ``(value, + index|key, collection)``. + + Args: + collection: Collection to iterate over. + iteratee: Iteratee applied per iteration. + + Returns: + Flattened mapped list. + + Example: + + >>> duplicate = lambda n: [[n, n]] + >>> flat_map([1, 2], duplicate) + [[1, 1], [2, 2]] + + .. versionadded:: 4.0.0 + """ + return pyd.flatten(itermap(collection, iteratee=iteratee)) + + +@t.overload +def flat_map_deep( + collection: t.Mapping[T, T2], + iteratee: t.Union[t.Callable[[T2, T, t.Dict[T, T2]], t.Any], None] = None, +) -> t.List[t.Any]: ... + + +@t.overload +def flat_map_deep( + collection: t.Mapping[T, T2], iteratee: t.Union[t.Callable[[T2, T], t.Any], None] = None +) -> t.List[t.Any]: ... + + +@t.overload +def flat_map_deep( + collection: t.Mapping[t.Any, T2], iteratee: t.Union[t.Callable[[T2], t.Any], None] = None +) -> t.List[t.Any]: ... + + +@t.overload +def flat_map_deep( + collection: t.Iterable[T], + iteratee: t.Union[t.Callable[[T, int, t.List[T]], t.Any], None] = None, +) -> t.List[t.Any]: ... + + +@t.overload +def flat_map_deep( + collection: t.Iterable[T], iteratee: t.Union[t.Callable[[T, int], t.Any], None] = None +) -> t.List[t.Any]: ... + + +@t.overload +def flat_map_deep( + collection: t.Iterable[T], iteratee: t.Union[t.Callable[[T], t.Any], None] = None +) -> t.List[t.Any]: ... + + +def flat_map_deep(collection, iteratee=None): + """ + This method is like :func:`flat_map` except that it recursively flattens the mapped results. + + Args: + collection: Collection to iterate over. + iteratee: Iteratee applied per iteration. + + Returns: + Flattened mapped list. + + Example: + + >>> duplicate = lambda n: [[n, n]] + >>> flat_map_deep([1, 2], duplicate) + [1, 1, 2, 2] + + .. versionadded:: 4.0.0 + """ + return pyd.flatten_deep(itermap(collection, iteratee=iteratee)) + + +@t.overload +def flat_map_depth( + collection: t.Mapping[T, T2], + iteratee: t.Union[t.Callable[[T2, T, t.Dict[T, T2]], t.Any], None] = None, + depth: int = 1, +) -> t.List[t.Any]: ... + + +@t.overload +def flat_map_depth( + collection: t.Mapping[T, T2], + iteratee: t.Union[t.Callable[[T2, T], t.Any], None] = None, + depth: int = 1, +) -> t.List[t.Any]: ... + + +@t.overload +def flat_map_depth( + collection: t.Mapping[t.Any, T2], + iteratee: t.Union[t.Callable[[T2], t.Any], None] = None, + depth: int = 1, +) -> t.List[t.Any]: ... + + +@t.overload +def flat_map_depth( + collection: t.Iterable[T], + iteratee: t.Union[t.Callable[[T, int, t.List[T]], t.Any], None] = None, + depth: int = 1, +) -> t.List[t.Any]: ... + + +@t.overload +def flat_map_depth( + collection: t.Iterable[T], + iteratee: t.Union[t.Callable[[T, int], t.Any], None] = None, + depth: int = 1, +) -> t.List[t.Any]: ... + + +@t.overload +def flat_map_depth( + collection: t.Iterable[T], + iteratee: t.Union[t.Callable[[T], t.Any], None] = None, + depth: int = 1, +) -> t.List[t.Any]: ... + + +def flat_map_depth(collection, iteratee=None, depth=1): + """ + This method is like :func:`flat_map` except that it recursively flattens the mapped results up + to `depth` times. + + Args: + collection: Collection to iterate over. + iteratee: Iteratee applied per iteration. + + Returns: + Flattened mapped list. + + Example: + + >>> duplicate = lambda n: [[n, n]] + >>> flat_map_depth([1, 2], duplicate, 1) + [[1, 1], [2, 2]] + >>> flat_map_depth([1, 2], duplicate, 2) + [1, 1, 2, 2] + + .. versionadded:: 4.0.0 + """ + return pyd.flatten_depth(itermap(collection, iteratee=iteratee), depth=depth) + + +@t.overload +def for_each( + collection: t.Dict[T, T2], + iteratee: t.Union[t.Callable[[T2, T, t.Dict[T, T2]], t.Any], IterateeObjT, None] = None, +) -> t.Dict[T, T2]: ... + + +@t.overload +def for_each( + collection: t.Dict[T, T2], + iteratee: t.Union[t.Callable[[T2, T], t.Any], IterateeObjT, None] = None, +) -> t.Dict[T, T2]: ... + + +@t.overload +def for_each( + collection: t.Dict[T, T2], + iteratee: t.Union[t.Callable[[T2], t.Any], IterateeObjT, None] = None, +) -> t.Dict[T, T2]: ... + + +@t.overload +def for_each( + collection: t.List[T], + iteratee: t.Union[t.Callable[[T, int, t.List[T]], t.Any], IterateeObjT, None] = None, +) -> t.List[T]: ... + + +@t.overload +def for_each( + collection: t.List[T], + iteratee: t.Union[t.Callable[[T, int], t.Any], IterateeObjT, None] = None, +) -> t.List[T]: ... + + +@t.overload +def for_each( + collection: t.List[T], + iteratee: t.Union[t.Callable[[T], t.Any], IterateeObjT, None] = None, +) -> t.List[T]: ... + + +def for_each(collection, iteratee=None): + """ + Iterates over elements of a collection, executing the iteratee for each element. + + Args: + collection: Collection to iterate over. + iteratee: Iteratee applied per iteration. + + Returns: + `collection` + + Example: + + >>> results = {} + >>> def cb(x): + ... results[x] = x**2 + >>> for_each([1, 2, 3, 4], cb) + [1, 2, 3, 4] + >>> assert results == {1: 1, 2: 4, 3: 9, 4: 16} + + .. versionadded:: 1.0.0 + + .. versionchanged:: 4.0.0 + Removed alias ``each``. + """ + next((None for ret, _, _, _ in iteriteratee(collection, iteratee) if ret is False), None) + return collection + + +@t.overload +def for_each_right( + collection: t.Dict[T, T2], + iteratee: t.Union[t.Callable[[T2, T, t.Dict[T, T2]], t.Any], IterateeObjT], +) -> t.Dict[T, T2]: ... + + +@t.overload +def for_each_right( + collection: t.Dict[T, T2], + iteratee: t.Union[t.Callable[[T2, T], t.Any], IterateeObjT], +) -> t.Dict[T, T2]: ... + + +@t.overload +def for_each_right( + collection: t.Dict[T, T2], + iteratee: t.Union[t.Callable[[T2], t.Any], IterateeObjT], +) -> t.Dict[T, T2]: ... + + +@t.overload +def for_each_right( + collection: t.List[T], + iteratee: t.Union[t.Callable[[T, int, t.List[T]], t.Any], IterateeObjT], +) -> t.List[T]: ... + + +@t.overload +def for_each_right( + collection: t.List[T], + iteratee: t.Union[t.Callable[[T, int], t.Any], IterateeObjT], +) -> t.List[T]: ... + + +@t.overload +def for_each_right( + collection: t.List[T], + iteratee: t.Union[t.Callable[[T], t.Any], IterateeObjT], +) -> t.List[T]: ... + + +def for_each_right(collection, iteratee): + """ + This method is like :func:`for_each` except that it iterates over elements of a `collection` + from right to left. + + Args: + collection: Collection to iterate over. + iteratee: Iteratee applied per iteration. + + Returns: + `collection` + + Example: + + >>> results = {"total": 1} + >>> def cb(x): + ... results["total"] = x * results["total"] + >>> for_each_right([1, 2, 3, 4], cb) + [1, 2, 3, 4] + >>> assert results == {"total": 24} + + .. versionadded:: 1.0.0 + + .. versionchanged:: 4.0.0 + Removed alias ``each_right``. + """ + next( + (None for ret, _, _, _ in iteriteratee(collection, iteratee, reverse=True) if ret is False), + None, + ) + return collection + + +@t.overload +def group_by(collection: t.Iterable[T], iteratee: t.Callable[[T], T2]) -> t.Dict[T2, t.List[T]]: ... + + +@t.overload +def group_by( + collection: t.Iterable[T], iteratee: t.Union[IterateeObjT, None] = None +) -> t.Dict[t.Any, t.List[T]]: ... + + +def group_by(collection, iteratee=None): + """ + Creates an object composed of keys generated from the results of running each element of a + `collection` through the iteratee. + + Args: + collection: Collection to iterate over. + iteratee: Iteratee applied per iteration. + + Returns: + Results of grouping by `iteratee`. + + Example: + + >>> results = group_by([{'a': 1, 'b': 2}, {'a': 3, 'b': 4}], 'a') + >>> assert results == {1: [{'a': 1, 'b': 2}], 3: [{'a': 3, 'b': 4}]} + >>> results = group_by([{'a': 1, 'b': 2}, {'a': 3, 'b': 4}], {'a': 1}) + >>> assert results == {False: [{'a': 3, 'b': 4}],\ + True: [{'a': 1, 'b': 2}]} + + .. versionadded:: 1.0.0 + """ + ret = {} + cbk = pyd.iteratee(iteratee) + + for value in collection: + key = cbk(value) + ret.setdefault(key, []) + ret[key].append(value) + + return ret + + +def includes( + collection: t.Union[t.Sequence[t.Any], t.Dict[t.Any, t.Any]], target: t.Any, from_index: int = 0 +) -> bool: + """ + Checks if a given value is present in a collection. If `from_index` is negative, it is used as + the offset from the end of the collection. + + Args: + collection: Collection to iterate over. + target: Target value to compare to. + from_index: Offset to start search from. + + Returns: + Whether `target` is in `collection`. + + Example: + + >>> includes([1, 2, 3, 4], 2) + True + >>> includes([1, 2, 3, 4], 2, from_index=2) + False + >>> includes({"a": 1, "b": 2, "c": 3, "d": 4}, 2) + True + + .. versionadded:: 1.0.0 + + .. versionchanged:: 4.0.0 + Renamed from ``contains`` to ``includes`` and removed alias + ``include``. + """ + collection_values: t.Container[t.Any] + if isinstance(collection, dict): + collection_values = collection.values() + else: + # only makes sense to do this if `collection` is not a dict + collection_values = collection[from_index:] + + return target in collection_values + + +def invoke_map( + collection: t.Iterable[t.Any], path: PathT, *args: t.Any, **kwargs: t.Any +) -> t.List[t.Any]: + """ + Invokes the method at `path` of each element in `collection`, returning a list of the results of + each invoked method. Any additional arguments are provided to each invoked method. If `path` is + a function, it's invoked for each element in `collection`. + + Args: + collection: Collection to iterate over. + path: String path to method to invoke or callable to invoke for each element in + `collection`. + args: Arguments to pass to method call. + kwargs: Keyword arguments to pass to method call. + + Returns: + List of results of invoking method of each item. + + Example: + + >>> items = [{"a": [{"b": 1}]}, {"a": [{"c": 2}]}] + >>> expected = [{"b": 1}.items(), {"c": 2}.items()] + >>> invoke_map(items, "a[0].items") == expected + True + + .. versionadded:: 4.0.0 + """ + return map_(collection, lambda item: pyd.invoke(item, path, *args, **kwargs)) + + +@t.overload +def key_by(collection: t.Iterable[T], iteratee: t.Callable[[T], T2]) -> t.Dict[T2, T]: ... + + +@t.overload +def key_by( + collection: t.Iterable[t.Any], iteratee: t.Union[IterateeObjT, None] = None +) -> t.Dict[t.Any, t.Any]: ... + + +def key_by(collection, iteratee=None): + """ + Creates an object composed of keys generated from the results of running each element of the + collection through the given iteratee. + + Args: + collection: Collection to iterate over. + iteratee: Iteratee applied per iteration. + + Returns: + Results of indexing by `iteratee`. + + Example: + + >>> results = key_by([{"a": 1, "b": 2}, {"a": 3, "b": 4}], "a") + >>> assert results == {1: {"a": 1, "b": 2}, 3: {"a": 3, "b": 4}} + + + .. versionadded:: 1.0.0 + + .. versionchanged:: 4.0.0 + Renamed from ``index_by`` to ``key_by``. + """ + ret = {} + cbk = pyd.iteratee(iteratee) + + for value in collection: + ret[cbk(value)] = value + + return ret + + +@t.overload +def map_(collection: t.Mapping[t.Any, T2], iteratee: t.Callable[[T2], T3]) -> t.List[T3]: ... + + +@t.overload +def map_(collection: t.Mapping[T, T2], iteratee: t.Callable[[T2, T], T3]) -> t.List[T3]: ... + + +@t.overload +def map_( + collection: t.Mapping[T, T2], iteratee: t.Callable[[T2, T, t.Dict[T, T2]], T3] +) -> t.List[T3]: ... + + +@t.overload +def map_(collection: t.Iterable[T], iteratee: t.Callable[[T], T2]) -> t.List[T2]: ... + + +@t.overload +def map_(collection: t.Iterable[T], iteratee: t.Callable[[T, int], T2]) -> t.List[T2]: ... + + +@t.overload +def map_( + collection: t.Iterable[T], iteratee: t.Callable[[T, int, t.List[T]], T2] +) -> t.List[T2]: ... + + +@t.overload +def map_( + collection: t.Iterable[t.Any], iteratee: t.Union[IterateeObjT, None] = None +) -> t.List[t.Any]: ... + + +def map_(collection, iteratee=None): + """ + Creates an array of values by running each element in the collection through the iteratee. The + iteratee is invoked with three arguments: ``(value, index|key, collection)``. If a property name + is passed for iteratee, the created :func:`pluck` style iteratee will return the property value + of the given element. If an object is passed for iteratee, the created :func:`.matches` style + iteratee will return ``True`` for elements that have the properties of the given object, else + ``False``. + + Args: + collection: Collection to iterate over. + iteratee: Iteratee applied per iteration. + + Returns: + Mapped list. + + Example: + + >>> map_([1, 2, 3, 4], str) + ['1', '2', '3', '4'] + >>> map_([{"a": 1, "b": 2}, {"a": 3, "b": 4}, {"a": 5, "b": 6}], "a") + [1, 3, 5] + >>> map_([[[0, 1]], [[2, 3]], [[4, 5]]], "0.1") + [1, 3, 5] + >>> map_([{"a": {"b": 1}}, {"a": {"b": 2}}], "a.b") + [1, 2] + >>> map_([{"a": {"b": [0, 1]}}, {"a": {"b": [2, 3]}}], "a.b[1]") + [1, 3] + + .. versionadded:: 1.0.0 + + .. versionchanged:: 4.0.0 + Removed alias ``collect``. + """ + return list(itermap(collection, iteratee)) + + +def nest(collection: t.Iterable[t.Any], *properties: t.Any) -> t.Any: + """ + This method is like :func:`group_by` except that it supports nested grouping by multiple string + `properties`. If only a single key is given, it is like calling ``group_by(collection, prop)``. + + Args: + collection: Collection to iterate over. + *properties: Properties to nest by. + + Returns: + Results of nested grouping by `properties`. + + Example: + + >>> results = nest([{'shape': 'square', 'color': 'red', 'qty': 5},\ + {'shape': 'square', 'color': 'blue', 'qty': 10},\ + {'shape': 'square', 'color': 'orange', 'qty': 5},\ + {'shape': 'circle', 'color': 'yellow', 'qty': 5},\ + {'shape': 'circle', 'color': 'pink', 'qty': 10},\ + {'shape': 'oval', 'color': 'purple', 'qty': 5}],\ + 'shape', 'qty') + >>> expected = {\ + 'square': {5: [{'shape': 'square', 'color': 'red', 'qty': 5},\ + {'shape': 'square', 'color': 'orange', 'qty': 5}],\ + 10: [{'shape': 'square', 'color': 'blue', 'qty': 10}]},\ + 'circle': {5: [{'shape': 'circle', 'color': 'yellow', 'qty': 5}],\ + 10: [{'shape': 'circle', 'color': 'pink', 'qty': 10}]},\ + 'oval': {5: [{'shape': 'oval', 'color': 'purple', 'qty': 5}]}} + >>> results == expected + True + + .. versionadded:: 4.3.0 + """ + if not properties: + return collection + + flat_properties = pyd.flatten(properties) + first, rest = flat_properties[0], flat_properties[1:] + + return pyd.map_values(group_by(collection, first), lambda value: nest(value, *rest)) + + +@t.overload +def order_by( + collection: t.Mapping[t.Any, T2], + keys: t.Iterable[t.Union[str, int]], + orders: t.Union[t.Iterable[bool], bool], + reverse: bool = False, +) -> t.List[T2]: ... + + +@t.overload +def order_by( + collection: t.Mapping[t.Any, T2], + keys: t.Iterable[str], + orders: None = None, + reverse: bool = False, +) -> t.List[T2]: ... + + +@t.overload +def order_by( + collection: t.Iterable[T], + keys: t.Iterable[t.Union[str, int]], + orders: t.Union[t.Iterable[bool], bool], + reverse: bool = False, +) -> t.List[T]: ... + + +@t.overload +def order_by( + collection: t.Iterable[T], + keys: t.Iterable[str], + orders: None = None, + reverse: bool = False, +) -> t.List[T]: ... + + +def order_by(collection, keys, orders=None, reverse=False): + """ + This method is like :func:`sort_by` except that it sorts by key names instead of an iteratee + function. Keys can be sorted in descending order by prepending a ``"-"`` to the key name (e.g. + ``"name"`` would become ``"-name"``) or by passing a list of boolean sort options via `orders` + where ``True`` is ascending and ``False`` is descending. + + Args: + collection: Collection to iterate over. + keys: List of keys to sort by. By default, keys will be sorted in ascending order. To + sort a key in descending order, prepend a ``"-"`` to the key name. For example, to sort + the key value for ``"name"`` in descending order, use ``"-name"``. + orders: List of boolean sort orders to apply for each key. ``True`` + corresponds to ascending order while ``False`` is descending. Defaults to ``None``. + reverse (bool, optional): Whether to reverse the sort. Defaults to ``False``. + + Returns: + Sorted list. + + Example: + + >>> items = [{'a': 2, 'b': 1}, {'a': 3, 'b': 2}, {'a': 1, 'b': 3}] + >>> results = order_by(items, ['b', 'a']) + >>> assert results == [{'a': 2, 'b': 1},\ + {'a': 3, 'b': 2},\ + {'a': 1, 'b': 3}] + >>> results = order_by(items, ['a', 'b']) + >>> assert results == [{'a': 1, 'b': 3},\ + {'a': 2, 'b': 1},\ + {'a': 3, 'b': 2}] + >>> results = order_by(items, ['-a', 'b']) + >>> assert results == [{'a': 3, 'b': 2},\ + {'a': 2, 'b': 1},\ + {'a': 1, 'b': 3}] + >>> results = order_by(items, ['a', 'b'], [False, True]) + >>> assert results == [{'a': 3, 'b': 2},\ + {'a': 2, 'b': 1},\ + {'a': 1, 'b': 3}] + + .. versionadded:: 3.0.0 + + .. versionchanged:: 3.2.0 + Added `orders` argument. + + .. versionchanged:: 3.2.0 + Added :func:`sort_by_order` as alias. + + .. versionchanged:: 4.0.0 + Renamed from ``order_by`` to ``order_by`` and removed alias + ``sort_by_order``. + """ + if isinstance(collection, dict): + collection = collection.values() + + # Maintain backwards compatibility. + if pyd.is_boolean(orders): + reverse = orders + orders = None + + comparers = [] + + if orders: + for i, key in enumerate(keys): + if pyd.has(orders, i): + order = 1 if orders[i] else -1 + else: + order = 1 + + comparers.append((pyd.property_(key), order)) + else: + for key in keys: + if key.startswith("-"): + order = -1 + key = key[1:] + else: + order = 1 + + comparers.append((pyd.property_(key), order)) + + def comparison(left, right): + # pylint: disable=useless-else-on-loop,missing-docstring + for func, mult in comparers: + result = cmp(func(left), func(right)) + if result: + return mult * result + return 0 + + return sorted(collection, key=cmp_to_key(comparison), reverse=reverse) + + +@t.overload +def partition( + collection: t.Mapping[T, T2], predicate: t.Callable[[T2, T, t.Dict[T, T2]], t.Any] +) -> t.List[t.List[T2]]: ... + + +@t.overload +def partition( + collection: t.Mapping[T, T2], predicate: t.Callable[[T2, T], t.Any] +) -> t.List[t.List[T2]]: ... + + +@t.overload +def partition( + collection: t.Mapping[t.Any, T2], predicate: t.Callable[[T2], t.Any] +) -> t.List[t.List[T2]]: ... + + +@t.overload +def partition( + collection: t.Mapping[t.Any, T2], predicate: t.Union[IterateeObjT, None] = None +) -> t.List[t.List[T2]]: ... + + +@t.overload +def partition( + collection: t.Iterable[T], predicate: t.Callable[[T, int, t.List[T]], t.Any] +) -> t.List[t.List[T]]: ... + + +@t.overload +def partition( + collection: t.Iterable[T], predicate: t.Callable[[T, int], t.Any] +) -> t.List[t.List[T]]: ... + + +@t.overload +def partition( + collection: t.Iterable[T], predicate: t.Callable[[T], t.Any] +) -> t.List[t.List[T]]: ... + + +@t.overload +def partition( + collection: t.Iterable[T], predicate: t.Union[IterateeObjT, None] = None +) -> t.List[t.List[T]]: ... + + +def partition(collection, predicate=None): + """ + Creates an array of elements split into two groups, the first of which contains elements the + `predicate` returns truthy for, while the second of which contains elements the `predicate` + returns falsey for. The `predicate` is invoked with three arguments: ``(value, index|key, + collection)``. + + If a property name is provided for `predicate` the created :func:`pluck` style predicate returns + the property value of the given element. + + If an object is provided for `predicate` the created :func:`.matches` style predicate returns + ``True`` for elements that have the properties of the given object, else ``False``. + + Args: + collection: Collection to iterate over. + predicate: Predicate applied per iteration. + + Returns: + List of grouped elements. + + Example: + + >>> partition([1, 2, 3, 4], lambda x: x >= 3) + [[3, 4], [1, 2]] + + .. versionadded:: 1.1.0 + """ + trues = [] + falses = [] + + for is_true, value, _, _ in iteriteratee(collection, predicate): + if is_true: + trues.append(value) + else: + falses.append(value) + + return [trues, falses] + + +def pluck(collection: t.Iterable[t.Any], path: PathT) -> t.List[t.Any]: + """ + Retrieves the value of a specified property from all elements in the collection. + + Args: + collection: List of dicts. + path: Collection's path to pluck + + Returns: + Plucked list. + + Example: + + >>> pluck([{"a": 1, "b": 2}, {"a": 3, "b": 4}, {"a": 5, "b": 6}], "a") + [1, 3, 5] + >>> pluck([[[0, 1]], [[2, 3]], [[4, 5]]], "0.1") + [1, 3, 5] + >>> pluck([{"a": {"b": 1}}, {"a": {"b": 2}}], "a.b") + [1, 2] + >>> pluck([{"a": {"b": [0, 1]}}, {"a": {"b": [2, 3]}}], "a.b.1") + [1, 3] + >>> pluck([{"a": {"b": [0, 1]}}, {"a": {"b": [2, 3]}}], ["a", "b", 1]) + [1, 3] + + .. versionadded:: 1.0.0 + + .. versionchanged:: 4.0.0 + Function removed. + + .. versionchanged:: 4.0.1 + Made property access deep. + """ + return map_(collection, pyd.property_(path)) + + +@t.overload +def reduce_( + collection: t.Mapping[T, T2], + iteratee: t.Callable[[T3, T2, T], T3], + accumulator: T3, +) -> T3: ... + + +@t.overload +def reduce_( + collection: t.Mapping[t.Any, T2], + iteratee: t.Callable[[T3, T2], T3], + accumulator: T3, +) -> T3: ... + + +@t.overload +def reduce_( + collection: t.Mapping[t.Any, t.Any], + iteratee: t.Callable[[T3], T3], + accumulator: T3, +) -> T3: ... + + +@t.overload +def reduce_( + collection: t.Mapping[T, T2], + iteratee: t.Callable[[T2, T2, T], T2], + accumulator: None = None, +) -> T2: ... + + +@t.overload +def reduce_( + collection: t.Mapping[t.Any, T2], + iteratee: t.Callable[[T2, T2], T2], + accumulator: None = None, +) -> T2: ... + + +@t.overload +def reduce_( + collection: t.Mapping[t.Any, t.Any], + iteratee: t.Callable[[T], T], + accumulator: None = None, +) -> T: ... + + +@t.overload +def reduce_( + collection: t.Iterable[T], + iteratee: t.Callable[[T2, T, int], T2], + accumulator: T2, +) -> T2: ... + + +@t.overload +def reduce_( + collection: t.Iterable[T], + iteratee: t.Callable[[T2, T], T2], + accumulator: T2, +) -> T2: ... + + +@t.overload +def reduce_( + collection: t.Iterable[t.Any], + iteratee: t.Callable[[T2], T2], + accumulator: T2, +) -> T2: ... + + +@t.overload +def reduce_( + collection: t.Iterable[T], + iteratee: t.Callable[[T, T, int], T], + accumulator: None = None, +) -> T: ... + + +@t.overload +def reduce_( + collection: t.Iterable[T], + iteratee: t.Callable[[T, T], T], + accumulator: None = None, +) -> T: ... + + +@t.overload +def reduce_( + collection: t.Iterable[t.Any], + iteratee: t.Callable[[T], T], + accumulator: None = None, +) -> T: ... + + +@t.overload +def reduce_( + collection: t.Iterable[T], iteratee: None = None, accumulator: t.Union[T, None] = None +) -> T: ... + + +def reduce_(collection, iteratee=None, accumulator=None): + """ + Reduces a collection to a value which is the accumulated result of running each element in the + collection through the iteratee, where each successive iteratee execution consumes the return + value of the previous execution. + + Args: + collection: Collection to iterate over. + iteratee: Iteratee applied per iteration. + accumulator: Initial value of aggregator. Default is to use the result of + the first iteration. + + Returns: + Accumulator object containing results of reduction. + + Example: + + >>> reduce_([1, 2, 3, 4], lambda total, x: total * x) + 24 + + .. versionadded:: 1.0.0 + + .. versionchanged:: 4.0.0 + Removed aliases ``foldl`` and ``inject``. + """ + iterable = iterator(collection) + + if accumulator is None: + try: + _, accumulator = next(iterable) + except StopIteration as exc: + raise TypeError("reduce_() of empty sequence with no initial value") from exc + + result = accumulator + + if iteratee is None: + iteratee = pyd.identity + argcount = 1 + else: + argcount = getargcount(iteratee, maxargs=3) + + for index, item in iterable: + result = callit(iteratee, result, item, index, argcount=argcount) + + return result + + +@t.overload +def reduce_right( + collection: t.Mapping[T, T2], + iteratee: t.Callable[[T3, T2, T], T3], + accumulator: T3, +) -> T3: ... + + +@t.overload +def reduce_right( + collection: t.Mapping[t.Any, T2], + iteratee: t.Callable[[T3, T2], T3], + accumulator: T3, +) -> T3: ... + + +@t.overload +def reduce_right( + collection: t.Mapping[t.Any, t.Any], + iteratee: t.Callable[[T3], T3], + accumulator: T3, +) -> T3: ... + + +@t.overload +def reduce_right( + collection: t.Mapping[T, T2], + iteratee: t.Callable[[T2, T2, T], T2], + accumulator: None = None, +) -> T2: ... + + +@t.overload +def reduce_right( + collection: t.Mapping[t.Any, T2], + iteratee: t.Callable[[T2, T2], T2], + accumulator: None = None, +) -> T2: ... + + +@t.overload +def reduce_right( + collection: t.Mapping[t.Any, t.Any], + iteratee: t.Callable[[T], T], + accumulator: None = None, +) -> T: ... + + +@t.overload +def reduce_right( + collection: t.Iterable[T], + iteratee: t.Callable[[T2, T, int], T2], + accumulator: T2, +) -> T2: ... + + +@t.overload +def reduce_right( + collection: t.Iterable[T], + iteratee: t.Callable[[T2, T], T2], + accumulator: T2, +) -> T2: ... + + +@t.overload +def reduce_right( + collection: t.Iterable[t.Any], + iteratee: t.Callable[[T2], T2], + accumulator: T2, +) -> T2: ... + + +@t.overload +def reduce_right( + collection: t.Iterable[T], + iteratee: t.Callable[[T, T, int], T], + accumulator: None = None, +) -> T: ... + + +@t.overload +def reduce_right( + collection: t.Iterable[T], + iteratee: t.Callable[[T, T], T], + accumulator: None = None, +) -> T: ... + + +@t.overload +def reduce_right( + collection: t.Iterable[t.Any], + iteratee: t.Callable[[T], T], + accumulator: None = None, +) -> T: ... + + +@t.overload +def reduce_right( + collection: t.Iterable[T], iteratee: None = None, accumulator: t.Union[T, None] = None +) -> T: ... + + +def reduce_right(collection, iteratee=None, accumulator=None): + """ + This method is like :func:`reduce_` except that it iterates over elements of a `collection` from + right to left. + + Args: + collection: Collection to iterate over. + iteratee: Iteratee applied per iteration. + accumulator: Initial value of aggregator. Default is to use the result of + the first iteration. + + Returns: + Accumulator object containing results of reduction. + + Example: + + >>> reduce_right([1, 2, 3, 4], lambda total, x: total**x) + 4096 + + .. versionadded:: 1.0.0 + + .. versionchanged:: 3.2.1 + Fix bug where collection was not reversed correctly. + + .. versionchanged:: 4.0.0 + Removed alias ``foldr``. + """ + if not isinstance(collection, dict): + collection = list(collection)[::-1] + + return reduce_(collection, iteratee, accumulator) + + +@t.overload +def reductions( + collection: t.Mapping[T, T2], + iteratee: t.Callable[[T3, T2, T], T3], + accumulator: T3, + from_right: bool = False, +) -> t.List[T3]: ... + + +@t.overload +def reductions( + collection: t.Mapping[t.Any, T2], + iteratee: t.Callable[[T3, T2], T3], + accumulator: T3, + from_right: bool = False, +) -> t.List[T3]: ... + + +@t.overload +def reductions( + collection: t.Mapping[t.Any, t.Any], + iteratee: t.Callable[[T3], T3], + accumulator: T3, + from_right: bool = False, +) -> t.List[T3]: ... + + +@t.overload +def reductions( + collection: t.Mapping[T, T2], + iteratee: t.Callable[[T2, T2, T], T2], + accumulator: None = None, + from_right: bool = False, +) -> t.List[T2]: ... + + +@t.overload +def reductions( + collection: t.Mapping[t.Any, T2], + iteratee: t.Callable[[T2, T2], T2], + accumulator: None = None, + from_right: bool = False, +) -> t.List[T2]: ... + + +@t.overload +def reductions( + collection: t.Mapping[t.Any, t.Any], + iteratee: t.Callable[[T], T], + accumulator: None = None, + from_right: bool = False, +) -> t.List[T]: ... + + +@t.overload +def reductions( + collection: t.Iterable[T], + iteratee: t.Callable[[T2, T, int], T2], + accumulator: T2, + from_right: bool = False, +) -> t.List[T2]: ... + + +@t.overload +def reductions( + collection: t.Iterable[T], + iteratee: t.Callable[[T2, T], T2], + accumulator: T2, + from_right: bool = False, +) -> t.List[T2]: ... + + +@t.overload +def reductions( + collection: t.Iterable[t.Any], + iteratee: t.Callable[[T2], T2], + accumulator: T2, + from_right: bool = False, +) -> t.List[T2]: ... + + +@t.overload +def reductions( + collection: t.Iterable[T], + iteratee: t.Callable[[T, T, int], T], + accumulator: None = None, + from_right: bool = False, +) -> t.List[T]: ... + + +@t.overload +def reductions( + collection: t.Iterable[T], + iteratee: t.Callable[[T, T], T], + accumulator: None = None, + from_right: bool = False, +) -> t.List[T]: ... + + +@t.overload +def reductions( + collection: t.Iterable[t.Any], + iteratee: t.Callable[[T], T], + accumulator: None = None, + from_right: bool = False, +) -> t.List[T]: ... + + +@t.overload +def reductions( + collection: t.Iterable[T], + iteratee: None = None, + accumulator: t.Union[T, None] = None, + from_right: bool = False, +) -> t.List[T]: ... + + +def reductions(collection, iteratee=None, accumulator=None, from_right=False): + """ + This function is like :func:`reduce_` except that it returns a list of every intermediate value + in the reduction operation. + + Args: + collection: Collection to iterate over. + iteratee: Iteratee applied per iteration. + accumulator: Initial value of aggregator. Default is to use the result of + the first iteration. + + Returns: + Results of each reduction operation. + + Example: + + >>> reductions([1, 2, 3, 4], lambda total, x: total * x) + [2, 6, 24] + + Note: + The last element of the returned list would be the result of using + :func:`reduce_`. + + .. versionadded:: 2.0.0 + """ + if iteratee is None: + iteratee = pyd.identity + argcount = 1 + else: + argcount = getargcount(iteratee, maxargs=3) + + results = [] + + def interceptor(result, item, index): + result = callit(iteratee, result, item, index, argcount=argcount) + results.append(result) + return result + + reducer = reduce_right if from_right else reduce_ + reducer(collection, interceptor, accumulator) + + return results + + +@t.overload +def reductions_right( + collection: t.Mapping[T, T2], + iteratee: t.Callable[[T3, T2, T], T3], + accumulator: T3, +) -> t.List[T3]: ... + + +@t.overload +def reductions_right( + collection: t.Mapping[t.Any, T2], + iteratee: t.Callable[[T3, T2], T3], + accumulator: T3, +) -> t.List[T3]: ... + + +@t.overload +def reductions_right( + collection: t.Mapping[t.Any, t.Any], + iteratee: t.Callable[[T3], T3], + accumulator: T3, +) -> t.List[T3]: ... + + +@t.overload +def reductions_right( + collection: t.Mapping[T, T2], + iteratee: t.Callable[[T2, T2, T], T2], + accumulator: None = None, +) -> t.List[T2]: ... + + +@t.overload +def reductions_right( + collection: t.Mapping[t.Any, T2], + iteratee: t.Callable[[T2, T2], T2], + accumulator: None = None, +) -> t.List[T2]: ... + + +@t.overload +def reductions_right( + collection: t.Mapping[t.Any, t.Any], + iteratee: t.Callable[[T], T], + accumulator: None = None, +) -> t.List[T]: ... + + +@t.overload +def reductions_right( + collection: t.Iterable[T], + iteratee: t.Callable[[T2, T, int], T2], + accumulator: T2, +) -> t.List[T2]: ... + + +@t.overload +def reductions_right( + collection: t.Iterable[T], + iteratee: t.Callable[[T2, T], T2], + accumulator: T2, +) -> t.List[T2]: ... + + +@t.overload +def reductions_right( + collection: t.Iterable[t.Any], + iteratee: t.Callable[[T2], T2], + accumulator: T2, +) -> t.List[T2]: ... + + +@t.overload +def reductions_right( + collection: t.Iterable[T], + iteratee: t.Callable[[T, T, int], T], + accumulator: None = None, +) -> t.List[T]: ... + + +@t.overload +def reductions_right( + collection: t.Iterable[T], + iteratee: t.Callable[[T, T], T], + accumulator: None = None, +) -> t.List[T]: ... + + +@t.overload +def reductions_right( + collection: t.Iterable[t.Any], + iteratee: t.Callable[[T], T], + accumulator: None = None, +) -> t.List[T]: ... + + +@t.overload +def reductions_right( + collection: t.Iterable[T], iteratee: None = None, accumulator: t.Union[T, None] = None +) -> t.List[T]: ... + + +def reductions_right(collection, iteratee=None, accumulator=None): + """ + This method is like :func:`reductions` except that it iterates over elements of a `collection` + from right to left. + + Args: + collection: Collection to iterate over. + iteratee: Iteratee applied per iteration. + accumulator: Initial value of aggregator. Default is to use the result of + the first iteration. + + Returns: + Results of each reduction operation. + + Example: + + >>> reductions_right([1, 2, 3, 4], lambda total, x: total**x) + [64, 4096, 4096] + + Note: + The last element of the returned list would be the result of using + :func:`reduce_`. + + .. versionadded:: 2.0.0 + """ + return reductions(collection, iteratee, accumulator, from_right=True) + + +@t.overload +def reject( + collection: t.Mapping[T, T2], + predicate: t.Union[t.Callable[[T2, T, t.Dict[T, T2]], t.Any], IterateeObjT, None] = None, +) -> t.List[T2]: ... + + +@t.overload +def reject( + collection: t.Mapping[T, T2], + predicate: t.Union[t.Callable[[T2, T], t.Any], IterateeObjT, None] = None, +) -> t.List[T2]: ... + + +@t.overload +def reject( + collection: t.Mapping[t.Any, T2], + predicate: t.Union[t.Callable[[T2], t.Any], IterateeObjT, None] = None, +) -> t.List[T2]: ... + + +@t.overload +def reject( + collection: t.Iterable[T], + predicate: t.Union[t.Callable[[T, int, t.List[T]], t.Any], IterateeObjT, None] = None, +) -> t.List[T]: ... + + +@t.overload +def reject( + collection: t.Iterable[T], + predicate: t.Union[t.Callable[[T, int], t.Any], IterateeObjT, None] = None, +) -> t.List[T]: ... + + +@t.overload +def reject( + collection: t.Iterable[T], + predicate: t.Union[t.Callable[[T], t.Any], IterateeObjT, None] = None, +) -> t.List[T]: ... + + +def reject(collection, predicate=None): + """ + The opposite of :func:`filter_` this method returns the elements of a collection that the + predicate does **not** return truthy for. + + Args: + collection: Collection to iterate over. + predicate: Predicate applied per iteration. + + Returns: + Rejected elements of `collection`. + + Example: + + >>> reject([1, 2, 3, 4], lambda x: x >= 3) + [1, 2] + >>> reject([{"a": 0}, {"a": 1}, {"a": 2}], "a") + [{'a': 0}] + >>> reject([{"a": 0}, {"a": 1}, {"a": 2}], {"a": 1}) + [{'a': 0}, {'a': 2}] + + .. versionadded:: 1.0.0 + """ + return [value for is_true, value, _, _ in iteriteratee(collection, predicate) if not is_true] + + +def sample(collection: t.Sequence[T]) -> T: + """ + Retrieves a random element from a given `collection`. + + Args: + collection: Collection to iterate over. + + Returns: + Random element from the given collection. + + Example: + + >>> items = [1, 2, 3, 4, 5] + >>> results = sample(items) + >>> assert results in items + + .. versionadded:: 1.0.0 + + .. versionchanged:: 4.0.0 + Moved multiple samples functionality to :func:`sample_size`. This + function now only returns a single random sample. + """ + return random.choice(collection) + + +def sample_size(collection: t.Sequence[T], n: t.Union[int, None] = None) -> t.List[T]: + """ + Retrieves list of `n` random elements from a collection. + + Args: + collection: Collection to iterate over. + n: Number of random samples to return. + + Returns: + List of `n` sampled collection values. + + Examples: + + >>> items = [1, 2, 3, 4, 5] + >>> results = sample_size(items, 2) + >>> assert len(results) == 2 + >>> assert set(items).intersection(results) == set(results) + + .. versionadded:: 4.0.0 + """ + num = min(n or 1, len(collection)) + return random.sample(collection, num) + + +@t.overload +def shuffle(collection: t.Mapping[t.Any, T]) -> t.List[T]: ... + + +@t.overload +def shuffle(collection: t.Iterable[T]) -> t.List[T]: ... + + +def shuffle(collection): + """ + Creates a list of shuffled values, using a version of the Fisher-Yates shuffle. + + Args: + collection: Collection to iterate over. + + Returns: + Shuffled list of values. + + Example: + + >>> items = [1, 2, 3, 4] + >>> results = shuffle(items) + >>> assert len(results) == len(items) + >>> assert set(results) == set(items) + + .. versionadded:: 1.0.0 + """ + if isinstance(collection, dict): + collection = collection.values() + + # Make copy of collection since random.shuffle works on list in-place. + collection = list(collection) + + # NOTE: random.shuffle uses Fisher-Yates. + random.shuffle(collection) + + return collection + + +def size(collection: t.Sized) -> int: + """ + Gets the size of the `collection` by returning `len(collection)` for iterable objects. + + Args: + collection: Collection to iterate over. + + Returns: + Collection length. + + Example: + + >>> size([1, 2, 3, 4]) + 4 + + .. versionadded:: 1.0.0 + """ + return len(collection) + + +def some( + collection: t.Iterable[T], predicate: t.Union[t.Callable[[T], t.Any], None] = None +) -> bool: + """ + Checks if the predicate returns a truthy value for any element of a collection. The predicate is + invoked with three arguments: ``(value, index|key, collection)``. If a property name is passed + for predicate, the created :func:`map_` style predicate will return the property value of the + given element. If an object is passed for predicate, the created :func:`.matches` style + predicate will return ``True`` for elements that have the properties of the given object, else + ``False``. + + Args: + collection: Collection to iterate over. + predicate: Predicate applied per iteration. + + Returns: + Whether any of the elements are truthy. + + Example: + + >>> some([False, True, 0]) + True + >>> some([False, 0, None]) + False + >>> some([1, 2, 3, 4], lambda x: x >= 3) + True + >>> some([1, 2, 3, 4], lambda x: x == 0) + False + + .. versionadded:: 1.0.0 + + .. versionchanged:: 4.0.0 + Removed alias ``any_``. + """ + if predicate: + cbk = pyd.iteratee(predicate) + collection = (cbk(item) for item in collection) + + return any(collection) + + +@t.overload +def sort_by( + collection: t.Mapping[t.Any, T2], + iteratee: t.Union[t.Callable[[T2], t.Any], IterateeObjT, None] = None, + reverse: bool = False, +) -> t.List[T2]: ... + + +@t.overload +def sort_by( + collection: t.Iterable[T], + iteratee: t.Union[t.Callable[[T], t.Any], IterateeObjT, None] = None, + reverse: bool = False, +) -> t.List[T]: ... + + +def sort_by(collection, iteratee=None, reverse=False): + """ + Creates a list of elements, sorted in ascending order by the results of running each element in + a `collection` through the iteratee. + + Args: + collection: Collection to iterate over. + iteratee: Iteratee applied per iteration. + reverse: Whether to reverse the sort. Defaults to ``False``. + + Returns: + Sorted list. + + Example: + + >>> sort_by({"a": 2, "b": 3, "c": 1}) + [1, 2, 3] + >>> sort_by({"a": 2, "b": 3, "c": 1}, reverse=True) + [3, 2, 1] + >>> sort_by([{"a": 2}, {"a": 3}, {"a": 1}], "a") + [{'a': 1}, {'a': 2}, {'a': 3}] + + .. versionadded:: 1.0.0 + """ + if isinstance(collection, dict): + collection = collection.values() + + return sorted(collection, key=pyd.iteratee(iteratee), reverse=reverse) + + +# +# Utility methods not a part of the main API +# + + +def itermap( + collection: t.Iterable[t.Any], + iteratee: t.Union[t.Callable[..., t.Any], IterateeObjT, None] = None, +) -> t.Generator[t.Any, None, None]: + """Generative mapper.""" + for result in iteriteratee(collection, iteratee): + yield result[0] diff --git a/.venv/lib/python3.12/site-packages/pydash/exceptions.py b/.venv/lib/python3.12/site-packages/pydash/exceptions.py new file mode 100644 index 00000000..e4d11ec9 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydash/exceptions.py @@ -0,0 +1,22 @@ +""" +Exception classes. + +.. versionadded:: 1.0.0 +""" + +from __future__ import annotations + + +__all__ = ("InvalidMethod",) + + +# NOTE: This needs to subclass AttributeError due to compatibility with typing.Protocol and +# runtime_checkable. See https://github.com/dgilland/pydash/issues/165 +class InvalidMethod(AttributeError): + """ + Raised when an invalid pydash method is invoked through :func:`pydash.chaining.chain`. + + .. versionadded:: 1.0.0 + """ + + pass diff --git a/.venv/lib/python3.12/site-packages/pydash/functions.py b/.venv/lib/python3.12/site-packages/pydash/functions.py new file mode 100644 index 00000000..30a8a607 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydash/functions.py @@ -0,0 +1,1461 @@ +""" +Functions that wrap other functions. + +.. versionadded:: 1.0.0 +""" + +from __future__ import annotations + +from functools import cached_property +from inspect import getfullargspec +import itertools +import time +import typing as t + +from typing_extensions import Concatenate, Literal, ParamSpec, Protocol + +import pydash as pyd +from pydash.helpers import getargcount + + +__all__ = ( + "after", + "ary", + "before", + "conjoin", + "curry", + "curry_right", + "debounce", + "delay", + "disjoin", + "flip", + "flow", + "flow_right", + "iterated", + "juxtapose", + "negate", + "once", + "over_args", + "partial", + "partial_right", + "rearg", + "spread", + "throttle", + "unary", + "wrap", +) + +T = t.TypeVar("T") +T1 = t.TypeVar("T1") +T2 = t.TypeVar("T2") +T3 = t.TypeVar("T3") +T4 = t.TypeVar("T4") +T5 = t.TypeVar("T5") +P = ParamSpec("P") + + +class _WithArgCount(Protocol): + func: t.Callable[..., t.Any] + + @cached_property + def _argcount(self) -> t.Optional[int]: + return getargcount(self.func, None) + + +class After(_WithArgCount, t.Generic[P, T]): + """Wrap a function in an after context.""" + + def __init__(self, func: t.Callable[P, T], n: t.SupportsInt) -> None: + try: + n = int(n) + assert n >= 0 + except (ValueError, TypeError, AssertionError): + n = 0 + + self.n = n + self.func = func + + def __call__(self, *args: P.args, **kwargs: P.kwargs) -> t.Union[T, None]: + """Return results of :attr:`func` after :attr:`n` calls.""" + self.n -= 1 + + if self.n <= 0: + return self.func(*args, **kwargs) + + return None + + +class Ary(_WithArgCount, t.Generic[T]): + """Wrap a function in an ary context.""" + + def __init__(self, func: t.Callable[..., T], n: t.Union[t.SupportsInt, None]) -> None: + try: + # Type error would be caught + n = int(n) # type: ignore + assert n >= 0 + except (ValueError, TypeError, AssertionError): + n = None + + self.n = n + self.func = func + + def __call__(self, *args: t.Any, **kwargs: t.Any) -> T: + """ + Return results of :attr:`func` with arguments capped to :attr:`n`. + + Only positional arguments are capped. Any number of keyword arguments are allowed. + """ + cut_args = args[: self.n] if self.n is not None else args + + return self.func(*cut_args, **kwargs) # type: ignore + + +class Before(After[P, T], t.Generic[P, T]): + """Wrap a function in a before context.""" + + def __call__(self, *args: P.args, **kwargs: P.kwargs) -> t.Union[T, None]: + self.n -= 1 + + if self.n > 0: + return self.func(*args, **kwargs) + + return None + + +class Flow(t.Generic[P, T]): + """Wrap a function in a flow context.""" + + @t.overload + def __init__( + self, + func1: t.Callable[P, T2], + func2: t.Callable[[T2], T3], + func3: t.Callable[[T3], T4], + func4: t.Callable[[T4], T5], + func5: t.Callable[[T5], T], + *, + from_right: bool = True, + ) -> None: ... + + @t.overload + def __init__( + self, + func1: t.Callable[P, T2], + func2: t.Callable[[T2], T3], + func3: t.Callable[[T3], T4], + func4: t.Callable[[T4], T], + *, + from_right: bool = True, + ) -> None: ... + + @t.overload + def __init__( + self, + func1: t.Callable[P, T2], + func2: t.Callable[[T2], T3], + func3: t.Callable[[T3], T], + *, + from_right: bool = True, + ) -> None: ... + + @t.overload + def __init__( + self, func1: t.Callable[P, T2], func2: t.Callable[[T2], T], *, from_right: bool = True + ) -> None: ... + + @t.overload + def __init__(self, func1: t.Callable[P, T], *, from_right: bool = True) -> None: ... + + def __init__(self, *funcs, from_right: bool = True) -> None: # type: ignore + self.funcs = funcs + self._from_index = -1 if from_right else 0 + + def __call__(self, *args: P.args, **kwargs: P.kwargs) -> T: + """Return results of composing :attr:`funcs`.""" + funcs = list(self.funcs) + + result = None + + while funcs: + result = funcs.pop(self._from_index)(*args, **kwargs) + # Incompatible type in assignements but needed here + # type safety is ensured from the `__init__` signature + args = (result,) # type: ignore + kwargs = {} # type: ignore + + # type safety is ensured from the `__init__` signature + return result # type: ignore + + @cached_property + def _argcount(self) -> t.Optional[int]: + return getargcount(self.funcs[self._from_index], None) + + +class Conjoin(t.Generic[T]): + """Wrap a set of functions in a conjoin context.""" + + def __init__(self, *funcs: t.Callable[[T], t.Any]) -> None: + self.funcs = funcs + + def __call__(self, obj: t.Iterable[T]) -> bool: + """Return result of conjoin `obj` with :attr:`funcs` predicates.""" + + def iteratee(item: T) -> bool: + return pyd.every(self.funcs, lambda func: func(item)) + + return pyd.every(obj, iteratee) + + +class Curry(t.Generic[T1, T]): + """Wrap a function in a curry context.""" + + def __init__(self, func, arity, args=None, kwargs=None) -> None: + self.func = func + self.arity = len(getfullargspec(func).args) if arity is None else arity + self.args = () if args is None else args + self.kwargs = {} if kwargs is None else kwargs + + def __call__(self, *args, **kwargs): + """Store `args` and `kwargs` and call :attr:`func` if we've reached or exceeded the function + arity.""" + args = self.compose_args(args) + kwargs.update(self.kwargs) + + if (len(args) + len(kwargs)) >= self.arity: + args_arity = self.arity - len(kwargs) + args = args[: (args_arity if args_arity > 0 else 0)] + curried = self.func(*args, **kwargs) + else: + # NOTE: Use self.__class__ so that subclasses will use their own + # class to generate next iteration of call. + curried = self.__class__(self.func, self.arity, args, kwargs) + + return curried + + def compose_args(self, new_args): + """Combine `self.args` with `new_args` and return.""" + return tuple(list(self.args) + list(new_args)) + + @cached_property + def _argcount(self) -> t.Optional[int]: + argcount = self.arity - len(self.args) - len(self.kwargs) + return argcount if argcount >= 0 else None + + +class CurryOne(Curry[T1, T]): + def __call__(self, arg_one: T1) -> T: + return super().__call__(arg_one) # pragma: no cover + + +class CurryTwo(Curry[T1, CurryOne[T2, T]]): + @t.overload + def __call__(self, arg_one: T1) -> CurryOne[T2, T]: ... + + @t.overload + def __call__(self, arg_one: T1, arg_two: T2) -> T: ... + + def __call__(self, *args, **kwargs): + return super().__call__(*args, **kwargs) # pragma: no cover + + +class CurryThree(Curry[T1, CurryTwo[T2, T3, T]]): + @t.overload + def __call__(self, arg_one: T1) -> CurryTwo[T2, T3, T]: ... + + @t.overload + def __call__(self, arg_one: T1, arg_two: T2) -> CurryOne[T3, T]: ... + + @t.overload + def __call__(self, arg_one: T1, arg_two: T2, arg_three: T3) -> T: ... + + def __call__(self, *args, **kwargs): + return super().__call__(*args, **kwargs) # pragma: no cover + + +class CurryFour(Curry[T1, CurryThree[T2, T3, T4, T]]): + @t.overload + def __call__(self, arg_one: T1) -> CurryThree[T2, T3, T4, T]: ... + + @t.overload + def __call__(self, arg_one: T1, arg_two: T2) -> CurryTwo[T3, T4, T]: ... + + @t.overload + def __call__(self, arg_one: T1, arg_two: T2, arg_three: T3) -> CurryOne[T4, T]: ... + + @t.overload + def __call__(self, arg_one: T1, arg_two: T2, arg_three: T3, arg_four: T4) -> T: ... + + def __call__(self, *args, **kwargs): + return super().__call__(*args, **kwargs) # pragma: no cover + + +class CurryFive(Curry[T1, CurryFour[T2, T3, T4, T5, T]]): + @t.overload + def __call__(self, arg_one: T1) -> CurryFour[T2, T3, T4, T5, T]: ... + + @t.overload + def __call__(self, arg_one: T1, arg_two: T2) -> CurryThree[T3, T4, T5, T]: ... + + @t.overload + def __call__(self, arg_one: T1, arg_two: T2, arg_three: T3) -> CurryTwo[T4, T5, T]: ... + + @t.overload + def __call__( + self, arg_one: T1, arg_two: T2, arg_three: T3, arg_four: T4 + ) -> CurryOne[T5, T]: ... + + @t.overload + def __call__( + self, arg_one: T1, arg_two: T2, arg_three: T3, arg_four: T4, arg_five: T5 + ) -> T: ... + + def __call__(self, *args, **kwargs): + return super().__call__(*args, **kwargs) # pragma: no cover + + +class CurryRight(Curry[T5, T]): + """Wrap a function in a curry-right context.""" + + def compose_args(self, new_args): + return tuple(list(new_args) + list(self.args)) + + +class CurryRightOne(CurryRight[T5, T]): + def __call__(self, arg_one: T5) -> T: + return super().__call__(arg_one) # pragma: no cover + + +class CurryRightTwo(CurryRight[T5, CurryRightOne[T4, T]]): + @t.overload + def __call__(self, arg_one: T5) -> CurryRightOne[T4, T]: ... + + @t.overload + def __call__(self, arg_one: T5, arg_two: T4) -> T: ... + + def __call__(self, *args, **kwargs): + return super().__call__(*args, **kwargs) # pragma: no cover + + +class CurryRightThree(CurryRight[T5, CurryRightTwo[T4, T3, T]]): + @t.overload + def __call__(self, arg_one: T5) -> CurryRightTwo[T4, T3, T]: ... + + @t.overload + def __call__(self, arg_one: T5, arg_two: T4) -> CurryRightOne[T3, T]: ... + + @t.overload + def __call__(self, arg_one: T5, arg_two: T4, arg_three: T3) -> T: ... + + def __call__(self, *args, **kwargs): + return super().__call__(*args, **kwargs) # pragma: no cover + + +class CurryRightFour(CurryRight[T5, CurryRightThree[T4, T3, T2, T]]): + @t.overload + def __call__(self, arg_one: T5) -> CurryRightThree[T4, T3, T2, T]: ... + + @t.overload + def __call__(self, arg_one: T5, arg_two: T4) -> CurryRightTwo[T3, T2, T]: ... + + @t.overload + def __call__(self, arg_one: T5, arg_two: T4, arg_three: T3) -> CurryRightOne[T2, T]: ... + + @t.overload + def __call__(self, arg_one: T5, arg_two: T4, arg_three: T3, arg_four: T2) -> T: ... + + def __call__(self, *args, **kwargs): + return super().__call__(*args, **kwargs) # pragma: no cover + + +class CurryRightFive(CurryRight[T5, CurryRightFour[T4, T3, T2, T1, T]]): + @t.overload + def __call__(self, arg_one: T5) -> CurryRightFour[T4, T3, T2, T1, T]: ... + + @t.overload + def __call__(self, arg_one: T5, arg_two: T4) -> CurryRightThree[T3, T2, T1, T]: ... + + @t.overload + def __call__(self, arg_one: T5, arg_two: T4, arg_three: T3) -> CurryRightTwo[T2, T1, T]: ... + + @t.overload + def __call__( + self, arg_one: T5, arg_two: T4, arg_three: T3, arg_four: T2 + ) -> CurryRightOne[T1, T]: ... + + @t.overload + def __call__( + self, arg_one: T5, arg_two: T4, arg_three: T3, arg_four: T2, arg_five: T1 + ) -> T: ... + + def __call__(self, *args, **kwargs): + return super().__call__(*args, **kwargs) # pragma: no cover + + +class Debounce(_WithArgCount, t.Generic[P, T]): + """Wrap a function in a debounce context.""" + + def __init__( + self, func: t.Callable[P, T], wait: int, max_wait: t.Union[int, Literal[False]] = False + ) -> None: + self.func = func + self.wait = wait + self.max_wait = max_wait + + self.last_result: t.Union[T, None] = None + + # Initialize last_* times to be prior to the wait periods so that func + # is primed to be executed on first call. + self.last_call = pyd.now() - self.wait + self.last_execution = pyd.now() - max_wait if pyd.is_number(max_wait) else None + + def __call__(self, *args: P.args, **kwargs: P.kwargs) -> T: + """ + Execute :attr:`func` if function hasn't been called within last :attr:`wait` milliseconds or + in last :attr:`max_wait` milliseconds. + + Return results of last successful call. + """ + present = pyd.now() + + if (present - self.last_call) >= self.wait or ( + self.max_wait and (present - self.last_execution) >= self.max_wait # type: ignore + ): + self.last_result = self.func(*args, **kwargs) + self.last_execution = present + + self.last_call = present + + # It will be set after first call, cannot be `None` anymore + return self.last_result # type: ignore + + +class Disjoin(t.Generic[T]): + """Wrap a set of functions in a disjoin context.""" + + def __init__(self, *funcs: t.Callable[[T], t.Any]) -> None: + self.funcs = funcs + + def __call__(self, obj: t.Iterable[T]) -> bool: + """Return result of disjoin `obj` with :attr:`funcs` predicates.""" + + def iteratee(item: T) -> bool: + return pyd.some(self.funcs, lambda func: func(item)) + + return pyd.some(obj, iteratee) + + +class Flip(_WithArgCount): + """Wrap a function in a flip context.""" + + def __init__(self, func: t.Callable[..., t.Any]) -> None: + self.func = func + + def __call__(self, *args, **kwargs): + return self.func(*reversed(args), **kwargs) + + +class Iterated(t.Generic[T]): + """Wrap a function in an iterated context.""" + + def __init__(self, func: t.Callable[[T], T]) -> None: + self.func = func + + def _iteration(self, initial: T) -> t.Iterator[T]: + """Iterator that composing :attr:`func` with itself.""" + value = initial + while True: + value = self.func(value) + yield value + + def __call__(self, initial: T, n: int) -> T: + """Return value of calling :attr:`func` `n` times using `initial` as seed value.""" + value = initial + iteration = self._iteration(value) + + for _ in range(n): + value = next(iteration) + + return value + + +class Juxtapose(t.Generic[P, T]): + """Wrap a function in a juxtapose context.""" + + def __init__(self, *funcs: t.Callable[P, T]) -> None: + self.funcs = funcs + + def __call__(self, *objs: P.args, **kwargs: P.kwargs) -> t.List[T]: + return [func(*objs, **kwargs) for func in self.funcs] + + @cached_property + def _argcount(self) -> t.Optional[int]: + return getargcount(self.funcs[0], None) if self.funcs else None + + +class OverArgs(_WithArgCount): + """Wrap a function in an over_args context.""" + + def __init__(self, func: t.Callable[..., t.Any], *transforms: t.Callable[..., t.Any]) -> None: + self.func = func + self.transforms = pyd.flatten(transforms) + + def __call__(self, *args): + args = (self.transforms[idx](args) for idx, args in enumerate(args)) + return self.func(*args) + + +class Negate(_WithArgCount, t.Generic[P]): + """Wrap a function in a negate context.""" + + def __init__(self, func: t.Callable[P, t.Any]) -> None: + self.func = func + + def __call__(self, *args: P.args, **kwargs: P.kwargs) -> bool: + """Return negated results of calling :attr:`func`.""" + return not self.func(*args, **kwargs) + + +class Once(_WithArgCount, t.Generic[P, T]): + """Wrap a function in a once context.""" + + def __init__(self, func: t.Callable[P, T]) -> None: + self.func = func + self.result: t.Union[T, None] = None + self.called = False + + def __call__(self, *args: P.args, **kwargs: P.kwargs) -> T: + """Return results from the first call of :attr:`func`.""" + if not self.called: + self.result = self.func(*args, **kwargs) + self.called = True + + # At this point the result will be set, cannot be `None` anymore + return self.result # type: ignore + + +class Partial(_WithArgCount, t.Generic[T]): + """Wrap a function in a partial context.""" + + def __init__( + self, func: t.Callable[..., T], args: t.Any, kwargs: t.Any = None, from_right: bool = False + ) -> None: + self.func = func + self.args = args + self.kwargs = kwargs or {} + self.from_right = from_right + + def __call__(self, *args: t.Any, **kwargs: t.Any) -> T: + """ + Return results from :attr:`func` with :attr:`args` + `args`. + + Apply arguments from left or right depending on :attr:`from_right`. + """ + if self.from_right: + args = itertools.chain(args, self.args) # type: ignore + else: + args = itertools.chain(self.args, args) # type: ignore + + kwargs = {**self.kwargs, **kwargs} + + return self.func(*args, **kwargs) + + @cached_property + def _argcount(self) -> t.Optional[int]: + func_argcount = getargcount(self.func, None) + if func_argcount is None: + return None + argcount = func_argcount - len(self.args) - len(self.kwargs) + return argcount if argcount >= 0 else None + + +class Rearg(_WithArgCount, t.Generic[P, T]): + """Wrap a function in a rearg context.""" + + def __init__(self, func: t.Callable[P, T], *indexes: int) -> None: + self.func = func + + # Index `indexes` by the index value, so we can do a lookup mapping by walking the function + # arguments. + self.indexes = { + src_index: dest_index for dest_index, src_index in enumerate(pyd.flatten(indexes)) + } + + def __call__(self, *args: P.args, **kwargs: P.kwargs) -> T: + """Return results from :attr:`func` using rearranged arguments.""" + reargs = {} + rest = [] + + # Walk arguments to ensure each one is added to the final argument list. + for src_index, arg in enumerate(args): + # NOTE: dest_index will range from 0 to len(indexes). + dest_index = self.indexes.get(src_index) + + if dest_index is not None: + # Remap argument index. + reargs[dest_index] = arg + else: + # Argumnet index is not contained in `indexes` so stick in the back. + rest.append(arg) + + args = itertools.chain((reargs[key] for key in sorted(reargs)), rest) # type: ignore + + return self.func(*args, **kwargs) + + +class Spread(t.Generic[T]): + """Wrap a function in a spread context.""" + + def __init__(self, func: t.Callable[..., T]) -> None: + self.func = func + + def __call__(self, args: t.Iterable[t.Any]) -> T: + """Return results from :attr:`func` using array of `args` provided.""" + return self.func(*args) + + +class Throttle(_WithArgCount, t.Generic[P, T]): + """Wrap a function in a throttle context.""" + + def __init__(self, func: t.Callable[P, T], wait: int) -> None: + self.func = func + self.wait = wait + + self.last_result: t.Union[T, None] = None + self.last_execution = pyd.now() - self.wait + + def __call__(self, *args: P.args, **kwargs: P.kwargs) -> T: + """ + Execute :attr:`func` if function hasn't been called within last :attr:`wait` milliseconds. + + Return results of last successful call. + """ + present = pyd.now() + + if (present - self.last_execution) >= self.wait: + self.last_result = self.func(*args, **kwargs) + self.last_execution = present + + # The last result will be filled on first execution, so it is always `T` + return self.last_result # type: ignore + + +def after(func: t.Callable[P, T], n: t.SupportsInt) -> After[P, T]: + """ + Creates a function that executes `func`, with the arguments of the created function, only after + being called `n` times. + + Args: + func: Function to execute. + n: Number of times `func` must be called before it is executed. + + Returns: + Function wrapped in an :class:`After` context. + + Example: + + >>> func = lambda a, b, c: (a, b, c) + >>> after_func = after(func, 3) + >>> after_func(1, 2, 3) + >>> after_func(1, 2, 3) + >>> after_func(1, 2, 3) + (1, 2, 3) + >>> after_func(4, 5, 6) + (4, 5, 6) + + .. versionadded:: 1.0.0 + + .. versionchanged:: 3.0.0 + Reordered arguments to make `func` first. + """ + return After(func, n) + + +def ary(func: t.Callable[..., T], n: t.Union[t.SupportsInt, None]) -> Ary[T]: + """ + Creates a function that accepts up to `n` arguments ignoring any additional arguments. Only + positional arguments are capped. All keyword arguments are allowed through. + + Args: + func: Function to cap arguments for. + n: Number of arguments to accept. + + Returns: + Function wrapped in an :class:`Ary` context. + + Example: + + >>> func = lambda a, b, c=0, d=5: (a, b, c, d) + >>> ary_func = ary(func, 2) + >>> ary_func(1, 2, 3, 4, 5, 6) + (1, 2, 0, 5) + >>> ary_func(1, 2, 3, 4, 5, 6, c=10, d=20) + (1, 2, 10, 20) + + .. versionadded:: 3.0.0 + """ + return Ary(func, n) + + +def before(func: t.Callable[P, T], n: t.SupportsInt) -> Before[P, T]: + """ + Creates a function that executes `func`, with the arguments of the created function, until it + has been called `n` times. + + Args: + func: Function to execute. + n: Number of times `func` may be executed. + + Returns: + Function wrapped in an :class:`Before` context. + + Example: + + >>> func = lambda a, b, c: (a, b, c) + >>> before_func = before(func, 3) + >>> before_func(1, 2, 3) + (1, 2, 3) + >>> before_func(1, 2, 3) + (1, 2, 3) + >>> before_func(1, 2, 3) + >>> before_func(1, 2, 3) + + .. versionadded:: 1.1.0 + + .. versionchanged:: 3.0.0 + Reordered arguments to make `func` first. + """ + return Before(func, n) + + +def conjoin(*funcs: t.Callable[[T], t.Any]) -> t.Callable[[t.Iterable[T]], bool]: + """ + Creates a function that composes multiple predicate functions into a single predicate that tests + whether **all** elements of an object pass each predicate. + + Args: + *funcs: Function(s) to conjoin. + + Returns: + Function(s) wrapped in a :class:`Conjoin` context. + + Example: + + >>> conjoiner = conjoin(lambda x: isinstance(x, int), lambda x: x > 3) + >>> conjoiner([1, 2, 3]) + False + >>> conjoiner([1.0, 2, 1]) + False + >>> conjoiner([4.0, 5, 6]) + False + >>> conjoiner([4, 5, 6]) + True + + .. versionadded:: 2.0.0 + """ + return Conjoin(*funcs) + + +@t.overload +def curry(func: t.Callable[[T1], T], arity: t.Union[int, None] = None) -> CurryOne[T1, T]: ... + + +@t.overload +def curry( + func: t.Callable[[T1, T2], T], arity: t.Union[int, None] = None +) -> CurryTwo[T1, T2, T]: ... + + +@t.overload +def curry( + func: t.Callable[[T1, T2, T3], T], arity: t.Union[int, None] = None +) -> CurryThree[T1, T2, T3, T]: ... + + +@t.overload +def curry( + func: t.Callable[[T1, T2, T3, T4], T], arity: t.Union[int, None] = None +) -> CurryFour[T1, T2, T3, T4, T]: ... + + +@t.overload +def curry( + func: t.Callable[[T1, T2, T3, T4, T5], T], arity: t.Union[int, None] = None +) -> CurryFive[T1, T2, T3, T4, T5, T]: ... + + +def curry(func, arity=None): + """ + Creates a function that accepts one or more arguments of `func` that when invoked either + executes `func` returning its result (if all `func` arguments have been provided) or returns a + function that accepts one or more of the remaining `func` arguments, and so on. + + Args: + func: Function to curry. + arity: Number of function arguments that can be accepted by curried + function. Default is to use the number of arguments that are accepted by `func`. + + Returns: + Function wrapped in a :class:`Curry` context. + + Example: + + >>> func = lambda a, b, c: (a, b, c) + >>> currier = curry(func) + >>> currier = currier(1) + >>> assert isinstance(currier, Curry) + >>> currier = currier(2) + >>> assert isinstance(currier, Curry) + >>> currier = currier(3) + >>> currier + (1, 2, 3) + + .. versionadded:: 1.0.0 + """ + return Curry(func, arity) + + +@t.overload +def curry_right( + func: t.Callable[[T1], T], arity: t.Union[int, None] = None +) -> CurryRightOne[T1, T]: ... + + +@t.overload +def curry_right( + func: t.Callable[[T1, T2], T], arity: t.Union[int, None] = None +) -> CurryRightTwo[T2, T1, T]: ... + + +@t.overload +def curry_right( + func: t.Callable[[T1, T2, T3], T], arity: t.Union[int, None] = None +) -> CurryRightThree[T3, T2, T1, T]: ... + + +@t.overload +def curry_right( + func: t.Callable[[T1, T2, T3, T4], T], arity: t.Union[int, None] = None +) -> CurryRightFour[T4, T3, T2, T1, T]: ... + + +@t.overload +def curry_right( + func: t.Callable[[T1, T2, T3, T4, T5], T], +) -> CurryRightFive[T5, T4, T3, T2, T1, T]: ... + + +def curry_right(func, arity=None): + """ + This method is like :func:`curry` except that arguments are applied to `func` in the manner of + :func:`partial_right` instead of :func:`partial`. + + Args: + func: Function to curry. + arity: Number of function arguments that can be accepted by curried + function. Default is to use the number of arguments that are accepted by `func`. + + Returns: + Function wrapped in a :class:`CurryRight` context. + + Example: + + >>> func = lambda a, b, c: (a, b, c) + >>> currier = curry_right(func) + >>> currier = currier(1) + >>> assert isinstance(currier, CurryRight) + >>> currier = currier(2) + >>> assert isinstance(currier, CurryRight) + >>> currier = currier(3) + >>> currier + (3, 2, 1) + + .. versionadded:: 1.1.0 + """ + return CurryRight(func, arity) + + +def debounce( + func: t.Callable[P, T], wait: int, max_wait: t.Union[int, Literal[False]] = False +) -> Debounce[P, T]: + """ + Creates a function that will delay the execution of `func` until after `wait` milliseconds have + elapsed since the last time it was invoked. Subsequent calls to the debounced function will + return the result of the last `func` call. + + Args: + func: Function to execute. + wait: Milliseconds to wait before executing `func`. + max_wait (optional): Maximum time to wait before executing `func`. + + Returns: + Function wrapped in a :class:`Debounce` context. + + .. versionadded:: 1.0.0 + """ + return Debounce(func, wait, max_wait=max_wait) + + +def delay(func: t.Callable[P, T], wait: int, *args: "P.args", **kwargs: "P.kwargs") -> T: + """ + Executes the `func` function after `wait` milliseconds. Additional arguments will be provided to + `func` when it is invoked. + + Args: + func: Function to execute. + wait: Milliseconds to wait before executing `func`. + *args: Arguments to pass to `func`. + **kwargs: Keyword arguments to pass to `func`. + + Returns: + Return from `func`. + + .. versionadded:: 1.0.0 + """ + time.sleep(wait / 1000.0) + return func(*args, **kwargs) + + +def disjoin(*funcs: t.Callable[[T], t.Any]) -> Disjoin[T]: + """ + Creates a function that composes multiple predicate functions into a single predicate that tests + whether **any** elements of an object pass each predicate. + + Args: + *funcs: Function(s) to disjoin. + + Returns: + Function(s) wrapped in a :class:`Disjoin` context. + + Example: + + >>> disjoiner = disjoin(lambda x: isinstance(x, float),\ + lambda x: isinstance(x, int)) + >>> disjoiner([1, '2', '3']) + True + >>> disjoiner([1.0, '2', '3']) + True + >>> disjoiner(['1', '2', '3']) + False + + .. versionadded:: 2.0.0 + """ + return Disjoin(*funcs) + + +@t.overload +def flip(func: t.Callable[[T1, T2, T3, T4, T5], T]) -> t.Callable[[T5, T4, T3, T2, T1], T]: ... + + +@t.overload +def flip(func: t.Callable[[T1, T2, T3, T4], T]) -> t.Callable[[T4, T3, T2, T1], T]: ... + + +@t.overload +def flip(func: t.Callable[[T1, T2, T3], T]) -> t.Callable[[T3, T2, T1], T]: ... + + +@t.overload +def flip(func: t.Callable[[T1, T2], T]) -> t.Callable[[T2, T1], T]: ... + + +@t.overload +def flip(func: t.Callable[[T1], T]) -> t.Callable[[T1], T]: ... + + +def flip(func: t.Callable[..., t.Any]) -> t.Callable[..., t.Any]: + """ + Creates a function that invokes the method with arguments reversed. + + Args: + func: Function to flip arguments for. + + Returns: + Function wrapped in a :class:`Flip` context. + + Example: + + >>> flipped = flip(lambda *args: args) + >>> flipped(1, 2, 3, 4) + (4, 3, 2, 1) + >>> flipped = flip(lambda *args: [i * 2 for i in args]) + >>> flipped(1, 2, 3, 4) + [8, 6, 4, 2] + + .. versionadded:: 4.0.0 + """ + return Flip(func) + + +@t.overload +def flow( + func1: t.Callable[P, T2], + func2: t.Callable[[T2], T3], + func3: t.Callable[[T3], T4], + func4: t.Callable[[T4], T5], + func5: t.Callable[[T5], T], +) -> Flow[P, T]: ... + + +@t.overload +def flow( + func1: t.Callable[P, T2], + func2: t.Callable[[T2], T3], + func3: t.Callable[[T3], T4], + func4: t.Callable[[T4], T], +) -> Flow[P, T]: ... + + +@t.overload +def flow( + func1: t.Callable[P, T2], + func2: t.Callable[[T2], T3], + func3: t.Callable[[T3], T], +) -> Flow[P, T]: ... + + +@t.overload +def flow(func1: t.Callable[P, T2], func2: t.Callable[[T2], T]) -> Flow[P, T]: ... + + +@t.overload +def flow(func1: t.Callable[P, T]) -> Flow[P, T]: ... + + +def flow(*funcs): + """ + Creates a function that is the composition of the provided functions, where each successive + invocation is supplied the return value of the previous. For example, composing the functions + ``f()``, ``g()``, and ``h()`` produces ``h(g(f()))``. + + Args: + *funcs: Function(s) to compose. + + Returns: + Function(s) wrapped in a :class:`Flow` context. + + Example: + + >>> mult_5 = lambda x: x * 5 + >>> div_10 = lambda x: x / 10.0 + >>> pow_2 = lambda x: x**2 + >>> ops = flow(sum, mult_5, div_10, pow_2) + >>> ops([1, 2, 3, 4]) + 25.0 + + .. versionadded:: 2.0.0 + + .. versionchanged:: 2.3.1 + Added :func:`pipe` as alias. + + .. versionchanged:: 4.0.0 + Removed alias ``pipe``. + """ + return Flow(*funcs, from_right=False) + + +@t.overload +def flow_right( + func5: t.Callable[[T4], T], + func4: t.Callable[[T3], T4], + func3: t.Callable[[T2], T3], + func2: t.Callable[[T1], T2], + func1: t.Callable[P, T1], +) -> Flow[P, T]: ... + + +@t.overload +def flow_right( + func4: t.Callable[[T3], T], + func3: t.Callable[[T2], T3], + func2: t.Callable[[T1], T2], + func1: t.Callable[P, T1], +) -> Flow[P, T]: ... + + +@t.overload +def flow_right( + func3: t.Callable[[T2], T], + func2: t.Callable[[T1], T2], + func1: t.Callable[P, T1], +) -> Flow[P, T]: ... + + +@t.overload +def flow_right(func2: t.Callable[[T1], T], func1: t.Callable[P, T1]) -> Flow[P, T]: ... + + +@t.overload +def flow_right(func1: t.Callable[P, T]) -> Flow[P, T]: ... + + +def flow_right(*funcs): + """ + This function is like :func:`flow` except that it creates a function that invokes the provided + functions from right to left. For example, composing the functions ``f()``, ``g()``, and ``h()`` + produces ``f(g(h()))``. + + Args: + *funcs: Function(s) to compose. + + Returns: + Function(s) wrapped in a :class:`Flow` context. + + Example: + + >>> mult_5 = lambda x: x * 5 + >>> div_10 = lambda x: x / 10.0 + >>> pow_2 = lambda x: x**2 + >>> ops = flow_right(mult_5, div_10, pow_2, sum) + >>> ops([1, 2, 3, 4]) + 50.0 + + .. versionadded:: 1.0.0 + + .. versionchanged:: 2.0.0 + Added :func:`flow_right` and made :func:`compose` an alias. + + .. versionchanged:: 2.3.1 + Added :func:`pipe_right` as alias. + + .. versionchanged:: 4.0.0 + Removed aliases ``pipe_right`` and ``compose``. + """ + return Flow(*funcs, from_right=True) + + +def iterated(func: t.Callable[[T], T]) -> Iterated[T]: + """ + Creates a function that is composed with itself. Each call to the iterated function uses the + previous function call's result as input. Returned :class:`Iterated` instance can be called with + ``(initial, n)`` where `initial` is the initial value to seed `func` with and `n` is the number + of times to call `func`. + + Args: + func: Function to iterate. + + Returns: + Function wrapped in a :class:`Iterated` context. + + Example: + + >>> doubler = iterated(lambda x: x * 2) + >>> doubler(4, 5) + 128 + >>> doubler(3, 9) + 1536 + + .. versionadded:: 2.0.0 + """ + return Iterated(func) + + +def juxtapose(*funcs: t.Callable[P, T]) -> Juxtapose[P, T]: + """ + Creates a function whose return value is a list of the results of calling each `funcs` with the + supplied arguments. + + Args: + *funcs: Function(s) to juxtapose. + + Returns: + Function wrapped in a :class:`Juxtapose` context. + + Example: + + >>> double = lambda x: x * 2 + >>> triple = lambda x: x * 3 + >>> quadruple = lambda x: x * 4 + >>> juxtapose(double, triple, quadruple)(5) + [10, 15, 20] + + + .. versionadded:: 2.0.0 + """ + return Juxtapose(*funcs) + + +def negate(func: t.Callable[P, t.Any]) -> Negate[P]: + """ + Creates a function that negates the result of the predicate `func`. The `func` function is + executed with the arguments of the created function. + + Args: + func: Function to negate execute. + + Returns: + Function wrapped in a :class:`Negate` context. + + Example: + + >>> not_is_number = negate(lambda x: isinstance(x, (int, float))) + >>> not_is_number(1) + False + >>> not_is_number("1") + True + + .. versionadded:: 1.1.0 + """ + return Negate(func) + + +def once(func: t.Callable[P, T]) -> Once[P, T]: + """ + Creates a function that is restricted to execute `func` once. Repeat calls to the function will + return the value of the first call. + + Args: + func: Function to execute. + + Returns: + Function wrapped in a :class:`Once` context. + + Example: + + >>> oncer = once(lambda *args: args[0]) + >>> oncer(5) + 5 + >>> oncer(6) + 5 + + .. versionadded:: 1.0.0 + """ + return Once(func) + + +@t.overload +def over_args( + func: t.Callable[[T1, T2, T3, T4, T5], T], + transform_one: t.Callable[[T1], T1], + transform_two: t.Callable[[T2], T2], + transform_three: t.Callable[[T3], T3], + transform_four: t.Callable[[T4], T4], + transform_five: t.Callable[[T5], T5], +) -> t.Callable[[T1, T2, T3, T4, T5], T]: ... + + +@t.overload +def over_args( + func: t.Callable[[T1, T2, T3, T4], T], + transform_one: t.Callable[[T1], T1], + transform_two: t.Callable[[T2], T2], + transform_three: t.Callable[[T3], T3], + transform_four: t.Callable[[T4], T4], +) -> t.Callable[[T1, T2, T3, T4], T]: ... + + +@t.overload +def over_args( + func: t.Callable[[T1, T2, T3], T], + transform_one: t.Callable[[T1], T1], + transform_two: t.Callable[[T2], T2], + transform_three: t.Callable[[T3], T3], +) -> t.Callable[[T1, T2, T3], T]: ... + + +@t.overload +def over_args( + func: t.Callable[[T1, T2], T], + transform_one: t.Callable[[T1], T1], + transform_two: t.Callable[[T2], T2], +) -> t.Callable[[T1, T2], T]: ... + + +@t.overload +def over_args( + func: t.Callable[[T1], T], + transform_one: t.Callable[[T1], T1], +) -> t.Callable[[T1], T]: ... + + +def over_args(func, *transforms): + """ + Creates a function that runs each argument through a corresponding transform function. + + Args: + func: Function to wrap. + *transforms: Functions to transform arguments, specified as individual functions + or lists of functions. + + Returns: + Function wrapped in a :class:`OverArgs` context. + + Example: + + >>> squared = lambda x: x**2 + >>> double = lambda x: x * 2 + >>> modder = over_args(lambda x, y: [x, y], squared, double) + >>> modder(5, 10) + [25, 20] + + .. versionadded:: 3.3.0 + + .. versionchanged:: 4.0.0 + Renamed from ``mod_args`` to ``over_args``. + """ + return OverArgs(func, *transforms) + + +def partial(func: t.Callable[..., T], *args: t.Any, **kwargs: t.Any) -> Partial[T]: + """ + Creates a function that, when called, invokes `func` with any additional partial arguments + prepended to those provided to the new function. + + Args: + func: Function to execute. + *args: Partial arguments to prepend to function call. + **kwargs: Partial keyword arguments to bind to function call. + + Returns: + Function wrapped in a :class:`Partial` context. + + Example: + + >>> dropper = partial(lambda array, n: array[n:], [1, 2, 3, 4]) + >>> dropper(2) + [3, 4] + >>> dropper(1) + [2, 3, 4] + >>> myrest = partial(lambda array, n: array[n:], n=1) + >>> myrest([1, 2, 3, 4]) + [2, 3, 4] + + .. versionadded:: 1.0.0 + """ + return Partial(func, args, kwargs) + + +def partial_right(func: t.Callable[..., T], *args: t.Any, **kwargs: t.Any) -> Partial[T]: + """ + This method is like :func:`partial` except that partial arguments are appended to those provided + to the new function. + + Args: + func: Function to execute. + *args: Partial arguments to append to function call. + **kwargs: Partial keyword arguments to bind to function call. + + Returns: + Function wrapped in a :class:`Partial` context. + + Example: + + >>> myrest = partial_right(lambda array, n: array[n:], 1) + >>> myrest([1, 2, 3, 4]) + [2, 3, 4] + + .. versionadded:: 1.0.0 + """ + return Partial(func, args, kwargs, from_right=True) + + +def rearg(func: t.Callable[P, T], *indexes: int) -> Rearg[P, T]: + """ + Creates a function that invokes `func` with arguments arranged according to the specified + indexes where the argument value at the first index is provided as the first argument, the + argument value at the second index is provided as the second argument, and so on. + + Args: + func: Function to rearrange arguments for. + *indexes: The arranged argument indexes. + + Returns: + Function wrapped in a :class:`Rearg` context. + + Example: + + >>> jumble = rearg(lambda *args: args, 1, 2, 3) + >>> jumble(1, 2, 3) + (2, 3, 1) + >>> jumble("a", "b", "c", "d", "e") + ('b', 'c', 'd', 'a', 'e') + + .. versionadded:: 3.0.0 + """ + return Rearg(func, *indexes) + + +def spread(func: t.Callable[..., T]) -> Spread[T]: + """ + Creates a function that invokes `func` with the array of arguments provided to the created + function. + + Args: + func: Function to spread. + + Returns: + Function wrapped in a :class:`Spread` context. + + Example: + + >>> greet = spread(lambda *people: "Hello " + ", ".join(people) + "!") + >>> greet(["Mike", "Don", "Leo"]) + 'Hello Mike, Don, Leo!' + + .. versionadded:: 3.1.0 + """ + return Spread(func) + + +def throttle(func: t.Callable[P, T], wait: int) -> Throttle[P, T]: + """ + Creates a function that, when executed, will only call the `func` function at most once per + every `wait` milliseconds. Subsequent calls to the throttled function will return the result of + the last `func` call. + + Args: + func: Function to throttle. + wait: Milliseconds to wait before calling `func` again. + + Returns: + Results of last `func` call. + + .. versionadded:: 1.0.0 + """ + return Throttle(func, wait) + + +def unary(func: t.Callable[..., T]) -> Ary[T]: + """ + Creates a function that accepts up to one argument, ignoring any additional arguments. + + Args: + func: Function to cap arguments for. + + Returns: + Function wrapped in an :class:`Ary` context. + + Example: + + >>> func = lambda a, b=1, c=0, d=5: (a, b, c, d) + >>> unary_func = unary(func) + >>> unary_func(1, 2, 3, 4, 5, 6) + (1, 1, 0, 5) + >>> unary_func(1, 2, 3, 4, 5, 6, b=0, c=10, d=20) + (1, 0, 10, 20) + + .. versionadded:: 4.0.0 + """ + return Ary(func, 1) + + +def wrap(value: T1, func: t.Callable[Concatenate[T1, P], T]) -> Partial[T]: + """ + Creates a function that provides value to the wrapper function as its first argument. Additional + arguments provided to the function are appended to those provided to the wrapper function. + + Args: + value: Value provided as first argument to function call. + func: Function to execute. + + Returns: + Function wrapped in a :class:`Partial` context. + + Example: + + >>> wrapper = wrap("hello", lambda *args: args) + >>> wrapper(1, 2) + ('hello', 1, 2) + + .. versionadded:: 1.0.0 + """ + return Partial(func, (value,)) 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 diff --git a/.venv/lib/python3.12/site-packages/pydash/numerical.py b/.venv/lib/python3.12/site-packages/pydash/numerical.py new file mode 100644 index 00000000..711cab0c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydash/numerical.py @@ -0,0 +1,1252 @@ +""" +Numerical/mathematical related functions. + +.. versionadded:: 2.1.0 +""" + +from __future__ import annotations + +import math +import operator +import typing as t + +import pydash as pyd + +from .helpers import UNSET, Unset, iterator, iterator_with_default, iteriteratee +from .types import IterateeObjT, NumberNoDecimalT, NumberT, SupportsMul, SupportsRound + + +if t.TYPE_CHECKING: + from decimal import Decimal # pragma: no cover + + from _typeshed import SupportsAdd, SupportsRichComparisonT, SupportsSub # pragma: no cover + + +__all__ = ( + "add", + "ceil", + "clamp", + "divide", + "floor", + "max_", + "max_by", + "mean", + "mean_by", + "median", + "min_", + "min_by", + "moving_mean", + "multiply", + "power", + "round_", + "scale", + "slope", + "std_deviation", + "sum_", + "sum_by", + "subtract", + "transpose", + "variance", + "zscore", +) + +T = t.TypeVar("T") +T2 = t.TypeVar("T2") +T3 = t.TypeVar("T3") + + +INFINITY = float("inf") + + +@t.overload +def add(a: "SupportsAdd[T, T2]", b: T) -> T2: ... + + +@t.overload +def add(a: T, b: "SupportsAdd[T, T2]") -> T2: ... + + +def add(a, b): + """ + Adds two numbers. + + Args: + a: First number to add. + b: Second number to add. + + Returns: + number + + Example: + + >>> add(10, 5) + 15 + + .. versionadded:: 2.1.0 + + .. versionchanged:: 3.3.0 + Support adding two numbers when passed as positional arguments. + + .. versionchanged:: 4.0.0 + Only support two argument addition. + """ + return a + b + + +@t.overload +def sum_(collection: t.Mapping[t.Any, "SupportsAdd[int, T]"]) -> T: ... + + +@t.overload +def sum_(collection: t.Iterable["SupportsAdd[int, T]"]) -> T: ... + + +def sum_(collection): + """ + Sum each element in `collection`. + + Args: + collection: Collection to process or first number to add. + + Returns: + Result of summation. + + Example: + + >>> sum_([1, 2, 3, 4]) + 10 + + .. versionadded:: 2.1.0 + + .. versionchanged:: 3.3.0 + Support adding two numbers when passed as positional arguments. + + .. versionchanged:: 4.0.0 + Move iteratee support to :func:`sum_by`. Move two argument addition to + :func:`add`. + """ + return sum_by(collection) + + +@t.overload +def sum_by( + collection: t.Mapping[T, T2], + iteratee: t.Callable[[T2, T, t.Dict[T, T2]], "SupportsAdd[int, T3]"], +) -> T3: ... + + +@t.overload +def sum_by( + collection: t.Mapping[T, T2], iteratee: t.Callable[[T2, T], "SupportsAdd[int, T3]"] +) -> T3: ... + + +@t.overload +def sum_by( + collection: t.Mapping[t.Any, T2], iteratee: t.Callable[[T2], "SupportsAdd[int, T3]"] +) -> T3: ... + + +@t.overload +def sum_by( + collection: t.Iterable[T], iteratee: t.Callable[[T, int, t.List[T]], "SupportsAdd[int, T2]"] +) -> T2: ... + + +@t.overload +def sum_by( + collection: t.Iterable[T], iteratee: t.Callable[[T, int], "SupportsAdd[int, T2]"] +) -> T2: ... + + +@t.overload +def sum_by(collection: t.Iterable[T], iteratee: t.Callable[[T], "SupportsAdd[int, T2]"]) -> T2: ... + + +@t.overload +def sum_by(collection: t.Mapping[t.Any, "SupportsAdd[int, T]"], iteratee: None = None) -> T: ... + + +@t.overload +def sum_by(collection: t.Iterable["SupportsAdd[int, T]"], iteratee: None = None) -> T: ... + + +def sum_by(collection, iteratee=None): + """ + Sum each element in `collection`. If iteratee is passed, each element of `collection` is passed + through an iteratee before the summation is computed. + + Args: + collection: Collection to process or first number to add. + iteratee: Iteratee applied per iteration or second number to add. + + Returns: + Result of summation. + + Example: + + >>> sum_by([1, 2, 3, 4], lambda x: x**2) + 30 + + .. versionadded:: 4.0.0 + """ + return sum(result[0] for result in iteriteratee(collection, iteratee)) + + +@t.overload +def mean(collection: t.Mapping[t.Any, "SupportsAdd[int, t.Any]"]) -> float: ... + + +@t.overload +def mean(collection: t.Iterable["SupportsAdd[int, t.Any]"]) -> float: ... + + +def mean(collection): + """ + Calculate arithmetic mean of each element in `collection`. + + Args: + collection: Collection to process. + + Returns: + Result of mean. + + Example: + + >>> mean([1, 2, 3, 4]) + 2.5 + + .. versionadded:: 2.1.0 + + .. versionchanged:: 4.0.0 + + - Removed ``average`` and ``avg`` aliases. + - Moved iteratee functionality to :func:`mean_by`. + """ + return mean_by(collection) + + +@t.overload +def mean_by( + collection: t.Mapping[T, T2], + iteratee: t.Callable[[T2, T, t.Dict[T, T2]], "SupportsAdd[int, t.Any]"], +) -> float: ... + + +@t.overload +def mean_by( + collection: t.Mapping[T, T2], iteratee: t.Callable[[T2, T], "SupportsAdd[int, t.Any]"] +) -> float: ... + + +@t.overload +def mean_by( + collection: t.Mapping[t.Any, T2], iteratee: t.Callable[[T2], "SupportsAdd[int, t.Any]"] +) -> float: ... + + +@t.overload +def mean_by( + collection: t.Iterable[T], iteratee: t.Callable[[T, int, t.List[T]], "SupportsAdd[int, t.Any]"] +) -> float: ... + + +@t.overload +def mean_by( + collection: t.Iterable[T], iteratee: t.Callable[[T, int], "SupportsAdd[int, t.Any]"] +) -> float: ... + + +@t.overload +def mean_by( + collection: t.Iterable[T], iteratee: t.Callable[[T], "SupportsAdd[int, t.Any]"] +) -> float: ... + + +@t.overload +def mean_by( + collection: t.Mapping[t.Any, "SupportsAdd[int, t.Any]"], iteratee: None = None +) -> float: ... + + +@t.overload +def mean_by(collection: t.Iterable["SupportsAdd[int, t.Any]"], iteratee: None = None) -> float: ... + + +def mean_by(collection, iteratee=None): + """ + Calculate arithmetic mean of each element in `collection`. If iteratee is passed, each element + of `collection` is passed through an iteratee before the mean is computed. + + Args: + collection: Collection to process. + iteratee: Iteratee applied per iteration. + + Returns: + Result of mean. + + Example: + + >>> mean_by([1, 2, 3, 4], lambda x: x**2) + 7.5 + + .. versionadded:: 4.0.0 + """ + return sum_by(collection, iteratee) / len(collection) + + +def ceil(x: NumberT, precision: int = 0) -> float: + """ + Round number up to precision. + + Args: + x: Number to round up. + precision: Rounding precision. Defaults to ``0``. + + Returns: + Number rounded up. + + Example: + + >>> ceil(3.275) == 4.0 + True + >>> ceil(3.215, 1) == 3.3 + True + >>> ceil(6.004, 2) == 6.01 + True + + .. versionadded:: 3.3.0 + """ + return rounder(math.ceil, x, precision) + + +NumT = t.TypeVar("NumT", int, float, "Decimal") +NumT2 = t.TypeVar("NumT2", int, float, "Decimal") +NumT3 = t.TypeVar("NumT3", int, float, "Decimal") + + +def clamp(x: NumT, lower: NumT2, upper: t.Union[NumT3, None] = None) -> t.Union[NumT, NumT2, NumT3]: + """ + Clamps number within the inclusive lower and upper bounds. + + Args: + x: Number to clamp. + lower: Lower bound. + upper: Upper bound + + Returns: + number + + Example: + + >>> clamp(-10, -5, 5) + -5 + >>> clamp(10, -5, 5) + 5 + >>> clamp(10, 5) + 5 + >>> clamp(-10, 5) + -10 + + .. versionadded:: 4.0.0 + """ + if upper is None: + upper = lower # type: ignore + lower = x # type: ignore + + if x < lower: + x = lower # type: ignore + elif x > upper: # type: ignore + x = upper # type: ignore + + return x + + +def divide(dividend: t.Union[NumberT, None], divisor: t.Union[NumberT, None]) -> float: + """ + Divide two numbers. + + Args: + dividend: The first number in a division. + divisor: The second number in a division. + + Returns: + Returns the quotient. + + Example: + + >>> divide(20, 5) + 4.0 + >>> divide(1.5, 3) + 0.5 + >>> divide(None, None) + 1.0 + >>> divide(5, None) + 5.0 + + .. versionadded:: 4.0.0 + """ + return call_math_operator(dividend, divisor, operator.truediv, 1) + + +def floor(x: NumberT, precision: int = 0) -> float: + """ + Round number down to precision. + + Args: + x: Number to round down. + precision: Rounding precision. Defaults to ``0``. + + Returns: + Number rounded down. + + Example: + + >>> floor(3.75) == 3.0 + True + >>> floor(3.215, 1) == 3.2 + True + >>> floor(0.046, 2) == 0.04 + True + + .. versionadded:: 3.3.0 + """ + return rounder(math.floor, x, precision) + + +@t.overload +def max_( + collection: t.Mapping[t.Any, "SupportsRichComparisonT"], default: Unset = UNSET +) -> "SupportsRichComparisonT": ... + + +@t.overload +def max_( + collection: t.Mapping[t.Any, "SupportsRichComparisonT"], default: T +) -> t.Union["SupportsRichComparisonT", T]: ... + + +@t.overload +def max_( + collection: t.Iterable["SupportsRichComparisonT"], default: Unset = UNSET +) -> "SupportsRichComparisonT": ... + + +@t.overload +def max_( + collection: t.Iterable["SupportsRichComparisonT"], default: T +) -> t.Union["SupportsRichComparisonT", T]: ... + + +def max_(collection, default=UNSET): + """ + Retrieves the maximum value of a `collection`. + + Args: + collection: Collection to iterate over. + default: Value to return if `collection` is empty. + + Returns: + Maximum value. + + Example: + + >>> max_([1, 2, 3, 4]) + 4 + >>> max_([], default=-1) + -1 + + .. versionadded:: 1.0.0 + + .. versionchanged:: 4.0.0 + Moved iteratee iteratee support to :func:`max_by`. + """ + return max_by(collection, default=default) + + +@t.overload +def max_by( + collection: t.Mapping[t.Any, "SupportsRichComparisonT"], + iteratee: None = None, + default: Unset = UNSET, +) -> "SupportsRichComparisonT": ... + + +@t.overload +def max_by( + collection: t.Mapping[t.Any, T2], + iteratee: t.Callable[[T2], "SupportsRichComparisonT"], + default: Unset = UNSET, +) -> T2: ... + + +@t.overload +def max_by( + collection: t.Mapping[t.Any, T2], + iteratee: t.Callable[[T2], "SupportsRichComparisonT"], + *, + default: T, +) -> t.Union[T2, T]: ... + + +@t.overload +def max_by( + collection: t.Mapping[t.Any, "SupportsRichComparisonT"], iteratee: None = None, *, default: T +) -> t.Union["SupportsRichComparisonT", T]: ... + + +@t.overload +def max_by( + collection: t.Iterable["SupportsRichComparisonT"], iteratee: None = None, default: Unset = UNSET +) -> "SupportsRichComparisonT": ... + + +@t.overload +def max_by( + collection: t.Iterable[T2], + iteratee: t.Callable[[T2], "SupportsRichComparisonT"], + default: Unset = UNSET, +) -> T2: ... + + +@t.overload +def max_by( + collection: t.Iterable[T2], iteratee: t.Callable[[T2], "SupportsRichComparisonT"], *, default: T +) -> t.Union[T2, T]: ... + + +@t.overload +def max_by( + collection: t.Iterable["SupportsRichComparisonT"], iteratee: None = None, *, default: T +) -> t.Union["SupportsRichComparisonT", T]: ... + + +@t.overload +def max_by(collection: t.Iterable[T], iteratee: IterateeObjT, default: Unset = UNSET) -> T: ... + + +@t.overload +def max_by(collection: t.Iterable[T], iteratee: IterateeObjT, default: T2) -> t.Union[T, T2]: ... + + +def max_by(collection, iteratee=None, default=UNSET): + """ + Retrieves the maximum value of a `collection`. + + Args: + collection: Collection to iterate over. + iteratee: Iteratee applied per iteration. + default: Value to return if `collection` is empty. + + Returns: + Maximum value. + + Example: + + >>> max_by([1.0, 1.5, 1.8], math.floor) + 1.0 + >>> max_by([{"a": 1}, {"a": 2}, {"a": 3}], "a") + {'a': 3} + >>> max_by([], default=-1) + -1 + + .. versionadded:: 4.0.0 + """ + if isinstance(collection, dict): + collection = collection.values() + + return max(iterator_with_default(collection, default), key=pyd.iteratee(iteratee)) + + +@t.overload +def median( + collection: t.Mapping[T, T2], iteratee: t.Callable[[T2, T, t.Dict[T, T2]], NumberT] +) -> t.Union[float, int]: ... + + +@t.overload +def median( + collection: t.Mapping[T, T2], iteratee: t.Callable[[T2, T], NumberT] +) -> t.Union[float, int]: ... + + +@t.overload +def median( + collection: t.Mapping[t.Any, T2], iteratee: t.Callable[[T2], NumberT] +) -> t.Union[float, int]: ... + + +@t.overload +def median( + collection: t.Iterable[T], iteratee: t.Callable[[T, int, t.List[T]], NumberT] +) -> t.Union[float, int]: ... + + +@t.overload +def median( + collection: t.Iterable[T], iteratee: t.Callable[[T, int], NumberT] +) -> t.Union[float, int]: ... + + +@t.overload +def median( + collection: t.Iterable[T], iteratee: t.Callable[[T], NumberT] +) -> t.Union[float, int]: ... + + +@t.overload +def median(collection: t.Iterable[NumberT], iteratee: None = None) -> t.Union[float, int]: ... + + +def median(collection, iteratee=None): + """ + Calculate median of each element in `collection`. If iteratee is passed, each element of + `collection` is passed through an iteratee before the median is computed. + + Args: + collection: Collection to process. + iteratee: Iteratee applied per iteration. + + Returns: + Result of median. + + Example: + + >>> median([1, 2, 3, 4, 5]) + 3 + >>> median([1, 2, 3, 4]) + 2.5 + + .. versionadded:: 2.1.0 + """ + length = len(collection) + middle = (length + 1) / 2 + collection = sorted(ret[0] for ret in iteriteratee(collection, iteratee)) + + if pyd.is_odd(length): + result = collection[int(middle - 1)] + else: + left = int(middle - 1.5) + right = int(middle - 0.5) + result = (collection[left] + collection[right]) / 2 + + return result + + +@t.overload +def min_( + collection: t.Mapping[t.Any, "SupportsRichComparisonT"], default: Unset = UNSET +) -> "SupportsRichComparisonT": ... + + +@t.overload +def min_( + collection: t.Mapping[t.Any, "SupportsRichComparisonT"], default: T +) -> t.Union["SupportsRichComparisonT", T]: ... + + +@t.overload +def min_( + collection: t.Iterable["SupportsRichComparisonT"], default: Unset = UNSET +) -> "SupportsRichComparisonT": ... + + +@t.overload +def min_( + collection: t.Iterable["SupportsRichComparisonT"], default: T +) -> t.Union["SupportsRichComparisonT", T]: ... + + +def min_(collection, default=UNSET): + """ + Retrieves the minimum value of a `collection`. + + Args: + collection: Collection to iterate over. + default: Value to return if `collection` is empty. + + Returns: + Minimum value. + + Example: + + >>> min_([1, 2, 3, 4]) + 1 + >>> min_([], default=100) + 100 + + .. versionadded:: 1.0.0 + + .. versionchanged:: 4.0.0 + Moved iteratee iteratee support to :func:`min_by`. + """ + return min_by(collection, default=default) + + +@t.overload +def min_by( + collection: t.Mapping[t.Any, "SupportsRichComparisonT"], + iteratee: None = None, + default: Unset = UNSET, +) -> "SupportsRichComparisonT": ... + + +@t.overload +def min_by( + collection: t.Mapping[t.Any, T2], + iteratee: t.Callable[[T2], "SupportsRichComparisonT"], + default: Unset = UNSET, +) -> T2: ... + + +@t.overload +def min_by( + collection: t.Mapping[t.Any, T2], + iteratee: t.Callable[[T2], "SupportsRichComparisonT"], + *, + default: T, +) -> t.Union[T2, T]: ... + + +@t.overload +def min_by( + collection: t.Mapping[t.Any, "SupportsRichComparisonT"], iteratee: None = None, *, default: T +) -> t.Union["SupportsRichComparisonT", T]: ... + + +@t.overload +def min_by( + collection: t.Iterable["SupportsRichComparisonT"], iteratee: None = None, default: Unset = UNSET +) -> "SupportsRichComparisonT": ... + + +@t.overload +def min_by( + collection: t.Iterable[T2], + iteratee: t.Callable[[T2], "SupportsRichComparisonT"], + default: Unset = UNSET, +) -> T2: ... + + +@t.overload +def min_by( + collection: t.Iterable[T2], iteratee: t.Callable[[T2], "SupportsRichComparisonT"], *, default: T +) -> t.Union[T2, T]: ... + + +@t.overload +def min_by( + collection: t.Iterable["SupportsRichComparisonT"], iteratee: None = None, *, default: T +) -> t.Union["SupportsRichComparisonT", T]: ... + + +@t.overload +def min_by(collection: t.Iterable[T], iteratee: IterateeObjT, default: Unset = UNSET) -> T: ... + + +@t.overload +def min_by(collection: t.Iterable[T], iteratee: IterateeObjT, default: T2) -> t.Union[T, T2]: ... + + +def min_by(collection, iteratee=None, default=UNSET): + """ + Retrieves the minimum value of a `collection`. + + Args: + collection: Collection to iterate over. + iteratee: Iteratee applied per iteration. + default: Value to return if `collection` is empty. + + Returns: + Minimum value. + + Example: + + >>> min_by([1.8, 1.5, 1.0], math.floor) + 1.8 + >>> min_by([{"a": 1}, {"a": 2}, {"a": 3}], "a") + {'a': 1} + >>> min_by([], default=100) + 100 + + .. versionadded:: 4.0.0 + """ + if isinstance(collection, dict): + collection = collection.values() + return min(iterator_with_default(collection, default), key=pyd.iteratee(iteratee)) + + +def moving_mean(array: t.Sequence["SupportsAdd[int, t.Any]"], size: t.SupportsInt) -> t.List[float]: + """ + Calculate moving mean of each element of `array`. + + Args: + array: List to process. + size: Window size. + + Returns: + Result of moving average. + + Example: + + >>> moving_mean(range(10), 1) + [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0] + >>> moving_mean(range(10), 5) + [2.0, 3.0, 4.0, 5.0, 6.0, 7.0] + >>> moving_mean(range(10), 10) + [4.5] + + .. versionadded:: 2.1.0 + + .. versionchanged:: 4.0.0 + Rename to ``moving_mean`` and remove ``moving_average`` and ``moving_avg`` aliases. + """ + result = [] + size = int(size) + + for i in range(size - 1, len(array) + 1): + window = array[i - size : i] + + if len(window) == size: + result.append(mean(window)) + + return result + + +@t.overload +def multiply(multiplier: SupportsMul[int, T2], multiplicand: None) -> T2: ... + + +@t.overload +def multiply(multiplier: None, multiplicand: SupportsMul[int, T2]) -> T2: ... + + +@t.overload +def multiply(multiplier: None, multiplicand: None) -> int: ... + + +@t.overload +def multiply(multiplier: SupportsMul[T, T2], multiplicand: T) -> T2: ... + + +@t.overload +def multiply(multiplier: T, multiplicand: SupportsMul[T, T2]) -> T2: ... + + +def multiply(multiplier, multiplicand): + """ + Multiply two numbers. + + Args: + multiplier: The first number in a multiplication. + multiplicand: The second number in a multiplication. + + Returns: + Returns the product. + + Example: + + >>> multiply(4, 5) + 20 + >>> multiply(10, 4) + 40 + >>> multiply(None, 10) + 10 + >>> multiply(None, None) + 1 + + .. versionadded:: 4.0.0 + """ + return call_math_operator(multiplier, multiplicand, operator.mul, 1) + + +@t.overload +def power(x: int, n: int) -> t.Union[int, float]: ... + + +@t.overload +def power(x: float, n: t.Union[int, float]) -> float: ... + + +@t.overload +def power(x: t.List[int], n: int) -> t.List[t.Union[int, float]]: ... + + +@t.overload +def power(x: t.List[float], n: t.List[t.Union[int, float]]) -> t.List[float]: ... + + +def power(x, n): + """ + Calculate exponentiation of `x` raised to the `n` power. + + Args: + x: Base number. + n: Exponent. + + Returns: + Result of calculation. + + Example: + + >>> power(5, 2) + 25 + >>> power(12.5, 3) + 1953.125 + + .. versionadded:: 2.1.0 + + .. versionchanged:: 4.0.0 + Removed alias ``pow_``. + """ + if pyd.is_number(x): + result = pow(x, n) + elif pyd.is_list(x): + result = [pow(item, n) for item in x] + else: + result = None + + return result + + +@t.overload +def round_(x: t.List[SupportsRound[NumberT]], precision: int = 0) -> t.List[float]: ... + + +@t.overload +def round_(x: SupportsRound[NumberT], precision: int = 0) -> float: ... + + +def round_(x, precision=0): + """ + Round number to precision. + + Args: + x: Number to round. + precision: Rounding precision. Defaults to ``0``. + + Returns: + Rounded number. + + Example: + + >>> round_(3.275) == 3.0 + True + >>> round_(3.275, 1) == 3.3 + True + + .. versionadded:: 2.1.0 + + .. versionchanged:: 4.0.0 + Remove alias ``curve``. + """ + return rounder(round, x, precision) + + +@t.overload +def scale(array: t.Iterable["Decimal"], maximum: "Decimal") -> t.List["Decimal"]: ... + + +@t.overload +def scale(array: t.Iterable[NumberNoDecimalT], maximum: NumberNoDecimalT) -> t.List[float]: ... + + +@t.overload +def scale(array: t.Iterable[NumberT], maximum: int = 1) -> t.List[float]: ... + + +def scale(array, maximum: NumberT = 1): + """ + Scale list of value to a maximum number. + + Args: + array: Numbers to scale. + maximum: Maximum scale value. + + Returns: + Scaled numbers. + + Example: + + >>> scale([1, 2, 3, 4]) + [0.25, 0.5, 0.75, 1.0] + >>> scale([1, 2, 3, 4], 1) + [0.25, 0.5, 0.75, 1.0] + >>> scale([1, 2, 3, 4], 4) + [1.0, 2.0, 3.0, 4.0] + >>> scale([1, 2, 3, 4], 2) + [0.5, 1.0, 1.5, 2.0] + + .. versionadded:: 2.1.0 + """ + array_max = max(array) + factor = maximum / array_max + return [item * factor for item in array] + + +@t.overload +def slope( + point1: t.Union[t.Tuple["Decimal", "Decimal"], t.List["Decimal"]], + point2: t.Union[t.Tuple["Decimal", "Decimal"], t.List["Decimal"]], +) -> "Decimal": ... + + +@t.overload +def slope( + point1: t.Union[t.Tuple[NumberNoDecimalT, NumberNoDecimalT], t.List[NumberNoDecimalT]], + point2: t.Union[t.Tuple[NumberNoDecimalT, NumberNoDecimalT], t.List[NumberNoDecimalT]], +) -> float: ... + + +def slope(point1, point2): + """ + Calculate the slope between two points. + + Args: + point1: X and Y coordinates of first point. + point2: X and Y cooredinates of second point. + + Returns: + Calculated slope. + + Example: + + >>> slope((1, 2), (4, 8)) + 2.0 + + .. versionadded:: 2.1.0 + """ + x1, y1 = point1[0], point1[1] + x2, y2 = point2[0], point2[1] + + if x1 == x2: + result = INFINITY + else: + result = (y2 - y1) / (x2 - x1) + + return result + + +def std_deviation(array: t.List[NumberT]) -> float: + """ + Calculate standard deviation of list of numbers. + + Args: + array: List to process. + + Returns: + Calculated standard deviation. + + Example: + + >>> round(std_deviation([1, 18, 20, 4]), 2) == 8.35 + True + + .. versionadded:: 2.1.0 + + .. versionchanged:: 4.0.0 + Remove alias ``sigma``. + """ + return math.sqrt(variance(array)) + + +@t.overload +def subtract(minuend: "SupportsSub[T, T2]", subtrahend: T) -> T2: ... + + +@t.overload +def subtract(minuend: T, subtrahend: "SupportsSub[T, T2]") -> T2: ... + + +def subtract(minuend, subtrahend): + """ + Subtracts two numbers. + + Args: + minuend: Value passed in by the user. + subtrahend: Value passed in by the user. + + Returns: + Result of the difference from the given values. + + Example: + + >>> subtract(10, 5) + 5 + >>> subtract(-10, 4) + -14 + >>> subtract(2, 0.5) + 1.5 + + .. versionadded:: 4.0.0 + """ + return call_math_operator(minuend, subtrahend, operator.sub, 0) + + +def transpose(array: t.Iterable[t.Iterable[T]]) -> t.List[t.List[T]]: + """ + Transpose the elements of `array`. + + Args: + array: List to process. + + Returns: + Transposed list. + + Example: + + >>> transpose([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) + [[1, 4, 7], [2, 5, 8], [3, 6, 9]] + + .. versionadded:: 2.1.0 + """ + trans: t.List[t.List[T]] = [] + + for y, row in iterator(array): + for x, col in iterator(row): + trans = pyd.set_(trans, [x, y], col) + + return trans + + +@t.overload +def variance(array: t.Mapping[t.Any, "SupportsAdd[int, t.Any]"]) -> float: ... + + +@t.overload +def variance(array: t.Iterable["SupportsAdd[int, t.Any]"]) -> float: ... + + +def variance(array): + """ + Calculate the variance of the elements in `array`. + + Args: + array: List to process. + + Returns: + Calculated variance. + + Example: + + >>> variance([1, 18, 20, 4]) + 69.6875 + + .. versionadded:: 2.1.0 + """ + avg = mean(array) + + def var(x): + return power(x - avg, 2) + + return pyd._(array).map_(var).mean().value() + + +@t.overload +def zscore( + collection: t.Mapping[T, T2], iteratee: t.Callable[[T2, T, t.Dict[T, T2]], NumberT] +) -> t.List[float]: ... + + +@t.overload +def zscore( + collection: t.Mapping[T, T2], iteratee: t.Callable[[T2, T], NumberT] +) -> t.List[float]: ... + + +@t.overload +def zscore( + collection: t.Mapping[t.Any, T2], iteratee: t.Callable[[T2], NumberT] +) -> t.List[float]: ... + + +@t.overload +def zscore( + collection: t.Iterable[T], iteratee: t.Callable[[T, int, t.List[T]], NumberT] +) -> t.List[float]: ... + + +@t.overload +def zscore(collection: t.Iterable[T], iteratee: t.Callable[[T, int], NumberT]) -> t.List[float]: ... + + +@t.overload +def zscore(collection: t.Iterable[T], iteratee: t.Callable[[T], NumberT]) -> t.List[float]: ... + + +@t.overload +def zscore(collection: t.Iterable[NumberT], iteratee: None = None) -> t.List[float]: ... + + +def zscore(collection, iteratee=None): + """ + Calculate the standard score assuming normal distribution. If iteratee is passed, each element + of `collection` is passed through an iteratee before the standard score is computed. + + Args: + collection: Collection to process. + iteratee: Iteratee applied per iteration. + + Returns: + Calculated standard score. + + Example: + + >>> results = zscore([1, 2, 3]) + + # [-1.224744871391589, 0.0, 1.224744871391589] + + .. versionadded:: 2.1.0 + """ + array = pyd.map_(collection, iteratee) + avg = mean(array) + sig = std_deviation(array) + + return [(item - avg) / sig for item in array] + + +# +# Utility methods not a part of the main API +# + + +def call_math_operator(value1, value2, op, default): + """Return the result of the math operation on the given values.""" + if value1 is None: + value1 = default + + if value2 is None: + value2 = default + + if not pyd.is_number(value1): + try: + value1 = float(value1) + except Exception: + pass + + if not pyd.is_number(value2): + try: + value2 = float(value2) + except Exception: + pass + + return op(value1, value2) + + +def rounder(func, x, precision): + precision = pow(10, precision) + + def rounder_func(item): + return func(item * precision) / precision + + result = None + + if pyd.is_number(x): + result = rounder_func(x) + elif pyd.is_iterable(x): + try: + result = [rounder_func(item) for item in x] + except TypeError: + pass + + return result 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 diff --git a/.venv/lib/python3.12/site-packages/pydash/predicates.py b/.venv/lib/python3.12/site-packages/pydash/predicates.py new file mode 100644 index 00000000..1c6b457d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydash/predicates.py @@ -0,0 +1,1664 @@ +""" +Predicate functions that return boolean evaluations of objects. + +.. versionadded:: 2.0.0 +""" + +from __future__ import annotations + +from collections.abc import Iterable, Mapping +import datetime +from itertools import islice +import json +import operator +import re +from types import BuiltinFunctionType +import typing as t + +from typing_extensions import TypeGuard + +import pydash as pyd + +from .helpers import BUILTINS, NUMBER_TYPES, UNSET, base_get, callit, iterator + + +if t.TYPE_CHECKING: + from _typeshed import ( # pragma: no cover + SupportsDunderGE, + SupportsDunderGT, + SupportsDunderLE, + SupportsDunderLT, + SupportsRichComparison, + ) + + +__all__ = ( + "eq", + "eq_cmp", + "gt", + "gt_cmp", + "gte", + "gte_cmp", + "lt", + "lt_cmp", + "lte", + "lte_cmp", + "in_range", + "in_range_cmp", + "is_associative", + "is_blank", + "is_boolean", + "is_builtin", + "is_date", + "is_decreasing", + "is_dict", + "is_empty", + "is_equal", + "is_equal_cmp", + "is_equal_with", + "is_equal_with_cmp", + "is_error", + "is_even", + "is_float", + "is_function", + "is_increasing", + "is_indexed", + "is_instance_of", + "is_instance_of_cmp", + "is_integer", + "is_iterable", + "is_json", + "is_list", + "is_match", + "is_match_cmp", + "is_match_with", + "is_match_with_cmp", + "is_monotone", + "is_monotone_cmp", + "is_nan", + "is_negative", + "is_none", + "is_number", + "is_object", + "is_odd", + "is_positive", + "is_reg_exp", + "is_set", + "is_strictly_decreasing", + "is_strictly_increasing", + "is_string", + "is_tuple", + "is_zero", +) + +T = t.TypeVar("T") +T2 = t.TypeVar("T2") +T3 = t.TypeVar("T3") + +RegExp = type(re.compile("")) + + +def eq(value: t.Any, other: t.Any) -> bool: + """ + Checks if :attr:`value` is equal to :attr:`other`. + + Args: + value: Value to compare. + other: Other value to compare. + + Returns: + Whether :attr:`value` is equal to :attr:`other`. + + Example: + + >>> eq(None, None) + True + >>> eq(None, "") + False + >>> eq("a", "a") + True + >>> eq(1, str(1)) + False + + .. versionadded:: 4.0.0 + """ + return value is other + + +def eq_cmp(other: T) -> t.Callable[[T], bool]: + """ + Curried version of :func:`eq`. + + Args: + other: Value to compare. + + Returns: + A predicate checking whether passed :attr:`value` is equal to :attr:`other`. + + Example: + + >>> eq_cmp(None)(None) + True + >>> eq_cmp(None)("") + False + >>> eq_cmp("a")("a") + True + >>> eq_cmp(1)(str(1)) + False + + .. versionadded:: 7.1.0 + """ + return lambda value: eq(value, other) + + +def gt(value: "SupportsDunderGT[T]", other: T) -> bool: + """ + Checks if `value` is greater than `other`. + + Args: + value: Value to compare. + other: Other value to compare. + + Returns: + Whether `value` is greater than `other`. + + Example: + + >>> gt(5, 3) + True + >>> gt(3, 5) + False + >>> gt(5, 5) + False + + .. versionadded:: 3.3.0 + """ + return value > other + + +def gt_cmp(other: T) -> t.Callable[["SupportsDunderGT[T]"], bool]: + """ + Curried version of :func:`gt`. + + Args: + other: Value to compare. + + Returns: + A predicate checking whether passed :attr:`value` is greater than :attr:`other`. + + Example: + + >>> gt_cmp(3)(5) + True + >>> gt_cmp(5)(3) + False + >>> gt_cmp(5)(5) + False + + .. versionadded:: 7.1.0 + """ + return lambda value: gt(value, other) + + +def gte(value: "SupportsDunderGE[T]", other: T) -> bool: + """ + Checks if `value` is greater than or equal to `other`. + + Args: + value: Value to compare. + other: Other value to compare. + + Returns: + Whether `value` is greater than or equal to `other`. + + Example: + + >>> gte(5, 3) + True + >>> gte(3, 5) + False + >>> gte(5, 5) + True + + .. versionadded:: 3.3.0 + """ + return value >= other + + +def gte_cmp(other: T) -> t.Callable[["SupportsDunderGE[T]"], bool]: + """ + Curried version of :func:`gte`. + + Args: + other: Value to compare. + + Returns: + A predicate checking whether passed :attr:`value` is greater than or equal to :attr:`other`. + + Example: + + >>> gte_cmp(3)(5) + True + >>> gte_cmp(5)(3) + False + >>> gte_cmp(5)(5) + True + + .. versionadded:: 7.1.0 + """ + return lambda value: gte(value, other) + + +def lt(value: "SupportsDunderLT[T]", other: T) -> bool: + """ + Checks if `value` is less than `other`. + + Args: + value: Value to compare. + other: Other value to compare. + + Returns: + Whether `value` is less than `other`. + + Example: + + >>> lt(5, 3) + False + >>> lt(3, 5) + True + >>> lt(5, 5) + False + + .. versionadded:: 3.3.0 + """ + return value < other + + +def lt_cmp(other: T) -> t.Callable[["SupportsDunderLT[T]"], bool]: + """ + Curried version of :func:`lt`. + + Args: + other: Value to compare. + + Returns: + A predicate checking whether passed :attr:`value` is less than :attr:`other`. + + Example: + + >>> lt_cmp(3)(5) + False + >>> lt_cmp(5)(3) + True + >>> lt_cmp(5)(5) + False + + .. versionadded:: 7.1.0 + """ + return lambda value: lt(value, other) + + +def lte(value: "SupportsDunderLE[T]", other: T) -> bool: + """ + Checks if `value` is less than or equal to `other`. + + Args: + value: Value to compare. + other: Other value to compare. + + Returns: + Whether `value` is less than or equal to `other`. + + Example: + + >>> lte(5, 3) + False + >>> lte(3, 5) + True + >>> lte(5, 5) + True + + .. versionadded:: 3.3.0 + """ + return value <= other + + +def lte_cmp(other: T) -> t.Callable[["SupportsDunderLE[T]"], bool]: + """ + Curried version of :func:`lte`. + + Args: + other: Value to compare. + + Returns: + A predicate checking whether passed :attr:`value` is less than or equal to :attr:`other`. + + Example: + + >>> lte_cmp(3)(5) + False + >>> lte_cmp(5)(3) + True + >>> lte_cmp(5)(5) + True + + .. versionadded:: 7.1.0 + """ + return lambda value: lte(value, other) + + +def in_range(value: t.Any, start: t.Any = 0, end: t.Any = None) -> bool: + """ + Checks if `value` is between `start` and up to but not including `end`. If `end` is not + specified it defaults to `start` with `start` becoming ``0``. + + Args: + + value: Number to check. + start: Start of range inclusive. Defaults to ``0``. + end: End of range exclusive. Defaults to `start`. + + Returns: + Whether `value` is in range. + + Example: + + >>> in_range(2, 4) + True + >>> in_range(4, 2) + False + >>> in_range(2, 1, 3) + True + >>> in_range(3, 1, 2) + False + >>> in_range(2.5, 3.5) + True + >>> in_range(3.5, 2.5) + False + + .. versionadded:: 3.1.0 + """ + if not is_number(value): + return False + + if not is_number(start): + start = 0 + + if end is None: + end = start + start = 0 + elif not is_number(end): + end = 0 + + return start <= value < end + + +def in_range_cmp(start: t.Any = 0, end: t.Any = None) -> t.Callable[[t.Any], bool]: + """ + Curried version of :func:`in_range`. + + Args: + start: Start of range inclusive. Defaults to ``0``. + end: End of range exclusive. Defaults to `start`. + + Returns: + A predicate checking whether passed :attr:`value` is in range. + + Example: + + >>> in_range_cmp(4)(2) + True + >>> in_range_cmp(2)(4) + False + >>> in_range_cmp(1, 3)(2) + True + >>> in_range_cmp(1, 2)(3) + False + >>> in_range_cmp(3.5)(2.5) + True + >>> in_range_cmp(2.5)(3.5) + False + + .. versionadded:: 7.1.0 + """ + return lambda value: in_range(value, start, end) + + +def is_associative(value: t.Any) -> bool: + """ + Checks if `value` is an associative object meaning that it can be accessed via an index or key. + + Args: + value: Value to check. + + Returns: + Whether `value` is associative. + + Example: + + >>> is_associative([]) + True + >>> is_associative({}) + True + >>> is_associative(1) + False + >>> is_associative(True) + False + + .. versionadded:: 2.0.0 + """ + return hasattr(value, "__getitem__") + + +def is_blank(text: t.Any) -> TypeGuard[str]: + r""" + Checks if `text` contains only whitespace characters. + + Args: + text: String to test. + + Returns: + Whether `text` is blank. + + Example: + + >>> is_blank("") + True + >>> is_blank(" \r\n ") + True + >>> is_blank(False) + False + + .. versionadded:: 3.0.0 + """ + try: + ret = bool(re.match(r"^(\s+)?$", text)) + except TypeError: + ret = False + + return ret + + +def is_boolean(value: t.Any) -> TypeGuard[bool]: + """ + Checks if `value` is a boolean value. + + Args: + value: Value to check. + + Returns: + Whether `value` is a boolean. + + Example: + + >>> is_boolean(True) + True + >>> is_boolean(False) + True + >>> is_boolean(0) + False + + .. versionadded:: 1.0.0 + + .. versionchanged:: 3.0.0 + Added ``is_bool`` as alias. + + .. versionchanged:: 4.0.0 + Removed alias ``is_bool``. + """ + return isinstance(value, bool) + + +def is_builtin(value: t.Any) -> bool: + """ + Checks if `value` is a Python builtin function or method. + + Args: + value: Value to check. + + Returns: + Whether `value` is a Python builtin function or method. + + Example: + + >>> is_builtin(1) + True + >>> is_builtin(list) + True + >>> is_builtin("foo") + False + + .. versionadded:: 3.0.0 + + .. versionchanged:: 4.0.0 + Removed alias ``is_native``. + """ + try: + return isinstance(value, BuiltinFunctionType) or value in BUILTINS + except TypeError: # pragma: no cover + return False + + +def is_date(value: t.Any) -> bool: + """ + Check if `value` is a date object. + + Args: + value: Value to check. + + Returns: + Whether `value` is a date object. + + Example: + + >>> import datetime + >>> is_date(datetime.date.today()) + True + >>> is_date(datetime.datetime.today()) + True + >>> is_date("2014-01-01") + False + + Note: + This will also return ``True`` for datetime objects. + + .. versionadded:: 1.0.0 + """ + return isinstance(value, datetime.date) + + +def is_decreasing( + value: t.Union["SupportsRichComparison", t.List["SupportsRichComparison"]], +) -> bool: + """ + Check if `value` is monotonically decreasing. + + Args: + value: Value to check. + + Returns: + Whether `value` is monotonically decreasing. + + Example: + + >>> is_decreasing([5, 4, 4, 3]) + True + >>> is_decreasing([5, 5, 5]) + True + >>> is_decreasing([5, 4, 5]) + False + + .. versionadded:: 2.0.0 + """ + return is_monotone(value, operator.ge) # type: ignore + + +def is_dict(value: t.Any) -> bool: + """ + Checks if `value` is a ``dict``. + + Args: + value: Value to check. + + Returns: + Whether `value` is a ``dict``. + + Example: + + >>> is_dict({}) + True + >>> is_dict([]) + False + + .. versionadded:: 1.0.0 + + .. versionchanged:: 3.0.0 + Added :func:`is_dict` as main definition and made `is_plain_object`` an alias. + + .. versionchanged:: 4.0.0 + Removed alias ``is_plain_object``. + """ + return isinstance(value, dict) + + +def is_empty(value: t.Any) -> bool: + """ + Checks if `value` is empty. + + Args: + value: Value to check. + + Returns: + Whether `value` is empty. + + Example: + + >>> is_empty(0) + True + >>> is_empty(1) + True + >>> is_empty(True) + True + >>> is_empty("foo") + False + >>> is_empty(None) + True + >>> is_empty({}) + True + + Note: + Returns ``True`` for booleans and numbers. + + .. versionadded:: 1.0.0 + """ + return is_boolean(value) or is_number(value) or not value + + +def is_equal(value: t.Any, other: t.Any) -> bool: + """ + Performs a comparison between two values to determine if they are equivalent to each other. + + Args: + value: Object to compare. + other: Object to compare. + + Returns: + Whether `value` and `other` are equal. + + Example: + + >>> is_equal([1, 2, 3], [1, 2, 3]) + True + >>> is_equal("a", "A") + False + + .. versionadded:: 1.0.0 + + .. versionchanged:: 4.0.0 + Removed :attr:`iteratee` from :func:`is_equal` and added it in + :func:`is_equal_with`. + """ + return is_equal_with(value, other, customizer=None) + + +def is_equal_cmp(other: T) -> t.Callable[[T], bool]: + """ + Curried version of :func:`is_equal`. + + Args: + other: Value to compare. + + Returns: + A predicate checking whether passed :attr:`value` is equal to :attr:`other`. + + Example: + + >>> is_equal_cmp([1, 2, 3])([1, 2, 3]) + True + >>> is_equal_cmp("a")("A") + False + + .. versionadded:: 7.1.0 + """ + return lambda value: is_equal(value, other) + + +@t.overload +def is_equal_with(value: T, other: T2, customizer: t.Callable[[T, T2], T3]) -> T3: ... + + +@t.overload +def is_equal_with(value: t.Any, other: t.Any, customizer: t.Callable[..., t.Any]) -> bool: ... + + +@t.overload +def is_equal_with(value: t.Any, other: t.Any, customizer: None) -> bool: ... + + +def is_equal_with(value, other, customizer): + """ + This method is like :func:`is_equal` except that it accepts customizer which is invoked to + compare values. A customizer is provided which will be executed to compare values. If the + customizer returns ``None``, comparisons will be handled by the method instead. The customizer + is invoked with two arguments: ``(value, other)``. + + Args: + value: Object to compare. + other: Object to compare. + customizer: Customizer used to compare values from `value` and `other`. + + Returns: + Whether `value` and `other` are equal. + + Example: + + >>> is_equal_with([1, 2, 3], [1, 2, 3], None) + True + >>> is_equal_with("a", "A", None) + False + >>> is_equal_with("a", "A", lambda a, b: a.lower() == b.lower()) + True + + .. versionadded:: 4.0.0 + """ + # If customizer provided, use it for comparison. + equal = customizer(value, other) if callable(customizer) else None + + # Return customizer results if anything but None. + if equal is not None: + pass + elif ( + callable(customizer) + and type(value) is type(other) + and isinstance(value, (list, dict)) + and isinstance(other, (list, dict)) + and len(value) == len(other) + ): + # Walk a/b to determine equality using customizer. + for key, val in iterator(value): + if pyd.has(other, key): + equal = is_equal_with(val, other[key], customizer) + else: + equal = False + + if not equal: + break + else: + # Use basic == comparison. + equal = value == other + + return equal + + +def is_equal_with_cmp(other: T, customizer: t.Callable[[T, T], T3]) -> t.Callable[[T], T3]: + """ + Curried version of :func:`is_equal_with`. + + Args: + other: Value to compare. + customizer: Customizer used to compare values from `value` and `other`. + + Returns: + A predicate checking whether passed :attr:`value` and :attr:`other` are equal. + + Example: + + >>> is_equal_with_cmp([1, 2, 3], None)([1, 2, 3]) + True + >>> is_equal_with_cmp("a", None)("A") + False + >>> is_equal_with_cmp("a", lambda a, b: a.lower() == b.lower())("A") + True + + .. versionadded:: 7.1.0 + """ + return lambda value: is_equal_with(value, other, customizer) + + +def is_error(value: t.Any) -> bool: + """ + Checks if `value` is an ``Exception``. + + Args: + value: Value to check. + + Returns: + Whether `value` is an exception. + + Example: + + >>> is_error(Exception()) + True + >>> is_error(Exception) + False + >>> is_error(None) + False + + .. versionadded:: 1.1.0 + """ + return isinstance(value, Exception) + + +def is_even(value: t.Any) -> bool: + """ + Checks if `value` is even. + + Args: + value: Value to check. + + Returns: + Whether `value` is even. + + Example: + + >>> is_even(2) + True + >>> is_even(3) + False + >>> is_even(False) + False + + .. versionadded:: 2.0.0 + """ + return is_number(value) and value % 2 == 0 + + +def is_float(value: t.Any) -> TypeGuard[float]: + """ + Checks if `value` is a float. + + Args: + value: Value to check. + + Returns: + Whether `value` is a float. + + Example: + + >>> is_float(1.0) + True + >>> is_float(1) + False + + .. versionadded:: 2.0.0 + """ + return isinstance(value, float) + + +def is_function(value: t.Any) -> bool: + """ + Checks if `value` is a function. + + Args: + value: Value to check. + + Returns: + Whether `value` is callable. + + Example: + + >>> is_function(list) + True + >>> is_function(lambda: True) + True + >>> is_function(1) + False + + .. versionadded:: 1.0.0 + """ + return callable(value) + + +def is_increasing( + value: t.Union["SupportsRichComparison", t.List["SupportsRichComparison"]], +) -> bool: + """ + Check if `value` is monotonically increasing. + + Args: + value: Value to check. + + Returns: + Whether `value` is monotonically increasing. + + Example: + + >>> is_increasing([1, 3, 5]) + True + >>> is_increasing([1, 1, 2, 3, 3]) + True + >>> is_increasing([5, 5, 5]) + True + >>> is_increasing([1, 2, 4, 3]) + False + + .. versionadded:: 2.0.0 + """ + return is_monotone(value, operator.le) # type: ignore + + +def is_indexed(value: t.Any) -> bool: + """ + Checks if `value` is integer indexed, i.e., ``list``, ``str`` or ``tuple``. + + Args: + value: Value to check. + + Returns: + Whether `value` is integer indexed. + + Example: + + >>> is_indexed("") + True + >>> is_indexed([]) + True + >>> is_indexed(()) + True + >>> is_indexed({}) + False + + .. versionadded:: 2.0.0 + + .. versionchanged:: 3.0.0 + Return ``True`` for tuples. + """ + return isinstance(value, (list, tuple, str)) + + +def is_instance_of(value: t.Any, types: t.Union[type, t.Tuple[type, ...]]) -> bool: + """ + Checks if `value` is an instance of `types`. + + Args: + value: Value to check. + types: Types to check against. Pass as ``tuple`` to check if `value` is one of + multiple types. + + Returns: + Whether `value` is an instance of `types`. + + Example: + + >>> is_instance_of({}, dict) + True + >>> is_instance_of({}, list) + False + + .. versionadded:: 2.0.0 + """ + return isinstance(value, types) + + +def is_instance_of_cmp( + types: t.Union[type, t.Tuple[type, ...]], +) -> t.Callable[[t.Any], bool]: + """ + Curried version of :func:`is_instance_of`. + + Args: + types: Types to check against. Pass as ``tuple`` to check if `value` is one of + multiple types. + + Returns: + A predicate checking whether passed :attr:`value` is an instance of :attr:`types`. + + Example: + + >>> is_instance_of_cmp(dict)({}) + True + >>> is_instance_of_cmp(list)({}) + False + + .. versionadded:: 7.1.0 + """ + return lambda value: is_instance_of(value, types) + + +def is_integer(value: t.Any) -> TypeGuard[int]: + """ + Checks if `value` is a integer. + + Args: + value: Value to check. + + Returns: + Whether `value` is an integer. + + Example: + + >>> is_integer(1) + True + >>> is_integer(1.0) + False + >>> is_integer(True) + False + + .. versionadded:: 2.0.0 + + .. versionchanged:: 3.0.0 + Added ``is_int`` as alias. + + .. versionchanged:: 4.0.0 + Removed alias ``is_int``. + """ + return is_number(value) and isinstance(value, int) + + +def is_iterable(value: t.Any) -> bool: + """ + Checks if `value` is an iterable. + + Args: + value: Value to check. + + Returns: + Whether `value` is an iterable. + + Example: + + >>> is_iterable([]) + True + >>> is_iterable({}) + True + >>> is_iterable(()) + True + >>> is_iterable(5) + False + >>> is_iterable(True) + False + + .. versionadded:: 3.3.0 + """ + try: + iter(value) + except TypeError: + return False + else: + return True + + +def is_json(value: t.Any) -> bool: + """ + Checks if `value` is a valid JSON string. + + Args: + value: Value to check. + + Returns: + Whether `value` is JSON. + + Example: + + >>> is_json({}) + False + >>> is_json("{}") + True + >>> is_json({"hello": 1, "world": 2}) + False + >>> is_json('{"hello": 1, "world": 2}') + True + + .. versionadded:: 2.0.0 + """ + try: + json.loads(value) + return True + except Exception: + return False + + +def is_list(value: t.Any) -> bool: + """ + Checks if `value` is a list. + + Args: + value: Value to check. + + Returns: + Whether `value` is a list. + + Example: + + >>> is_list([]) + True + >>> is_list({}) + False + >>> is_list(()) + False + + .. versionadded:: 1.0.0 + """ + return isinstance(value, list) + + +def is_match(obj: t.Any, source: t.Any) -> bool: + """ + Performs a partial deep comparison between `obj` and `source` to determine if `obj` contains + equivalent property values. + + Args: + obj: Object to compare. + source: Object of property values to match. + + Returns: + Whether `obj` is a match or not. + + Example: + + >>> is_match({'a': 1, 'b': 2}, {'b': 2}) + True + >>> is_match({'a': 1, 'b': 2}, {'b': 3}) + False + >>> is_match({'a': [{'b': [{'c': 3, 'd': 4}]}]},\ + {'a': [{'b': [{'d': 4}]}]}) + True + + .. versionadded:: 3.0.0 + + .. versionchanged:: 3.2.0 + Don't compare `obj` and `source` using ``type``. Use ``isinstance`` + exclusively. + + .. versionchanged:: 4.0.0 + Move `iteratee` argument to :func:`is_match_with`. + """ + return is_match_with(obj, source) + + +def is_match_cmp(source: t.Any) -> t.Callable[[t.Any], bool]: + """ + Curried version of :func:`is_match`. + + Args: + source: Object of property values to match. + + Returns: + A predicate checking whether passed :attr:`obj` is a match or not. + + Example: + + >>> is_match_cmp({"b": 2})({"a": 1, "b": 2}) + True + >>> is_match_cmp({"b": 3})({"a": 1, "b": 2}) + False + >>> is_match_cmp({"a": [{"b": [{"d": 4}]}]})({"a": [{"b": [{"c": 3, "d": 4}]}]}) + True + + .. versionadded:: 7.1.0 + """ + return lambda obj: is_match(obj, source) + + +def is_match_with( + obj: t.Any, + source: t.Any, + customizer: t.Any = None, + _key: t.Any = UNSET, + _obj: t.Any = UNSET, + _source: t.Any = UNSET, +) -> bool: + """ + This method is like :func:`is_match` except that it accepts customizer which is invoked to + compare values. If customizer returns ``None``, comparisons are handled by the method instead. + The customizer is invoked with five arguments: ``(obj_value, src_value, index|key, obj, + source)``. + + Args: + obj: Object to compare. + source: Object of property values to match. + customizer: Customizer used to compare values from `obj` and `source`. + + Returns: + Whether `obj` is a match or not. + + Example: + + >>> is_greeting = lambda val: val in ("hello", "hi") + >>> customizer = lambda ov, sv: is_greeting(ov) and is_greeting(sv) + >>> obj = {"greeting": "hello"} + >>> src = {"greeting": "hi"} + >>> is_match_with(obj, src, customizer) + True + + .. versionadded:: 4.0.0 + """ + if _obj is UNSET: + _obj = obj + + if _source is UNSET: + _source = source + + if not callable(customizer): + + def cbk(obj_value, src_value): + return obj_value == src_value + + # no attribute `_argcount` + cbk._argcount = 2 # type: ignore + else: + cbk = customizer + + if isinstance(source, (Mapping, Iterable)) and not isinstance(source, str): + # Set equal to True if source is empty, otherwise, False and then allow deep comparison to + # determine equality. + equal = not source + + # Walk a/b to determine equality. + for key, value in iterator(source): + try: + obj_value = base_get(obj, key) + equal = is_match_with(obj_value, value, cbk, _key=key, _obj=_obj, _source=_source) + except Exception: + equal = False + + if not equal: + break + else: + equal = callit(cbk, obj, source, _key, _obj, _source) + + return equal + + +def is_match_with_cmp(source: t.Any, customizer: t.Any = None) -> t.Callable[[t.Any], bool]: + """ + Curried version of :func:`is_match_with`. + + Args: + source: Object of property values to match. + customizer: Customizer used to compare values from `obj` and `source`. + + Returns: + A predicate checking whether passed :attr:`obj` is a match or not. + + Example: + + >>> is_greeting = lambda val: val in ("hello", "hi") + >>> customizer = lambda ov, sv: is_greeting(ov) and is_greeting(sv) + >>> obj = {"greeting": "hello"} + >>> src = {"greeting": "hi"} + >>> is_match_with_cmp(src, customizer)(obj) + True + + .. versionadded:: 7.1.0 + """ + return lambda obj: is_match_with(obj, source, customizer) + + +def is_monotone(value: t.Union[T, t.List[T]], op: t.Callable[[T, T], t.Any]) -> bool: + """ + Checks if `value` is monotonic when `operator` used for comparison. + + Args: + value: Value to check. + op: Operation to used for comparison. + + Returns: + Whether `value` is monotone. + + Example: + + >>> is_monotone([1, 1, 2, 3], operator.le) + True + >>> is_monotone([1, 1, 2, 3], operator.lt) + False + + .. versionadded:: 2.0.0 + """ + if not is_list(value): + l_value = [value] + else: + l_value = value # type: ignore + + search = ( + False + for x, y in zip(l_value, islice(l_value, 1, None)) + if not op(x, y) # type: ignore + ) + + return next(search, True) + + +def is_monotone_cmp( + op: t.Callable[[T, T], t.Any], +) -> t.Callable[[t.Union[T, t.List[T]]], bool]: + """ + Curried version of :func:`is_monotone`. + + Args: + op: Operation to used for comparison. + + Returns: + A predicate checking whether passed :attr:`value` is monotone. + + Example: + + >>> is_monotone_cmp(operator.le)([1, 1, 2, 3]) + True + >>> is_monotone_cmp(operator.lt)([1, 1, 2, 3]) + False + + .. versionadded:: 7.1.0 + """ + return lambda value: is_monotone(value, op) + + +def is_nan(value: t.Any) -> bool: + """ + Checks if `value` is not a number. + + Args: + value: Value to check. + + Returns: + Whether `value` is not a number. + + Example: + + >>> is_nan("a") + True + >>> is_nan(1) + False + >>> is_nan(1.0) + False + + .. versionadded:: 1.0.0 + """ + return not is_number(value) + + +def is_negative(value: t.Any) -> bool: + """ + Checks if `value` is negative. + + Args: + value: Value to check. + + Returns: + Whether `value` is negative. + + Example: + + >>> is_negative(-1) + True + >>> is_negative(0) + False + >>> is_negative(1) + False + + .. versionadded:: 2.0.0 + """ + return is_number(value) and value < 0 + + +def is_none(value: t.Any) -> TypeGuard[None]: + """ + Checks if `value` is `None`. + + Args: + value: Value to check. + + Returns: + Whether `value` is ``None``. + + Example: + + >>> is_none(None) + True + >>> is_none(False) + False + + .. versionadded:: 1.0.0 + """ + return value is None + + +def is_number(value: t.Any) -> bool: + """ + Checks if `value` is a number. + + Args: + value: Value to check. + + Returns: + Whether `value` is a number. + + Note: + Returns ``True`` for ``int``, ``long`` (PY2), ``float``, and + ``decimal.Decimal``. + + Example: + + >>> is_number(1) + True + >>> is_number(1.0) + True + >>> is_number("a") + False + + .. versionadded:: 1.0.0 + + .. versionchanged:: 3.0.0 + Added ``is_num`` as alias. + + .. versionchanged:: 4.0.0 + Removed alias ``is_num``. + """ + return not is_boolean(value) and isinstance(value, NUMBER_TYPES) + + +def is_object(value: t.Any) -> bool: + """ + Checks if `value` is a ``list`` or ``dict``. + + Args: + value: Value to check. + + Returns: + Whether `value` is ``list`` or ``dict``. + + Example: + + >>> is_object([]) + True + >>> is_object({}) + True + >>> is_object(()) + False + >>> is_object(1) + False + + .. versionadded:: 1.0.0 + """ + return isinstance(value, (list, dict)) + + +def is_odd(value: t.Any) -> bool: + """ + Checks if `value` is odd. + + Args: + value: Value to check. + + Returns: + Whether `value` is odd. + + Example: + + >>> is_odd(3) + True + >>> is_odd(2) + False + >>> is_odd("a") + False + + .. versionadded:: 2.0.0 + """ + return is_number(value) and value % 2 != 0 + + +def is_positive(value: t.Any) -> bool: + """ + Checks if `value` is positive. + + Args: + value: Value to check. + + Returns: + Whether `value` is positive. + + Example: + + >>> is_positive(1) + True + >>> is_positive(0) + False + >>> is_positive(-1) + False + + .. versionadded:: 2.0.0 + """ + return is_number(value) and value > 0 + + +def is_reg_exp(value: t.Any) -> TypeGuard[re.Pattern[t.Any]]: + """ + Checks if `value` is a ``RegExp`` object. + + Args: + value: Value to check. + + Returns: + Whether `value` is a RegExp object. + + Example: + + >>> is_reg_exp(re.compile("")) + True + >>> is_reg_exp("") + False + + .. versionadded:: 1.1.0 + + .. versionchanged:: 4.0.0 + Removed alias ``is_re``. + """ + return isinstance(value, RegExp) + + +def is_set(value: t.Any) -> bool: + """ + Checks if the given value is a set object or not. + + Args: + value: Value passed in by the user. + + Returns: + True if the given value is a set else False. + + Example: + + >>> is_set(set([1, 2])) + True + >>> is_set([1, 2, 3]) + False + + .. versionadded:: 4.0.0 + """ + return isinstance(value, set) + + +def is_strictly_decreasing( + value: t.Union["SupportsRichComparison", t.List["SupportsRichComparison"]], +) -> bool: + """ + Check if `value` is strictly decreasing. + + Args: + value: Value to check. + + Returns: + Whether `value` is strictly decreasing. + + Example: + + >>> is_strictly_decreasing([4, 3, 2, 1]) + True + >>> is_strictly_decreasing([4, 4, 2, 1]) + False + + .. versionadded:: 2.0.0 + """ + return is_monotone(value, operator.gt) # type: ignore + + +def is_strictly_increasing( + value: t.Union["SupportsRichComparison", t.List["SupportsRichComparison"]], +) -> bool: + """ + Check if `value` is strictly increasing. + + Args: + value: Value to check. + + Returns: + Whether `value` is strictly increasing. + + Example: + + >>> is_strictly_increasing([1, 2, 3, 4]) + True + >>> is_strictly_increasing([1, 1, 3, 4]) + False + + .. versionadded:: 2.0.0 + """ + return is_monotone(value, operator.lt) # type: ignore + + +def is_string(value: t.Any) -> TypeGuard[str]: + """ + Checks if `value` is a string. + + Args: + value: Value to check. + + Returns: + Whether `value` is a string. + + Example: + + >>> is_string("") + True + >>> is_string(1) + False + + .. versionadded:: 1.0.0 + """ + return isinstance(value, str) + + +def is_tuple(value: t.Any) -> bool: + """ + Checks if `value` is a tuple. + + Args: + value: Value to check. + + Returns: + Whether `value` is a tuple. + + Example: + + >>> is_tuple(()) + True + >>> is_tuple({}) + False + >>> is_tuple([]) + False + + .. versionadded:: 3.0.0 + """ + return isinstance(value, tuple) + + +def is_zero(value: t.Any) -> TypeGuard[int]: + """ + Checks if `value` is ``0``. + + Args: + value: Value to check. + + Returns: + Whether `value` is ``0``. + + Example: + + >>> is_zero(0) + True + >>> is_zero(1) + False + + .. versionadded:: 2.0.0 + """ + return value == 0 and is_integer(value) diff --git a/.venv/lib/python3.12/site-packages/pydash/py.typed b/.venv/lib/python3.12/site-packages/pydash/py.typed new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydash/py.typed diff --git a/.venv/lib/python3.12/site-packages/pydash/strings.py b/.venv/lib/python3.12/site-packages/pydash/strings.py new file mode 100644 index 00000000..40d2953e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydash/strings.py @@ -0,0 +1,2363 @@ +""" +String functions. + +.. versionadded:: 1.1.0 +""" + +from __future__ import annotations + +import html +import math +import re +import typing +import typing as t +import unicodedata +from urllib.parse import parse_qsl, urlencode, urlsplit, urlunsplit + +import pydash as pyd + +from .helpers import UNSET, Unset +from .types import NumberT + + +__all__ = ( + "camel_case", + "capitalize", + "chop", + "chop_right", + "chars", + "clean", + "count_substr", + "deburr", + "decapitalize", + "ends_with", + "ensure_ends_with", + "ensure_starts_with", + "escape", + "escape_reg_exp", + "has_substr", + "human_case", + "insert_substr", + "join", + "kebab_case", + "lines", + "lower_case", + "lower_first", + "number_format", + "pad", + "pad_end", + "pad_start", + "pascal_case", + "predecessor", + "prune", + "quote", + "reg_exp_js_match", + "reg_exp_js_replace", + "reg_exp_replace", + "repeat", + "replace", + "replace_end", + "replace_start", + "separator_case", + "series_phrase", + "series_phrase_serial", + "slugify", + "snake_case", + "split", + "start_case", + "starts_with", + "strip_tags", + "substr_left", + "substr_left_end", + "substr_right", + "substr_right_end", + "successor", + "surround", + "swap_case", + "title_case", + "to_lower", + "to_upper", + "trim", + "trim_end", + "trim_start", + "truncate", + "unescape", + "unquote", + "upper_case", + "upper_first", + "url", + "words", +) + +T = t.TypeVar("T") +T2 = t.TypeVar("T2") + + +class JSRegExp: + """ + Javascript-style regular expression pattern. + + Converts a Javascript-style regular expression to the equivalent Python version. + """ + + def __init__(self, reg_exp: str) -> None: + pattern, options = reg_exp[1:].rsplit("/", 1) + + self._global = "g" in options + self._ignore_case = "i" in options + + flags = re.I if self._ignore_case else 0 + self.pattern = re.compile(pattern, flags=flags) + + def find(self, text: str) -> t.List[str]: + """Return list of regular expression matches.""" + if self._global: + results = self.pattern.findall(text) + else: + res = self.pattern.search(text) + if res: + results = [res.group()] + else: + results = [] + return results + + def replace(self, text: str, repl: t.Union[str, t.Callable[[re.Match[str]], str]]) -> str: + """Replace parts of text that match the regular expression.""" + count = 0 if self._global else 1 + return self.pattern.sub(repl, text, count=count) + + +HTML_ESCAPES = {"&": "&", "<": "<", ">": ">", '"': """, "'": "'", "`": "`"} + +DEBURRED_LETTERS = { + "\xc0": "A", + "\xc1": "A", + "\xc2": "A", + "\xc3": "A", + "\xc4": "A", + "\xc5": "A", + "\xe0": "a", + "\xe1": "a", + "\xe2": "a", + "\xe3": "a", + "\xe4": "a", + "\xe5": "a", + "\xc7": "C", + "\xe7": "c", + "\xd0": "D", + "\xf0": "d", + "\xc8": "E", + "\xc9": "E", + "\xca": "E", + "\xcb": "E", + "\xe8": "e", + "\xe9": "e", + "\xea": "e", + "\xeb": "e", + "\xcc": "I", + "\xcd": "I", + "\xce": "I", + "\xcf": "I", + "\xec": "i", + "\xed": "i", + "\xee": "i", + "\xef": "i", + "\xd1": "N", + "\xf1": "n", + "\xd2": "O", + "\xd3": "O", + "\xd4": "O", + "\xd5": "O", + "\xd6": "O", + "\xd8": "O", + "\xf2": "o", + "\xf3": "o", + "\xf4": "o", + "\xf5": "o", + "\xf6": "o", + "\xf8": "o", + "\xd9": "U", + "\xda": "U", + "\xdb": "U", + "\xdc": "U", + "\xf9": "u", + "\xfa": "u", + "\xfb": "u", + "\xfc": "u", + "\xdd": "Y", + "\xfd": "y", + "\xff": "y", + "\xc6": "Ae", + "\xe6": "ae", + "\xde": "Th", + "\xfe": "th", + "\xdf": "ss", + "\xd7": " ", + "\xf7": " ", +} + +# Use Javascript style regex to make Lo-Dash compatibility easier. +# Lodash Regex definitions: https://github.com/lodash/lodash/blob/master/.internal/unicodeWords.js + +# References: https://github.com/lodash/lodash/blob/master/words.js#L8 +RS_ASCII_WORDS = "/[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g" +RS_LATIN1 = "/[\xc0-\xff]/g" + +# Used to compose unicode character classes. +RS_ASTRAL_RANGE = "\\ud800-\\udfff" +RS_COMBO_MARKS_RANGE = "\\u0300-\\u036f" +RE_COMBO_HALF_MARKS_RANGE = "\\ufe20-\\ufe2f" +RS_COMBO_SYMBOLS_RANGE = "\\u20d0-\\u20ff" +RS_COMBO_MARKS_EXTENDED_RANGE = "\\u1ab0-\\u1aff" +RS_COMBO_MARKS_SUPPLEMENT_RANGE = "\\u1dc0-\\u1dff" +RS_COMBO_RANGE = ( + RS_COMBO_MARKS_RANGE + + RE_COMBO_HALF_MARKS_RANGE + + RS_COMBO_SYMBOLS_RANGE + + RS_COMBO_MARKS_EXTENDED_RANGE + + RS_COMBO_MARKS_SUPPLEMENT_RANGE +) +RS_DINGBAT_RANGE = "\\u2700-\\u27bf" +RS_LOWER_RANGE = "a-z\\xdf-\\xf6\\xf8-\\xff" +RS_MATH_OP_RANGE = "\\xac\\xb1\\xd7\\xf7" +RS_NON_CHAR_RANGE = "\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf" +RS_PUNCTUATION_RANGE = "\\u2000-\\u206f" +RS_SPACE_RANGE = ( + " \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\" + "u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\" + "u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000" +) +RS_UPPER_RANGE = "A-Z\\xc0-\\xd6\\xd8-\\xde" +RS_VAR_RANGE = "\\ufe0e\\ufe0f" +RS_BREAK_RANGE = RS_MATH_OP_RANGE + RS_NON_CHAR_RANGE + RS_PUNCTUATION_RANGE + RS_SPACE_RANGE + +# Used to compose unicode capture groups. +RS_APOS = "['\u2019]" +RS_BREAK = f"[{RS_BREAK_RANGE}]" +RS_COMBO = f"[{RS_COMBO_RANGE}]" +RS_DIGIT = "\\d" +RS_DINGBAT = f"[{RS_DINGBAT_RANGE}]" +RS_LOWER = f"[{RS_LOWER_RANGE}]" +RS_MISC = ( + f"[^{RS_ASTRAL_RANGE}{RS_BREAK_RANGE}{RS_DIGIT}" + f"{RS_DINGBAT_RANGE}{RS_LOWER_RANGE}{RS_UPPER_RANGE}]" +) +RS_FITZ = "\\ud83c[\\udffb-\\udfff]" +RS_MODIFIER = f"(?:{RS_COMBO}|{RS_FITZ})" +RS_NON_ASTRAL = f"[^{RS_ASTRAL_RANGE}]" +RS_REGIONAL = "(?:\\ud83c[\\udde6-\\uddff]){2}" +RS_SURR_PAIR = "[\\ud800-\\udbff][\\udc00-\\udfff]" +RS_UPPER = f"[{RS_UPPER_RANGE}]" +RS_ZWJ = "\\u200d" + +# Used to compose unicode regexes. +RS_MISC_LOWER = f"(?:{RS_LOWER}|{RS_MISC})" +RS_MISC_UPPER = f"(?:{RS_UPPER}|{RS_MISC})" +RS_OPT_CONTR_LOWER = f"(?:{RS_APOS}(?:d|ll|m|re|s|t|ve))?" +RS_OPT_CONTR_UPPER = f"(?:{RS_APOS}(?:D|LL|M|RE|S|T|VE))?" +RE_OPT_MOD = f"{RS_MODIFIER}?" +RS_OPT_VAR = f"[{RS_VAR_RANGE}]?" +RS_OPT_JOIN = ( + f"(?:{RS_ZWJ}(?:{RS_NON_ASTRAL}|{RS_REGIONAL}|{RS_SURR_PAIR}){RS_OPT_VAR}{RE_OPT_MOD})*" +) +RS_ORD_LOWER = "\\d*(?:1st|2nd|3rd|(?![123])\\dth)(?=\\b|[A-Z_])" +RS_ORD_UPPER = "\\d*(?:1ST|2ND|3RD|(?![123])\\dTH)(?=\\b|[a-z_])" +RS_SEQ = RS_OPT_VAR + RE_OPT_MOD + RS_OPT_JOIN +RS_EMOJI = f"(?:{RS_DINGBAT}|{RS_REGIONAL}|{RS_SURR_PAIR}){RS_SEQ}" + +RS_HAS_UNICODE_WORD = "[a-z][A-Z]|[A-Z]{2}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]" +RS_UNICODE_WORDS = ( + f"/" + f"{RS_UPPER}?{RS_LOWER}+{RS_OPT_CONTR_LOWER}(?={RS_BREAK}|{RS_UPPER}|$)" + f"|{RS_MISC_UPPER}+{RS_OPT_CONTR_UPPER}(?={RS_BREAK}|{RS_UPPER}{RS_MISC_LOWER}|$)" + f"|{RS_UPPER}?{RS_MISC_LOWER}+{RS_OPT_CONTR_LOWER}" + f"|{RS_UPPER}+{RS_OPT_CONTR_UPPER}" + f"|{RS_ORD_UPPER}" + f"|{RS_ORD_LOWER}" + f"|{RS_DIGIT}+" + f"|{RS_EMOJI}" + f"/g" +) + +# Compiled regexes for use in functions. +JS_RE_ASCII_WORDS = JSRegExp(RS_ASCII_WORDS) +JS_RE_UNICODE_WORDS = JSRegExp(RS_UNICODE_WORDS) +JS_RE_LATIN1 = JSRegExp(RS_LATIN1) +RE_HAS_UNICODE_WORD = re.compile(RS_HAS_UNICODE_WORD) +RE_APOS = re.compile(RS_APOS) +RE_HTML_TAGS = re.compile(r"<\/?[^>]+>") + + +def camel_case(text: t.Any) -> str: + """ + Converts `text` to camel case. + + Args: + text: String to convert. + + Returns: + String converted to camel case. + + Example: + + >>> camel_case("FOO BAR_bAz") + 'fooBarBAz' + + .. versionadded:: 1.1.0 + + .. versionchanged:: 5.0.0 + Improved unicode word support. + """ + text = "".join(word.title() for word in compounder(text)) + return text[:1].lower() + text[1:] + + +def capitalize(text: t.Any, strict: bool = True) -> str: + """ + Capitalizes the first character of `text`. + + Args: + text: String to capitalize. + strict: Whether to cast rest of string to lower case. Defaults to ``True``. + + Returns: + Capitalized string. + + Example: + + >>> capitalize("once upon a TIME") + 'Once upon a time' + >>> capitalize("once upon a TIME", False) + 'Once upon a TIME' + + .. versionadded:: 1.1.0 + + .. versionchanged:: 3.0.0 + Added `strict` option. + """ + text = pyd.to_string(text) + return text.capitalize() if strict else text[:1].upper() + text[1:] + + +def chars(text: t.Any) -> t.List[str]: + """ + Split `text` into a list of single characters. + + Args: + text: String to split up. + + Returns: + List of individual characters. + + Example: + + >>> chars("onetwo") + ['o', 'n', 'e', 't', 'w', 'o'] + + .. versionadded:: 3.0.0 + """ + return list(pyd.to_string(text)) + + +def chop(text: t.Any, step: int) -> t.List[str]: + """ + Break up `text` into intervals of length `step`. + + Args: + text: String to chop. + step: Interval to chop `text`. + + Returns: + List of chopped characters. If `text` is `None` an empty list is returned. + + Example: + + >>> chop("abcdefg", 3) + ['abc', 'def', 'g'] + + .. versionadded:: 3.0.0 + """ + if text is None: + return [] + + text = pyd.to_string(text) + + if step <= 0: + chopped = [text] + else: + chopped = [text[i : i + step] for i in range(0, len(text), step)] + + return chopped + + +def chop_right(text: t.Any, step: int) -> t.List[str]: + """ + Like :func:`chop` except `text` is chopped from right. + + Args: + text: String to chop. + step: Interval to chop `text`. + + Returns: + List of chopped characters. + + Example: + + >>> chop_right("abcdefg", 3) + ['a', 'bcd', 'efg'] + + .. versionadded:: 3.0.0 + """ + if text is None: + return [] + + text = pyd.to_string(text) + + if step <= 0: + chopped = [text] + else: + text_len = len(text) + chopped = [text[-(i + step) : text_len - i] for i in range(0, text_len, step)][::-1] + + return chopped + + +def clean(text: t.Any) -> str: + """ + Trim and replace multiple spaces with a single space. + + Args: + text: String to clean. + + Returns: + Cleaned string. + + Example: + + >>> clean("a b c d") + 'a b c d' + + .. versionadded:: 3.0.0 + """ + text = pyd.to_string(text) + return " ".join(pyd.compact(text.split())) + + +def count_substr(text: t.Any, subtext: t.Any) -> int: + """ + Count the occurrences of `subtext` in `text`. + + Args: + text: Source string to count from. + subtext: String to count. + + Returns: + Number of occurrences of `subtext` in `text`. + + Example: + + >>> count_substr("aabbccddaabbccdd", "bc") + 2 + + .. versionadded:: 3.0.0 + """ + if text is None or subtext is None: + return 0 + + text = pyd.to_string(text) + subtext = pyd.to_string(subtext) + + return text.count(subtext) + + +def deburr(text: t.Any) -> str: + """ + Deburrs `text` by converting latin-1 supplementary letters to basic latin letters. + + Args: + text: String to deburr. + + Returns: + Deburred string. + + Example: + + >>> deburr("déjà vu") + '... + >>> "deja vu" + 'deja vu' + + .. versionadded:: 2.0.0 + """ + text = pyd.to_string(text) + return JS_RE_LATIN1.replace( + text, lambda match: DEBURRED_LETTERS.get(match.group(), match.group()) + ) + + +def decapitalize(text: t.Any) -> str: + """ + Decaptitalizes the first character of `text`. + + Args: + text: String to decapitalize. + + Returns: + Decapitalized string. + + Example: + + >>> decapitalize("FOO BAR") + 'fOO BAR' + + .. versionadded:: 3.0.0 + """ + text = pyd.to_string(text) + return text[:1].lower() + text[1:] + + +def ends_with(text: t.Any, target: t.Any, position: t.Union[int, None] = None) -> bool: + """ + Checks if `text` ends with a given target string. + + Args: + text: String to check. + target: String to check for. + position: Position to search from. Defaults to end of `text`. + + Returns: + Whether `text` ends with `target`. + + Example: + + >>> ends_with("abc def", "def") + True + >>> ends_with("abc def", 4) + False + + .. versionadded:: 1.1.0 + """ + target = pyd.to_string(target) + text = pyd.to_string(text) + + if position is None: + position = len(text) + + return text[:position].endswith(target) + + +def ensure_ends_with(text: t.Any, suffix: t.Any) -> str: + """ + Append a given suffix to a string, but only if the source string does not end with that suffix. + + Args: + text: Source string to append `suffix` to. + suffix: String to append to the source string if the source string does not end with + `suffix`. + + Returns: + source string possibly extended by `suffix`. + + Example: + + >>> ensure_ends_with("foo bar", "!") + 'foo bar!' + >>> ensure_ends_with("foo bar!", "!") + 'foo bar!' + + .. versionadded:: 2.4.0 + """ + text = pyd.to_string(text) + suffix = pyd.to_string(suffix) + if text.endswith(suffix): + return text + return f"{text}{suffix}" + + +def ensure_starts_with(text: t.Any, prefix: t.Any) -> str: + """ + Prepend a given prefix to a string, but only if the source string does not start with that + prefix. + + Args: + text: Source string to prepend `prefix` to. + prefix: String to prepend to the source string if the source string does not start + with `prefix`. + + Returns: + source string possibly prefixed by `prefix` + + Example: + + >>> ensure_starts_with("foo bar", "Oh my! ") + 'Oh my! foo bar' + >>> ensure_starts_with("Oh my! foo bar", "Oh my! ") + 'Oh my! foo bar' + + .. versionadded:: 2.4.0 + """ + text = pyd.to_string(text) + prefix = pyd.to_string(prefix) + if text.startswith(prefix): + return text + return f"{prefix}{text}" + + +def escape(text: t.Any) -> str: + r""" + Converts the characters ``&``, ``<``, ``>``, ``"``, ``'``, and ``\``` in `text` to their + corresponding HTML entities. + + Args: + text: String to escape. + + Returns: + HTML escaped string. + + Example: + + >>> escape('"1 > 2 && 3 < 4"') + '"1 > 2 && 3 < 4"' + + .. versionadded:: 1.0.0 + + .. versionchanged:: 1.1.0 + Moved function to :mod:`pydash.strings`. + """ + text = pyd.to_string(text) + # NOTE: Not using html.escape because Lo-Dash escapes certain chars differently (e.g. `'` isn't + # escaped by html.escape() but is by Lo-Dash). + return "".join(HTML_ESCAPES.get(char, char) for char in text) + + +def escape_reg_exp(text: t.Any) -> str: + """ + Escapes the RegExp special characters in `text`. + + Args: + text: String to escape. + + Returns: + RegExp escaped string. + + Example: + + >>> escape_reg_exp("[()]") + '\\\\[\\\\(\\\\)\\\\]' + + .. versionadded:: 1.1.0 + + .. versionchanged:: 4.0.0 + Removed alias ``escape_re`` + """ + text = pyd.to_string(text) + return re.escape(text) + + +def has_substr(text: t.Any, subtext: t.Any) -> bool: + """ + Returns whether `subtext` is included in `text`. + + Args: + text: String to search. + subtext: String to search for. + + Returns: + Whether `subtext` is found in `text`. + + Example: + + >>> has_substr("abcdef", "bc") + True + >>> has_substr("abcdef", "bb") + False + + .. versionadded:: 3.0.0 + """ + text = pyd.to_string(text) + subtext = pyd.to_string(subtext) + return text.find(subtext) >= 0 + + +def human_case(text: t.Any) -> str: + """ + Converts `text` to human case which has only the first letter capitalized and each word + separated by a space. + + Args: + text: String to convert. + + Returns: + String converted to human case. + + Example: + + >>> human_case("abc-def_hij lmn") + 'Abc def hij lmn' + >>> human_case("user_id") + 'User' + + .. versionadded:: 3.0.0 + + .. versionchanged:: 5.0.0 + Improved unicode word support. + """ + return ( + pyd.chain(text) + .snake_case() + .reg_exp_replace("_id$", "") + .replace("_", " ") + .capitalize() + .value() + ) + + +def insert_substr(text: t.Any, index: int, subtext: t.Any) -> str: + """ + Insert `subtext` in `text` starting at position `index`. + + Args: + text: String to add substring to. + index: String index to insert into. + subtext: String to insert. + + Returns: + Modified string. + + Example: + + >>> insert_substr("abcdef", 3, "--") + 'abc--def' + + .. versionadded:: 3.0.0 + """ + text = pyd.to_string(text) + subtext = pyd.to_string(subtext) + return text[:index] + subtext + text[index:] + + +def join(array: t.Iterable[t.Any], separator: t.Any = "") -> str: + """ + Joins an iterable into a string using `separator` between each element. + + Args: + array: Iterable to implode. + separator: Separator to using when joining. Defaults to ``''``. + + Returns: + Joined string. + + Example: + + >>> join(["a", "b", "c"]) == "abc" + True + >>> join([1, 2, 3, 4], "&") == "1&2&3&4" + True + >>> join("abcdef", "-") == "a-b-c-d-e-f" + True + + .. versionadded:: 2.0.0 + + .. versionchanged:: 4.0.0 + Removed alias ``implode``. + """ + return pyd.to_string(separator).join(pyd.map_(array or (), pyd.to_string)) + + +def kebab_case(text: t.Any) -> str: + """ + Converts `text` to kebab case (a.k.a. spinal case). + + Args: + text: String to convert. + + Returns: + String converted to kebab case. + + Example: + + >>> kebab_case("a b c_d-e!f") + 'a-b-c-d-e-f' + + .. versionadded:: 1.1.0 + + .. versionchanged:: 5.0.0 + Improved unicode word support. + """ + return "-".join(word.lower() for word in compounder(text) if word) + + +def lines(text: t.Any) -> t.List[str]: + r""" + Split lines in `text` into an array. + + Args: + text: String to split. + + Returns: + String split by lines. + + Example: + + >>> lines("a\nb\r\nc") + ['a', 'b', 'c'] + + .. versionadded:: 3.0.0 + """ + text = pyd.to_string(text) + return text.splitlines() + + +def lower_case(text: t.Any) -> str: + """ + Converts string to lower case as space separated words. + + Args: + text: String to convert. + + Returns: + String converted to lower case as space separated words. + + Example: + + >>> lower_case("fooBar") + 'foo bar' + >>> lower_case("--foo-Bar--") + 'foo bar' + >>> lower_case('/?*Foo10/;"B*Ar') + 'foo 10 b ar' + + .. versionadded:: 4.0.0 + + .. versionchanged:: 5.0.0 + Improved unicode word support. + """ + return " ".join(compounder(text)).lower() + + +def lower_first(text: str) -> str: + """ + Converts the first character of string to lower case. + + Args: + text: String passed in by the user. + + Returns: + String in which the first character is converted to lower case. + + Example: + + >>> lower_first("FRED") + 'fRED' + >>> lower_first("Foo Bar") + 'foo Bar' + >>> lower_first("1foobar") + '1foobar' + >>> lower_first(";foobar") + ';foobar' + + .. versionadded:: 4.0.0 + """ + return text[:1].lower() + text[1:] + + +def number_format( + number: NumberT, scale: int = 0, decimal_separator: str = ".", order_separator: str = "," +) -> str: + """ + Format a number to scale with custom decimal and order separators. + + Args: + number: Number to format. + scale: Number of decimals to include. Defaults to ``0``. + decimal_separator: Decimal separator to use. Defaults to ``'.'``. + order_separator: Order separator to use. Defaults to ``','``. + + Returns: + Number formatted as string. + + Example: + + >>> number_format(1234.5678) + '1,235' + >>> number_format(1234.5678, 2, ",", ".") + '1.234,57' + + .. versionadded:: 3.0.0 + """ + # Create a string formatter which converts number to the appropriately scaled representation. + fmt = f"{{0:.{scale:d}f}}" + + try: + num_parts = fmt.format(number).split(".") + except ValueError: + text = "" + else: + int_part = num_parts[0] + dec_part = (num_parts + [""])[1] + + # Reverse the integer part, chop it into groups of 3, join on `order_separator`, and then + # un-reverse the string. + int_part = order_separator.join(chop(int_part[::-1], 3))[::-1] + + text = decimal_separator.join(pyd.compact([int_part, dec_part])) + + return text + + +def pad(text: t.Any, length: int, chars: t.Any = " ") -> str: + """ + Pads `text` on the left and right sides if it is shorter than the given padding length. The + `chars` string may be truncated if the number of padding characters can't be evenly divided by + the padding length. + + Args: + text: String to pad. + length: Amount to pad. + chars: Characters to pad with. Defaults to ``" "``. + + Returns: + Padded string. + + Example: + + >>> pad("abc", 5) + ' abc ' + >>> pad("abc", 6, "x") + 'xabcxx' + >>> pad("abc", 5, "...") + '.abc.' + + .. versionadded:: 1.1.0 + + .. versionchanged:: 3.0.0 + Fix handling of multiple `chars` so that padded string isn't over padded. + """ + # pylint: disable=redefined-outer-name + text = pyd.to_string(text) + text_len = len(text) + + if text_len >= length: + return text + + mid = (length - text_len) / 2.0 + left_len = int(math.floor(mid)) + right_len = int(math.ceil(mid)) + chars = pad_end("", right_len, chars) + + return chars[:left_len] + text + chars + + +def pad_end(text: t.Any, length: int, chars: t.Any = " ") -> str: + """ + Pads `text` on the right side if it is shorter than the given padding length. The `chars` string + may be truncated if the number of padding characters can't be evenly divided by the padding + length. + + Args: + text: String to pad. + length: Amount to pad. + chars: Characters to pad with. Defaults to ``" "``. + + Returns: + Padded string. + + Example: + + >>> pad_end("abc", 5) + 'abc ' + >>> pad_end("abc", 5, ".") + 'abc..' + + .. versionadded:: 1.1.0 + + .. versionchanged:: 4.0.0 + Renamed from ``pad_right`` to ``pad_end``. + """ + # pylint: disable=redefined-outer-name + text = pyd.to_string(text) + length = max((length, len(text))) + return (text + repeat(chars, length))[:length] + + +def pad_start(text: t.Any, length: int, chars: t.Any = " ") -> str: + """ + Pads `text` on the left side if it is shorter than the given padding length. The `chars` string + may be truncated if the number of padding characters can't be evenly divided by the padding + length. + + Args: + text: String to pad. + length: Amount to pad. + chars: Characters to pad with. Defaults to ``" "``. + + Returns: + Padded string. + + Example: + + >>> pad_start("abc", 5) + ' abc' + >>> pad_start("abc", 5, ".") + '..abc' + + .. versionadded:: 1.1.0 + + .. versionchanged:: 4.0.0 + Renamed from ``pad_left`` to ``pad_start``. + """ + # pylint: disable=redefined-outer-name + text = pyd.to_string(text) + length = max(length, len(text)) + return (repeat(chars, length) + text)[-length:] + + +def pascal_case(text: t.Any, strict: bool = True) -> str: + """ + Like :func:`camel_case` except the first letter is capitalized. + + Args: + text: String to convert. + strict: Whether to cast rest of string to lower case. Defaults to ``True``. + + Returns: + String converted to class case. + + Example: + + >>> pascal_case("FOO BAR_bAz") + 'FooBarBaz' + >>> pascal_case("FOO BAR_bAz", False) + 'FooBarBAz' + + .. versionadded:: 3.0.0 + + .. versionchanged:: 5.0.0 + Improved unicode word support. + """ + text = pyd.to_string(text) + + if strict: + text = text.lower() + + return capitalize(camel_case(text), strict=False) + + +def predecessor(char: t.Any) -> str: + """ + Return the predecessor character of `char`. + + Args: + char: Character to find the predecessor of. + + Returns: + Predecessor character. + + Example: + + >>> predecessor("c") + 'b' + >>> predecessor("C") + 'B' + >>> predecessor("3") + '2' + + .. versionadded:: 3.0.0 + """ + char = pyd.to_string(char) + return chr(ord(char) - 1) + + +def prune(text: t.Any, length: int = 0, omission: str = "...") -> str: + """ + Like :func:`truncate` except it ensures that the pruned string doesn't exceed the original + length, i.e., it avoids half-chopped words when truncating. If the pruned text + `omission` text + is longer than the original text, then the original text is returned. + + Args: + text: String to prune. + length: Target prune length. Defaults to ``0``. + omission: Omission text to append to the end of the pruned string. Defaults + to ``'...'``. + + Returns: + Pruned string. + + Example: + + >>> prune("Fe fi fo fum", 5) + 'Fe fi...' + >>> prune("Fe fi fo fum", 6) + 'Fe fi...' + >>> prune("Fe fi fo fum", 7) + 'Fe fi...' + >>> prune("Fe fi fo fum", 8, ",,,") + 'Fe fi fo,,,' + + .. versionadded:: 3.0.0 + """ + text = pyd.to_string(text) + text_len = len(text) + omission_len = len(omission) + + if text_len <= length: + return text + + # Replace non-alphanumeric chars with whitespace. + def repl(match): + char = match.group(0) + return " " if char.upper() == char.lower() else char + + subtext = reg_exp_replace(text[: length + 1], r".(?=\W*\w*$)", repl) + + if re.match(r"\w\w", subtext[-2:]): + # Last two characters are alphanumeric. Remove last "word" from end of string so that we + # prune to the next whole word. + subtext = reg_exp_replace(subtext, r"\s*\S+$", "") + else: + # Last character (at least) is whitespace. So remove that character as well as any other + # whitespace. + subtext = subtext[:-1].rstrip() + + subtext_len = len(subtext) + + # Only add omission text if doing so will result in a string that is equal to or smaller than + # the original. + if (subtext_len + omission_len) <= text_len: + text = text[:subtext_len] + omission + + return text + + +def quote(text: t.Any, quote_char: t.Any = '"') -> str: + """ + Quote a string with another string. + + Args: + text: String to be quoted. + quote_char: the quote character. Defaults to ``'"'``. + + Returns: + the quoted string. + + Example: + + >>> quote("To be or not to be") + '"To be or not to be"' + >>> quote("To be or not to be", "'") + "'To be or not to be'" + + .. versionadded:: 2.4.0 + """ + return surround(text, quote_char) + + +def reg_exp_js_match(text: t.Any, reg_exp: str) -> t.List[str]: + """ + Return list of matches using Javascript style regular expression. + + Args: + text: String to evaluate. + reg_exp: Javascript style regular expression. + + Returns: + List of matches. + + Example: + + >>> reg_exp_js_match("aaBBcc", "/bb/") + [] + >>> reg_exp_js_match("aaBBcc", "/bb/i") + ['BB'] + >>> reg_exp_js_match("aaBBccbb", "/bb/i") + ['BB'] + >>> reg_exp_js_match("aaBBccbb", "/bb/gi") + ['BB', 'bb'] + + .. versionadded:: 2.0.0 + + .. versionchanged:: 3.0.0 + Reordered arguments to make `text` first. + + .. versionchanged:: 4.0.0 + Renamed from ``js_match`` to ``reg_exp_js_match``. + """ + text = pyd.to_string(text) + return JSRegExp(reg_exp).find(text) + + +def reg_exp_js_replace( + text: t.Any, reg_exp: str, repl: t.Union[str, t.Callable[[re.Match[str]], str]] +) -> str: + """ + Replace `text` with `repl` using Javascript style regular expression to find matches. + + Args: + text: String to evaluate. + reg_exp: Javascript style regular expression. + repl: Replacement string or callable. + + Returns: + Modified string. + + Example: + + >>> reg_exp_js_replace("aaBBcc", "/bb/", "X") + 'aaBBcc' + >>> reg_exp_js_replace("aaBBcc", "/bb/i", "X") + 'aaXcc' + >>> reg_exp_js_replace("aaBBccbb", "/bb/i", "X") + 'aaXccbb' + >>> reg_exp_js_replace("aaBBccbb", "/bb/gi", "X") + 'aaXccX' + + .. versionadded:: 2.0.0 + + .. versionchanged:: 3.0.0 + Reordered arguments to make `text` first. + + .. versionchanged:: 4.0.0 + Renamed from ``js_replace`` to ``reg_exp_js_replace``. + """ + text = pyd.to_string(text) + if not pyd.is_function(repl): + repl = pyd.to_string(repl) + return JSRegExp(reg_exp).replace(text, repl) + + +def reg_exp_replace( + text: t.Any, + pattern: t.Any, + repl: t.Union[str, t.Callable[[re.Match[str]], str]], + ignore_case: bool = False, + count: int = 0, +) -> str: + """ + Replace occurrences of regex `pattern` with `repl` in `text`. Optionally, ignore case when + replacing. Optionally, set `count` to limit number of replacements. + + Args: + text: String to replace. + pattern: Pattern to find and replace. + repl: String to substitute `pattern` with. + ignore_case: Whether to ignore case when replacing. Defaults to ``False``. + count: Maximum number of occurrences to replace. Defaults to ``0`` which + replaces all. + + Returns: + Replaced string. + + Example: + + >>> reg_exp_replace("aabbcc", "b", "X") + 'aaXXcc' + >>> reg_exp_replace("aabbcc", "B", "X", ignore_case=True) + 'aaXXcc' + >>> reg_exp_replace("aabbcc", "b", "X", count=1) + 'aaXbcc' + >>> reg_exp_replace("aabbcc", "[ab]", "X") + 'XXXXcc' + + .. versionadded:: 3.0.0 + + .. versionchanged:: 4.0.0 + Renamed from ``re_replace`` to ``reg_exp_replace``. + """ + if pattern is None: + return pyd.to_string(text) + + return replace(text, pattern, repl, ignore_case=ignore_case, count=count, escape=False) + + +def repeat(text: t.Any, n: t.SupportsInt = 0) -> str: + """ + Repeats the given string `n` times. + + Args: + text: String to repeat. + n: Number of times to repeat the string. + + Returns: + Repeated string. + + Example: + + >>> repeat(".", 5) + '.....' + + .. versionadded:: 1.1.0 + """ + return pyd.to_string(text) * int(n) + + +def replace( + text: t.Any, + pattern: t.Any, + repl: t.Union[str, t.Callable[[re.Match[str]], str]], + ignore_case: bool = False, + count: int = 0, + escape: bool = True, + from_start: bool = False, + from_end: bool = False, +) -> str: + """ + Replace occurrences of `pattern` with `repl` in `text`. Optionally, ignore case when replacing. + Optionally, set `count` to limit number of replacements. + + Args: + text: String to replace. + pattern: Pattern to find and replace. + repl: String to substitute `pattern` with. + ignore_case: Whether to ignore case when replacing. Defaults to ``False``. + count: Maximum number of occurrences to replace. Defaults to ``0`` which + replaces all. + escape: Whether to escape `pattern` when searching. This is needed if a + literal replacement is desired when `pattern` may contain special regular expression + characters. Defaults to ``True``. + from_start: Whether to limit replacement to start of string. + from_end: Whether to limit replacement to end of string. + + Returns: + Replaced string. + + Example: + + >>> replace("aabbcc", "b", "X") + 'aaXXcc' + >>> replace("aabbcc", "B", "X", ignore_case=True) + 'aaXXcc' + >>> replace("aabbcc", "b", "X", count=1) + 'aaXbcc' + >>> replace("aabbcc", "[ab]", "X") + 'aabbcc' + >>> replace("aabbcc", "[ab]", "X", escape=False) + 'XXXXcc' + + .. versionadded:: 3.0.0 + + .. versionchanged:: 4.1.0 + Added ``from_start`` and ``from_end`` arguments. + + .. versionchanged:: 5.0.0 + Added support for ``pattern`` as ``typing.Pattern`` object. + """ + text = pyd.to_string(text) + + if pattern is None: + return text + + if not pyd.is_function(repl): + repl = pyd.to_string(repl) + + flags = re.IGNORECASE if ignore_case else 0 + + if isinstance(pattern, typing.Pattern): + pat = pattern + else: + pattern = pyd.to_string(pattern) + + if escape: + pattern = re.escape(pattern) + + if from_start and not pattern.startswith("^"): + pattern = "^" + pattern + + if from_end and not pattern.endswith("$"): + pattern += "$" + + pat = re.compile(pattern, flags=flags) + + return pat.sub(repl, text, count=count) + + +def replace_end( + text: t.Any, + pattern: t.Any, + repl: t.Union[str, t.Callable[[re.Match[str]], str]], + ignore_case: bool = False, + escape: bool = True, +) -> str: + """ + Like :func:`replace` except it only replaces `text` with `repl` if `pattern` mathces the end of + `text`. + + Args: + text: String to replace. + pattern: Pattern to find and replace. + repl: String to substitute `pattern` with. + ignore_case: Whether to ignore case when replacing. Defaults to ``False``. + escape: Whether to escape `pattern` when searching. This is needed if a + literal replacement is desired when `pattern` may contain special regular expression + characters. Defaults to ``True``. + + Returns: + Replaced string. + + Example: + + >>> replace_end("aabbcc", "b", "X") + 'aabbcc' + >>> replace_end("aabbcc", "c", "X") + 'aabbcX' + + .. versionadded:: 4.1.0 + """ + return replace(text, pattern, repl, ignore_case=ignore_case, escape=escape, from_end=True) + + +def replace_start( + text: t.Any, + pattern: t.Any, + repl: t.Union[str, t.Callable[[re.Match[str]], str]], + ignore_case: bool = False, + escape: bool = True, +) -> str: + """ + Like :func:`replace` except it only replaces `text` with `repl` if `pattern` mathces the start + of `text`. + + Args: + text: String to replace. + pattern: Pattern to find and replace. + repl: String to substitute `pattern` with. + ignore_case: Whether to ignore case when replacing. Defaults to ``False``. + escape: Whether to escape `pattern` when searching. This is needed if a + literal replacement is desired when `pattern` may contain special regular expression + characters. Defaults to ``True``. + + Returns: + Replaced string. + + Example: + + >>> replace_start("aabbcc", "b", "X") + 'aabbcc' + >>> replace_start("aabbcc", "a", "X") + 'Xabbcc' + + .. versionadded:: 4.1.0 + """ + return replace(text, pattern, repl, ignore_case=ignore_case, escape=escape, from_start=True) + + +def separator_case(text: t.Any, separator: str) -> str: + """ + Splits `text` on words and joins with `separator`. + + Args: + text: String to convert. + separator: Separator to join words with. + + Returns: + Converted string. + + Example: + + >>> separator_case("a!!b___c.d", "-") + 'a-b-c-d' + + .. versionadded:: 3.0.0 + + .. versionchanged:: 5.0.0 + Improved unicode word support. + """ + return separator.join(word.lower() for word in words(text) if word) + + +def series_phrase( + items: t.List[t.Any], + separator: t.Any = ", ", + last_separator: t.Any = " and ", + serial: bool = False, +) -> str: + """ + Join items into a grammatical series phrase, e.g., ``"item1, item2, item3 and item4"``. + + Args: + items: List of string items to join. + separator: Item separator. Defaults to ``', '``. + last_separator: Last item separator. Defaults to ``' and '``. + serial: Whether to include `separator` with `last_separator` when number of + items is greater than 2. Defaults to ``False``. + + Returns: + Joined string. + + Example: + + >>> series_phrase(["apples", "bananas", "peaches"]) + 'apples, bananas and peaches' + >>> series_phrase(["apples", "bananas", "peaches"], serial=True) + 'apples, bananas, and peaches' + >>> series_phrase(["apples", "bananas", "peaches"], "; ", ", or ") + 'apples; bananas, or peaches' + + + .. versionadded:: 3.0.0 + """ + items = pyd.chain(items).map(pyd.to_string).compact().value() + item_count = len(items) + + separator = pyd.to_string(separator) + last_separator = pyd.to_string(last_separator) + + if item_count > 2 and serial: + last_separator = separator.rstrip() + last_separator + + if item_count >= 2: + items = items[:-2] + [last_separator.join(items[-2:])] + + return separator.join(items) + + +def series_phrase_serial( + items: t.List[t.Any], separator: t.Any = ", ", last_separator: t.Any = " and " +) -> str: + """ + Join items into a grammatical series phrase using a serial separator, e.g., ``"item1, item2, + item3, and item4"``. + + Args: + items: List of string items to join. + separator: Item separator. Defaults to ``', '``. + last_separator: Last item separator. Defaults to ``' and '``. + + Returns: + Joined string. + + Example: + + >>> series_phrase_serial(["apples", "bananas", "peaches"]) + 'apples, bananas, and peaches' + + .. versionadded:: 3.0.0 + """ + return series_phrase(items, separator, last_separator, serial=True) + + +def slugify(text: t.Any, separator: str = "-") -> str: + """ + Convert `text` into an ASCII slug which can be used safely in URLs. Incoming `text` is converted + to unicode and noramlzied using the ``NFKD`` form. This results in some accented characters + being converted to their ASCII "equivalent" (e.g. ``é`` is converted to ``e``). Leading and + trailing whitespace is trimmed and any remaining whitespace or other special characters without + an ASCII equivalent are replaced with ``-``. + + Args: + text: String to slugify. + separator: Separator to use. Defaults to ``'-'``. + + Returns: + Slugified string. + + Example: + + >>> slugify("This is a slug.") == "this-is-a-slug" + True + >>> slugify("This is a slug.", "+") == "this+is+a+slug" + True + + .. versionadded:: 3.0.0 + + .. versionchanged:: 5.0.0 + Improved unicode word support. + + .. versionchanged:: 7.0.0 + Remove single quotes from output. + """ + normalized = ( + unicodedata.normalize("NFKD", pyd.to_string(text)) + .encode("ascii", "ignore") + .decode("utf8") + .replace("'", "") + ) + + return separator_case(normalized, separator) + + +def snake_case(text: t.Any) -> str: + """ + Converts `text` to snake case. + + Args: + text: String to convert. + + Returns: + String converted to snake case. + + Example: + + >>> snake_case("This is Snake Case!") + 'this_is_snake_case' + + .. versionadded:: 1.1.0 + + .. versionchanged:: 4.0.0 + Removed alias ``underscore_case``. + + .. versionchanged:: 5.0.0 + Improved unicode word support. + """ + return "_".join(word.lower() for word in compounder(text) if word) + + +def split(text: t.Any, separator: t.Union[str, Unset, None] = UNSET) -> t.List[str]: + """ + Splits `text` on `separator`. If `separator` not provided, then `text` is split on whitespace. + If `separator` is falsey, then `text` is split on every character. + + Args: + text: String to explode. + separator: Separator string to split on. Defaults to ``NoValue``. + + Returns: + Split string. + + Example: + + >>> split("one potato, two potatoes, three potatoes, four!") + ['one', 'potato,', 'two', 'potatoes,', 'three', 'potatoes,', 'four!'] + >>> split("one potato, two potatoes, three potatoes, four!", ",") + ['one potato', ' two potatoes', ' three potatoes', ' four!'] + + .. versionadded:: 2.0.0 + + .. versionchanged:: 3.0.0 + Changed `separator` default to ``NoValue`` and supported splitting on whitespace by default. + + .. versionchanged:: 4.0.0 + Removed alias ``explode``. + """ + text = pyd.to_string(text) + + if separator is UNSET: + ret = text.split() + elif separator: + ret = text.split(separator) + else: + ret = chars(text) + + return ret + + +def start_case(text: t.Any) -> str: + """ + Convert `text` to start case. + + Args: + text: String to convert. + + Returns: + String converted to start case. + + Example: + + >>> start_case("fooBar") + 'Foo Bar' + + .. versionadded:: 3.1.0 + + .. versionchanged:: 5.0.0 + Improved unicode word support. + """ + return " ".join(capitalize(word, strict=False) for word in compounder(text)) + + +def starts_with(text: t.Any, target: t.Any, position: int = 0) -> bool: + """ + Checks if `text` starts with a given target string. + + Args: + text: String to check. + target: String to check for. + position: Position to search from. Defaults to beginning of `text`. + + Returns: + Whether `text` starts with `target`. + + Example: + + >>> starts_with("abcdef", "a") + True + >>> starts_with("abcdef", "b") + False + >>> starts_with("abcdef", "a", 1) + False + + .. versionadded:: 1.1.0 + """ + text = pyd.to_string(text) + target = pyd.to_string(target) + return text[position:].startswith(target) + + +def strip_tags(text: t.Any) -> str: + """ + Removes all HTML tags from `text`. + + Args: + text: String to strip. + + Returns: + String without HTML tags. + + Example: + + >>> strip_tags('<a href="#">Some link</a>') + 'Some link' + + .. versionadded:: 3.0.0 + """ + return RE_HTML_TAGS.sub("", pyd.to_string(text)) + + +def substr_left(text: t.Any, subtext: str) -> str: + """ + Searches `text` from left-to-right for `subtext` and returns a substring consisting of the + characters in `text` that are to the left of `subtext` or all string if no match found. + + Args: + text: String to partition. + subtext: String to search for. + + Returns: + Substring to left of `subtext`. + + Example: + + >>> substr_left("abcdefcdg", "cd") + 'ab' + + .. versionadded:: 3.0.0 + """ + text = pyd.to_string(text) + return text.partition(subtext)[0] if subtext else text + + +def substr_left_end(text: t.Any, subtext: str) -> str: + """ + Searches `text` from right-to-left for `subtext` and returns a substring consisting of the + characters in `text` that are to the left of `subtext` or all string if no match found. + + Args: + text: String to partition. + subtext: String to search for. + + Returns: + Substring to left of `subtext`. + + Example: + + >>> substr_left_end("abcdefcdg", "cd") + 'abcdef' + + .. versionadded:: 3.0.0 + """ + text = pyd.to_string(text) + return text.rpartition(subtext)[0] or text if subtext else text + + +def substr_right(text: t.Any, subtext: str) -> str: + """ + Searches `text` from right-to-left for `subtext` and returns a substring consisting of the + characters in `text` that are to the right of `subtext` or all string if no match found. + + Args: + text: String to partition. + subtext: String to search for. + + Returns: + Substring to right of `subtext`. + + Example: + + >>> substr_right("abcdefcdg", "cd") + 'efcdg' + + .. versionadded:: 3.0.0 + """ + text = pyd.to_string(text) + return text.partition(subtext)[2] or text if subtext else text + + +def substr_right_end(text: t.Any, subtext: str) -> str: + """ + Searches `text` from left-to-right for `subtext` and returns a substring consisting of the + characters in `text` that are to the right of `subtext` or all string if no match found. + + Args: + text: String to partition. + subtext: String to search for. + + Returns: + Substring to right of `subtext`. + + Example: + + >>> substr_right_end("abcdefcdg", "cd") + 'g' + + .. versionadded:: 3.0.0 + """ + text = pyd.to_string(text) + return text.rpartition(subtext)[2] if subtext else text + + +def successor(char: t.Any) -> str: + """ + Return the successor character of `char`. + + Args: + char: Character to find the successor of. + + Returns: + Successor character. + + Example: + + >>> successor("b") + 'c' + >>> successor("B") + 'C' + >>> successor("2") + '3' + + .. versionadded:: 3.0.0 + """ + char = pyd.to_string(char) + return chr(ord(char) + 1) + + +def surround(text: t.Any, wrapper: t.Any) -> str: + """ + Surround a string with another string. + + Args: + text: String to surround with `wrapper`. + wrapper: String by which `text` is to be surrounded. + + Returns: + Surrounded string. + + Example: + + >>> surround("abc", '"') + '"abc"' + >>> surround("abc", "!") + '!abc!' + + .. versionadded:: 2.4.0 + """ + text = pyd.to_string(text) + wrapper = pyd.to_string(wrapper) + return f"{wrapper}{text}{wrapper}" + + +def swap_case(text: t.Any) -> str: + """ + Swap case of `text` characters. + + Args: + text: String to swap case. + + Returns: + String with swapped case. + + Example: + + >>> swap_case("aBcDeF") + 'AbCdEf' + + .. versionadded:: 3.0.0 + """ + text = pyd.to_string(text) + return text.swapcase() + + +def title_case(text: t.Any) -> str: + """ + Convert `text` to title case. + + Args: + text: String to convert. + + Returns: + String converted to title case. + + Example: + + >>> title_case("bob's shop") + "Bob's Shop" + + .. versionadded:: 3.0.0 + """ + text = pyd.to_string(text) + # NOTE: Can't use text.title() since it doesn't handle apostrophes. + return " ".join(word.capitalize() for word in re.split(" ", text)) + + +def to_lower(text: t.Any) -> str: + """ + Converts the given :attr:`text` to lower text. + + Args: + text: String to convert. + + Returns: + String converted to lower case. + + Example: + + >>> to_lower("--Foo-Bar--") + '--foo-bar--' + >>> to_lower("fooBar") + 'foobar' + >>> to_lower("__FOO_BAR__") + '__foo_bar__' + + .. versionadded:: 4.0.0 + """ + return pyd.to_string(text).lower() + + +def to_upper(text: t.Any) -> str: + """ + Converts the given :attr:`text` to upper text. + + Args: + text: String to convert. + + Returns: + String converted to upper case. + + Example: + + >>> to_upper("--Foo-Bar--") + '--FOO-BAR--' + >>> to_upper("fooBar") + 'FOOBAR' + >>> to_upper("__FOO_BAR__") + '__FOO_BAR__' + + .. versionadded:: 4.0.0 + """ + return pyd.to_string(text).upper() + + +def trim(text: t.Any, chars: t.Union[str, None] = None) -> str: + r""" + Removes leading and trailing whitespace or specified characters from `text`. + + Args: + text: String to trim. + chars: Specific characters to remove. + + Returns: + Trimmed string. + + Example: + + >>> trim(" abc efg\r\n ") + 'abc efg' + + .. versionadded:: 1.1.0 + """ + # pylint: disable=redefined-outer-name + text = pyd.to_string(text) + return text.strip(chars) + + +def trim_end(text: t.Any, chars: t.Union[str, None] = None) -> str: + r""" + Removes trailing whitespace or specified characters from `text`. + + Args: + text: String to trim. + chars: Specific characters to remove. + + Returns: + Trimmed string. + + Example: + + >>> trim_end(" abc efg\r\n ") + ' abc efg' + + .. versionadded:: 1.1.0 + + .. versionchanged:: 4.0.0 + Renamed from ``trim_right`` to ``trim_end``. + """ + text = pyd.to_string(text) + return text.rstrip(chars) + + +def trim_start(text: t.Any, chars: t.Union[str, None] = None) -> str: + r""" + Removes leading whitespace or specified characters from `text`. + + Args: + text: String to trim. + chars: Specific characters to remove. + + Returns: + Trimmed string. + + Example: + + >>> trim_start(" abc efg\r\n ") + 'abc efg\r\n ' + + .. versionadded:: 1.1.0 + + .. versionchanged:: 4.0.0 + Renamed from ``trim_left`` to ``trim_start``. + """ + text = pyd.to_string(text) + return text.lstrip(chars) + + +def truncate( + text: t.Any, + length: int = 30, + omission: str = "...", + separator: t.Union[str, re.Pattern[str], None] = None, +) -> str: + """ + Truncates `text` if it is longer than the given maximum string length. The last characters of + the truncated string are replaced with the omission string which defaults to ``...``. + + Args: + text: String to truncate. + length: Maximum string length. Defaults to ``30``. + omission: String to indicate text is omitted. + separator: Separator pattern to truncate to. + + Returns: + Truncated string. + + Example: + + >>> truncate("hello world", 5) + 'he...' + >>> truncate("hello world", 5, "..") + 'hel..' + >>> truncate("hello world", 10) + 'hello w...' + >>> truncate("hello world", 10, separator=" ") + 'hello...' + + .. versionadded:: 1.1.0 + + .. versionchanged:: 4.0.0 + Removed alias ``trunc``. + """ + text = pyd.to_string(text) + + if len(text) <= length: + return text + + omission_len = len(omission) + text_len = length - omission_len + text = text[:text_len] + + trunc_len = len(text) + + if pyd.is_string(separator): + trunc_len = text.rfind(separator) + elif pyd.is_reg_exp(separator): + last = None + for match in separator.finditer(text): + last = match + + if last is not None: + trunc_len = last.start() + + return text[:trunc_len] + omission + + +def unescape(text: t.Any) -> str: + """ + The inverse of :func:`escape`. This method converts the HTML entities ``&``, ``<``, + ``>``, ``"``, ``'``, and ````` in `text` to their corresponding characters. + + Args: + text: String to unescape. + + Returns: + HTML unescaped string. + + Example: + + >>> results = unescape(""1 > 2 && 3 < 4"") + >>> results == '"1 > 2 && 3 < 4"' + True + + .. versionadded:: 1.0.0 + + .. versionchanged:: 1.1.0 + Moved to :mod:`pydash.strings`. + """ + text = pyd.to_string(text) + return html.unescape(text) + + +def upper_case(text: t.Any) -> str: + """ + Converts string to upper case, as space separated words. + + Args: + text: String to be converted to uppercase. + + Returns: + String converted to uppercase, as space separated words. + + Example: + + >>> upper_case("--foo-bar--") + 'FOO BAR' + >>> upper_case("fooBar") + 'FOO BAR' + >>> upper_case('/?*Foo10/;"B*Ar') + 'FOO 10 B AR' + + .. versionadded:: 4.0.0 + + .. versionchanged:: 5.0.0 + Improved unicode word support. + """ + return " ".join(compounder(text)).upper() + + +def upper_first(text: str) -> str: + """ + Converts the first character of string to upper case. + + Args: + text: String passed in by the user. + + Returns: + String in which the first character is converted to upper case. + + Example: + + >>> upper_first("fred") + 'Fred' + >>> upper_first("foo bar") + 'Foo bar' + >>> upper_first("1foobar") + '1foobar' + >>> upper_first(";foobar") + ';foobar' + + .. versionadded:: 4.0.0 + """ + return text[:1].upper() + text[1:] + + +def unquote(text: t.Any, quote_char: t.Any = '"') -> str: + """ + Unquote `text` by removing `quote_char` if `text` begins and ends with it. + + Args: + text: String to unquote. + quote_char: Quote character to remove. Defaults to `"`. + + Returns: + Unquoted string. + + Example: + + >>> unquote('"abc"') + 'abc' + >>> unquote('"abc"', "#") + '"abc"' + >>> unquote("#abc", "#") + '#abc' + >>> unquote("#abc#", "#") + 'abc' + + .. versionadded:: 3.0.0 + """ + text = pyd.to_string(text) + inner = text[1:-1] + + if text == f"{quote_char}{inner}{quote_char}": + text = inner + + return text + + +def url(*paths: t.Any, **params: t.Any) -> str: + """ + Combines a series of URL paths into a single URL. Optionally, pass in keyword arguments to + append query parameters. + + Args: + paths: URL paths to combine. + + Keyword Args: + params: Query parameters. + + Returns: + URL string. + + Example: + + >>> link = url("a", "b", ["c", "d"], "/", q="X", y="Z") + >>> path, params = link.split("?") + >>> path == "a/b/c/d/" + True + >>> set(params.split("&")) == set(["q=X", "y=Z"]) + True + + .. versionadded:: 2.2.0 + """ + # allow reassignment different type + paths = pyd.chain(paths).flatten_deep().map(pyd.to_string).value() # type: ignore + paths_list = [] + params_list = flatten_url_params(params) + + for path in paths: + scheme, netloc, path, query, fragment = urlsplit(path) + query = parse_qsl(query) + params_list += query + paths_list.append(urlunsplit((scheme, netloc, path, "", fragment))) + + path = delimitedpathjoin("/", *paths_list) + scheme, netloc, path, query, fragment = urlsplit(path) + query = urlencode(params_list) + + return urlunsplit((scheme, netloc, path, query, fragment)) + + +def words(text: t.Any, pattern: t.Union[str, None] = None) -> t.List[str]: + """ + Return list of words contained in `text`. + + References: + https://github.com/lodash/lodash/blob/master/words.js#L30 + + Args: + text: String to split. + pattern: Custom pattern to split words on. Defaults to ``None``. + + Returns: + List of words. + + Example: + + >>> words("a b, c; d-e") + ['a', 'b', 'c', 'd', 'e'] + >>> words("fred, barney, & pebbles", "/[^, ]+/g") + ['fred', 'barney', '&', 'pebbles'] + + .. versionadded:: 2.0.0 + + .. versionchanged:: 3.2.0 + Added `pattern` argument. + + .. versionchanged:: 3.2.0 + Improved matching for one character words. + + .. versionchanged:: 5.0.0 + Improved unicode word support. + """ + text = pyd.to_string(text) + if pattern is None: + if has_unicode_word(text): + reg_exp = JS_RE_UNICODE_WORDS + else: + reg_exp = JS_RE_ASCII_WORDS + else: + reg_exp = JSRegExp(pattern) + return reg_exp.find(text) + + +# +# Utility functions not a part of main API +# + + +def compounder(text): + """ + Remove single quote before passing into words() to match Lodash-style outputs. + + Required by certain functions such as kebab_case, camel_case, start_case etc. + + References: + https://github.com/lodash/lodash/blob/4.17.15/lodash.js#L4968 + """ + return words(deburr(RE_APOS.sub("", pyd.to_string(text)))) + + +def has_unicode_word(text): + """ + Check if the text contains unicode or requires more complex regex to handle. + + References: + https://github.com/lodash/lodash/blob/master/words.js#L3 + """ + result = RE_HAS_UNICODE_WORD.search(text) + return bool(result) + + +def delimitedpathjoin(delimiter, *paths): + """ + Join delimited path using specified delimiter. + + >>> assert delimitedpathjoin(".", "") == "" + >>> assert delimitedpathjoin(".", ".") == "." + >>> assert delimitedpathjoin(".", ["", ".a"]) == ".a" + >>> assert delimitedpathjoin(".", ["a", "."]) == "a." + >>> assert delimitedpathjoin(".", ["", ".a", "", "", "b"]) == ".a.b" + >>> ret = ".a.b.c.d.e." + >>> assert delimitedpathjoin(".", [".a.", "b.", ".c", "d", "e."]) == ret + >>> assert delimitedpathjoin(".", ["a", "b", "c"]) == "a.b.c" + >>> ret = "a.b.c.d.e.f" + >>> assert delimitedpathjoin(".", ["a.b", ".c.d.", ".e.f"]) == ret + >>> ret = ".a.b.c.1." + >>> assert delimitedpathjoin(".", ".", "a", "b", "c", 1, ".") == ret + >>> assert delimitedpathjoin(".", []) == "" + """ + paths = [pyd.to_string(path) for path in pyd.flatten_deep(paths) if path] + + if len(paths) == 1: + # Special case where there's no need to join anything. Doing this because if + # path==[delimiter], then an extra delimiter would be added if the else clause ran instead. + path = paths[0] + else: + leading = delimiter if paths and paths[0].startswith(delimiter) else "" + trailing = delimiter if paths and paths[-1].endswith(delimiter) else "" + middle = delimiter.join([path.strip(delimiter) for path in paths if path.strip(delimiter)]) + path = "".join([leading, middle, trailing]) + + return path + + +def flatten_url_params( + params: t.Union[ + t.Dict[T, t.Union[T2, t.Iterable[T2]]], + t.List[t.Tuple[T, t.Union[T2, t.Iterable[T2]]]], + ], +) -> t.List[t.Tuple[T, T2]]: + """ + Flatten URL params into list of tuples. If any param value is a list or tuple, then map each + value to the param key. + + >>> params = [("a", 1), ("a", [2, 3])] + >>> assert flatten_url_params(params) == [("a", 1), ("a", 2), ("a", 3)] + >>> params = {"a": [1, 2, 3]} + >>> assert flatten_url_params(params) == [("a", 1), ("a", 2), ("a", 3)] + """ + if isinstance(params, dict): + params = list(params.items()) + + flattened: t.List[t.Any] = [] + for param, value in params: + if isinstance(value, (list, tuple)): + flattened += zip([param] * len(value), value) + else: + flattened.append((param, value)) + + return flattened diff --git a/.venv/lib/python3.12/site-packages/pydash/types.py b/.venv/lib/python3.12/site-packages/pydash/types.py new file mode 100644 index 00000000..a909677c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydash/types.py @@ -0,0 +1,30 @@ +""" +Common types. + +.. versionadded:: 7.0.0 +""" + +from __future__ import annotations + +from decimal import Decimal +import typing as t + +from typing_extensions import Protocol + + +IterateeObjT = t.Union[int, str, t.List[t.Any], t.Tuple[t.Any, ...], t.Dict[t.Any, t.Any]] +NumberT = t.Union[float, int, Decimal] +NumberNoDecimalT = t.Union[float, int] +PathT = t.Union[t.Hashable, t.List[t.Hashable]] + + +_T_co = t.TypeVar("_T_co", covariant=True) +_T_contra = t.TypeVar("_T_contra", contravariant=True) + + +class SupportsMul(Protocol[_T_contra, _T_co]): + def __mul__(self, x: _T_contra) -> _T_co: ... + + +class SupportsRound(Protocol[_T_co]): + def __round__(self) -> _T_co: ... diff --git a/.venv/lib/python3.12/site-packages/pydash/utilities.py b/.venv/lib/python3.12/site-packages/pydash/utilities.py new file mode 100644 index 00000000..e55e5d28 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydash/utilities.py @@ -0,0 +1,1511 @@ +""" +Utility functions. + +.. versionadded:: 1.0.0 +""" + +from __future__ import annotations + +from collections import namedtuple +from datetime import datetime, timezone +from functools import partial, wraps +import math +from random import randint, uniform +import re +import time +import typing as t + +from typing_extensions import Literal, ParamSpec, Protocol, Type + +import pydash as pyd + +from .helpers import NUMBER_TYPES, UNSET, base_get, callit, getargcount, iterator +from .types import PathT + + +__all__ = ( + "attempt", + "cond", + "conforms", + "conforms_to", + "constant", + "default_to", + "default_to_any", + "identity", + "iteratee", + "matches", + "matches_property", + "memoize", + "method", + "method_of", + "noop", + "nth_arg", + "now", + "over", + "over_every", + "over_some", + "properties", + "property_", + "property_of", + "random", + "range_", + "range_right", + "result", + "retry", + "stub_list", + "stub_dict", + "stub_false", + "stub_string", + "stub_true", + "times", + "to_path", + "unique_id", +) + +T = t.TypeVar("T") +T2 = t.TypeVar("T2") +CallableT = t.TypeVar("CallableT", bound=t.Callable[..., t.Any]) +P = ParamSpec("P") + +# These regexes are used in to_path() to parse deep path strings. + +# This is used to split a deep path string into dict keys or list indexes. This matches "." as +# delimiter (unless it is escaped by "//") and "[<integer>]" as delimiter while keeping the +# "[<integer>]" as an item. +RE_PATH_KEY_DELIM = re.compile(r"(?<!\\)(?:\\\\)*\.|(\[-?\d+\])") + +# Matches on path strings like "[<integer>]". This is used to test whether a path string part is a +# list index. +RE_PATH_LIST_INDEX = re.compile(r"^\[-?\d+\]$") + + +ID_COUNTER = 0 + +PathToken = namedtuple("PathToken", ["key", "default_factory"]) + + +def attempt(func: t.Callable[P, T], *args: "P.args", **kwargs: "P.kwargs") -> t.Union[T, Exception]: + """ + Attempts to execute `func`, returning either the result or the caught error object. + + Args: + func: The function to attempt. + + Returns: + Returns the `func` result or error object. + + Example: + + >>> results = attempt(lambda x: x / 0, 1) + >>> assert isinstance(results, ZeroDivisionError) + + .. versionadded:: 1.1.0 + """ + try: + ret = func(*args, **kwargs) + except Exception as ex: + # allow different type reassignment + ret = ex # type: ignore + + return ret + + +@t.overload +def cond( + pairs: t.List[t.Tuple[t.Callable[P, t.Any], t.Callable[P, T]]], + *extra_pairs: t.Tuple[t.Callable[P, t.Any], t.Callable[P, T]], +) -> t.Callable[P, T]: ... + + +@t.overload +def cond( + pairs: t.List[t.List[t.Callable[P, t.Any]]], *extra_pairs: t.List[t.Callable[P, t.Any]] +) -> t.Callable[P, t.Any]: ... + + +def cond(pairs, *extra_pairs): + """ + Creates a function that iterates over `pairs` and invokes the corresponding function of the + first predicate to return truthy. + + Args: + pairs: A list of predicate-function pairs. + + Returns: + Returns the new composite function. + + Example: + + >>> func = cond([[matches({'a': 1}), constant('matches A')],\ + [matches({'b': 2}), constant('matches B')],\ + [stub_true, lambda value: value]]) + >>> func({'a': 1, 'b': 2}) + 'matches A' + >>> func({'a': 0, 'b': 2}) + 'matches B' + >>> func({'a': 0, 'b': 0}) == {'a': 0, 'b': 0} + True + + .. versionadded:: 4.0.0 + + .. versionchanged:: 4.2.0 + Fixed missing argument passing to matched function and added support for passing in a single + list of pairs instead of just pairs as separate arguments. + """ + if extra_pairs: + pairs = [pairs] + list(extra_pairs) + + for pair in pairs: + is_valid = False + try: + is_valid = len(pair) == 2 + except Exception: + pass + + if not is_valid: + raise ValueError("Each predicate-function pair should contain exactly two elements") + + if not all(map(callable, pair)): + raise TypeError("Both predicate-function pair should be callable") + + def _cond(*args): + for pair in pairs: + predicate, iteratee = pair + + if callit(predicate, *args): + return iteratee(*args) + + return _cond + + +@t.overload +def conforms(source: t.Dict[T, t.Callable[[T2], t.Any]]) -> t.Callable[[t.Dict[T, T2]], bool]: ... + + +@t.overload +def conforms(source: t.List[t.Callable[[T], t.Any]]) -> t.Callable[[t.List[T]], bool]: ... + + +def conforms(source: t.Union[t.List[t.Any], t.Dict[t.Any, t.Any]]) -> t.Callable[..., t.Any]: + """ + Creates a function that invokes the predicate properties of `source` with the corresponding + property values of a given object, returning ``True`` if all predicates return truthy, else + ``False``. + + Args: + source: The object of property predicates to conform to. + + Returns: + Returns the new spec function. + + Example: + + >>> func = conforms({"b": lambda n: n > 1}) + >>> func({"b": 2}) + True + >>> func({"b": 0}) + False + >>> func = conforms([lambda n: n > 1, lambda n: n == 0]) + >>> func([2, 0]) + True + >>> func([0, 0]) + False + + .. versionadded:: 4.0.0 + """ + + def _conforms(obj): + for key, predicate in iterator(source): + if not pyd.has(obj, key) or not predicate(obj[key]): + return False + return True + + return _conforms + + +@t.overload +def conforms_to(obj: t.Dict[T, T2], source: t.Dict[T, t.Callable[[T2], t.Any]]) -> bool: ... + + +@t.overload +def conforms_to(obj: t.List[T], source: t.List[t.Callable[[T], t.Any]]) -> bool: ... + + +def conforms_to(obj, source): + """ + Checks if `obj` conforms to `source` by invoking the predicate properties of `source` with the + corresponding property values of `obj`. + + Args: + obj: The object to inspect. + source: The object of property predicates to conform to. + + Example: + + >>> conforms_to({"b": 2}, {"b": lambda n: n > 1}) + True + >>> conforms_to({"b": 0}, {"b": lambda n: n > 1}) + False + >>> conforms_to([2, 0], [lambda n: n > 1, lambda n: n == 0]) + True + >>> conforms_to([0, 0], [lambda n: n > 1, lambda n: n == 0]) + False + + .. versionadded:: 4.0.0 + """ + return conforms(source)(obj) + + +def constant(value: T) -> t.Callable[..., T]: + """ + Creates a function that returns `value`. + + Args: + value: Constant value to return. + + Returns: + Function that always returns `value`. + + Example: + + >>> pi = constant(3.14) + >>> pi() == 3.14 + True + + .. versionadded:: 1.0.0 + + .. versionchanged:: 4.0.0 + Returned function ignores arguments instead of raising exception. + """ + return partial(identity, value) + + +def default_to(value: t.Union[T, None], default_value: T2) -> t.Union[T, T2]: + """ + Checks `value` to determine whether a default value should be returned in its place. The + `default_value` is returned if value is None. + + Args: + default_value: Default value passed in by the user. + + Returns: + Returns `value` if :attr:`value` is given otherwise returns `default_value`. + + Example: + + >>> default_to(1, 10) + 1 + >>> default_to(None, 10) + 10 + + .. versionadded:: 4.0.0 + """ + return default_to_any(value, default_value) + + +@t.overload +def default_to_any(value: None, *default_values: None) -> None: ... + + +@t.overload +def default_to_any( + value: t.Union[T, None], + default_value1: None, + default_value2: T2, +) -> t.Union[T, T2]: ... + + +@t.overload +def default_to_any( + value: t.Union[T, None], + default_value1: None, + default_value2: None, + default_value3: T2, +) -> t.Union[T, T2]: ... + + +@t.overload +def default_to_any( + value: t.Union[T, None], + default_value1: None, + default_value2: None, + default_value3: None, + default_value4: T2, +) -> t.Union[T, T2]: ... + + +@t.overload +def default_to_any( + value: t.Union[T, None], + default_value1: None, + default_value2: None, + default_value3: None, + default_value4: None, + default_value5: T2, +) -> t.Union[T, T2]: ... + + +@t.overload +def default_to_any(value: t.Union[T, None], *default_values: T2) -> t.Union[T, T2]: ... + + +def default_to_any(value, *default_values): + """ + Checks `value` to determine whether a default value should be returned in its place. The first + item that is not None of the `default_values` is returned. + + Args: + value: Value passed in by the user. + *default_values: Default values passed in by the user. + + Returns: + Returns `value` if :attr:`value` is given otherwise returns the first not None value + of `default_values`. + + Example: + + >>> default_to_any(1, 10, 20) + 1 + >>> default_to_any(None, 10, 20) + 10 + >>> default_to_any(None, None, 20) + 20 + + + .. versionadded:: 4.9.0 + """ + values = (value,) + default_values + for val in values: + if val is not None: + return val + + +@t.overload +def identity(arg: T, *args: t.Any) -> T: ... + + +@t.overload +def identity(arg: None = None, *args: t.Any) -> None: ... + + +def identity(arg=None, *args): + """ + Return the first argument provided to it. + + Args: + *args: Arguments. + + Returns: + First argument or ``None``. + + Example: + + >>> identity(1) + 1 + >>> identity(1, 2, 3) + 1 + >>> identity() is None + True + + .. versionadded:: 1.0.0 + """ + return arg + + +@t.overload +def iteratee(func: t.Callable[P, T]) -> t.Callable[P, T]: ... + + +@t.overload +def iteratee(func: t.Any) -> t.Callable[..., t.Any]: ... + + +def iteratee(func): + """ + Return a pydash style iteratee. If `func` is a property name the created iteratee will return + the property value for a given element. If `func` is an object the created iteratee will return + ``True`` for elements that contain the equivalent object properties, otherwise it will return + ``False``. + + Args: + func: Object to create iteratee function from. + + Returns: + Iteratee function. + + Example: + + >>> get_data = iteratee("data") + >>> get_data({"data": [1, 2, 3]}) + [1, 2, 3] + >>> is_active = iteratee({"active": True}) + >>> is_active({"active": True}) + True + >>> is_active({"active": 0}) + False + >>> iteratee(["a", 5])({"a": 5}) + True + >>> iteratee(["a.b"])({"a.b": 5}) + 5 + >>> iteratee("a.b")({"a": {"b": 5}}) + 5 + >>> iteratee(("a", ["c", "d", "e"]))({"a": 1, "c": {"d": {"e": 3}}}) + [1, 3] + >>> iteratee(lambda a, b: a + b)(1, 2) + 3 + >>> ident = iteratee(None) + >>> ident("a") + 'a' + >>> ident(1, 2, 3) + 1 + + .. versionadded:: 1.0.0 + + .. versionchanged:: 2.0.0 + Renamed ``create_iteratee()`` to :func:`iteratee`. + + .. versionchanged:: 3.0.0 + Made pluck style iteratee support deep property access. + + .. versionchanged:: 3.1.0 + - Added support for shallow pluck style property access via single item list/tuple. + - Added support for matches property style iteratee via two item list/tuple. + + .. versionchanged:: 4.0.0 + Removed alias ``callback``. + + .. versionchanged:: 4.1.0 + Return :func:`properties` callback when `func` is a ``tuple``. + """ + if callable(func): + cbk = func + else: + if isinstance(func, int): + func = str(func) + + if isinstance(func, str): + cbk = property_(func) + elif isinstance(func, list) and len(func) == 1: + cbk = property_(func) + elif isinstance(func, list) and len(func) > 1: + cbk = matches_property(*func[:2]) + elif isinstance(func, tuple): + cbk = properties(*func) + elif isinstance(func, dict): + cbk = matches(func) + else: + cbk = identity + + # Optimize iteratee by specifying the exact number of arguments the iteratee takes so that + # arg inspection (costly process) can be skipped in helpers.callit(). + cbk._argcount = 1 + + return cbk + + +def matches(source: t.Any) -> t.Callable[[t.Any], bool]: + """ + Creates a matches-style predicate function which performs a deep comparison between a given + object and the `source` object, returning ``True`` if the given object has equivalent property + values, else ``False``. + + Args: + source: Source object used for comparision. + + Returns: + Function that compares an object to `source` and returns whether the two objects + contain the same items. + + Example: + + >>> matches({"a": {"b": 2}})({"a": {"b": 2, "c": 3}}) + True + >>> matches({"a": 1})({"b": 2, "a": 1}) + True + >>> matches({"a": 1})({"b": 2, "a": 2}) + False + + .. versionadded:: 1.0.0 + + .. versionchanged:: 3.0.0 + Use :func:`pydash.predicates.is_match` as matching function. + """ + return lambda obj: pyd.is_match(obj, source) + + +def matches_property(key: t.Any, value: t.Any) -> t.Callable[[t.Any], bool]: + """ + Creates a function that compares the property value of `key` on a given object to `value`. + + Args: + key: Object key to match against. + value: Value to compare to. + + Returns: + Function that compares `value` to an object's `key` and returns whether they are + equal. + + Example: + + >>> matches_property("a", 1)({"a": 1, "b": 2}) + True + >>> matches_property(0, 1)([1, 2, 3]) + True + >>> matches_property("a", 2)({"a": 1, "b": 2}) + False + + .. versionadded:: 3.1.0 + """ + prop_accessor = property_(key) + return lambda obj: matches(value)(prop_accessor(obj)) + + +class MemoizedFunc(Protocol[P, T, T2]): + cache: t.Dict[T2, T] + + def __call__(self, *args: P.args, **kwargs: P.kwargs) -> T: ... # pragma: no cover + + +@t.overload +def memoize(func: t.Callable[P, T], resolver: None = None) -> MemoizedFunc[P, T, str]: ... + + +@t.overload +def memoize( + func: t.Callable[P, T], resolver: t.Union[t.Callable[P, T2], None] = None +) -> MemoizedFunc[P, T, T2]: ... + + +def memoize(func, resolver=None): + """ + Creates a function that memoizes the result of `func`. If `resolver` is provided it will be used + to determine the cache key for storing the result based on the arguments provided to the + memoized function. By default, all arguments provided to the memoized function are used as the + cache key. The result cache is exposed as the cache property on the memoized function. + + Args: + func: Function to memoize. + resolver: Function that returns the cache key to use. + + Returns: + Memoized function. + + Example: + + >>> ident = memoize(identity) + >>> ident(1) + 1 + >>> ident.cache["(1,){}"] == 1 + True + >>> ident(1, 2, 3) + 1 + >>> ident.cache["(1, 2, 3){}"] == 1 + True + + .. versionadded:: 1.0.0 + """ + + def memoized(*args: P.args, **kwargs: P.kwargs): + if resolver: + key = resolver(*args, **kwargs) + else: + key = f"{args}{kwargs}" + + if key not in memoized.cache: # type: ignore + memoized.cache[key] = func(*args, **kwargs) # type:ignore + + return memoized.cache[key] # type: ignore + + memoized.cache = {} + + return memoized + + +def method(path: PathT, *args: t.Any, **kwargs: t.Any) -> t.Callable[..., t.Any]: + """ + Creates a function that invokes the method at `path` on a given object. Any additional arguments + are provided to the invoked method. + + Args: + path: Object path of method to invoke. + *args: Global arguments to apply to method when invoked. + **kwargs: Global keyword argument to apply to method when invoked. + + Returns: + Function that invokes method located at path for object. + + Example: + + >>> obj = {"a": {"b": [None, lambda x: x]}} + >>> echo = method("a.b.1") + >>> echo(obj, 1) == 1 + True + >>> echo(obj, "one") == "one" + True + + .. versionadded:: 3.3.0 + """ + + def _method(obj, *_args, **_kwargs): + func = pyd.partial(pyd.get(obj, path), *args, **kwargs) + return func(*_args, **_kwargs) + + return _method + + +def method_of(obj: t.Any, *args: t.Any, **kwargs: t.Any) -> t.Callable[..., t.Any]: + """ + The opposite of :func:`method`. This method creates a function that invokes the method at a + given path on object. Any additional arguments are provided to the invoked method. + + Args: + obj: The object to query. + *args: Global arguments to apply to method when invoked. + **kwargs: Global keyword argument to apply to method when invoked. + + Returns: + Function that invokes method located at path for object. + + Example: + + >>> obj = {"a": {"b": [None, lambda x: x]}} + >>> dispatch = method_of(obj) + >>> dispatch("a.b.1", 1) == 1 + True + >>> dispatch("a.b.1", "one") == "one" + True + + .. versionadded:: 3.3.0 + """ + + def _method_of(path, *_args, **_kwargs): + func = pyd.partial(pyd.get(obj, path), *args, **kwargs) + return func(*_args, **_kwargs) + + return _method_of + + +def noop(*args: t.Any, **kwargs: t.Any) -> None: # pylint: disable=unused-argument + """ + A no-operation function. + + .. versionadded:: 1.0.0 + """ + pass + + +def nth_arg(pos: int = 0) -> t.Callable[..., t.Any]: + """ + Creates a function that gets the argument at index n. If n is negative, the nth argument from + the end is returned. + + Args: + pos: The index of the argument to return. + + Returns: + Returns the new pass-thru function. + + Example: + + >>> func = nth_arg(1) + >>> func(11, 22, 33, 44) + 22 + >>> func = nth_arg(-1) + >>> func(11, 22, 33, 44) + 44 + + .. versionadded:: 4.0.0 + """ + + def _nth_arg(*args): + try: + position = math.ceil(float(pos)) + except ValueError: + position = 0 + + return pyd.get(args, position) + + return _nth_arg + + +def now() -> int: + """ + Return the number of milliseconds that have elapsed since the Unix epoch (1 January 1970 + 00:00:00 UTC). + + Returns: + Milliseconds since Unix epoch. + + .. versionadded:: 1.0.0 + + .. versionchanged:: 3.0.0 + Use ``datetime`` module for calculating elapsed time. + """ + epoch = datetime.fromtimestamp(0, timezone.utc) + delta = datetime.now(timezone.utc) - epoch + return int(delta.total_seconds() * 1000) + + +def over(funcs: t.Iterable[t.Callable[P, T]]) -> t.Callable[P, t.List[T]]: + """ + Creates a function that invokes all functions in `funcs` with the arguments it receives and + returns their results. + + Args: + funcs: List of functions to be invoked. + + Returns: + Returns the new pass-thru function. + + Example: + + >>> func = over([max, min]) + >>> func(1, 2, 3, 4) + [4, 1] + + .. versionadded:: 4.0.0 + """ + + def _over(*args: P.args, **kwargs: P.kwargs) -> t.List[T]: + return [func(*args, **kwargs) for func in funcs] + + return _over + + +def over_every(funcs: t.Iterable[t.Callable[P, t.Any]]) -> t.Callable[P, bool]: + """ + Creates a function that checks if all the functions in `funcs` return truthy when invoked with + the arguments it receives. + + Args: + funcs: List of functions to be invoked. + + Returns: + Returns the new pass-thru function. + + Example: + + >>> func = over_every([bool, lambda x: x is not None]) + >>> func(1) + True + + .. versionadded:: 4.0.0 + """ + + def _over_every(*args: P.args, **kwargs: P.kwargs) -> bool: + return all(func(*args, **kwargs) for func in funcs) + + return _over_every + + +def over_some(funcs: t.Iterable[t.Callable[P, t.Any]]) -> t.Callable[P, bool]: + """ + Creates a function that checks if any of the functions in `funcs` return truthy when invoked + with the arguments it receives. + + Args: + funcs: List of functions to be invoked. + + Returns: + Returns the new pass-thru function. + + Example: + + >>> func = over_some([bool, lambda x: x is None]) + >>> func(1) + True + + .. versionadded:: 4.0.0 + """ + + def _over_some(*args: P.args, **kwargs: P.kwargs) -> bool: + return any(func(*args, **kwargs) for func in funcs) + + return _over_some + + +def property_(path: PathT) -> t.Callable[[t.Any], t.Any]: + """ + Creates a function that returns the value at path of a given object. + + Args: + path: Path value to fetch from object. + + Returns: + Function that returns object's path value. + + Example: + + >>> get_data = property_("data") + >>> get_data({"data": 1}) + 1 + >>> get_data({}) is None + True + >>> get_first = property_(0) + >>> get_first([1, 2, 3]) + 1 + + .. versionadded:: 1.0.0 + + .. versionchanged:: 4.0.1 + Made property accessor work with deep path strings. + """ + return lambda obj: pyd.get(obj, path) + + +def properties(*paths: t.Any) -> t.Callable[[t.Any], t.Any]: + """ + Like :func:`property_` except that it returns a list of values at each path in `paths`. + + Args: + *path: Path values to fetch from object. + + Returns: + Function that returns object's path value. + + Example: + + >>> getter = properties("a", "b", ["c", "d", "e"]) + >>> getter({"a": 1, "b": 2, "c": {"d": {"e": 3}}}) + [1, 2, 3] + + .. versionadded:: 4.1.0 + """ + return lambda obj: [getter(obj) for getter in (pyd.property_(path) for path in paths)] + + +def property_of(obj: t.Any) -> t.Callable[[PathT], t.Any]: + """ + The inverse of :func:`property_`. This method creates a function that returns the key value of a + given key on `obj`. + + Args: + obj: Object to fetch values from. + + Returns: + Function that returns object's key value. + + Example: + + >>> getter = property_of({"a": 1, "b": 2, "c": 3}) + >>> getter("a") + 1 + >>> getter("b") + 2 + >>> getter("x") is None + True + + .. versionadded:: 3.0.0 + + .. versionchanged:: 4.0.0 + Removed alias ``prop_of``. + """ + return lambda key: pyd.get(obj, key) + + +@t.overload +def random(start: int = 0, stop: int = 1, *, floating: Literal[False] = False) -> int: ... + + +@t.overload +def random(start: float, stop: int = 1, floating: bool = False) -> float: ... + + +@t.overload +def random(start: int = 0, *, stop: float, floating: bool = False) -> float: ... + + +@t.overload +def random(start: float, stop: float, floating: bool = False) -> float: ... + + +@t.overload +def random( + start: t.Union[float, int] = 0, stop: t.Union[float, int] = 1, *, floating: Literal[True] +) -> float: ... + + +def random(start: t.Union[float, int] = 0, stop: t.Union[float, int] = 1, floating: bool = False): + """ + Produces a random number between `start` and `stop` (inclusive). If only one argument is + provided a number between 0 and the given number will be returned. If floating is truthy or + either `start` or `stop` are floats a floating-point number will be returned instead of an + integer. + + Args: + start: Minimum value. + stop: Maximum value. + floating: Whether to force random value to ``float``. Defaults to + ``False``. + + Returns: + Random value. + + Example: + + >>> 0 <= random() <= 1 + True + >>> 5 <= random(5, 10) <= 10 + True + >>> isinstance(random(floating=True), float) + True + + .. versionadded:: 1.0.0 + """ + floating = isinstance(start, float) or isinstance(stop, float) or floating is True + + if stop < start: + stop, start = start, stop + + if floating: + rnd = uniform(start, stop) + else: + rnd = randint(start, stop) # type: ignore + + return rnd + + +@t.overload +def range_(stop: int) -> t.Generator[int, None, None]: ... + + +@t.overload +def range_(start: int, stop: int, step: int = 1) -> t.Generator[int, None, None]: ... + + +def range_(*args): + """ + Creates a list of numbers (positive and/or negative) progressing from start up to but not + including end. If `start` is less than `stop`, a zero-length range is created unless a negative + `step` is specified. + + Args: + start: Integer to start with. Defaults to ``0``. + stop: Integer to stop at. + step: The value to increment or decrement by. Defaults to ``1``. + + Yields: + Next integer in range. + + Example: + + >>> list(range_(5)) + [0, 1, 2, 3, 4] + >>> list(range_(1, 4)) + [1, 2, 3] + >>> list(range_(0, 6, 2)) + [0, 2, 4] + >>> list(range_(4, 1)) + [4, 3, 2] + + .. versionadded:: 1.0.0 + + .. versionchanged:: 1.1.0 + Moved to :mod:`pydash.uilities`. + + .. versionchanged:: 3.0.0 + Return generator instead of list. + + .. versionchanged:: 4.0.0 + Support decrementing when start argument is greater than stop argument. + """ + return base_range(*args) + + +@t.overload +def range_right(stop: int) -> t.Generator[int, None, None]: ... + + +@t.overload +def range_right(start: int, stop: int, step: int = 1) -> t.Generator[int, None, None]: ... + + +def range_right(*args): + """ + Similar to :func:`range_`, except that it populates the values in descending order. + + Args: + start: Integer to start with. Defaults to ``0``. + stop: Integer to stop at. + step: The value to increment or decrement by. Defaults to ``1`` if `start` + < `stop` else ``-1``. + + Yields: + Next integer in range. + + Example: + + >>> list(range_right(5)) + [4, 3, 2, 1, 0] + >>> list(range_right(1, 4)) + [3, 2, 1] + >>> list(range_right(0, 6, 2)) + [4, 2, 0] + + .. versionadded:: 4.0.0 + """ + return base_range(*args, from_right=True) + + +# TODO +@t.overload +def result(obj: None, key: t.Any, default: None = None) -> None: ... + + +@t.overload +def result(obj: None, key: t.Any, default: T) -> T: ... + + +@t.overload +def result(obj: t.Any, key: t.Any, default: t.Any = None) -> t.Any: ... + + +def result(obj, key, default=None): + """ + Return the value of property `key` on `obj`. If `key` value is a function it will be invoked and + its result returned, else the property value is returned. If `obj` is falsey then `default` is + returned. + + Args: + obj: Object to retrieve result from. + key: Key or index to get result from. + default: Default value to return if `obj` is falsey. Defaults to ``None``. + + Returns: + Result of ``obj[key]`` or ``None``. + + Example: + + >>> result({"a": 1, "b": lambda: 2}, "a") + 1 + >>> result({"a": 1, "b": lambda: 2}, "b") + 2 + >>> result({"a": 1, "b": lambda: 2}, "c") is None + True + >>> result({"a": 1, "b": lambda: 2}, "c", default=False) + False + + .. versionadded:: 1.0.0 + + .. versionchanged:: 2.0.0 + Added ``default`` argument. + """ + if not obj: + return default + + ret = base_get(obj, key, default=default) + + if callable(ret): + ret = ret() + + return ret + + +def retry( + attempts: int = 3, + delay: t.Union[int, float] = 0.5, + max_delay: t.Union[int, float] = 150.0, + scale: t.Union[int, float] = 2.0, + jitter: t.Union[int, float, t.Tuple[t.Union[int, float], t.Union[int, float]]] = 0, + exceptions: t.Iterable[Type[Exception]] = (Exception,), + on_exception: t.Union[t.Callable[[Exception, int], t.Any], None] = None, +) -> t.Callable[[CallableT], CallableT]: + """ + Decorator that retries a function multiple times if it raises an exception with an optional + delay between each attempt. + + When a `delay` is supplied, there will be a sleep period in between retry + attempts. The first delay time will always be equal to `delay`. After + subsequent retries, the delay time will be scaled by `scale` up to + `max_delay`. If `max_delay` is ``0``, then `delay` can increase unbounded. + + Args: + attempts: Number of retry attempts. Defaults to ``3``. + delay: Base amount of seconds to sleep between retry attempts. + Defaults to ``0.5``. + max_delay: Maximum number of seconds to sleep between retries. Is + ignored when equal to ``0``. Defaults to ``150.0`` (2.5 minutes). + scale: Scale factor to increase `delay` after first retry fails. + Defaults to ``2.0``. + jitter: Random jitter to add to `delay` time. Can be a positive + number or 2-item tuple of numbers representing the random range to choose from. When a + number is given, the random range will be from ``[0, jitter]``. When jitter is a float + or contains a float, then a random float will be chosen; otherwise, a random integer + will be selected. Defaults to ``0`` which disables jitter. + exceptions: Tuple of exceptions that trigger a retry attempt. Exceptions + not in the tuple will be ignored. Defaults to ``(Exception,)`` (all exceptions). + on_exception: Function that is called when a retryable exception is + caught. It is invoked with ``on_exception(exc, attempt)`` where ``exc`` is the caught + exception and ``attempt`` is the attempt count. All arguments are optional. Defaults to + ``None``. + + Example: + + >>> @retry(attempts=3, delay=0) + ... def do_something(): + ... print("something") + ... raise Exception("something went wrong") + >>> try: + ... do_something() + ... except Exception: + ... print("caught something") + something + something + something + caught something + + ..versionadded:: 4.4.0 + + ..versionchanged:: 4.5.0 + Added ``jitter`` argument. + """ + if not isinstance(attempts, int) or attempts <= 0: + raise ValueError("attempts must be an integer greater than 0") + + if not isinstance(delay, NUMBER_TYPES) or delay < 0: + raise ValueError("delay must be a number greater than or equal to 0") + + if not isinstance(max_delay, NUMBER_TYPES) or max_delay < 0: + raise ValueError("scale must be a number greater than or equal to 0") + + if not isinstance(scale, NUMBER_TYPES) or scale <= 0: + raise ValueError("scale must be a number greater than 0") + + if ( + not isinstance(jitter, NUMBER_TYPES + (tuple,)) + or (isinstance(jitter, NUMBER_TYPES) and jitter < 0) + or ( + isinstance(jitter, tuple) + and (len(jitter) != 2 or not all(isinstance(jit, NUMBER_TYPES) for jit in jitter)) + ) + ): + raise ValueError("jitter must be a number greater than 0 or a 2-item tuple of numbers") + + if not isinstance(exceptions, tuple) or not all( + issubclass(exc, Exception) for exc in exceptions + ): + raise TypeError("exceptions must be a tuple of Exception types") + + if on_exception and not callable(on_exception): + raise TypeError("on_exception must be a callable") + + if jitter and not isinstance(jitter, tuple): + jitter = (0, jitter) + + on_exc_argcount = getargcount(on_exception, maxargs=2) if on_exception else None + + def decorator(func): + @wraps(func) + def decorated(*args, **kwargs): + delay_time = delay + + for attempt in range(1, attempts + 1): + # pylint: disable=catching-non-exception + try: + return func(*args, **kwargs) + except exceptions as exc: + if on_exception: + callit(on_exception, exc, attempt, argcount=on_exc_argcount) + + if attempt == attempts: + raise + + if jitter: + delay_time += max(0, random(*jitter)) + + if delay_time < 0: # pragma: no cover + continue + + if max_delay: + delay_time = min(delay_time, max_delay) + + time.sleep(delay_time) + + # Scale after first iteration. + delay_time *= scale + + return decorated + + return decorator + + +def stub_list() -> t.List[t.Any]: + """ + Returns empty "list". + + Returns: + Empty list. + + Example: + + >>> stub_list() + [] + + .. versionadded:: 4.0.0 + """ + return [] + + +def stub_dict() -> t.Dict[t.Any, t.Any]: + """ + Returns empty "dict". + + Returns: + Empty dict. + + Example: + + >>> stub_dict() + {} + + .. versionadded:: 4.0.0 + """ + return {} + + +def stub_false() -> Literal[False]: + """ + Returns ``False``. + + Returns: + False + + Example: + + >>> stub_false() + False + + .. versionadded:: 4.0.0 + """ + return False + + +def stub_string() -> str: + """ + Returns an empty string. + + Returns: + Empty string + + Example: + + >>> stub_string() + '' + + .. versionadded:: 4.0.0 + """ + return "" + + +def stub_true() -> Literal[True]: + """ + Returns ``True``. + + Returns: + True + + Example: + + >>> stub_true() + True + + .. versionadded:: 4.0.0 + """ + return True + + +@t.overload +def times(n: int, iteratee: t.Callable[..., T]) -> t.List[T]: ... + + +@t.overload +def times(n: int, iteratee: None = None) -> t.List[int]: ... + + +def times(n: int, iteratee=None): + """ + Executes the iteratee `n` times, returning a list of the results of each iteratee execution. The + iteratee is invoked with one argument: ``(index)``. + + Args: + n: Number of times to execute `iteratee`. + iteratee: Function to execute. + + Returns: + A list of results from calling `iteratee`. + + Example: + + >>> times(5, lambda i: i) + [0, 1, 2, 3, 4] + + .. versionadded:: 1.0.0 + + .. versionchanged:: 3.0.0 + Reordered arguments to make `iteratee` first. + + .. versionchanged:: 4.0.0 + + - Re-reordered arguments to make `iteratee` last argument. + - Added functionality for handling `iteratee` with zero positional arguments. + """ + if iteratee is None: + iteratee = identity + argcount = 1 + else: + argcount = getargcount(iteratee, maxargs=1) + + return [callit(iteratee, index, argcount=argcount) for index in range(n)] + + +def to_path(value: PathT) -> t.List[t.Hashable]: + """ + Converts values to a property path array. + + Args: + value: Value to convert. + + Returns: + Returns the new property path array. + + Example: + + >>> to_path("a.b.c") + ['a', 'b', 'c'] + >>> to_path("a[0].b.c") + ['a', 0, 'b', 'c'] + >>> to_path("a[0][1][2].b.c") + ['a', 0, 1, 2, 'b', 'c'] + + .. versionadded:: 4.0.0 + + .. versionchanged:: 4.2.1 + Ensure returned path is always a list. + """ + path = [token.key for token in to_path_tokens(value)] + return path + + +def unique_id(prefix: t.Union[str, None] = None) -> str: + """ + Generates a unique ID. If `prefix` is provided the ID will be appended to it. + + Args: + prefix: String prefix to prepend to ID value. + + Returns: + ID value. + + Example: + + >>> unique_id() + '1' + >>> unique_id("id_") + 'id_2' + >>> unique_id() + '3' + + .. versionadded:: 1.0.0 + """ + # pylint: disable=global-statement + global ID_COUNTER # noqa: PLW0603 + ID_COUNTER += 1 + + if prefix is None: + prefix = "" + else: + prefix = pyd.to_string(prefix) + return f"{prefix}{ID_COUNTER}" + + +# +# Helper functions not a part of main API +# + + +def _maybe_list_index(key): + if isinstance(key, int): + return key + if pyd.is_string(key) and RE_PATH_LIST_INDEX.match(key): + return int(key[1:-1]) + return None + + +def _to_path_token(key) -> PathToken: + list_index = _maybe_list_index(key) + if list_index is not None: + return PathToken(list_index, default_factory=list) + return PathToken( + unescape_path_key(key) if pyd.is_string(key) else key, + default_factory=dict, + ) + + +def to_path_tokens(value) -> t.List[PathToken]: + """Parse `value` into :class:`PathToken` objects.""" + if pyd.is_string(value) and ("." in value or "[" in value): + # Since we can't tell whether a bare number is supposed to be dict key or a list index, we + # support a special syntax where any string-integer surrounded by brackets is treated as a + # list index and converted to an integer. + keys = [_to_path_token(key) for key in filter(None, RE_PATH_KEY_DELIM.split(value))] + elif pyd.is_string(value) or pyd.is_number(value): + keys = [PathToken(value, default_factory=dict)] + elif value is UNSET: + keys = [] + elif pyd.is_list(value): + keys = [_to_path_token(key) for key in value] + else: + keys = [_to_path_token(value)] + + return keys + + +def unescape_path_key(key): + """Unescape path key.""" + key = key.replace(r"\\", "\\") + key = key.replace(r"\.", r".") + return key + + +def base_range(*args, **kwargs): + """Yield range values.""" + from_right = kwargs.get("from_right", False) + + if len(args) >= 3: + args = args[:3] + elif len(args) == 2: + args = (args[0], args[1], None) + elif len(args) == 1: + args = (0, args[0], None) + + if args and args[2] is None: + check_args = args[:2] + else: + check_args = args + + for arg in check_args: + if not isinstance(arg, int): # pragma: no cover + raise TypeError(f"range cannot interpret {type(arg).__name__!r} object as an integer") + + def gen(): + if not args: + return + + start, stop, step = args + + if step is None: + step = 1 if start < stop else -1 + + length = int(max([math.ceil((stop - start) / (step or 1)), 0])) + + if from_right: + start += (step * length) - step + step *= -1 + + while length: + yield start + + start += step + length -= 1 + + return gen() |