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
21 """
22 I represent a set of properties that can be interpolated into various
23 strings in buildsteps.
24
25 @ivar properties: dictionary mapping property values to tuples
26 (value, source), where source is a string identifing the source
27 of the property.
28
29 Objects of this class can be read like a dictionary -- in this case,
30 only the property value is returned.
31
32 As a special case, a property value of None is returned as an empty
33 string when used as a mapping.
34 """
35
36 compare_attrs = ('properties',)
37
39 """
40 @param kwargs: initial property values (for testing)
41 """
42 self.properties = {}
43
44
45 self.runtime = set()
46 self.pmap = PropertyMap(self)
47 if kwargs: self.update(kwargs, "TEST")
48
50 d = self.__dict__.copy()
51 del d['pmap']
52 return d
53
55 self.__dict__ = d
56 self.pmap = PropertyMap(self)
57 if not hasattr(self, 'runtime'):
58 self.runtime = set()
59
62
64 """Just get the value for this property."""
65 rv = self.properties[name][0]
66 return rv
67
70
72 """Get the value for the given property."""
73 return self.properties.get(name, (default,))[0]
74
77
79 """Return the properties as a sorted list of (name, value, source)"""
80 l = [ (k, v[0], v[1]) for k,v in self.properties.items() ]
81 l.sort()
82 return l
83
85 return repr(dict([ (k,v[0]) for k,v in self.properties.iteritems() ]))
86
87 - def setProperty(self, name, value, source, runtime=False):
91
92 - def update(self, dict, source, runtime=False):
93 """Update this object from a dictionary, with an explicit source specified."""
94 for k, v in dict.items():
95 self.properties[k] = (v, source)
96 if runtime:
97 self.runtime.add(k)
98
103
105 """Update this object based on another object, but don't
106 include properties that were marked as runtime."""
107 for k,v in other.properties.iteritems():
108 if k not in other.runtime:
109 self.properties[k] = v
110
112 """
113 Return a variant of value that has any WithProperties objects
114 substituted. This recurses into Python's compound data types.
115 """
116
117
118 if isinstance(value, (str, unicode)):
119 return value
120 elif isinstance(value, WithProperties):
121 return value.render(self.pmap)
122 elif isinstance(value, list):
123 return [ self.render(e) for e in value ]
124 elif isinstance(value, tuple):
125 return tuple([ self.render(e) for e in value ])
126 elif isinstance(value, dict):
127 return dict([ (self.render(k), self.render(v)) for k,v in value.iteritems() ])
128 else:
129 return value
130
132 """
133 Privately-used mapping object to implement WithProperties' substitutions,
134 including the rendering of None as ''.
135 """
136 colon_minus_re = re.compile(r"(.*):-(.*)")
137 colon_tilde_re = re.compile(r"(.*):~(.*)")
138 colon_plus_re = re.compile(r"(.*):\+(.*)")
143
145 properties = self.properties()
146 assert properties is not None
147
148 def colon_minus(mo):
149
150
151 prop, repl = mo.group(1,2)
152 if prop in self.temp_vals:
153 return self.temp_vals[prop]
154 elif properties.has_key(prop):
155 return properties[prop]
156 else:
157 return repl
158
159 def colon_tilde(mo):
160
161
162 prop, repl = mo.group(1,2)
163 if prop in self.temp_vals and self.temp_vals[prop]:
164 return self.temp_vals[prop]
165 elif properties.has_key(prop) and properties[prop]:
166 return properties[prop]
167 else:
168 return repl
169
170 def colon_plus(mo):
171
172
173 prop, repl = mo.group(1,2)
174 if properties.has_key(prop) or prop in self.temp_vals:
175 return repl
176 else:
177 return ''
178
179 for regexp, fn in [
180 ( self.colon_minus_re, colon_minus ),
181 ( self.colon_tilde_re, colon_tilde ),
182 ( self.colon_plus_re, colon_plus ),
183 ]:
184 mo = regexp.match(key)
185 if mo:
186 rv = fn(mo)
187 break
188 else:
189
190
191 if key in self.temp_vals:
192 rv = self.temp_vals[key]
193 else:
194 rv = properties[key]
195
196
197 if rv is None: rv = ''
198 return rv
199
201 'Add a temporary value (to support keyword arguments to WithProperties)'
202 self.temp_vals[key] = val
203
206
208 """
209 This is a marker class, used fairly widely to indicate that we
210 want to interpolate build properties.
211 """
212
213 compare_attrs = ('fmtstring', 'args')
214
215 - def __init__(self, fmtstring, *args, **lambda_subs):
216 self.fmtstring = fmtstring
217 self.args = args
218 if not self.args:
219 self.lambda_subs = lambda_subs
220 for key, val in self.lambda_subs.iteritems():
221 if not callable(val):
222 raise ValueError('Value for lambda substitution "%s" must be callable.' % key)
223 elif lambda_subs:
224 raise ValueError('WithProperties takes either positional or keyword substitutions, not both.')
225
239