1 import re
2 import weakref
3 from buildbot import util
4
6 """
7 I represent a set of properties that can be interpolated into various
8 strings in buildsteps.
9
10 @ivar properties: dictionary mapping property values to tuples
11 (value, source), where source is a string identifing the source
12 of the property.
13
14 Objects of this class can be read like a dictionary -- in this case,
15 only the property value is returned.
16
17 As a special case, a property value of None is returned as an empty
18 string when used as a mapping.
19 """
20
21 compare_attrs = ('properties',)
22
24 """
25 @param kwargs: initial property values (for testing)
26 """
27 self.properties = {}
28
29
30 self.runtime = set()
31 self.pmap = PropertyMap(self)
32 if kwargs: self.update(kwargs, "TEST")
33
35 d = self.__dict__.copy()
36 del d['pmap']
37 return d
38
40 self.__dict__ = d
41 self.pmap = PropertyMap(self)
42 if not hasattr(self, 'runtime'):
43 self.runtime = set()
44
47
49 """Just get the value for this property."""
50 rv = self.properties[name][0]
51 return rv
52
55
57 """Get the value for the given property."""
58 return self.properties.get(name, (default,))[0]
59
62
64 """Return the properties as a sorted list of (name, value, source)"""
65 l = [ (k, v[0], v[1]) for k,v in self.properties.items() ]
66 l.sort()
67 return l
68
70 return repr(dict([ (k,v[0]) for k,v in self.properties.iteritems() ]))
71
72 - def setProperty(self, name, value, source, runtime=False):
76
77 - def update(self, dict, source, runtime=False):
78 """Update this object from a dictionary, with an explicit source specified."""
79 for k, v in dict.items():
80 self.properties[k] = (v, source)
81 if runtime:
82 self.runtime.add(k)
83
88
90 """Update this object based on another object, but don't
91 include properties that were marked as runtime."""
92 for k,v in other.properties.iteritems():
93 if k not in other.runtime:
94 self.properties[k] = v
95
97 """
98 Return a variant of value that has any WithProperties objects
99 substituted. This recurses into Python's compound data types.
100 """
101
102
103 if isinstance(value, (str, unicode)):
104 return value
105 elif isinstance(value, WithProperties):
106 return value.render(self.pmap)
107 elif isinstance(value, list):
108 return [ self.render(e) for e in value ]
109 elif isinstance(value, tuple):
110 return tuple([ self.render(e) for e in value ])
111 elif isinstance(value, dict):
112 return dict([ (self.render(k), self.render(v)) for k,v in value.iteritems() ])
113 else:
114 return value
115
117 """
118 Privately-used mapping object to implement WithProperties' substitutions,
119 including the rendering of None as ''.
120 """
121 colon_minus_re = re.compile(r"(.*):-(.*)")
122 colon_tilde_re = re.compile(r"(.*):~(.*)")
123 colon_plus_re = re.compile(r"(.*):\+(.*)")
127
140
141 def colon_tilde(mo):
142
143
144 prop, repl = mo.group(1,2)
145 if properties.has_key(prop) and properties[prop]:
146 return properties[prop]
147 else:
148 return repl
149
150 def colon_plus(mo):
151
152
153 prop, repl = mo.group(1,2)
154 if properties.has_key(prop):
155 return repl
156 else:
157 return ''
158
159 for regexp, fn in [
160 ( self.colon_minus_re, colon_minus ),
161 ( self.colon_tilde_re, colon_tilde ),
162 ( self.colon_plus_re, colon_plus ),
163 ]:
164 mo = regexp.match(key)
165 if mo:
166 rv = fn(mo)
167 break
168 else:
169 rv = properties[key]
170
171
172 if rv is None: rv = ''
173 return rv
174
176 """
177 This is a marker class, used fairly widely to indicate that we
178 want to interpolate build properties.
179 """
180
181 compare_attrs = ('fmtstring', 'args')
182
184 self.fmtstring = fmtstring
185 self.args = args
186
188 if self.args:
189 strings = []
190 for name in self.args:
191 strings.append(pmap[name])
192 s = self.fmtstring % tuple(strings)
193 else:
194 s = self.fmtstring % pmap
195 return s
196