| OLD | NEW |
| (Empty) |
| 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 self.pmap = PropertyMap(self) | |
| 29 if kwargs: self.update(kwargs, "TEST") | |
| 30 | |
| 31 def __getstate__(self): | |
| 32 d = self.__dict__.copy() | |
| 33 del d['pmap'] | |
| 34 return d | |
| 35 | |
| 36 def __setstate__(self, d): | |
| 37 self.__dict__ = d | |
| 38 self.pmap = PropertyMap(self) | |
| 39 | |
| 40 def __contains__(self, name): | |
| 41 return name in self.properties | |
| 42 | |
| 43 def __getitem__(self, name): | |
| 44 """Just get the value for this property.""" | |
| 45 rv = self.properties[name][0] | |
| 46 return rv | |
| 47 | |
| 48 def has_key(self, name): | |
| 49 return self.properties.has_key(name) | |
| 50 | |
| 51 def getProperty(self, name, default=None): | |
| 52 """Get the value for the given property.""" | |
| 53 return self.properties.get(name, (default,))[0] | |
| 54 | |
| 55 def getPropertySource(self, name): | |
| 56 return self.properties[name][1] | |
| 57 | |
| 58 def asList(self): | |
| 59 """Return the properties as a sorted list of (name, value, source)""" | |
| 60 l = [ (k, v[0], v[1]) for k,v in self.properties.items() ] | |
| 61 l.sort() | |
| 62 return l | |
| 63 | |
| 64 def __repr__(self): | |
| 65 return repr(dict([ (k,v[0]) for k,v in self.properties.iteritems() ])) | |
| 66 | |
| 67 def setProperty(self, name, value, source): | |
| 68 self.properties[name] = (value, source) | |
| 69 | |
| 70 def update(self, dict, source): | |
| 71 """Update this object from a dictionary, with an explicit source specifi
ed.""" | |
| 72 for k, v in dict.items(): | |
| 73 self.properties[k] = (v, source) | |
| 74 | |
| 75 def updateFromProperties(self, other): | |
| 76 """Update this object based on another object; the other object's """ | |
| 77 self.properties.update(other.properties) | |
| 78 | |
| 79 def render(self, value): | |
| 80 """ | |
| 81 Return a variant of value that has any WithProperties objects | |
| 82 substituted. This recurses into Python's compound data types. | |
| 83 """ | |
| 84 # we use isinstance to detect Python's standard data types, and call | |
| 85 # this function recursively for the values in those types | |
| 86 if isinstance(value, (str, unicode)): | |
| 87 return value | |
| 88 elif isinstance(value, WithProperties): | |
| 89 return value.render(self.pmap) | |
| 90 elif isinstance(value, list): | |
| 91 return [ self.render(e) for e in value ] | |
| 92 elif isinstance(value, tuple): | |
| 93 return tuple([ self.render(e) for e in value ]) | |
| 94 elif isinstance(value, dict): | |
| 95 return dict([ (self.render(k), self.render(v)) for k,v in value.iter
items() ]) | |
| 96 else: | |
| 97 return value | |
| 98 | |
| 99 class PropertyMap: | |
| 100 """ | |
| 101 Privately-used mapping object to implement WithProperties' substitutions, | |
| 102 including the rendering of None as ''. | |
| 103 """ | |
| 104 colon_minus_re = re.compile(r"(.*):-(.*)") | |
| 105 colon_plus_re = re.compile(r"(.*):\+(.*)") | |
| 106 def __init__(self, properties): | |
| 107 # use weakref here to avoid a reference loop | |
| 108 self.properties = weakref.ref(properties) | |
| 109 | |
| 110 def __getitem__(self, key): | |
| 111 properties = self.properties() | |
| 112 assert properties is not None | |
| 113 | |
| 114 # %(prop:-repl)s | |
| 115 # if prop exists, use it; otherwise, use repl | |
| 116 mo = self.colon_minus_re.match(key) | |
| 117 if mo: | |
| 118 prop, repl = mo.group(1,2) | |
| 119 if properties.has_key(prop): | |
| 120 rv = properties[prop] | |
| 121 else: | |
| 122 rv = repl | |
| 123 else: | |
| 124 # %(prop:+repl)s | |
| 125 # if prop exists, use repl; otherwise, an empty string | |
| 126 mo = self.colon_plus_re.match(key) | |
| 127 if mo: | |
| 128 prop, repl = mo.group(1,2) | |
| 129 if properties.has_key(prop): | |
| 130 rv = repl | |
| 131 else: | |
| 132 rv = '' | |
| 133 else: | |
| 134 rv = properties[key] | |
| 135 | |
| 136 # translate 'None' to an empty string | |
| 137 if rv is None: rv = '' | |
| 138 return rv | |
| 139 | |
| 140 class WithProperties(util.ComparableMixin): | |
| 141 """ | |
| 142 This is a marker class, used fairly widely to indicate that we | |
| 143 want to interpolate build properties. | |
| 144 """ | |
| 145 | |
| 146 compare_attrs = ('fmtstring', 'args') | |
| 147 | |
| 148 def __init__(self, fmtstring, *args): | |
| 149 self.fmtstring = fmtstring | |
| 150 self.args = args | |
| 151 | |
| 152 def render(self, pmap): | |
| 153 if self.args: | |
| 154 strings = [] | |
| 155 for name in self.args: | |
| 156 strings.append(pmap[name]) | |
| 157 s = self.fmtstring % tuple(strings) | |
| 158 else: | |
| 159 s = self.fmtstring % pmap | |
| 160 return s | |
| OLD | NEW |