Package buildbot :: Package process :: Module properties
[frames] | no frames]

Source Code for Module buildbot.process.properties

  1  import re 
  2  import weakref 
  3  from buildbot import util 
  4   
5 -class Properties(util.ComparableMixin):
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
23 - def __init__(self, **kwargs):
24 """ 25 @param kwargs: initial property values (for testing) 26 """ 27 self.properties = {} 28 # Track keys which are 'runtime', and should not be 29 # persisted if a build is rebuilt 30 self.runtime = set() 31 self.pmap = PropertyMap(self) 32 if kwargs: self.update(kwargs, "TEST")
33
34 - def __getstate__(self):
35 d = self.__dict__.copy() 36 del d['pmap'] 37 return d
38
39 - def __setstate__(self, d):
40 self.__dict__ = d 41 self.pmap = PropertyMap(self) 42 if not hasattr(self, 'runtime'): 43 self.runtime = set()
44
45 - def __contains__(self, name):
46 return name in self.properties
47
48 - def __getitem__(self, name):
49 """Just get the value for this property.""" 50 rv = self.properties[name][0] 51 return rv
52
53 - def has_key(self, name):
54 return self.properties.has_key(name)
55
56 - def getProperty(self, name, default=None):
57 """Get the value for the given property.""" 58 return self.properties.get(name, (default,))[0]
59
60 - def getPropertySource(self, name):
61 return self.properties[name][1]
62
63 - def asList(self):
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
69 - def __repr__(self):
70 return repr(dict([ (k,v[0]) for k,v in self.properties.iteritems() ]))
71
72 - def setProperty(self, name, value, source, runtime=False):
73 self.properties[name] = (value, source) 74 if runtime: 75 self.runtime.add(name)
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
84 - def updateFromProperties(self, other):
85 """Update this object based on another object; the other object's """ 86 self.properties.update(other.properties) 87 self.runtime.update(other.runtime)
88
89 - def updateFromPropertiesNoRuntime(self, other):
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
96 - def render(self, value):
97 """ 98 Return a variant of value that has any WithProperties objects 99 substituted. This recurses into Python's compound data types. 100 """ 101 # we use isinstance to detect Python's standard data types, and call 102 # this function recursively for the values in those types 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
116 -class PropertyMap:
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"(.*):\+(.*)")
124 - def __init__(self, properties):
125 # use weakref here to avoid a reference loop 126 self.properties = weakref.ref(properties)
127
128 - def __getitem__(self, key):
129 properties = self.properties() 130 assert properties is not None 131 132 def colon_minus(mo): 133 # %(prop:-repl)s 134 # if prop exists, use it; otherwise, use repl 135 prop, repl = mo.group(1,2) 136 if properties.has_key(prop): 137 return properties[prop] 138 else: 139 return repl
140 141 def colon_tilde(mo): 142 # %(prop:~repl)s 143 # if prop exists and is true (nonempty), use it; otherwise, use repl 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 # %(prop:+repl)s 152 # if prop exists, use repl; otherwise, an empty string 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 # translate 'None' to an empty string 172 if rv is None: rv = '' 173 return rv 174
175 -class WithProperties(util.ComparableMixin):
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
183 - def __init__(self, fmtstring, *args):
184 self.fmtstring = fmtstring 185 self.args = args
186
187 - def render(self, pmap):
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