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, runtime=None):
169
172
176
177
178
180 """
181 Privately-used mapping object to implement WithProperties' substitutions,
182 including the rendering of None as ''.
183 """
184 colon_minus_re = re.compile(r"(.*):-(.*)")
185 colon_tilde_re = re.compile(r"(.*):~(.*)")
186 colon_plus_re = re.compile(r"(.*):\+(.*)")
191
193 properties = self.properties()
194 assert properties is not None
195
196 def colon_minus(mo):
197
198
199 prop, repl = mo.group(1,2)
200 if prop in self.temp_vals:
201 return self.temp_vals[prop]
202 elif properties.has_key(prop):
203 return properties[prop]
204 else:
205 return repl
206
207 def colon_tilde(mo):
208
209
210 prop, repl = mo.group(1,2)
211 if prop in self.temp_vals and self.temp_vals[prop]:
212 return self.temp_vals[prop]
213 elif properties.has_key(prop) and properties[prop]:
214 return properties[prop]
215 else:
216 return repl
217
218 def colon_plus(mo):
219
220
221 prop, repl = mo.group(1,2)
222 if properties.has_key(prop) or prop in self.temp_vals:
223 return repl
224 else:
225 return ''
226
227 for regexp, fn in [
228 ( self.colon_minus_re, colon_minus ),
229 ( self.colon_tilde_re, colon_tilde ),
230 ( self.colon_plus_re, colon_plus ),
231 ]:
232 mo = regexp.match(key)
233 if mo:
234 rv = fn(mo)
235 break
236 else:
237
238
239 if key in self.temp_vals:
240 rv = self.temp_vals[key]
241 else:
242 rv = properties[key]
243
244
245 if rv is None: rv = ''
246 return rv
247
249 'Add a temporary value (to support keyword arguments to WithProperties)'
250 self.temp_vals[key] = val
251
254
256 """
257 This is a marker class, used fairly widely to indicate that we
258 want to interpolate build properties.
259 """
260
261 implements(IRenderable)
262 compare_attrs = ('fmtstring', 'args')
263
264 - def __init__(self, fmtstring, *args, **lambda_subs):
265 self.fmtstring = fmtstring
266 self.args = args
267 if not self.args:
268 self.lambda_subs = lambda_subs
269 for key, val in self.lambda_subs.iteritems():
270 if not callable(val):
271 raise ValueError('Value for lambda substitution "%s" must be callable.' % key)
272 elif lambda_subs:
273 raise ValueError('WithProperties takes either positional or keyword substitutions, not both.')
274
288
289
291 """
292 An instance of this class renders a property of a build.
293 """
294
295 implements(IRenderable)
296
297 compare_attrs = ('key','default', 'defaultWhenFalse')
298
299 - def __init__(self, key, default=None, defaultWhenFalse=True):
300 """
301 @param key: Property to render.
302 @param default: Value to use if property isn't set.
303 @param defaultWhenFalse: When true (default), use default value
304 if property evaluates to False. Otherwise, use default value
305 only when property isn't set.
306 """
307 self.key = key
308 self.default = default
309 self.defaultWhenFalse = defaultWhenFalse
310
316
317
319 """
320 Default IRenderable adaptor. Calls .getRenderingFor if availble, otherwise
321 returns argument unchanged.
322 """
323
324 implements(IRenderable)
325
327 try:
328 self.renderer = value.getRenderingFor
329 except AttributeError:
330 self.renderer = lambda _: value
331
333 return self.renderer(build)
334
335 registerAdapter(_DefaultRenderer, object, IRenderable)
336
337
339 """
340 List IRenderable adaptor. Maps Build.render over the list.
341 """
342
343 implements(IRenderable)
344
347
350
351 registerAdapter(_ListRenderer, list, IRenderable)
352
353
355 """
356 Tuple IRenderable adaptor. Maps Build.render over the tuple.
357 """
358
359 implements(IRenderable)
360
363
365 return tuple([ build.render(e) for e in self.value ])
366
367 registerAdapter(_TupleRenderer, tuple, IRenderable)
368
369
371 """
372 Dict IRenderable adaptor. Maps Build.render over the keya and values in the dict.
373 """
374
375 implements(IRenderable)
376
379
382
383
384 registerAdapter(_DictRenderer, dict, IRenderable)
385