Create text_helper shortened(...)
authorChristian Herdtweck <christian.herdtweck@intra2net.com>
Wed, 7 Dec 2016 10:21:47 +0000 (11:21 +0100)
committerChristian Herdtweck <christian.herdtweck@intra2net.com>
Wed, 7 Dec 2016 10:21:47 +0000 (11:21 +0100)
src/text_helpers.py

index e055f20..d5e208f 100644 (file)
@@ -27,7 +27,73 @@ except ImportError:    # different name in py2
     from __builtin__ import print as _builtin_print
 
 from functools import partial
+from itertools import islice
 
+from type_helpers import isstr
+
+
+def shortened(iterable, n_head=20, n_tail=20, n_elems=None,
+              skip_elem="...(skipping {n_skipped} elements)..."):
+    """ convenient way to shorten a possibly very long iterable before printing
+
+    Will not modify short iterables, but for longer lists/tuples/... will only
+    yield first few, then a message how many were skipped and the last few
+
+    The iterable does not even have to have a `len(..)` method if argument
+    `n_elems` is given. Only needs a `next(..)` method. However, for very long
+    iterables this will be faster if radnom element access is provided via []
+
+    :param iterable: an iterable
+    :param int n_head: number of starting elements to yield (optional)
+    :param int n_tail: number of ending elements to yield (optional)
+    :param int n_elems: number of elements in iterable; give this to avoid a
+                        call to `len(iterable)` (optional)
+    :param skip_elem: element to replace bulge of skipped elements; yielded
+                      once at most; None to not yield a skip replacement; if str
+                      then it will be formatted; optional, defaults to string
+                      with number of skipped elems
+
+    ..seealso: :py:func:`slice`, py:func:`itertools.islice`
+    """
+
+    if n_elems is None:
+        n_elems = len(iterable)
+
+    # yield first n_head elems
+    index = 0
+    for elem in iterable:
+        index += 1
+        if index > n_head:
+            break
+        yield elem
+
+    # yield skip element
+    if n_elems > n_head + n_tail:
+        if skip_elem is not None:
+            if (isstr(skip_elem)):
+                yield skip_elem.format(n_skipped=n_elems-n_head-n_tail)
+            else:
+                yield skip_elem
+    elif n_elems <= n_head:
+        raise StopIteration()
+
+    # yield end
+    try:
+        # try to access end directly
+        for elem in iterable[n_elems-n_tail:]:
+            yield elem
+    except TypeError:
+        # if this did not work, then need to iterate through whole thing
+        # do this as in itertool recipe for consume():
+        n_skip = n_elems - n_head - n_tail - 1
+        next(islice(iterable, n_skip, n_skip), None)
+        for elem in iterable:
+            yield elem
+
+
+###############################################################################
+# TEXT FORMATTING/COLORING
+###############################################################################
 
 COLOR_BLACK = 'black'
 COLOR_RED = 'red'