(closes #493) convert remaining GET form to use POST
[buildbot.git] / buildbot / util.py
blobf1ca205fe0af1d0cbf7e0bc50cb54b77f1100db6
1 # -*- test-case-name: buildbot.test.test_util -*-
3 from twisted.internet.defer import Deferred
4 from twisted.spread import pb
5 import time, re
7 def naturalSort(l):
8 """Returns a sorted copy of l, so that numbers in strings are sorted in the
9 proper order.
11 e.g. ['foo10', 'foo1', 'foo2'] will be sorted as ['foo1', 'foo2', 'foo10']
12 instead of the default ['foo1', 'foo10', 'foo2']"""
13 l = l[:]
14 def try_int(s):
15 try:
16 return int(s)
17 except:
18 return s
19 def key_func(item):
20 return [try_int(s) for s in re.split('(\d+)', item)]
21 # prepend integer keys to each element, sort them, then strip the keys
22 keyed_l = [ (key_func(i), i) for i in l ]
23 keyed_l.sort()
24 l = [ i[1] for i in keyed_l ]
25 return l
27 def now():
28 #return int(time.time())
29 return time.time()
31 def earlier(old, new):
32 # minimum of two things, but "None" counts as +infinity
33 if old:
34 if new < old:
35 return new
36 return old
37 return new
39 def later(old, new):
40 # maximum of two things, but "None" counts as -infinity
41 if old:
42 if new > old:
43 return new
44 return old
45 return new
47 def formatInterval(eta):
48 eta_parts = []
49 if eta > 3600:
50 eta_parts.append("%d hrs" % (eta / 3600))
51 eta %= 3600
52 if eta > 60:
53 eta_parts.append("%d mins" % (eta / 60))
54 eta %= 60
55 eta_parts.append("%d secs" % eta)
56 return ", ".join(eta_parts)
58 class CancelableDeferred(Deferred):
59 """I am a version of Deferred that can be canceled by calling my
60 .cancel() method. After being canceled, no callbacks or errbacks will be
61 executed.
62 """
63 def __init__(self):
64 Deferred.__init__(self)
65 self.canceled = 0
66 def cancel(self):
67 self.canceled = 1
68 def _runCallbacks(self):
69 if self.canceled:
70 self.callbacks = []
71 return
72 Deferred._runCallbacks(self)
74 def ignoreStaleRefs(failure):
75 """d.addErrback(util.ignoreStaleRefs)"""
76 r = failure.trap(pb.DeadReferenceError, pb.PBConnectionLost)
77 return None
79 class _None:
80 pass
82 class ComparableMixin:
83 """Specify a list of attributes that are 'important'. These will be used
84 for all comparison operations."""
86 compare_attrs = []
88 def __hash__(self):
89 alist = [self.__class__] + \
90 [getattr(self, name, _None) for name in self.compare_attrs]
91 return hash(tuple(alist))
93 def __cmp__(self, them):
94 result = cmp(type(self), type(them))
95 if result:
96 return result
98 result = cmp(self.__class__, them.__class__)
99 if result:
100 return result
102 assert self.compare_attrs == them.compare_attrs
103 self_list= [getattr(self, name, _None) for name in self.compare_attrs]
104 them_list= [getattr(them, name, _None) for name in self.compare_attrs]
105 return cmp(self_list, them_list)
107 def to_text(s):
108 if isinstance(s, (str, unicode)):
109 return s
110 else:
111 return str(s)