Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/buildbot/buildbot/process/properties.py
diff options
context:
space:
mode:
Diffstat (limited to 'buildbot/buildbot/process/properties.py')
-rw-r--r--buildbot/buildbot/process/properties.py157
1 files changed, 157 insertions, 0 deletions
diff --git a/buildbot/buildbot/process/properties.py b/buildbot/buildbot/process/properties.py
new file mode 100644
index 0000000..2d07db9
--- /dev/null
+++ b/buildbot/buildbot/process/properties.py
@@ -0,0 +1,157 @@
+import re
+import weakref
+from buildbot import util
+
+class Properties(util.ComparableMixin):
+ """
+ I represent a set of properties that can be interpolated into various
+ strings in buildsteps.
+
+ @ivar properties: dictionary mapping property values to tuples
+ (value, source), where source is a string identifing the source
+ of the property.
+
+ Objects of this class can be read like a dictionary -- in this case,
+ only the property value is returned.
+
+ As a special case, a property value of None is returned as an empty
+ string when used as a mapping.
+ """
+
+ compare_attrs = ('properties',)
+
+ def __init__(self, **kwargs):
+ """
+ @param kwargs: initial property values (for testing)
+ """
+ self.properties = {}
+ self.pmap = PropertyMap(self)
+ if kwargs: self.update(kwargs, "TEST")
+
+ def __getstate__(self):
+ d = self.__dict__.copy()
+ del d['pmap']
+ return d
+
+ def __setstate__(self, d):
+ self.__dict__ = d
+ self.pmap = PropertyMap(self)
+
+ def __getitem__(self, name):
+ """Just get the value for this property."""
+ rv = self.properties[name][0]
+ return rv
+
+ def has_key(self, name):
+ return self.properties.has_key(name)
+
+ def getProperty(self, name, default=None):
+ """Get the value for the given property."""
+ return self.properties.get(name, (default,))[0]
+
+ def getPropertySource(self, name):
+ return self.properties[name][1]
+
+ def asList(self):
+ """Return the properties as a sorted list of (name, value, source)"""
+ l = [ (k, v[0], v[1]) for k,v in self.properties.items() ]
+ l.sort()
+ return l
+
+ def __repr__(self):
+ return repr(dict([ (k,v[0]) for k,v in self.properties.iteritems() ]))
+
+ def setProperty(self, name, value, source):
+ self.properties[name] = (value, source)
+
+ def update(self, dict, source):
+ """Update this object from a dictionary, with an explicit source specified."""
+ for k, v in dict.items():
+ self.properties[k] = (v, source)
+
+ def updateFromProperties(self, other):
+ """Update this object based on another object; the other object's """
+ self.properties.update(other.properties)
+
+ def render(self, value):
+ """
+ Return a variant of value that has any WithProperties objects
+ substituted. This recurses into Python's compound data types.
+ """
+ # we use isinstance to detect Python's standard data types, and call
+ # this function recursively for the values in those types
+ if isinstance(value, (str, unicode)):
+ return value
+ elif isinstance(value, WithProperties):
+ return value.render(self.pmap)
+ elif isinstance(value, list):
+ return [ self.render(e) for e in value ]
+ elif isinstance(value, tuple):
+ return tuple([ self.render(e) for e in value ])
+ elif isinstance(value, dict):
+ return dict([ (self.render(k), self.render(v)) for k,v in value.iteritems() ])
+ else:
+ return value
+
+class PropertyMap:
+ """
+ Privately-used mapping object to implement WithProperties' substitutions,
+ including the rendering of None as ''.
+ """
+ colon_minus_re = re.compile(r"(.*):-(.*)")
+ colon_plus_re = re.compile(r"(.*):\+(.*)")
+ def __init__(self, properties):
+ # use weakref here to avoid a reference loop
+ self.properties = weakref.ref(properties)
+
+ def __getitem__(self, key):
+ properties = self.properties()
+ assert properties is not None
+
+ # %(prop:-repl)s
+ # if prop exists, use it; otherwise, use repl
+ mo = self.colon_minus_re.match(key)
+ if mo:
+ prop, repl = mo.group(1,2)
+ if properties.has_key(prop):
+ rv = properties[prop]
+ else:
+ rv = repl
+ else:
+ # %(prop:+repl)s
+ # if prop exists, use repl; otherwise, an empty string
+ mo = self.colon_plus_re.match(key)
+ if mo:
+ prop, repl = mo.group(1,2)
+ if properties.has_key(prop):
+ rv = repl
+ else:
+ rv = ''
+ else:
+ rv = properties[key]
+
+ # translate 'None' to an empty string
+ if rv is None: rv = ''
+ return rv
+
+class WithProperties(util.ComparableMixin):
+ """
+ This is a marker class, used fairly widely to indicate that we
+ want to interpolate build properties.
+ """
+
+ compare_attrs = ('fmtstring', 'args')
+
+ def __init__(self, fmtstring, *args):
+ self.fmtstring = fmtstring
+ self.args = args
+
+ def render(self, pmap):
+ if self.args:
+ strings = []
+ for name in self.args:
+ strings.append(pmap[name])
+ s = self.fmtstring % tuple(strings)
+ else:
+ s = self.fmtstring % pmap
+ return s