1ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh"""Various utility functions.""" 2ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehfrom collections import namedtuple, OrderedDict 3ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 4ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 5ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh__unittest = True 6ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 7ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh_MAX_LENGTH = 80 8ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehdef safe_repr(obj, short=False): 9ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh try: 10ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh result = repr(obj) 11ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh except Exception: 12ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh result = object.__repr__(obj) 13ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh if not short or len(result) < _MAX_LENGTH: 14ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh return result 15ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh return result[:_MAX_LENGTH] + ' [truncated]...' 16ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 17ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 18ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehdef strclass(cls): 19ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh return "%s.%s" % (cls.__module__, cls.__name__) 20ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 21ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehdef sorted_list_difference(expected, actual): 22ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh """Finds elements in only one or the other of two, sorted input lists. 23ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 24ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh Returns a two-element tuple of lists. The first list contains those 25ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh elements in the "expected" list but not in the "actual" list, and the 26ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh second contains those elements in the "actual" list but not in the 27ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh "expected" list. Duplicate elements in either input list are ignored. 28ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh """ 29ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh i = j = 0 30ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh missing = [] 31ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh unexpected = [] 32ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh while True: 33ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh try: 34ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh e = expected[i] 35ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh a = actual[j] 36ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh if e < a: 37ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh missing.append(e) 38ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh i += 1 39ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh while expected[i] == e: 40ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh i += 1 41ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh elif e > a: 42ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh unexpected.append(a) 43ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh j += 1 44ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh while actual[j] == a: 45ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh j += 1 46ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh else: 47ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh i += 1 48ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh try: 49ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh while expected[i] == e: 50ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh i += 1 51ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh finally: 52ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh j += 1 53ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh while actual[j] == a: 54ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh j += 1 55ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh except IndexError: 56ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh missing.extend(expected[i:]) 57ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh unexpected.extend(actual[j:]) 58ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh break 59ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh return missing, unexpected 60ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 61ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 62ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehdef unorderable_list_difference(expected, actual, ignore_duplicate=False): 63ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh """Same behavior as sorted_list_difference but 64ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh for lists of unorderable items (like dicts). 65ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 66ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh As it does a linear search per item (remove) it 67ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh has O(n*n) performance. 68ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh """ 69ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh missing = [] 70ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh unexpected = [] 71ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh while expected: 72ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh item = expected.pop() 73ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh try: 74ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh actual.remove(item) 75ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh except ValueError: 76ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh missing.append(item) 77ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh if ignore_duplicate: 78ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh for lst in expected, actual: 79ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh try: 80ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh while True: 81ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh lst.remove(item) 82ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh except ValueError: 83ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh pass 84ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh if ignore_duplicate: 85ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh while actual: 86ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh item = actual.pop() 87ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh unexpected.append(item) 88ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh try: 89ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh while True: 90ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh actual.remove(item) 91ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh except ValueError: 92ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh pass 93ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh return missing, unexpected 94ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 95ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh # anything left in actual is unexpected 96ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh return missing, actual 97ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 98ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh_Mismatch = namedtuple('Mismatch', 'actual expected value') 99ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 100ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehdef _count_diff_all_purpose(actual, expected): 101ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 'Returns list of (cnt_act, cnt_exp, elem) triples where the counts differ' 102ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh # elements need not be hashable 103ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh s, t = list(actual), list(expected) 104ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh m, n = len(s), len(t) 105ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh NULL = object() 106ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh result = [] 107ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh for i, elem in enumerate(s): 108ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh if elem is NULL: 109ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh continue 110ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh cnt_s = cnt_t = 0 111ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh for j in range(i, m): 112ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh if s[j] == elem: 113ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh cnt_s += 1 114ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh s[j] = NULL 115ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh for j, other_elem in enumerate(t): 116ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh if other_elem == elem: 117ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh cnt_t += 1 118ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh t[j] = NULL 119ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh if cnt_s != cnt_t: 120ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh diff = _Mismatch(cnt_s, cnt_t, elem) 121ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh result.append(diff) 122ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 123ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh for i, elem in enumerate(t): 124ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh if elem is NULL: 125ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh continue 126ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh cnt_t = 0 127ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh for j in range(i, n): 128ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh if t[j] == elem: 129ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh cnt_t += 1 130ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh t[j] = NULL 131ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh diff = _Mismatch(0, cnt_t, elem) 132ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh result.append(diff) 133ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh return result 134ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 135ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehdef _ordered_count(iterable): 136ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 'Return dict of element counts, in the order they were first seen' 137ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh c = OrderedDict() 138ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh for elem in iterable: 139ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh c[elem] = c.get(elem, 0) + 1 140ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh return c 141ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 142ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehdef _count_diff_hashable(actual, expected): 143ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 'Returns list of (cnt_act, cnt_exp, elem) triples where the counts differ' 144ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh # elements must be hashable 145ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh s, t = _ordered_count(actual), _ordered_count(expected) 146ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh result = [] 147ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh for elem, cnt_s in s.items(): 148ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh cnt_t = t.get(elem, 0) 149ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh if cnt_s != cnt_t: 150ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh diff = _Mismatch(cnt_s, cnt_t, elem) 151ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh result.append(diff) 152ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh for elem, cnt_t in t.items(): 153ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh if elem not in s: 154ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh diff = _Mismatch(0, cnt_t, elem) 155ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh result.append(diff) 156ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh return result 157