Repository: buildbot/buildbot

Issue:
Protect Interpolate rendering from change of _notHasKey constant
Should fix http://trac.buildbot.net/ticket/3505#ticket for Nine

It's not the only solution possible, but that one does not require rewriting much.
The very same fix also resolves the issue on 0.8.12, but the test has been developed on current master.



Discussion:

Resolving patch:
diff --git a/master/buildbot/buildslave.py b/master/buildbot/buildslave.py
--- a/master/buildbot/buildslave.py
+++ b/master/buildbot/buildslave.py
@@ -76,6 +76,7 @@ def __init__(self, name, password, max_builds=None,
         self.access = []
         if locks:
             self.access = locks
+        self.lock_subscriptions = []
 
         self.properties = Properties()
         self.properties.update(properties, "BuildSlave")
@@ -118,6 +119,7 @@ def update(self, new):
         assert self.identity() == new.identity()
         self.max_builds = new.max_builds
         self.access = new.access
+        assert new.lock_subscriptions == []
         self.notify_on_missing = new.notify_on_missing
         self.missing_timeout = new.missing_timeout
         self.keepalive_interval = new.keepalive_interval
@@ -139,6 +141,12 @@ def __repr__(self):
                 (self.__class__.__name__, self.slavename)
 
     def updateLocks(self):
+        """Convert the L{LockAccess} objects in C{self.locks} into real lock
+        objects, while also maintaining the subscriptions to lock releases."""
+        # unsubscribe from any old locks
+        for s in self.lock_subscriptions:
+            s.unsubscribe()
+
         # convert locks into their real form
         locks = []
         for access in self.access:
@@ -147,6 +155,8 @@ def updateLocks(self):
             lock = self.botmaster.getLockByID(access.lockid)
             locks.append((lock, access))
         self.locks = [(l.getLock(self), la) for l, la in locks]
+        self.lock_subscriptions = [ l.subscribeToReleases(self._lockReleased)
+                                    for l, la in self.locks ]
 
     def locksAvailable(self):
         """
@@ -184,6 +194,13 @@ def releaseLocks(self):
         for lock, access in self.locks:
             lock.release(self, access)
 
+    def _lockReleased(self):
+        """One of the locks for this slave was released; try scheduling
+        builds."""
+        if not self.botmaster:
+            return # oh well..
+        self.botmaster.maybeStartBuildsForSlave(self.slavename)
+
     def setBotmaster(self, botmaster):
         assert not self.botmaster, "BuildSlave already has a botmaster"
         self.botmaster = botmaster
diff --git a/master/buildbot/locks.py b/master/buildbot/locks.py
--- a/master/buildbot/locks.py
+++ b/master/buildbot/locks.py
@@ -17,6 +17,7 @@
 from twisted.python import log
 from twisted.internet import reactor, defer
 from buildbot import util
+from buildbot.util import subscription
 
 if False: # for debugging
     debuglog = log.msg
@@ -42,6 +43,10 @@ def __init__(self, name, maxCount=1):
         self.owners = []          # Current owners, tuples (owner, LockAccess)
         self.maxCount = maxCount  # maximal number of counting owners
 
+        # subscriptions to this lock being released
+        self.release_subs = subscription.SubscriptionPoint("%r releases"
+                                                             % (self,))
+
     def __repr__(self):
         return self.description
 
@@ -85,6 +90,11 @@ def claim(self, owner, access):
         self.owners.append((owner, access))
         debuglog(" %s is claimed '%s'" % (self, access.mode))
 
+    def subscribeToReleases(self, callback):
+        """Schedule C{callback} to be invoked every time this lock is
+        released.  Returns a L{Subscription}."""
+        return self.release_subs.subscribe(callback)
+
     def release(self, owner, access):
         """ Release the lock """
         assert isinstance(access, LockAccess)
@@ -114,6 +124,9 @@ def release(self, owner, access):
             del self.waiting[0]
             reactor.callLater(0, d.callback, self)
 
+        # notify any listeners
+        self.release_subs.deliver()
+
     def waitUntilMaybeAvailable(self, owner, access):
         """Fire when the lock *might* be available. The caller will need to
         check with isAvailable() when the deferred fires. This loose form is


Test patch: