Source code for pydash.api.utilities

"""Utility functions.

.. versionadded:: 1.0.0
"""

from __future__ import absolute_import

from functools import wraps
import time
from random import uniform, randint
import warnings

from .._compat import (
    _range,
    string_types,
    text_type,
    iteritems
)


__all__ = [
    'attempt',
    'constant',
    'callback',
    'create_callback',
    'identity',
    'matches',
    'memoize',
    'noop',
    'now',
    'property_',
    'prop',
    'random',
    'range_',
    'result',
    'times',
    'unique_id',
]


ID_COUNTER = 0


[docs]def attempt(func, *args, **kargs): """Attempts to execute `func`, returning either the result or the caught error object. Args: func (function): The function to attempt. Returns: mixed: Returns the `func` result or error object. .. versionadded:: 1.1.0 """ try: ret = func(*args, **kargs) except Exception as ex: ret = ex return ret
[docs]def constant(value): """Creates a function that returns `value`. Args: value (mixed): Constant value to return. Returns: function: Function that always returns `value`. .. versionadded:: 1.0.0 """ return lambda: value
[docs]def callback(func): """Return a callback. If `func` is a property name the created callback will return the property value for a given element. If `func` is an object the created callback will return ``True`` for elements that contain the equivalent object properties, otherwise it will return ``False``. Args: func (mixed): Object to create callback function from. Returns: function: Callback function. See Also: - :func:`callback` (main definition) - :func:`create_callback` (alias) .. versionadded:: 1.0.0 """ if callable(func): cbk = func elif isinstance(func, string_types): cbk = property_(func) elif isinstance(func, dict): cbk = matches(func) else: cbk = identity return cbk
create_callback = callback
[docs]def identity(*args): """Return the first argument provided to it. Args: *args (mixed): Arguments. Returns: mixed: First argument or ``None``. .. versionadded:: 1.0.0 """ return args[0] if args else None
[docs]def matches(source): """Creates a :func:`pydash.api.collections.where` 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 (dict): Source object used for comparision. Returns: function: Function that compares a ``dict`` to `source` and returns whether the two objects contain the same items. .. versionadded:: 1.0.0 """ return lambda obj, *args: all(item in obj.items() for item in source.items())
[docs]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): Function to memoize. resolver (function, optional): Function that returns the cache key to use. Returns: function: Memoized function. .. versionadded:: 1.0.0 """ def memoized(*args, **kargs): # pylint: disable=missing-docstring if resolver: key = resolver(*args, **kargs) else: key = '{0}{1}'.format(args, kargs) if key not in memoized.cache: memoized.cache[key] = func(*args, **kargs) return memoized.cache[key] memoized.cache = {} return memoized
[docs]def noop(*args, **kargs): # pylint: disable=unused-argument """A no-operation function. .. versionadded:: 1.0.0 """ pass
[docs]def now(): """Return the number of milliseconds that have elapsed since the Unix epoch (1 January 1970 00:00:00 UTC). Returns: int: Milliseconds since Unix epoch. .. versionadded:: 1.0.0 """ return int(time.time() * 1000)
[docs]def property_(key): """Creates a :func:`pydash.collections.pluck` style function, which returns the key value of a given object. Args: key (mixed): Key value to fetch from object. Returns: function: Function that returns object's key value. See Also: - :func:`property_` (main definition) - :func:`prop` (alias) .. versionadded:: 1.0.0 """ return lambda obj, *args: _get_item(obj, key, default=None)
prop = property_
[docs]def random(start=0, stop=1, floating=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 (int): Minimum value. stop (int): Maximum value. floating (bool, optional): Whether to force random value to ``float``. Default is ``False``. Returns: int|float: Random value. .. versionadded:: 1.0.0 """ floating = any([isinstance(start, float), isinstance(stop, float), floating is True]) if stop < start: stop, start = start, stop if floating: rnd = uniform(start, stop) else: rnd = randint(start, stop) return rnd
[docs]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: stop (int): Integer - 1 to stop at. Defaults to ``1``. start (int, optional): Integer to start with. Defaults to ``0``. step (int, optional): If positive the last element is the largest ``start + i * step`` less than `stop`. If negative the last element is the smallest ``start + i * step`` greater than `stop`. Defaults to ``1``. Returns: list: List of integers in range .. versionadded:: 1.0.0 .. versionchanged:: 1.1.0 Moved to Utilities module. """ return list(_range(*args))
[docs]def result(obj, key): """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 ``None`` is returned. Args: obj (list|dict): Object to retrieve result from. key (mixed): Key or index to get result from. Returns: mixed: Result of ``obj[key]`` or ``None``. .. versionadded:: 1.0.0 """ if not obj: return None ret = _get_item(obj, key, default=None) if callable(ret): ret = ret() return ret
[docs]def times(n, callback): """Executes the callback `n` times, returning a list of the results of each callback execution. The callback is invoked with one argument: ``(index)``. Args: n (int): Number of times to execute `callback`. callback (function): Function to execute. Returns: list: A list of results from calling `callback`. .. versionadded:: 1.0.0 """ # pylint: disable=redefined-outer-name return [callback(index) for index in _range(n)]
[docs]def unique_id(prefix=None): """Generates a unique ID. If `prefix` is provided the ID will be appended to it. Args: prefix (str, optional): String prefix to prepend to ID value. Returns: str: ID value. .. versionadded:: 1.0.0 """ # pylint: disable=global-statement global ID_COUNTER ID_COUNTER += 1 return text_type('' if prefix is None else prefix) + text_type(ID_COUNTER) # # Generic utility methods not part of main API. #
def _iter_callback(collection, callback=None, reverse=False): """Return iterative callback based on collection type.""" # pylint: disable=redefined-outer-name if isinstance(collection, dict): return _iter_dict_callback(collection, callback, reverse=reverse) else: return _iter_list_callback(collection, callback, reverse=reverse) def _iter_list_callback(array, callback=None, reverse=False): """Return iterative list callback.""" # pylint: disable=redefined-outer-name cbk = create_callback(callback) array_len = len(array) if reverse: iterator = list(reversed(array)) else: iterator = array for index, item in enumerate(iterator): if reverse: index = array_len - index - 1 yield (cbk(item, index, array), item, index, array) def _iter_dict_callback(collection, callback=None, reverse=False): """Return iterative dict callback.""" # pylint: disable=redefined-outer-name cbk = create_callback(callback) if reverse: items = reversed(list(iteritems(collection))) else: items = iteritems(collection) for key, value in items: yield (cbk(value, key, collection), value, key, collection) def _iterate(collection): """Return iterative based on collection type.""" if isinstance(collection, dict): return _iter_dict(collection) else: return _iter_list(collection) def _iter_dict(collection): """Return iterative dict.""" return iteritems(collection) def _iter_list(array): """Return iterative list.""" for i, item in enumerate(array): yield i, item def _iter_unique(array): """Return iterator to find unique list.""" seen = [] for i, item in enumerate(array): if item not in seen: seen.append(item) yield (i, item) def _get_item(obj, key, **kargs): """Safely get an item by `key` from a sequence or mapping object. Args: obj (list|dict): Sequence or mapping to retrieve item from. key (mixed): Key or index identifying which item to retrieve. Keyword Args: default (mixed, optional): Default value to return if `key` not found in `obj`. Returns: mixed: `obj[key]` or `default`. Raises: KeyError|IndexError: If `obj` is missing key or index and no default value provided. """ use_default = 'default' in kargs default = kargs.get('default') try: ret = obj[key] except (KeyError, IndexError): if use_default: ret = default else: # pragma: no cover raise return ret def _set_item(obj, key, value): """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, raise ``IndexError``. Args: obj (list|dict): Object to assign value to. key (mixed): Key or index to assign to. value (mixed): Value to assign. Returns: None Raises: IndexError: If `obj` is a ``list`` and `key` is greater than length of `obj`. """ if isinstance(obj, dict): obj[key] = value elif isinstance(obj, list): if key < len(obj): obj[key] = value elif key == len(obj): obj.append(value) else: # pragma: no cover # Trigger exception by assigning to invalid index. obj[key] = value 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, **kargs): # pylint: disable=missing-docstring warnings.warn('Call to deprecated function {0}.'.format(func.__name__), category=DeprecationWarning, stacklevel=3) return func(*args, **kargs) return wrapper