Example #1
0
 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)
Example #2
0
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)
Example #3
0
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)
Example #4
0
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))
Example #5
0
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
Example #6
0
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))
Example #7
0
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
Example #8
0
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
Example #9
0
 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)
Example #10
0
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
Example #11
0
 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)
Example #12
0
 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)
Example #13
0
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
Example #14
0
 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)
Example #15
0
 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)
Example #16
0
 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)
Example #17
0
 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)
Example #18
0
 def test_optional__some_object(self):
     with self.assertRaises(TypeError):
         __unit__.ensure_keyword_args(self.ALL_KWARGS, optional=object())
Example #19
0
 def test_optional__none(self):
     with self.assertRaises(TypeError):
         __unit__.ensure_keyword_args(self.ALL_KWARGS, optional=None)
Example #20
0
 def test_optional__some_object(self):
     with self.assertRaises(TypeError):
         __unit__.ensure_keyword_args(self.ALL_KWARGS, optional=object())
Example #21
0
 def test_no_specs(self):
     with self.assertRaises(ValueError):
         __unit__.ensure_keyword_args(self.ALL_KWARGS)
Example #22
0
 def test_mandatory__some_object(self):
     with self.assertRaises(TypeError):
         __unit__.ensure_keyword_args(self.ALL_KWARGS, mandatory=object())
Example #23
0
 def test_kwargs__some_object(self):
     with self.assertRaises(TypeError):
         __unit__.ensure_keyword_args(object())
Example #24
0
 def test_kwargs__none(self):
     with self.assertRaises(TypeError):
         __unit__.ensure_keyword_args(None)
Example #25
0
 def test_no_specs(self):
     with self.assertRaises(ValueError):
         __unit__.ensure_keyword_args(self.ALL_KWARGS)
Example #26
0
 def test_mandatory__none(self):
     with self.assertRaises(TypeError):
         __unit__.ensure_keyword_args(self.ALL_KWARGS, mandatory=None)
Example #27
0
 def test_kwargs__some_object(self):
     with self.assertRaises(TypeError):
         __unit__.ensure_keyword_args(object())
Example #28
0
 def test_mandatory__none(self):
     with self.assertRaises(TypeError):
         __unit__.ensure_keyword_args(self.ALL_KWARGS, mandatory=None)
Example #29
0
 def test_kwargs__none(self):
     with self.assertRaises(TypeError):
         __unit__.ensure_keyword_args(None)
Example #30
0
 def test_mandatory__some_object(self):
     with self.assertRaises(TypeError):
         __unit__.ensure_keyword_args(self.ALL_KWARGS, mandatory=object())
Example #31
0
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)
Example #32
0
 def test_optional__none(self):
     with self.assertRaises(TypeError):
         __unit__.ensure_keyword_args(self.ALL_KWARGS, optional=None)