blob: 0407ae9b6a97019765d0c76b684acd36356ef20b [file] [log] [blame]
Benjamin Petersonbed7d042009-07-19 21:01:52 +00001"""Various utility functions."""
2
Raymond Hettinger93e233d2010-12-24 10:02:22 +00003from collections import namedtuple, Counter
4
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
119def _count_diff_hashable(actual, expected):
120 'Returns list of (cnt_act, cnt_exp, elem) triples where the counts differ'
121 # elements must be hashable
122 s, t = Counter(actual), Counter(expected)
123 if s == t:
124 return []
125 result = []
126 for elem, cnt_s in s.items():
127 cnt_t = t[elem]
128 if cnt_s != cnt_t:
129 diff = _Mismatch(cnt_s, cnt_t, elem)
130 result.append(diff)
131 for elem, cnt_t in t.items():
132 if elem not in s:
133 diff = _Mismatch(0, cnt_t, elem)
134 result.append(diff)
135 return result