1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 """Utility functions with algorithms.
22
23 """
24
25 import re
26 import time
27
28
29 _SORTER_GROUPS = 8
30 _SORTER_RE = re.compile("^%s(.*)$" % (_SORTER_GROUPS * "(\D+|\d+)?"))
31 _SORTER_DIGIT = re.compile("^\d+$")
32
33
35 """Returns a list with unique elements.
36
37 Element order is preserved.
38
39 @type seq: sequence
40 @param seq: the sequence with the source elements
41 @rtype: list
42 @return: list of unique elements from seq
43
44 """
45 seen = set()
46 return [i for i in seq if i not in seen and not seen.add(i)]
47
48
50 """Identifies duplicates in a list.
51
52 Does not preserve element order.
53
54 @type seq: sequence
55 @param seq: Sequence with source elements
56 @rtype: list
57 @return: List of duplicate elements from seq
58
59 """
60 dup = set()
61 seen = set()
62
63 for item in seq:
64 if item in seen:
65 dup.add(item)
66 else:
67 seen.add(item)
68
69 return list(dup)
70
71
73 """Attempts to convert a string to an integer.
74
75 """
76 if val and _SORTER_DIGIT.match(val):
77 return int(val)
78 else:
79 return val
80
81
88
89
91 """Sort a list of strings based on digit and non-digit groupings.
92
93 Given a list of names C{['a1', 'a10', 'a11', 'a2']} this function
94 will sort the list in the logical order C{['a1', 'a2', 'a10',
95 'a11']}.
96
97 The sort algorithm breaks each name in groups of either only-digits
98 or no-digits. Only the first eight such groups are considered, and
99 after that we just use what's left of the string.
100
101 @type values: list
102 @param values: the names to be sorted
103 @type key: callable or None
104 @param key: function of one argument to extract a comparison key from each
105 list element, must return string
106 @rtype: list
107 @return: a copy of the name list sorted with our algorithm
108
109 """
110 if key is None:
111 keyfunc = NiceSortKey
112 else:
113 keyfunc = lambda value: NiceSortKey(key(value))
114
115 return sorted(values, key=keyfunc)
116
117
119 """Inverts the key/value mapping of a dict.
120
121 @param dict_in: The dict to invert
122 @return: the inverted dict
123
124 """
125 return dict(zip(dict_in.values(), dict_in.keys()))
126
127
129 """Class to calculate remaining timeout when doing several operations.
130
131 """
132 __slots__ = [
133 "_allow_negative",
134 "_start_time",
135 "_time_fn",
136 "_timeout",
137 ]
138
139 - def __init__(self, timeout, allow_negative, _time_fn=time.time):
140 """Initializes this class.
141
142 @type timeout: float
143 @param timeout: Timeout duration
144 @type allow_negative: bool
145 @param allow_negative: Whether to return values below zero
146 @param _time_fn: Time function for unittests
147
148 """
149 object.__init__(self)
150
151 if timeout is not None and timeout < 0.0:
152 raise ValueError("Timeout must not be negative")
153
154 self._timeout = timeout
155 self._allow_negative = allow_negative
156 self._time_fn = _time_fn
157
158 self._start_time = None
159
160 - def Remaining(self):
161 """Returns the remaining timeout.
162
163 """
164 if self._timeout is None:
165 return None
166
167
168 if self._start_time is None:
169 self._start_time = self._time_fn()
170
171
172 remaining_timeout = self._start_time + self._timeout - self._time_fn()
173
174 if not self._allow_negative:
175
176 return max(0.0, remaining_timeout)
177
178 return remaining_timeout
179