123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170 |
- """Various utility functions."""
- from collections import namedtuple, Counter
- from os.path import commonprefix
- __unittest = True
- _MAX_LENGTH = 80
- _PLACEHOLDER_LEN = 12
- _MIN_BEGIN_LEN = 5
- _MIN_END_LEN = 5
- _MIN_COMMON_LEN = 5
- _MIN_DIFF_LEN = _MAX_LENGTH - \
- (_MIN_BEGIN_LEN + _PLACEHOLDER_LEN + _MIN_COMMON_LEN +
- _PLACEHOLDER_LEN + _MIN_END_LEN)
- assert _MIN_DIFF_LEN >= 0
- def _shorten(s, prefixlen, suffixlen):
- skip = len(s) - prefixlen - suffixlen
- if skip > _PLACEHOLDER_LEN:
- s = '%s[%d chars]%s' % (s[:prefixlen], skip, s[len(s) - suffixlen:])
- return s
- def _common_shorten_repr(*args):
- args = tuple(map(safe_repr, args))
- maxlen = max(map(len, args))
- if maxlen <= _MAX_LENGTH:
- return args
- prefix = commonprefix(args)
- prefixlen = len(prefix)
- common_len = _MAX_LENGTH - \
- (maxlen - prefixlen + _MIN_BEGIN_LEN + _PLACEHOLDER_LEN)
- if common_len > _MIN_COMMON_LEN:
- assert _MIN_BEGIN_LEN + _PLACEHOLDER_LEN + _MIN_COMMON_LEN + \
- (maxlen - prefixlen) < _MAX_LENGTH
- prefix = _shorten(prefix, _MIN_BEGIN_LEN, common_len)
- return tuple(prefix + s[prefixlen:] for s in args)
- prefix = _shorten(prefix, _MIN_BEGIN_LEN, _MIN_COMMON_LEN)
- return tuple(prefix + _shorten(s[prefixlen:], _MIN_DIFF_LEN, _MIN_END_LEN)
- for s in args)
- 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.__qualname__)
- 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):
- """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 = []
- while expected:
- item = expected.pop()
- try:
- actual.remove(item)
- except ValueError:
- missing.append(item)
- # anything left in actual is unexpected
- return missing, actual
- def three_way_cmp(x, y):
- """Return -1 if x < y, 0 if x == y and 1 if x > y"""
- return (x > y) - (x < y)
- _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 _count_diff_hashable(actual, expected):
- 'Returns list of (cnt_act, cnt_exp, elem) triples where the counts differ'
- # elements must be hashable
- s, t = Counter(actual), Counter(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
|