123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156 |
- """Various utility functions."""
- from collections import namedtuple, OrderedDict
- __unittest = True
- _MAX_LENGTH = 80
- def safe_repr(obj, short=False):
- try:
- result = repr(obj)
- except Exception:
- result = object.__repr__(obj)
- if not short or len(result) < _MAX_LENGTH:
- return result
- return result[:_MAX_LENGTH] + ' [truncated]...'
- def strclass(cls):
- return "%s.%s" % (cls.__module__, cls.__name__)
- def sorted_list_difference(expected, actual):
- """Finds elements in only one or the other of two, sorted input lists.
- Returns a two-element tuple of lists. The first list contains those
- elements in the "expected" list but not in the "actual" list, and the
- second contains those elements in the "actual" list but not in the
- "expected" list. Duplicate elements in either input list are ignored.
- """
- i = j = 0
- missing = []
- unexpected = []
- while True:
- try:
- e = expected[i]
- a = actual[j]
- if e < a:
- missing.append(e)
- i += 1
- while expected[i] == e:
- i += 1
- elif e > a:
- unexpected.append(a)
- j += 1
- while actual[j] == a:
- j += 1
- else:
- i += 1
- try:
- while expected[i] == e:
- i += 1
- finally:
- j += 1
- while actual[j] == a:
- j += 1
- except IndexError:
- missing.extend(expected[i:])
- unexpected.extend(actual[j:])
- break
- return missing, unexpected
- def unorderable_list_difference(expected, actual, ignore_duplicate=False):
- """Same behavior as sorted_list_difference but
- for lists of unorderable items (like dicts).
- As it does a linear search per item (remove) it
- has O(n*n) performance.
- """
- missing = []
- unexpected = []
- while expected:
- item = expected.pop()
- try:
- actual.remove(item)
- except ValueError:
- missing.append(item)
- if ignore_duplicate:
- for lst in expected, actual:
- try:
- while True:
- lst.remove(item)
- except ValueError:
- pass
- if ignore_duplicate:
- while actual:
- item = actual.pop()
- unexpected.append(item)
- try:
- while True:
- actual.remove(item)
- except ValueError:
- pass
- return missing, unexpected
- # anything left in actual is unexpected
- return missing, actual
- _Mismatch = namedtuple('Mismatch', 'actual expected value')
- def _count_diff_all_purpose(actual, expected):
- 'Returns list of (cnt_act, cnt_exp, elem) triples where the counts differ'
- # elements need not be hashable
- s, t = list(actual), list(expected)
- m, n = len(s), len(t)
- NULL = object()
- result = []
- for i, elem in enumerate(s):
- if elem is NULL:
- continue
- cnt_s = cnt_t = 0
- for j in range(i, m):
- if s[j] == elem:
- cnt_s += 1
- s[j] = NULL
- for j, other_elem in enumerate(t):
- if other_elem == elem:
- cnt_t += 1
- t[j] = NULL
- if cnt_s != cnt_t:
- diff = _Mismatch(cnt_s, cnt_t, elem)
- result.append(diff)
- for i, elem in enumerate(t):
- if elem is NULL:
- continue
- cnt_t = 0
- for j in range(i, n):
- if t[j] == elem:
- cnt_t += 1
- t[j] = NULL
- diff = _Mismatch(0, cnt_t, elem)
- result.append(diff)
- return result
- def _ordered_count(iterable):
- 'Return dict of element counts, in the order they were first seen'
- c = OrderedDict()
- for elem in iterable:
- c[elem] = c.get(elem, 0) + 1
- return c
- def _count_diff_hashable(actual, expected):
- 'Returns list of (cnt_act, cnt_exp, elem) triples where the counts differ'
- # elements must be hashable
- s, t = _ordered_count(actual), _ordered_count(expected)
- result = []
- for elem, cnt_s in s.items():
- cnt_t = t.get(elem, 0)
- if cnt_s != cnt_t:
- diff = _Mismatch(cnt_s, cnt_t, elem)
- result.append(diff)
- for elem, cnt_t in t.items():
- if elem not in s:
- diff = _Mismatch(0, cnt_t, elem)
- result.append(diff)
- return result
|