Statistics
| Branch: | Tag: | Revision:

root / lib / utils / algo.py @ cdf71b12

History | View | Annotate | Download (4.9 kB)

1
#
2
#
3

    
4
# Copyright (C) 2006, 2007, 2010, 2011 Google Inc.
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
# General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
# 02110-1301, USA.
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

    
34
def UniqueSequence(seq):
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

    
49
def JoinDisjointDicts(dict_a, dict_b):
50
  """Joins dictionaries with no conflicting keys.
51

52
  Enforces the constraint that the two key sets must be disjoint, and then
53
  merges the two dictionaries in a new dictionary that is returned to the
54
  caller.
55

56
  @type dict_a: dict
57
  @param dict_a: the first dictionary
58
  @type dict_b: dict
59
  @param dict_b: the second dictionary
60
  @rtype: dict
61
  @return: a new dictionary containing all the key/value pairs contained in the
62
  two dictionaries.
63

64
  """
65
  assert not (set(dict_a) & set(dict_b)), ("Duplicate keys found while joining"
66
                                           " %s and %s" % (dict_a, dict_b))
67
  result = dict_a.copy()
68
  result.update(dict_b)
69
  return result
70

    
71

    
72
def FindDuplicates(seq):
73
  """Identifies duplicates in a list.
74

75
  Does not preserve element order.
76

77
  @type seq: sequence
78
  @param seq: Sequence with source elements
79
  @rtype: list
80
  @return: List of duplicate elements from seq
81

82
  """
83
  dup = set()
84
  seen = set()
85

    
86
  for item in seq:
87
    if item in seen:
88
      dup.add(item)
89
    else:
90
      seen.add(item)
91

    
92
  return list(dup)
93

    
94

    
95
def _NiceSortTryInt(val):
96
  """Attempts to convert a string to an integer.
97

98
  """
99
  if val and _SORTER_DIGIT.match(val):
100
    return int(val)
101
  else:
102
    return val
103

    
104

    
105
def NiceSortKey(value):
106
  """Extract key for sorting.
107

108
  """
109
  return [_NiceSortTryInt(grp)
110
          for grp in _SORTER_RE.match(value).groups()]
111

    
112

    
113
def NiceSort(values, key=None):
114
  """Sort a list of strings based on digit and non-digit groupings.
115

116
  Given a list of names C{['a1', 'a10', 'a11', 'a2']} this function
117
  will sort the list in the logical order C{['a1', 'a2', 'a10',
118
  'a11']}.
119

120
  The sort algorithm breaks each name in groups of either only-digits
121
  or no-digits. Only the first eight such groups are considered, and
122
  after that we just use what's left of the string.
123

124
  @type values: list
125
  @param values: the names to be sorted
126
  @type key: callable or None
127
  @param key: function of one argument to extract a comparison key from each
128
    list element, must return string
129
  @rtype: list
130
  @return: a copy of the name list sorted with our algorithm
131

132
  """
133
  if key is None:
134
    keyfunc = NiceSortKey
135
  else:
136
    keyfunc = lambda value: NiceSortKey(key(value))
137

    
138
  return sorted(values, key=keyfunc)
139

    
140

    
141
def InvertDict(dict_in):
142
  """Inverts the key/value mapping of a dict.
143

144
  @param dict_in: The dict to invert
145
  @return: the inverted dict
146

147
  """
148
  return dict(zip(dict_in.values(), dict_in.keys()))
149

    
150

    
151
class RunningTimeout(object):
152
  """Class to calculate remaining timeout when doing several operations.
153

154
  """
155
  __slots__ = [
156
    "_allow_negative",
157
    "_start_time",
158
    "_time_fn",
159
    "_timeout",
160
    ]
161

    
162
  def __init__(self, timeout, allow_negative, _time_fn=time.time):
163
    """Initializes this class.
164

165
    @type timeout: float
166
    @param timeout: Timeout duration
167
    @type allow_negative: bool
168
    @param allow_negative: Whether to return values below zero
169
    @param _time_fn: Time function for unittests
170

171
    """
172
    object.__init__(self)
173

    
174
    if timeout is not None and timeout < 0.0:
175
      raise ValueError("Timeout must not be negative")
176

    
177
    self._timeout = timeout
178
    self._allow_negative = allow_negative
179
    self._time_fn = _time_fn
180

    
181
    self._start_time = None
182

    
183
  def Remaining(self):
184
    """Returns the remaining timeout.
185

186
    """
187
    if self._timeout is None:
188
      return None
189

    
190
    # Get start time on first calculation
191
    if self._start_time is None:
192
      self._start_time = self._time_fn()
193

    
194
    # Calculate remaining time
195
    remaining_timeout = self._start_time + self._timeout - self._time_fn()
196

    
197
    if not self._allow_negative:
198
      # Ensure timeout is always >= 0
199
      return max(0.0, remaining_timeout)
200

    
201
    return remaining_timeout