(closes #493) convert remaining GET form to use POST
[buildbot.git] / buildbot / process / properties.py
blobc98b91dbfca89f0341122d6f420f978823c217a2
1 import re
2 import weakref
3 from buildbot import util
5 class Properties(util.ComparableMixin):
6 """
7 I represent a set of properties that can be interpolated into various
8 strings in buildsteps.
10 @ivar properties: dictionary mapping property values to tuples
11 (value, source), where source is a string identifing the source
12 of the property.
14 Objects of this class can be read like a dictionary -- in this case,
15 only the property value is returned.
17 As a special case, a property value of None is returned as an empty
18 string when used as a mapping.
19 """
21 compare_attrs = ('properties',)
23 def __init__(self, **kwargs):
24 """
25 @param kwargs: initial property values (for testing)
26 """
27 self.properties = {}
28 self.pmap = PropertyMap(self)
29 if kwargs: self.update(kwargs, "TEST")
31 def __getstate__(self):
32 d = self.__dict__.copy()
33 del d['pmap']
34 return d
36 def __setstate__(self, d):
37 self.__dict__ = d
38 self.pmap = PropertyMap(self)
40 def __contains__(self, name):
41 return name in self.properties
43 def __getitem__(self, name):
44 """Just get the value for this property."""
45 rv = self.properties[name][0]
46 return rv
48 def has_key(self, name):
49 return self.properties.has_key(name)
51 def getProperty(self, name, default=None):
52 """Get the value for the given property."""
53 return self.properties.get(name, (default,))[0]
55 def getPropertySource(self, name):
56 return self.properties[name][1]
58 def asList(self):
59 """Return the properties as a sorted list of (name, value, source)"""
60 l = [ (k, v[0], v[1]) for k,v in self.properties.items() ]
61 l.sort()
62 return l
64 def __repr__(self):
65 return repr(dict([ (k,v[0]) for k,v in self.properties.iteritems() ]))
67 def setProperty(self, name, value, source):
68 self.properties[name] = (value, source)
70 def update(self, dict, source):
71 """Update this object from a dictionary, with an explicit source specified."""
72 for k, v in dict.items():
73 self.properties[k] = (v, source)
75 def updateFromProperties(self, other):
76 """Update this object based on another object; the other object's """
77 self.properties.update(other.properties)
79 def render(self, value):
80 """
81 Return a variant of value that has any WithProperties objects
82 substituted. This recurses into Python's compound data types.
83 """
84 # we use isinstance to detect Python's standard data types, and call
85 # this function recursively for the values in those types
86 if isinstance(value, (str, unicode)):
87 return value
88 elif isinstance(value, WithProperties):
89 return value.render(self.pmap)
90 elif isinstance(value, list):
91 return [ self.render(e) for e in value ]
92 elif isinstance(value, tuple):
93 return tuple([ self.render(e) for e in value ])
94 elif isinstance(value, dict):
95 return dict([ (self.render(k), self.render(v)) for k,v in value.iteritems() ])
96 else:
97 return value
99 class PropertyMap:
101 Privately-used mapping object to implement WithProperties' substitutions,
102 including the rendering of None as ''.
104 colon_minus_re = re.compile(r"(.*):-(.*)")
105 colon_plus_re = re.compile(r"(.*):\+(.*)")
106 def __init__(self, properties):
107 # use weakref here to avoid a reference loop
108 self.properties = weakref.ref(properties)
110 def __getitem__(self, key):
111 properties = self.properties()
112 assert properties is not None
114 # %(prop:-repl)s
115 # if prop exists, use it; otherwise, use repl
116 mo = self.colon_minus_re.match(key)
117 if mo:
118 prop, repl = mo.group(1,2)
119 if properties.has_key(prop):
120 rv = properties[prop]
121 else:
122 rv = repl
123 else:
124 # %(prop:+repl)s
125 # if prop exists, use repl; otherwise, an empty string
126 mo = self.colon_plus_re.match(key)
127 if mo:
128 prop, repl = mo.group(1,2)
129 if properties.has_key(prop):
130 rv = repl
131 else:
132 rv = ''
133 else:
134 rv = properties[key]
136 # translate 'None' to an empty string
137 if rv is None: rv = ''
138 return rv
140 class WithProperties(util.ComparableMixin):
142 This is a marker class, used fairly widely to indicate that we
143 want to interpolate build properties.
146 compare_attrs = ('fmtstring', 'args')
148 def __init__(self, fmtstring, *args):
149 self.fmtstring = fmtstring
150 self.args = args
152 def render(self, pmap):
153 if self.args:
154 strings = []
155 for name in self.args:
156 strings.append(pmap[name])
157 s = self.fmtstring % tuple(strings)
158 else:
159 s = self.fmtstring % pmap
160 return s