def test_kwargs__mandatory_and_optional(self): with self._assertUnexpectedKwargsException(self.OPTIONAL): __unit__.ensure_keyword_args(self.ALL_KWARGS, mandatory=self.MANDATORY) __unit__.ensure_keyword_args(self.ALL_KWARGS, mandatory=self.MANDATORY, optional=self.OPTIONAL)
def join(delimiter, iterable, **kwargs): """Returns a string which is a concatenation of strings in ``iterable``, separated by given ``delimiter``. :param delimiter: Delimiter to put between strings :param iterable: Iterable to join Optional keyword arguments control the exact joining strategy: :param errors: What to do with erroneous non-strings in the input. Possible values include: * ``'ignore'`` (or ``None``) * ``'cast'`` (or ``False``) -- convert non-strings to strings * ``'raise'`` (or ``True``) -- raise exception for any non-strings * ``'replace'`` -- replace non-strings with alternative value :param with_: Replacement used when ``errors == 'replace'``. This can be a string, or a callable taking erroneous value and returning a string replacement. .. versionadded:: 0.0.3 Allow to specify error handling policy through ``errors`` parameter """ ensure_string(delimiter) ensure_iterable(iterable) ensure_keyword_args(kwargs, optional=('errors', 'with_')) errors = kwargs.get('errors', True) if errors in ('raise', True): iterable = imap(ensure_string, iterable) elif errors in ('ignore', None): iterable = ifilter(is_string, iterable) elif errors in ('cast', False): iterable = imap(delimiter.__class__, iterable) elif errors == 'replace': if 'with_' not in kwargs: raise ValueError("'replace' error policy requires specifying " "replacement through with_=") with_ = kwargs['with_'] if is_string(with_): replacement = lambda x: with_ elif callable(with_): replacement = with_ else: raise TypeError("error replacement must be a string or function, " "got %s" % type(with_).__name__) iterable = (x if is_string(x) else ensure_string(replacement(x)) for x in iterable) else: raise TypeError( "%r is not a valid error handling policy for join()" % (errors,)) return delimiter.join(iterable)
def join(delimiter, iterable, **kwargs): """Returns a string which is a concatenation of strings in ``iterable``, separated by given ``delimiter``. :param delimiter: Delimiter to put between strings :param iterable: Iterable to join Optional keyword arguments control the exact joining strategy: :param errors: What to do with erroneous non-strings in the input. Possible values include: * ``'ignore'`` (or ``None``) * ``'cast'`` (or ``False``) -- convert non-strings to strings * ``'raise'`` (or ``True``) -- raise exception for any non-strings * ``'replace'`` -- replace non-strings with alternative value :param with_: Replacement used when ``errors == 'replace'``. This can be a string, or a callable taking erroneous value and returning a string replacement. .. versionadded:: 0.0.3 Allow to specify error handling policy through ``errors`` parameter """ ensure_string(delimiter) ensure_iterable(iterable) ensure_keyword_args(kwargs, optional=('errors', 'with_')) errors = kwargs.get('errors', True) if errors in ('raise', True): iterable = imap(ensure_string, iterable) elif errors in ('ignore', None): iterable = ifilter(is_string, iterable) elif errors in ('cast', False): iterable = imap(delimiter.__class__, iterable) elif errors == 'replace': if 'with_' not in kwargs: raise ValueError("'replace' error policy requires specifying " "replacement through with_=") with_ = kwargs['with_'] if is_string(with_): replacement = lambda x: with_ elif callable(with_): replacement = with_ else: raise TypeError("error replacement must be a string or function, " "got %s" % type(with_).__name__) iterable = (x if is_string(x) else ensure_string(replacement(x)) for x in iterable) else: raise TypeError("%r is not a valid error handling policy for join()" % (errors, )) return delimiter.join(iterable)
def extend(dict_, *dicts, **kwargs): """Extend a dictionary with keys and values from other dictionaries. :param dict_: Dictionary to extend Optional keyword arguments allow to control the exact way in which ``dict_`` will be extended. :param overwrite: Whether repeated keys should have their values overwritten, retaining the last value, as per given order of dictionaries. This is the default behavior (equivalent to ``overwrite=True``). If ``overwrite=False``, repeated keys are simply ignored. Example:: >> foo = {'a': 1} >> extend(foo, {'a': 10, 'b': 2}, overwrite=True) {'a': 10, 'b': 2} >> foo = {'a': 1} >> extend(foo, {'a': 10, 'b': 2}, overwrite=False) {'a': 1, 'b': 2} :param deep: Whether extending should proceed recursively, and cause corresponding subdictionaries to be merged into each other. By default, this does not happen (equivalent to ``deep=False``). Example:: >> foo = {'a': {'b': 1}} >> extend(foo, {'a': {'c': 2}}, deep=False) {'a': {'c': 2}} >> foo = {'a': {'b': 1}} >> extend(foo, {'a': {'c': 2}}, deep=True) {'a': {'b': 1, 'c': 2}} :return: Extended ``dict_`` .. versionadded:: 0.0.2 """ ensure_mapping(dict_) dicts = list(imap(ensure_mapping, dicts)) ensure_keyword_args(kwargs, optional=('deep', 'overwrite')) return _nary_dict_update([dict_] + dicts, copy=False, deep=kwargs.get('deep', False), overwrite=kwargs.get('overwrite', True))
def attr_func(*attrs, **kwargs): """Creates an "attribute function" for given attribute name(s). Resulting function will retrieve attributes with given names, in order, from the object that has been passed to it. For example, ``attr_func('a', 'b')(foo)`` yields the same as ``foo.a.b`` :param attrs: Attribute names :param default: Optional keyword argument specifying default value that will be returned when some attribute is not present :return: Unary attribute function """ ensure_argcount(attrs, min_=1) ensure_keyword_args(kwargs, optional=('default', )) # preprocess argument list: # * allow dots in arguments, interpreting them as multiple attributes, # e.g. ``attr_func('a.b')`` as ``attr_func('a', 'b')`` # * make sure the attribute names are valid Python identifiers attrs = map(ensure_string, attrs) attrs = flatten( attr.split('.') if '.' in attr else [attr] for attr in attrs) for attr in attrs: if not is_identifier(attr): raise ValueError("'%s' is not a valid attribute name", attr) if 'default' in kwargs: default = kwargs['default'] if len(attrs) == 1: getattrs = lambda obj: getattr(obj, attrs[0], default) else: def getattrs(obj): for attr in attrs: try: obj = getattr(obj, attr) except AttributeError: return default return obj else: if len(attrs) == 1: getattrs = operator.attrgetter(attrs[0]) else: def getattrs(obj): for attr in attrs: obj = getattr(obj, attr) return obj return getattrs
def merge(*dicts, **kwargs): """Merges two or more dictionaries into a single one. Optional keyword arguments allow to control the exact way in which the dictionaries will be merged. :param overwrite: Whether repeated keys should have their values overwritten, retaining the last value, as per given order of dictionaries. This is the default behavior (equivalent to ``overwrite=True``). If ``overwrite=False``, repeated keys are simply ignored. Example:: >> merge({'a': 1}, {'a': 10, 'b': 2}, overwrite=True) {'a': 10, 'b': 2} >> merge({'a': 1}, {'a': 10, 'b': 2}, overwrite=False) {'a': 1, 'b': 2} :param deep: Whether merging should proceed recursively, and cause corresponding subdictionaries to be merged into each other. By default, this does not happen (equivalent to ``deep=False``). Example:: >> merge({'a': {'b': 1}}, {'a': {'c': 2}}, deep=False) {'a': {'c': 2}} >> merge({'a': {'b': 1}}, {'a': {'c': 2}}, deep=True) {'a': {'b': 1, 'c': 2}} :return: Merged dictionary .. note:: For ``dict``\ s ``a`` and ``b``, ``merge(a, b)`` is equivalent to ``extend({}, a, b)``. .. versionadded:: 0.0.2 The ``overwrite`` keyword argument. """ ensure_argcount(dicts, min_=1) dicts = list(imap(ensure_mapping, dicts)) ensure_keyword_args(kwargs, optional=('deep', 'overwrite')) return _nary_dict_update(dicts, copy=True, deep=kwargs.get('deep', False), overwrite=kwargs.get('overwrite', True))
def attr_func(*attrs, **kwargs): """Creates an "attribute function" for given attribute name(s). Resulting function will retrieve attributes with given names, in order, from the object that has been passed to it. For example, ``attr_func('a', 'b')(foo)`` yields the same as ``foo.a.b`` :param attrs: Attribute names :param default: Optional keyword argument specifying default value that will be returned when some attribute is not present :return: Unary attribute function """ ensure_argcount(attrs, min_=1) ensure_keyword_args(kwargs, optional=('default',)) # preprocess argument list: # * allow dots in arguments, interpreting them as multiple attributes, # e.g. ``attr_func('a.b')`` as ``attr_func('a', 'b')`` # * make sure the attribute names are valid Python identifiers attrs = map(ensure_string, attrs) attrs = flatten(attr.split('.') if '.' in attr else [attr] for attr in attrs) for attr in attrs: if not is_identifier(attr): raise ValueError("'%s' is not a valid attribute name", attr) if 'default' in kwargs: default = kwargs['default'] if len(attrs) == 1: getattrs = lambda obj: getattr(obj, attrs[0], default) else: def getattrs(obj): for attr in attrs: try: obj = getattr(obj, attr) except AttributeError: return default return obj else: if len(attrs) == 1: getattrs = operator.attrgetter(attrs[0]) else: def getattrs(obj): for attr in attrs: obj = getattr(obj, attr) return obj return getattrs
def _index(*args, **kwargs): """Implementation of list searching. :param of: Element to search for :param where: Predicate to search for :param in_: List to search in :param start: Start index for the lookup :param step: Counter step (i.e. in/decrement) for each iteration :return: Pair of ``(list, index)``, where ``list`` is the list we searched in and ``index`` is the index of the first element found, or -1 """ start = kwargs.pop('start', 0) step = kwargs.pop('step', 1) if len(args) == 2: elem, list_ = args ensure_sequence(list_) predicate = lambda item: item == elem else: ensure_keyword_args(kwargs, mandatory=('in_',), optional=('of', 'where')) if 'of' in kwargs and 'where' in kwargs: raise TypeError( "either an item or predicate must be supplied, not both") if not ('of' in kwargs or 'where' in kwargs): raise TypeError("an item or predicate must be supplied") list_ = ensure_sequence(kwargs['in_']) if 'where' in kwargs: predicate = ensure_callable(kwargs['where']) else: elem = kwargs['of'] predicate = lambda item: item == elem len_ = len(list_) start = max(0, min(len_ - 1, start)) i = start while 0 <= i < len_: if predicate(list_[i]): return list_, i i += step else: return list_, -1
def test_kwargs__empty(self): __unit__.ensure_keyword_args({}, optional=self.OPTIONAL) with self._assertMissingKwargsException(self.MANDATORY): __unit__.ensure_keyword_args({}, mandatory=self.MANDATORY) with self._assertMissingKwargsException(self.MANDATORY): __unit__.ensure_keyword_args( {}, mandatory=self.MANDATORY, optional=self.OPTIONAL)
def key_func(*keys, **kwargs): """Creates a "key function" based on given keys. Resulting function will perform lookup using specified keys, in order, on the object passed to it as an argument. For example, ``key_func('a', 'b')(foo)`` is equivalent to ``foo['a']['b']``. :param keys: Lookup keys :param default: Optional keyword argument specifying default value that will be returned when some lookup key is not present :return: Unary key function """ ensure_argcount(keys, min_=1) ensure_keyword_args(kwargs, optional=('default', )) keys = list(map(ensure_string, keys)) if 'default' in kwargs: default = kwargs['default'] def getitems(obj): for key in keys: try: obj = obj[key] except KeyError: return default return obj else: if len(keys) == 1: getitems = operator.itemgetter(keys[0]) else: def getitems(obj): for key in keys: obj = obj[key] return obj return getitems
def test_kwargs__empty(self): __unit__.ensure_keyword_args({}, optional=self.OPTIONAL) with self._assertMissingKwargsException(self.MANDATORY): __unit__.ensure_keyword_args({}, mandatory=self.MANDATORY) with self._assertMissingKwargsException(self.MANDATORY): __unit__.ensure_keyword_args({}, mandatory=self.MANDATORY, optional=self.OPTIONAL)
def test_kwargs__just_mandatory(self): __unit__.ensure_keyword_args( self.JUST_MANDATORY, mandatory=self.MANDATORY) with self._assertUnexpectedKwargsException(self.MANDATORY): __unit__.ensure_keyword_args( self.JUST_MANDATORY, optional=self.OPTIONAL) __unit__.ensure_keyword_args(self.JUST_MANDATORY, mandatory=self.MANDATORY, optional=self.OPTIONAL)
def key_func(*keys, **kwargs): """Creates a "key function" based on given keys. Resulting function will perform lookup using specified keys, in order, on the object passed to it as an argument. For example, ``key_func('a', 'b')(foo)`` is equivalent to ``foo['a']['b']``. :param keys: Lookup keys :param default: Optional keyword argument specifying default value that will be returned when some lookup key is not present :return: Unary key function """ ensure_argcount(keys, min_=1) ensure_keyword_args(kwargs, optional=('default',)) keys = list(map(ensure_string, keys)) if 'default' in kwargs: default = kwargs['default'] def getitems(obj): for key in keys: try: obj = obj[key] except KeyError: return default return obj else: if len(keys) == 1: getitems = operator.itemgetter(keys[0]) else: def getitems(obj): for key in keys: obj = obj[key] return obj return getitems
def test_kwargs__just_mandatory(self): __unit__.ensure_keyword_args(self.JUST_MANDATORY, mandatory=self.MANDATORY) with self._assertUnexpectedKwargsException(self.MANDATORY): __unit__.ensure_keyword_args(self.JUST_MANDATORY, optional=self.OPTIONAL) __unit__.ensure_keyword_args(self.JUST_MANDATORY, mandatory=self.MANDATORY, optional=self.OPTIONAL)
def test_kwargs__just_optional(self): with self._assertMissingKwargsException(self.MANDATORY): __unit__.ensure_keyword_args( self.JUST_OPTIONAL, mandatory=self.MANDATORY) __unit__.ensure_keyword_args( self.JUST_OPTIONAL, optional=self.OPTIONAL) with self._assertMissingKwargsException(self.MANDATORY): __unit__.ensure_keyword_args(self.JUST_OPTIONAL, mandatory=self.MANDATORY, optional=self.OPTIONAL)
def test_kwargs__just_optional(self): with self._assertMissingKwargsException(self.MANDATORY): __unit__.ensure_keyword_args(self.JUST_OPTIONAL, mandatory=self.MANDATORY) __unit__.ensure_keyword_args(self.JUST_OPTIONAL, optional=self.OPTIONAL) with self._assertMissingKwargsException(self.MANDATORY): __unit__.ensure_keyword_args(self.JUST_OPTIONAL, mandatory=self.MANDATORY, optional=self.OPTIONAL)
def test_kwargs__mandatory_and_optional(self): with self._assertUnexpectedKwargsException(self.OPTIONAL): __unit__.ensure_keyword_args( self.ALL_KWARGS, mandatory=self.MANDATORY) __unit__.ensure_keyword_args( self.ALL_KWARGS, mandatory=self.MANDATORY, optional=self.OPTIONAL)
def test_optional__some_object(self): with self.assertRaises(TypeError): __unit__.ensure_keyword_args(self.ALL_KWARGS, optional=object())
def test_optional__none(self): with self.assertRaises(TypeError): __unit__.ensure_keyword_args(self.ALL_KWARGS, optional=None)
def test_no_specs(self): with self.assertRaises(ValueError): __unit__.ensure_keyword_args(self.ALL_KWARGS)
def test_mandatory__some_object(self): with self.assertRaises(TypeError): __unit__.ensure_keyword_args(self.ALL_KWARGS, mandatory=object())
def test_kwargs__some_object(self): with self.assertRaises(TypeError): __unit__.ensure_keyword_args(object())
def test_kwargs__none(self): with self.assertRaises(TypeError): __unit__.ensure_keyword_args(None)
def test_mandatory__none(self): with self.assertRaises(TypeError): __unit__.ensure_keyword_args(self.ALL_KWARGS, mandatory=None)
def merge(arg, *rest, **kwargs): """Merge a collection, with functions as items, into a single function that takes a collection and maps its items through corresponding functions. :param arg: A collection of functions, such as list, tuple, or dictionary :param default: Optional default function to use for items within merged function's arguments that do not have corresponding functions in ``arg`` Example with two-element tuple:: >> dict_ = {'Alice': -5, 'Bob': 4} >> func = merge((str.upper, abs)) >> dict(map(func, dict_.items())) {'ALICE': 5, 'BOB': 4} Example with a dictionary:: >> func = merge({'id': int, 'name': str.split}) >> data = [ {'id': '1', 'name': "John Doe"}, {'id': '2', 'name': "Anne Arbor"}, ] >> list(map(func, data)) [{'id': 1, 'name': ['John', 'Doe']}, {'id': 2, 'name': ['Anne', 'Arbor']}] :return: Merged function .. versionadded:: 0.0.2 """ ensure_keyword_args(kwargs, optional=('default', )) has_default = 'default' in kwargs if has_default: default = ensure_callable(kwargs['default']) # if more than one argument was given, they must all be functions; # result will be a function that takes multiple arguments (rather than # a single collection) and returns a tuple unary_result = True if rest: fs = (ensure_callable(arg), ) + tuple(imap(ensure_callable, rest)) unary_result = False else: fs = arg if is_mapping(fs): if has_default: return lambda arg_: fs.__class__( (k, fs.get(k, default)(arg_[k])) for k in arg_) else: return lambda arg_: fs.__class__((k, fs[k](arg_[k])) for k in arg_) else: ensure_sequence(fs) if has_default: # we cannot use ``izip_longest(fs, arg_, fillvalue=default)``, # because we want to terminate the generator # only when ``arg_`` is exhausted (not when just ``fs`` is) func = lambda arg_: fs.__class__((fs[i] if i < len(fs) else default)(x) for i, x in enumerate(arg_)) else: # we cannot use ``izip(fs, arg_)`` because it would short-circuit # if ``arg_`` is longer than ``fs``, rather than raising # the required ``IndexError`` func = lambda arg_: fs.__class__(fs[i](x) for i, x in enumerate(arg_)) return func if unary_result else lambda *args: func(args)