[Buildbot-commits] buildbot/buildbot/status builder.py,1.69,1.70

Brian Warner warner at users.sourceforge.net
Sun Oct 23 05:06:05 UTC 2005


Update of /cvsroot/buildbot/buildbot/buildbot/status
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv4351/buildbot/status

Modified Files:
	builder.py 
Log Message:
Revision: arch at buildbot.sf.net--2004/buildbot--dev--0--patch-358
Creator:  Brian Warner <warner at lothar.com>

fix some upgrade problems, fix the forgotten-builds problem

	* buildbot/status/builder.py (BuildStatus): derive from
	styles.Versioned, fix upgrade of .sourceStamp attribute. Also set
	the default (i.e. unknown) .slavename to "???" instead of None,
	since even unknown slavenames need to be printed eventually.
	(BuilderStatus): also derive from styles.Versioned . More
	importantly, determine .nextBuildNumber at creation/unpickling
	time by scanning the directory of saved BuildStatus instances and
	choosing one larger than the highest-numbered one found. This
	should fix the problem where random errors during upgrades cause
	the buildbot to forget about earlier builds. .nextBuildNumber is
	no longer stored in the pickle.
	(Status.builderAdded): if we can't unpickle the BuilderStatus,
	at least log the error. Also call Builder.determineNextBuildNumber
	once the basedir is set.

	* buildbot/master.py (BuildMaster.loadChanges): do
	styles.doUpgrade afterwards, in case I decide to make Changes
	derived from styles.Versioned some day and forget to make this
	change later.

--This line, and those below, will be ignored--
Files to commit:
   <can't compute list>

This list might be incomplete or outdated if editing the log
message was not invoked from an up-to-date changes buffer!


Index: builder.py
===================================================================
RCS file: /cvsroot/buildbot/buildbot/buildbot/status/builder.py,v
retrieving revision 1.69
retrieving revision 1.70
diff -u -d -r1.69 -r1.70
--- builder.py	16 Oct 2005 06:25:34 -0000	1.69
+++ builder.py	23 Oct 2005 05:06:02 -0000	1.70
@@ -894,11 +894,12 @@
             loog.step = self
 
 
-class BuildStatus:
+class BuildStatus(styles.Versioned):
     if implements:
         implements(interfaces.IBuildStatus, interfaces.IStatusEvent)
     else:
         __implements__ = interfaces.IBuildStatus, interfaces.IStatusEvent
+    persistenceVersion = 1
 
     source = None
     reason = None
@@ -911,7 +912,7 @@
     text = []
     color = None
     results = None
-    slavename = None
+    slavename = "???"
 
     # these lists/dicts are defined here so that unserialized instances have
     # (empty) values. They are set in __init__ to new objects to make sure
@@ -1186,7 +1187,7 @@
         return filename
 
     def __getstate__(self):
-        d = self.__dict__.copy()
+        d = styles.Versioned.__getstate__(self)
         # for now, a serialized Build is always "finished". We will never
         # save unfinished builds.
         if not self.finished:
@@ -1202,18 +1203,21 @@
         return d
 
     def __setstate__(self, d):
-        self.__dict__ = d
+        styles.Versioned.__setstate__(self, d)
         # self.builder must be filled in by our parent when loading
         for step in self.steps:
             step.build = self
         self.watchers = []
         self.updates = {}
         self.finishedWatchers = []
-        if d.has_key('sourceStamp'):
-            revision, patch = d['sourceStamp']
-            changes = d.get('changes', [])
+
+    def upgradeToVersion1(self):
+        if hasattr(self, "sourceStamp"):
+            # the old .sourceStamp attribute wasn't actually very useful
+            maxChangeNumber, patch = self.sourceStamp
+            changes = getattr(self, 'changes', [])
             source = sourcestamp.SourceStamp(branch=None,
-                                             revision=revision,
+                                             revision=None,
                                              patch=patch,
                                              changes=changes)
             self.source = source
@@ -1258,7 +1262,7 @@
 
 
 
-class BuilderStatus:
+class BuilderStatus(styles.Versioned):
     """I handle status information for a single process.base.Builder object.
     That object sends status changes to me (frequently as Events), and I
     provide them on demand to the various status recipients, like the HTML
@@ -1281,6 +1285,7 @@
         implements(interfaces.IBuilderStatus)
     else:
         __implements__ = interfaces.IBuilderStatus,
+    persistenceVersion = 1
 
     # these limit the amount of memory we consume, as well as the size of the
     # main Builder pickle. The Build and LogFile pickles on disk must be
@@ -1291,7 +1296,6 @@
 
     category = None
     currentBigState = "offline" # or idle/waiting/interlocked/building
-    nextBuildNumber = 0
     basedir = None # filled in by our parent
 
     def __init__(self, buildername, category=None):
@@ -1312,10 +1316,13 @@
         self.buildCache = [] # TODO: age builds out of the cache
 
     # persistence
-    # TODO: why am I not using styles.Versioned for this?
 
     def __getstate__(self):
-        d = self.__dict__.copy()
+        # when saving, don't record transient stuff like what builds are
+        # currently running, because they won't be there when we start back
+        # up. Nor do we save self.watchers, nor anything that gets set by our
+        # parent like .basedir and .status
+        d = styles.Versioned.__getstate__(self)
         d['watchers'] = []
         del d['buildCache']
         for b in self.currentBuilds:
@@ -1326,20 +1333,42 @@
         del d['currentBigState']
         del d['basedir']
         del d['status']
+        del d['nextBuildNumber']
         return d
 
     def __setstate__(self, d):
-        self.__dict__ = d
+        # when loading, re-initialize the transient stuff. Remember that
+        # upgradeToVersion1 and such will be called after this finishes.
+        styles.Versioned.__setstate__(self, d)
         self.buildCache = []
         self.currentBuilds = []
         self.pendingBuilds = []
         self.watchers = []
-        if d.has_key('slavename'):
-            self.slavenames = [self.slavename]
-            del self.slavename
+        self.slavenames = []
         # self.basedir must be filled in by our parent
         # self.status must be filled in by our parent
 
+    def upgradeToVersion1(self):
+        if hasattr(self, 'slavename'):
+            self.slavenames = [self.slavename]
+            del self.slavename
+        if hasattr(self, 'nextBuildNumber'):
+            del self.nextBuildNumber # determineNextBuildNumber chooses this
+
+    def determineNextBuildNumber(self):
+        """Scan our directory of saved BuildStatus instances to determine
+        what our self.nextBuildNumber should be. Set it one larger than the
+        highest-numbered build we discover. This is called by the top-level
+        Status object shortly after we are created or loaded from disk.
+        """
+        existing_builds = [int(f)
+                           for f in os.listdir(self.basedir)
+                           if re.match("^\d+$", f)]
+        if existing_builds:
+            self.nextBuildNumber = max(existing_builds) + 1
+        else:
+            self.nextBuildNumber = 0
+
     def saveYourself(self):
         for b in self.buildCache:
             if not b.isFinished:
@@ -1379,6 +1408,7 @@
         filename = os.path.join(self.basedir, "%d" % number)
         try:
             build = pickle.load(open(filename, "r"))
+            styles.doUpgrade()
             build.builder = self
             # handle LogFiles from after 0.5.0 and before 0.6.5
             build.upgradeLogfiles()
@@ -1523,7 +1553,9 @@
         number = self.nextBuildNumber
         self.nextBuildNumber += 1
         # TODO: self.saveYourself(), to make sure we don't forget about the
-        # build number we've just allocated
+        # build number we've just allocated. This is not quite as important
+        # as it was before we switch to determineNextBuildNumber, but I think
+        # it may still be useful to have the new build save itself.
         s = BuildStatus(self, number)
         s.waitUntilFinished().addCallback(self._buildFinished)
         return s
@@ -1816,10 +1848,13 @@
         builder_status = None
         try:
             builder_status = pickle.load(open(filename, "r"))
+            styles.doUpgrade()
         except IOError:
             log.msg("no saved status pickle, creating a new one")
         except:
             log.msg("error while loading status pickle, creating a new one")
+            log.msg("error follows:")
+            log.err()
         if not builder_status:
             builder_status = BuilderStatus(name, category)
             builder_status.addPointEvent(["builder", "created"])
@@ -1833,6 +1868,8 @@
 
         if not os.path.isdir(builder_status.basedir):
             os.mkdir(builder_status.basedir)
+        builder_status.determineNextBuildNumber()
+
         builder_status.setBigState("offline")
 
         for t in self.watchers:





More information about the Commits mailing list