1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 import re
17 import weakref
18 from buildbot import util
19 from buildbot.interfaces import IRenderable, IProperties
20 from twisted.python.components import registerAdapter
21 from zope.interface import implements
22
24 """
25 I represent a set of properties that can be interpolated into various
26 strings in buildsteps.
27
28 @ivar properties: dictionary mapping property values to tuples
29 (value, source), where source is a string identifing the source
30 of the property.
31
32 Objects of this class can be read like a dictionary -- in this case,
33 only the property value is returned.
34
35 As a special case, a property value of None is returned as an empty
36 string when used as a mapping.
37 """
38
39 compare_attrs = ('properties',)
40 implements(IProperties)
41
43 """
44 @param kwargs: initial property values (for testing)
45 """
46 self.properties = {}
47
48
49 self.runtime = set()
50 self.pmap = PropertyMap(self)
51 self.build = None
52 if kwargs: self.update(kwargs, "TEST")
53
55 d = self.__dict__.copy()
56 del d['pmap']
57 d['build'] = None
58 return d
59
61 self.__dict__ = d
62 self.pmap = PropertyMap(self)
63 if not hasattr(self, 'runtime'):
64 self.runtime = set()
65
68
70 """Just get the value for this property."""
71 rv = self.properties[name][0]
72 return rv
73
76
79
81 """Return the properties as a sorted list of (name, value, source)"""
82 l = [ (k, v[0], v[1]) for k,v in self.properties.iteritems() ]
83 l.sort()
84 return l
85
87 """Return the properties as a simple key:value dictionary"""
88 return dict(self.properties)
89
91 return ('Properties(**' +
92 repr(dict((k,v[0]) for k,v in self.properties.iteritems())) +
93 ')')
94
95 - def update(self, dict, source, runtime=False):
96 """Update this object from a dictionary, with an explicit source specified."""
97 for k, v in dict.items():
98 self.properties[k] = (v, source)
99 if runtime:
100 self.runtime.add(k)
101
106
108 """Update this object based on another object, but don't
109 include properties that were marked as runtime."""
110 for k,v in other.properties.iteritems():
111 if k not in other.runtime:
112 self.properties[k] = v
113
114
115
118
121
122 has_key = hasProperty
123
124 - def setProperty(self, name, value, source, runtime=False):
128
131
134
138
139
141 """
142 A mixin to add L{IProperties} methods to a class which does not implement
143 the interface, but which can be coerced to the interface via an adapter.
144
145 This is useful because L{IProperties} methods are often called on L{Build}
146 and L{BuildStatus} objects without first coercing them.
147
148 @ivar set_runtime_properties: the default value for the C{runtime}
149 parameter of L{setProperty}.
150 """
151
152 set_runtime_properties = False
153
157
161
162 has_key = hasProperty
163
164 - def setProperty(self, propname, value, source='Unknown', runtime=None):
171
174
178
179
180
182 """
183 Privately-used mapping object to implement WithProperties' substitutions,
184 including the rendering of None as ''.
185 """
186 colon_minus_re = re.compile(r"(.*):-(.*)")
187 colon_tilde_re = re.compile(r"(.*):~(.*)")
188 colon_plus_re = re.compile(r"(.*):\+(.*)")
193
195 properties = self.properties()
196 assert properties is not None
197
198 def colon_minus(mo):
199
200
201 prop, repl = mo.group(1,2)
202 if prop in self.temp_vals:
203 return self.temp_vals[prop]
204 elif properties.has_key(prop):
205 return properties[prop]
206 else:
207 return repl
208
209 def colon_tilde(mo):
210
211
212 prop, repl = mo.group(1,2)
213 if prop in self.temp_vals and self.temp_vals[prop]:
214 return self.temp_vals[prop]
215 elif properties.has_key(prop) and properties[prop]:
216 return properties[prop]
217 else:
218 return repl
219
220 def colon_plus(mo):
221
222
223 prop, repl = mo.group(1,2)
224 if properties.has_key(prop) or prop in self.temp_vals:
225 return repl
226 else:
227 return ''
228
229 for regexp, fn in [
230 ( self.colon_minus_re, colon_minus ),
231 ( self.colon_tilde_re, colon_tilde ),
232 ( self.colon_plus_re, colon_plus ),
233 ]:
234 mo = regexp.match(key)
235 if mo:
236 rv = fn(mo)
237 break
238 else:
239
240
241 if key in self.temp_vals:
242 rv = self.temp_vals[key]
243 else:
244 rv = properties[key]
245
246
247 if rv is None: rv = ''
248 return rv
249
251 'Add a temporary value (to support keyword arguments to WithProperties)'
252 self.temp_vals[key] = val
253
256
258 """
259 This is a marker class, used fairly widely to indicate that we
260 want to interpolate build properties.
261 """
262
263 implements(IRenderable)
264 compare_attrs = ('fmtstring', 'args')
265
266 - def __init__(self, fmtstring, *args, **lambda_subs):
267 self.fmtstring = fmtstring
268 self.args = args
269 if not self.args:
270 self.lambda_subs = lambda_subs
271 for key, val in self.lambda_subs.iteritems():
272 if not callable(val):
273 raise ValueError('Value for lambda substitution "%s" must be callable.' % key)
274 elif lambda_subs:
275 raise ValueError('WithProperties takes either positional or keyword substitutions, not both.')
276
290
291
293 """
294 An instance of this class renders a property of a build.
295 """
296
297 implements(IRenderable)
298
299 compare_attrs = ('key','default', 'defaultWhenFalse')
300
301 - def __init__(self, key, default=None, defaultWhenFalse=True):
302 """
303 @param key: Property to render.
304 @param default: Value to use if property isn't set.
305 @param defaultWhenFalse: When true (default), use default value
306 if property evaluates to False. Otherwise, use default value
307 only when property isn't set.
308 """
309 self.key = key
310 self.default = default
311 self.defaultWhenFalse = defaultWhenFalse
312
323
325 """
326 Default IRenderable adaptor. Calls .getRenderingFor if availble, otherwise
327 returns argument unchanged.
328 """
329
330 implements(IRenderable)
331
333 try:
334 self.renderer = value.getRenderingFor
335 except AttributeError:
336 self.renderer = lambda _: value
337
339 return self.renderer(build)
340
341 registerAdapter(_DefaultRenderer, object, IRenderable)
342
343
345 """
346 List IRenderable adaptor. Maps Build.render over the list.
347 """
348
349 implements(IRenderable)
350
353
356
357 registerAdapter(_ListRenderer, list, IRenderable)
358
359
361 """
362 Tuple IRenderable adaptor. Maps Build.render over the tuple.
363 """
364
365 implements(IRenderable)
366
369
371 return tuple([ build.render(e) for e in self.value ])
372
373 registerAdapter(_TupleRenderer, tuple, IRenderable)
374
375
377 """
378 Dict IRenderable adaptor. Maps Build.render over the keya and values in the dict.
379 """
380
381 implements(IRenderable)
382
385
388
389
390 registerAdapter(_DictRenderer, dict, IRenderable)
391