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))
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)
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)
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
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
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)
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
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)
def _assertIsMapping(self, obj, msg=None): if not is_mapping(obj): self.fail(msg or "%r is not a mapping" % (obj, ))
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_()
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)))
def _assertIsMapping(self, obj, msg=None): if not is_mapping(obj): self.fail(msg or "%r is not a mapping" % (obj,))