import math import itertools def is_sequence_like(x): """ Returns True if x exposes a sequence-like interface. """ required_attrs = ( '__len__', '__getitem__' ) return all(hasattr(x, attr) for attr in required_attrs) def is_dict_like(x): """ Returns True if x exposes a dict-like interface. """ required_attrs = ( '__len__', '__getitem__', 'keys', 'values', ) return all(hasattr(x, attr) for attr in required_attrs) def join_with(iterable, separator): """ Joins elements from iterable with separator and returns the produced sequence as a list. separator must be addable to a list. """ inputs = list(iterable) b = [] for i, element in enumerate(inputs): if isinstance(element, (list, tuple, set)): b += tuple(element) else: b += [element] if i < len(inputs)-1: b += separator return b def chunkate_string(text, length): """ Iterates over the given seq in chunks of at maximally the given length. Will never break a whole word. """ iterator_index = 0 def next_newline(): try: return next(i for (i, c) in enumerate(text) if i > iterator_index and c == '\n') except StopIteration: return len(text) def next_breaker(): try: return next(i for (i, c) in reversed(tuple(enumerate(text))) if i >= iterator_index and (i < iterator_index+length) and c in (' ', '\t')) except StopIteration: return len(text) while iterator_index < len(text): next_chunk = text[iterator_index:min(next_newline(), next_breaker()+1)] iterator_index += len(next_chunk) yield next_chunk def flatten_nested(nested_dicts): """ Flattens dicts and sequences into one dict with tuples of keys representing the nested keys. Example >>> dd = { \ 'dict1': {'name': 'Jon', 'id': 42}, \ 'dict2': {'name': 'Sam', 'id': 41}, \ 'seq1': [{'one': 1, 'two': 2}] \ } >>> flatten_nested(dd) == { \ ('dict1', 'name'): 'Jon', ('dict1', 'id'): 42, \ ('dict2', 'name'): 'Sam', ('dict2', 'id'): 41, \ ('seq1', 0, 'one'): 1, ('seq1', 0, 'two'): 2, \ } True """ assert isinstance(nested_dicts, (dict, list, tuple)), 'Only works with a collection parameter' def items(c): if isinstance(c, dict): return c.items() elif isinstance(c, (list, tuple)): return enumerate(c) else: raise RuntimeError('c must be a collection') def flatten(dd): output = {} for k, v in items(dd): if isinstance(v, (dict, list, tuple)): for child_key, child_value in flatten(v).items(): output[(k,) + child_key] = child_value else: output[(k,)] = v return output return flatten(nested_dicts) class PeekableIterator: # Returned by peek() when the iterator is exhausted. Truthiness is False. Nothing = tuple() def __init__(self, iter): self._iter = iter def __next__(self): return next(self._iter) def next(self): return self.__next__() def __iter__(self): return self def peek(self): """ Returns PeekableIterator.Nothing when the iterator is exhausted. """ try: v = next(self._iter) self._iter = itertools.chain((v,), self._iter) return v except StopIteration: return PeekableIterator.Nothing