blob: ccdf0b81fa8874b04a2ec21aab1898c3d6106965 [file] [log] [blame]
Benjamin Petersonbed7d042009-07-19 21:01:52 +00001"""Various utility functions."""
2
Raymond Hettinger9d668da2010-12-24 11:20:30 +00003from collections import namedtuple, OrderedDict
Raymond Hettinger93e233d2010-12-24 10:02:22 +00004
Benjamin Petersondccc1fc2010-03-22 00:15:53 +00005__unittest = True
6
Michael Foordcb11b252010-06-05 13:14:43 +00007_MAX_LENGTH = 80
8def safe_repr(obj, short=False):
Benjamin Peterson847a4112010-03-14 15:04:17 +00009 try:
Michael Foordcb11b252010-06-05 13:14:43 +000010 result = repr(obj)
Benjamin Peterson847a4112010-03-14 15:04:17 +000011 except Exception:
Michael Foordcb11b252010-06-05 13:14:43 +000012 result = object.__repr__(obj)
13 if not short or len(result) < _MAX_LENGTH:
14 return result
15 return result[:_MAX_LENGTH] + ' [truncated]...'
16
Benjamin Petersonbed7d042009-07-19 21:01:52 +000017def strclass(cls):
18 return "%s.%s" % (cls.__module__, cls.__name__)
19
20def sorted_list_difference(expected, actual):
21 """Finds elements in only one or the other of two, sorted input lists.
22
23 Returns a two-element tuple of lists. The first list contains those
24 elements in the "expected" list but not in the "actual" list, and the
25 second contains those elements in the "actual" list but not in the
26 "expected" list. Duplicate elements in either input list are ignored.
27 """
28 i = j = 0
29 missing = []
30 unexpected = []
31 while True:
32 try:
33 e = expected[i]
34 a = actual[j]
35 if e < a:
36 missing.append(e)
37 i += 1
38 while expected[i] == e:
39 i += 1
40 elif e > a:
41 unexpected.append(a)
42 j += 1
43 while actual[j] == a:
44 j += 1
45 else:
46 i += 1
47 try:
48 while expected[i] == e:
49 i += 1
50 finally:
51 j += 1
52 while actual[j] == a:
53 j += 1
54 except IndexError:
55 missing.extend(expected[i:])
56 unexpected.extend(actual[j:])
57 break
58 return missing, unexpected
59
60
61def unorderable_list_difference(expected, actual):
62 """Same behavior as sorted_list_difference but
63 for lists of unorderable items (like dicts).
64
65 As it does a linear search per item (remove) it
66 has O(n*n) performance."""
67 missing = []
68 while expected:
69 item = expected.pop()
70 try:
71 actual.remove(item)
72 except ValueError:
73 missing.append(item)
74
75 # anything left in actual is unexpected
76 return missing, actual
77
Benjamin Petersonbed7d042009-07-19 21:01:52 +000078def three_way_cmp(x, y):
79 """Return -1 if x < y, 0 if x == y and 1 if x > y"""
80 return (x > y) - (x < y)
Raymond Hettinger93e233d2010-12-24 10:02:22 +000081
82_Mismatch = namedtuple('Mismatch', 'actual expected value')
83
84def _count_diff_all_purpose(actual, expected):
85 'Returns list of (cnt_act, cnt_exp, elem) triples where the counts differ'
86 # elements need not be hashable
87 s, t = list(actual), list(expected)
88 m, n = len(s), len(t)
89 NULL = object()
90 result = []
91 for i, elem in enumerate(s):
92 if elem is NULL:
93 continue
94 cnt_s = cnt_t = 0
95 for j in range(i, m):
96 if s[j] == elem:
97 cnt_s += 1
98 s[j] = NULL
99 for j, other_elem in enumerate(t):
100 if other_elem == elem:
101 cnt_t += 1
102 t[j] = NULL
103 if cnt_s != cnt_t:
104 diff = _Mismatch(cnt_s, cnt_t, elem)
105 result.append(diff)
106
107 for i, elem in enumerate(t):
108 if elem is NULL:
109 continue
110 cnt_t = 0
111 for j in range(i, n):
112 if t[j] == elem:
113 cnt_t += 1
114 t[j] = NULL
115 diff = _Mismatch(0, cnt_t, elem)
116 result.append(diff)
117 return result
118
Raymond Hettingerefbcb1b2010-12-24 11:24:00 +0000119def _ordered_count(iterable):
Raymond Hettinger9d668da2010-12-24 11:20:30 +0000120 'Return dict of element counts, in the order they were first seen'
121 c = OrderedDict()
122 for elem in iterable:
123 c[elem] = c.get(elem, 0) + 1
124 return c
125
Raymond Hettinger93e233d2010-12-24 10:02:22 +0000126def _count_diff_hashable(actual, expected):
127 'Returns list of (cnt_act, cnt_exp, elem) triples where the counts differ'
128 # elements must be hashable
Raymond Hettingerefbcb1b2010-12-24 11:24:00 +0000129 s, t = _ordered_count(actual), _ordered_count(expected)
Raymond Hettinger93e233d2010-12-24 10:02:22 +0000130 result = []
131 for elem, cnt_s in s.items():
Raymond Hettinger9d668da2010-12-24 11:20:30 +0000132 cnt_t = t.get(elem, 0)
Raymond Hettinger93e233d2010-12-24 10:02:22 +0000133 if cnt_s != cnt_t:
134 diff = _Mismatch(cnt_s, cnt_t, elem)
135 result.append(diff)
136 for elem, cnt_t in t.items():
137 if elem not in s:
138 diff = _Mismatch(0, cnt_t, elem)
139 result.append(diff)
140 return result