Exemple #1
0
 def _assertReplacer(self, arg, replacements=None):
     self.assertIsInstance(arg, __unit__.Replacer)
     if replacements is not None:
         if is_mapping(replacements):
             self.assertEquals(replacements, arg._replacements)
         else:
             self.assertItemsEqual(replacements, arg._replacements)
             self.assertFalse(is_mapping(arg._replacements))
Exemple #2
0
 def _assertReplacer(self, arg, replacements=None):
     self.assertIsInstance(arg, __unit__.Replacer)
     if replacements is not None:
         if is_mapping(replacements):
             self.assertEquals(replacements, arg._replacements)
         else:
             self.assertItemsEqual(replacements, arg._replacements)
             self.assertFalse(is_mapping(arg._replacements))
Exemple #3
0
    def in_(self, haystack):
        """Perform replacement in given string.

        :param haystack: String to perform replacements in

        :return: ``haystack`` after the replacements

        :raise TypeError: If ``haystack`` if not a string
        :raise ReplacementError: If no replacement(s) have been provided yet
        """
        from taipan.collections import dicts

        ensure_string(haystack)
        if not is_mapping(self._replacements):
            raise ReplacementError("string replacements not provided")

        # handle special cases
        if not self._replacements:
            return haystack
        if len(self._replacements) == 1:
            return haystack.replace(*dicts.peekitem(self._replacements))

        # construct a regex matching any of the needles in the order
        # of descending length (to prevent issues if they contain each other)
        or_ = haystack.__class__('|')
        regex = join(or_, imap(
            re.escape, sorted(self._replacements, key=len, reverse=True)))

        # do the substituion, looking up the replacement for every match
        do_replace = lambda match: self._replacements[match.group()]
        return re.sub(regex, do_replace, haystack)
Exemple #4
0
    def in_(self, haystack):
        """Perform replacement in given string.

        :param haystack: String to perform replacements in

        :return: ``haystack`` after the replacements

        :raise TypeError: If ``haystack`` if not a string
        :raise ReplacementError: If no replacement(s) have been provided yet
        """
        from taipan.collections import dicts

        ensure_string(haystack)
        if not is_mapping(self._replacements):
            raise ReplacementError("string replacements not provided")

        # handle special cases
        if not self._replacements:
            return haystack
        if len(self._replacements) == 1:
            return haystack.replace(*dicts.peekitem(self._replacements))

        # construct a regex matching any of the needles in the order
        # of descending length (to prevent issues if they contain each other)
        or_ = haystack.__class__('|')
        regex = join(
            or_,
            imap(re.escape, sorted(self._replacements, key=len, reverse=True)))

        # do the substituion, looking up the replacement for every match
        do_replace = lambda match: self._replacements[match.group()]
        return re.sub(regex, do_replace, haystack)
Exemple #5
0
def replace(needle, with_=None, in_=None):
    """Replace occurrences of string(s) with other string(s) in (a) string(s).

    Unlike the built in :meth:`str.replace` method, this function provides
    clean API that clearly distinguishes the "needle" (string to replace),
    the replacement string, and the target string to perform replacement in
    (the "haystack").

    Additionally, a simultaneous replacement of several needles is possible.
    Note that this is different from performing multiple separate replacements
    one after another.

    Examples::

        replace('foo', with_='bar', in_=some_text)
        replace('foo', with_='bar').in_(other_text)
        replace('foo').with_('bar').in_(another_text)
        replace(['foo', 'bar']).with_('baz').in_(perhaps_a_long_text)
        replace({'foo': 'bar', 'baz': 'qud'}).in_(even_longer_text)

    :param needle: String to replace, iterable thereof,
                   or a mapping from needles to corresponding replacements
    :param with_: Replacement string, if ``needle`` was not a mapping
    :param in_: Optional string to perform replacement in

    :return: If all parameters were provided, result is the final string
             after performing a specified replacement.
             Otherwise, a :class:`Replacer` object is returned, allowing
             e.g. to perform the same replacements in many haystacks.
    """
    if needle is None:
        raise TypeError("replacement needle cannot be None")
    if not needle:
        raise ValueError("replacement needle cannot be empty")

    if is_string(needle):
        replacer = Replacer((needle,))
    else:
        ensure_iterable(needle)
        if not is_mapping(needle):
            if all(imap(is_pair, needle)):
                needle = dict(needle)
            elif not all(imap(is_string, needle)):
                raise TypeError("invalid replacement needle")
        replacer = Replacer(needle)

    if with_ is not None:
        ensure_string(with_)
        replacer = replacer.with_(with_)

    if in_ is not None:
        ensure_string(in_)
        return replacer.in_(in_)

    return replacer
Exemple #6
0
def replace(needle, with_=None, in_=None):
    """Replace occurrences of string(s) with other string(s) in (a) string(s).

    Unlike the built in :meth:`str.replace` method, this function provides
    clean API that clearly distinguishes the "needle" (string to replace),
    the replacement string, and the target string to perform replacement in
    (the "haystack").

    Additionally, a simultaneous replacement of several needles is possible.
    Note that this is different from performing multiple separate replacements
    one after another.

    Examples::

        replace('foo', with_='bar', in_=some_text)
        replace('foo', with_='bar').in_(other_text)
        replace('foo').with_('bar').in_(another_text)
        replace(['foo', 'bar']).with_('baz').in_(perhaps_a_long_text)
        replace({'foo': 'bar', 'baz': 'qud'}).in_(even_longer_text)

    :param needle: String to replace, iterable thereof,
                   or a mapping from needles to corresponding replacements
    :param with_: Replacement string, if ``needle`` was not a mapping
    :param in_: Optional string to perform replacement in

    :return: If all parameters were provided, result is the final string
             after performing a specified replacement.
             Otherwise, a :class:`Replacer` object is returned, allowing
             e.g. to perform the same replacements in many haystacks.
    """
    if needle is None:
        raise TypeError("replacement needle cannot be None")
    if not needle:
        raise ValueError("replacement needle cannot be empty")

    if is_string(needle):
        replacer = Replacer((needle, ))
    else:
        ensure_iterable(needle)
        if not is_mapping(needle):
            if all(imap(is_pair, needle)):
                needle = dict(needle)
            elif not all(imap(is_string, needle)):
                raise TypeError("invalid replacement needle")
        replacer = Replacer(needle)

    if with_ is not None:
        ensure_string(with_)
        replacer = replacer.with_(with_)

    if in_ is not None:
        ensure_string(in_)
        return replacer.in_(in_)

    return replacer
Exemple #7
0
def _recursive_dict_update(dict_, other, **kwargs):
    """Deep/recursive version of ``dict.update``.

    If a key is present in both dictionaries, and points to
    "child" dictionaries, those will be appropriately merged.

    :param overwrite: Whether to overwrite exisiting dictionary values
    """
    overwrite = kwargs['overwrite']

    for key, other_value in iteritems(other):
        if key in dict_:
            value = dict_[key]
            if is_mapping(value) and is_mapping(other_value):
                _recursive_dict_update(value, other_value, overwrite=overwrite)
                continue
        if overwrite:
            dict_[key] = other_value
        else:
            dict_.setdefault(key, other_value)
Exemple #8
0
    def with_(self, replacement):
        """Provide replacement for string "needles".

        :param replacement: Target replacement for needles given in constructor
        :return: The :class:`Replacement` object

        :raise TypeError: If ``replacement`` is not a string
        :raise ReplacementError: If replacement has been already given
        """
        ensure_string(replacement)
        if is_mapping(self._replacements):
            raise ReplacementError("string replacements already provided")

        self._replacements = dict.fromkeys(self._replacements, replacement)
        return self
Exemple #9
0
    def with_(self, replacement):
        """Provide replacement for string "needles".

        :param replacement: Target replacement for needles given in constructor
        :return: The :class:`Replacement` object

        :raise TypeError: If ``replacement`` is not a string
        :raise ReplacementError: If replacement has been already given
        """
        ensure_string(replacement)
        if is_mapping(self._replacements):
            raise ReplacementError("string replacements already provided")

        self._replacements = dict.fromkeys(self._replacements, replacement)
        return self
Exemple #10
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)
Exemple #11
0
 def _assertIsMapping(self, obj, msg=None):
     if not is_mapping(obj):
         self.fail(msg or "%r is not a mapping" % (obj, ))
Exemple #12
0
def try_(block, except_=None, else_=None, finally_=None):
    """Emulate a ``try`` block.

    :param block: Function to be executed within the ``try`` statement
    :param except_: Function to execute when an :class:`Exception` occurs.
                    It receives a single argument: the exception object.
                    Alternatively, a list of key-value pairs can be passed,
                    mapping exception types to their handler functions.
    :param else_: Function to execute when ``block`` completes successfully.
                  Note that it requires ``except_`` to be provided as well
    :param finally_: Function to execute at the end,
                     regardless of whether an exception occurred or not

    :return:  If no exception was raised while executing ``block``,
              the result of ``else_`` (if provided) or ``block`` is returned.
              Otherwise, the result of ``except_`` is returned.

    Note that :func:`try_` can _still_ raise an exception if it occurs
    while evaluating ``except_``, ``else_`` or ``finally_`` functions.
    """
    ensure_callable(block)
    if not (except_ or else_ or finally_):
        raise TypeError("at least one of `except_`, `else_` or `finally_` "
                        "functions must be provided")
    if else_ and not except_:
        raise TypeError("`else_` can only be provided along with `except_`")

    if except_:
        if callable(except_):
            except_ = [(Exception, except_)]
        else:
            ensure_iterable(except_)
            if is_mapping(except_):
                ensure_ordered_mapping(except_)
                except_ = except_.items()

        def handle_exception():
            """Dispatch current exception to proper handler in ``except_``."""
            exc_type, exc_object = sys.exc_info()[:2]
            for t, handler in except_:
                if issubclass(exc_type, t):
                    return handler(exc_object)
            raise

        if else_:
            ensure_callable(else_)
            if finally_:
                ensure_callable(finally_)
                try:
                    block()
                except:
                    return handle_exception()
                else:
                    return else_()
                finally:
                    finally_()
            else:
                try:
                    block()
                except:
                    return handle_exception()
                else:
                    return else_()
        else:
            if finally_:
                ensure_callable(finally_)
                try:
                    return block()
                except:
                    return handle_exception()
                finally:
                    finally_()
            else:
                try:
                    return block()
                except:
                    return handle_exception()
    elif finally_:
        ensure_callable(finally_)
        try:
            return block()
        finally:
            finally_()
Exemple #13
0
def try_(block, except_=None, else_=None, finally_=None):
    """Emulate a ``try`` block.

    :param block: Function to be executed within the ``try`` statement
    :param except_: Function to execute when an :class:`Exception` occurs.
                    It receives a single argument: the exception object.
                    Alternatively, a list of key-value pairs can be passed,
                    mapping exception types to their handler functions.
    :param else_: Function to execute when ``block`` completes successfully.
                  Note that it requires ``except_`` to be provided as well
    :param finally_: Function to execute at the end,
                     regardless of whether an exception occurred or not

    :return:  If no exception was raised while executing ``block``,
              the result of ``else_`` (if provided) or ``block`` is returned.
              Otherwise, the result of ``except_`` is returned.

    Note that :func:`try_` can _still_ raise an exception if it occurs
    while evaluating ``except_``, ``else_`` or ``finally_`` functions.
    """
    ensure_callable(block)
    if not (except_ or else_ or finally_):
        raise TypeError("at least one of `except_`, `else_` or `finally_` "
                        "functions must be provided")
    if else_ and not except_:
        raise TypeError("`else_` can only be provided along with `except_`")

    if except_:
        if callable(except_):
            except_ = [(Exception, except_)]
        else:
            ensure_iterable(except_)
            if is_mapping(except_):
                ensure_ordered_mapping(except_)
                except_ = except_.items()

        def handle_exception():
            """Dispatch current exception to proper handler in ``except_``."""
            exc_type, exc_object = sys.exc_info()[:2]
            for t, handler in except_:
                if issubclass(exc_type, t):
                    return handler(exc_object)
            raise

        if else_:
            ensure_callable(else_)
            if finally_:
                ensure_callable(finally_)
                try:
                    block()
                except:
                    return handle_exception()
                else:
                    return else_()
                finally:
                    finally_()
            else:
                try:
                    block()
                except:
                    return handle_exception()
                else:
                    return else_()
        else:
            if finally_:
                ensure_callable(finally_)
                try:
                    return block()
                except:
                    return handle_exception()
                finally:
                    finally_()
            else:
                try:
                    return block()
                except:
                    return handle_exception()
    elif finally_:
        ensure_callable(finally_)
        try:
            return block()
        finally:
            finally_()
Exemple #14
0
 def __init__(self, iterable=(), **kwargs):
     if is_mapping(iterable):
         iterable = iteritems(iterable)
     super(AbsentDict, self).__init__(
         chain(((k, v) for k, v in iterable if v is not ABSENT),
               ((k, v) for k, v in iteritems(kwargs) if v is not ABSENT)))
Exemple #15
0
 def _assertIsMapping(self, obj, msg=None):
     if not is_mapping(obj):
         self.fail(msg or "%r is not a mapping" % (obj,))