[Buildbot-commits] buildbot/buildbot/changes mail.py,1.24,1.25
Brian Warner
warner at users.sourceforge.net
Sat Jul 28 05:57:52 UTC 2007
Update of /cvsroot/buildbot/buildbot/buildbot/changes
In directory sc8-pr-cvs3.sourceforge.net:/tmp/cvs-serv28244/buildbot/changes
Modified Files:
mail.py
Log Message:
[project @ changes/mail.py: refactor, giving each parser a separate subclass]
Original author: warner at lothar.com
Date: 2007-07-28 05:38:27+00:00
Index: mail.py
===================================================================
RCS file: /cvsroot/buildbot/buildbot/buildbot/changes/mail.py,v
retrieving revision 1.24
retrieving revision 1.25
diff -u -d -r1.24 -r1.25
--- mail.py 23 Jan 2007 21:04:32 -0000 1.24
+++ mail.py 28 Jul 2007 05:57:50 -0000 1.25
@@ -12,197 +12,228 @@
from buildbot.changes import changes
from buildbot.changes.maildir import MaildirService
-def parseFreshCVSMail(self, fd, prefix=None, sep="/"):
- """Parse mail sent by FreshCVS"""
- # this uses rfc822.Message so it can run under python2.1 . In the future
- # it will be updated to use python2.2's "email" module.
+class MaildirSource(MaildirService, util.ComparableMixin):
+ """This source will watch a maildir that is subscribed to a FreshCVS
+ change-announcement mailing list.
+ """
+ implements(IChangeSource)
- m = Message(fd)
- # FreshCVS sets From: to "user CVS <user>", but the <> part may be
- # modified by the MTA (to include a local domain)
- name, addr = m.getaddr("from")
- if not name:
- return None # no From means this message isn't from FreshCVS
- cvs = name.find(" CVS")
- if cvs == -1:
- return None # this message isn't from FreshCVS
- who = name[:cvs]
+ compare_attrs = ["basedir", "pollinterval"]
+ name = None
- # we take the time of receipt as the time of checkin. Not correct, but it
- # avoids the out-of-order-changes issue. See the comment in parseSyncmail
- # about using the 'Date:' header
- when = util.now()
+ def __init__(self, maildir, prefix=None):
+ MaildirService.__init__(self, maildir)
+ self.prefix = prefix
- files = []
- comments = ""
- isdir = 0
- lines = m.fp.readlines()
- while lines:
- line = lines.pop(0)
- if line == "Modified files:\n":
- break
- while lines:
- line = lines.pop(0)
- if line == "\n":
- break
- line = line.rstrip("\n")
- linebits = line.split(None, 1)
- file = linebits[0]
- if prefix:
- # insist that the file start with the prefix: FreshCVS sends
- # changes we don't care about too
- bits = file.split(sep)
- if bits[0] == prefix:
- file = sep.join(bits[1:])
- else:
+ def describe(self):
+ return "%s mailing list in maildir %s" % (self.name, self.basedir)
+
+ def messageReceived(self, filename):
+ path = os.path.join(self.basedir, "new", filename)
+ change = self.parse(open(path, "r"), self.prefix)
+ if change:
+ self.parent.addChange(change)
+ os.rename(os.path.join(self.basedir, "new", filename),
+ os.path.join(self.basedir, "cur", filename))
+
+class FCMaildirSource(MaildirSource):
+ name = "FreshCVS"
+
+ def parse(self, fd, prefix=None, sep="/"):
+ """Parse mail sent by FreshCVS"""
+ # this uses rfc822.Message so it can run under python2.1 . In the
+ # future it will be updated to use python2.2's "email" module.
+
+ m = Message(fd)
+ # FreshCVS sets From: to "user CVS <user>", but the <> part may be
+ # modified by the MTA (to include a local domain)
+ name, addr = m.getaddr("from")
+ if not name:
+ return None # no From means this message isn't from FreshCVS
+ cvs = name.find(" CVS")
+ if cvs == -1:
+ return None # this message isn't from FreshCVS
+ who = name[:cvs]
+
+ # we take the time of receipt as the time of checkin. Not correct,
+ # but it avoids the out-of-order-changes issue. See the comment in
+ # parseSyncmail about using the 'Date:' header
+ when = util.now()
+
+ files = []
+ comments = ""
+ isdir = 0
+ lines = m.fp.readlines()
+ while lines:
+ line = lines.pop(0)
+ if line == "Modified files:\n":
break
- if len(linebits) == 1:
- isdir = 1
- elif linebits[1] == "0 0":
- isdir = 1
- files.append(file)
- while lines:
- line = lines.pop(0)
- if line == "Log message:\n":
- break
- # message is terminated by "ViewCVS links:" or "Index:..." (patch)
- while lines:
- line = lines.pop(0)
- if line == "ViewCVS links:\n":
- break
- if line.find("Index: ") == 0:
- break
- comments += line
- comments = comments.rstrip() + "\n"
+ while lines:
+ line = lines.pop(0)
+ if line == "\n":
+ break
+ line = line.rstrip("\n")
+ linebits = line.split(None, 1)
+ file = linebits[0]
+ if prefix:
+ # insist that the file start with the prefix: FreshCVS sends
+ # changes we don't care about too
+ bits = file.split(sep)
+ if bits[0] == prefix:
+ file = sep.join(bits[1:])
+ else:
+ break
+ if len(linebits) == 1:
+ isdir = 1
+ elif linebits[1] == "0 0":
+ isdir = 1
+ files.append(file)
+ while lines:
+ line = lines.pop(0)
+ if line == "Log message:\n":
+ break
+ # message is terminated by "ViewCVS links:" or "Index:..." (patch)
+ while lines:
+ line = lines.pop(0)
+ if line == "ViewCVS links:\n":
+ break
+ if line.find("Index: ") == 0:
+ break
+ comments += line
+ comments = comments.rstrip() + "\n"
- if not files:
- return None
-
- change = changes.Change(who, files, comments, isdir, when=when)
+ if not files:
+ return None
- return change
+ change = changes.Change(who, files, comments, isdir, when=when)
-def parseSyncmail(self, fd, prefix=None, sep="/"):
- """Parse messages sent by the 'syncmail' program, as suggested by the
- sourceforge.net CVS Admin documentation. Syncmail is maintained at
- syncmail.sf.net .
- """
- # pretty much the same as freshcvs mail, not surprising since CVS is the
- # one creating most of the text
+ return change
- m = Message(fd)
- # The mail is sent from the person doing the checkin. Assume that the
- # local username is enough to identify them (this assumes a one-server
- # cvs-over-rsh environment rather than the server-dirs-shared-over-NFS
- # model)
- name, addr = m.getaddr("from")
- if not addr:
- return None # no From means this message isn't from FreshCVS
- at = addr.find("@")
- if at == -1:
- who = addr # might still be useful
- else:
- who = addr[:at]
+class SyncmailMaildirSource(MaildirSource):
+ name = "Syncmail"
- # we take the time of receipt as the time of checkin. Not correct (it
- # depends upon the email latency), but it avoids the out-of-order-changes
- # issue. Also syncmail doesn't give us anything better to work with,
- # unless you count pulling the v1-vs-v2 timestamp out of the diffs, which
- # would be ugly. TODO: Pulling the 'Date:' header from the mail is a
- # possibility, and email.Utils.parsedate_tz may be useful. It should be
- # configurable, however, because there are a lot of broken clocks out
- # there.
- when = util.now()
+ def parse(self, fd, prefix=None, sep="/"):
+ """Parse messages sent by the 'syncmail' program, as suggested by the
+ sourceforge.net CVS Admin documentation. Syncmail is maintained at
+ syncmail.sf.net .
+ """
+ # pretty much the same as freshcvs mail, not surprising since CVS is
+ # the one creating most of the text
- subject = m.getheader("subject")
- # syncmail puts the repository-relative directory in the subject:
- # mprefix + "%(dir)s %(file)s,%(oldversion)s,%(newversion)s", where
- # 'mprefix' is something that could be added by a mailing list
- # manager.
- # this is the only reasonable way to determine the directory name
- space = subject.find(" ")
- if space != -1:
- directory = subject[:space]
- else:
- directory = subject
-
- files = []
- comments = ""
- isdir = 0
- branch = None
+ m = Message(fd)
+ # The mail is sent from the person doing the checkin. Assume that the
+ # local username is enough to identify them (this assumes a one-server
+ # cvs-over-rsh environment rather than the server-dirs-shared-over-NFS
+ # model)
+ name, addr = m.getaddr("from")
+ if not addr:
+ return None # no From means this message isn't from FreshCVS
+ at = addr.find("@")
+ if at == -1:
+ who = addr # might still be useful
+ else:
+ who = addr[:at]
- lines = m.fp.readlines()
- while lines:
- line = lines.pop(0)
+ # we take the time of receipt as the time of checkin. Not correct (it
+ # depends upon the email latency), but it avoids the
+ # out-of-order-changes issue. Also syncmail doesn't give us anything
+ # better to work with, unless you count pulling the v1-vs-v2
+ # timestamp out of the diffs, which would be ugly. TODO: Pulling the
+ # 'Date:' header from the mail is a possibility, and
+ # email.Utils.parsedate_tz may be useful. It should be configurable,
+ # however, because there are a lot of broken clocks out there.
+ when = util.now()
- if (line == "Modified Files:\n" or
- line == "Added Files:\n" or
- line == "Removed Files:\n"):
- break
+ subject = m.getheader("subject")
+ # syncmail puts the repository-relative directory in the subject:
+ # mprefix + "%(dir)s %(file)s,%(oldversion)s,%(newversion)s", where
+ # 'mprefix' is something that could be added by a mailing list
+ # manager.
+ # this is the only reasonable way to determine the directory name
+ space = subject.find(" ")
+ if space != -1:
+ directory = subject[:space]
+ else:
+ directory = subject
- while lines:
- line = lines.pop(0)
- if line == "\n":
- break
- if line == "Log Message:\n":
- lines.insert(0, line)
- break
- line = line.lstrip()
- line = line.rstrip()
- # note: syncmail will send one email per directory involved in a
- # commit, with multiple files if they were in the same directory.
- # Unlike freshCVS, it makes no attempt to collect all related
- # commits into a single message.
+ files = []
+ comments = ""
+ isdir = 0
+ branch = None
- # note: syncmail will report a Tag underneath the ... Files: line
- # e.g.: Tag: BRANCH-DEVEL
+ lines = m.fp.readlines()
+ while lines:
+ line = lines.pop(0)
- if line.startswith('Tag:'):
- branch = line.split(' ')[-1].rstrip()
- continue
+ if (line == "Modified Files:\n" or
+ line == "Added Files:\n" or
+ line == "Removed Files:\n"):
+ break
- # note: it doesn't actually make sense to use portable functions
- # like os.path.join and os.sep, because these filenames all use
- # separator conventions established by the remote CVS server (which
- # is probably running on unix), not the local buildmaster system.
- thesefiles = line.split(" ")
- for f in thesefiles:
- f = sep.join([directory, f])
- if prefix:
- # insist that the file start with the prefix: we may get
- # changes we don't care about too
- bits = f.split(sep)
- if bits[0] == prefix:
- f = sep.join(bits[1:])
- else:
- break
- # TODO: figure out how new directories are described, set .isdir
- files.append(f)
+ while lines:
+ line = lines.pop(0)
+ if line == "\n":
+ break
+ if line == "Log Message:\n":
+ lines.insert(0, line)
+ break
+ line = line.lstrip()
+ line = line.rstrip()
+ # note: syncmail will send one email per directory involved in a
+ # commit, with multiple files if they were in the same directory.
+ # Unlike freshCVS, it makes no attempt to collect all related
+ # commits into a single message.
- if not files:
- return None
+ # note: syncmail will report a Tag underneath the ... Files: line
+ # e.g.: Tag: BRANCH-DEVEL
- while lines:
- line = lines.pop(0)
- if line == "Log Message:\n":
- break
- # message is terminated by "Index:..." (patch) or "--- NEW FILE.."
- # or "--- filename DELETED ---". Sigh.
- while lines:
- line = lines.pop(0)
- if line.find("Index: ") == 0:
- break
- if re.search(r"^--- NEW FILE", line):
- break
- if re.search(r" DELETED ---$", line):
- break
- comments += line
- comments = comments.rstrip() + "\n"
-
- change = changes.Change(who, files, comments, isdir, when=when,
- branch=branch)
+ if line.startswith('Tag:'):
+ branch = line.split(' ')[-1].rstrip()
+ continue
- return change
+ # note: it doesn't actually make sense to use portable functions
+ # like os.path.join and os.sep, because these filenames all use
+ # separator conventions established by the remote CVS server (which
+ # is probably running on unix), not the local buildmaster system.
+ thesefiles = line.split(" ")
+ for f in thesefiles:
+ f = sep.join([directory, f])
+ if prefix:
+ # insist that the file start with the prefix: we may get
+ # changes we don't care about too
+ bits = f.split(sep)
+ if bits[0] == prefix:
+ f = sep.join(bits[1:])
+ else:
+ break
+ # TODO: figure out how new directories are described, set
+ # .isdir
+ files.append(f)
+
+ if not files:
+ return None
+
+ while lines:
+ line = lines.pop(0)
+ if line == "Log Message:\n":
+ break
+ # message is terminated by "Index:..." (patch) or "--- NEW FILE.."
+ # or "--- filename DELETED ---". Sigh.
+ while lines:
+ line = lines.pop(0)
+ if line.find("Index: ") == 0:
+ break
+ if re.search(r"^--- NEW FILE", line):
+ break
+ if re.search(r" DELETED ---$", line):
+ break
+ comments += line
+ comments = comments.rstrip() + "\n"
+
+ change = changes.Change(who, files, comments, isdir, when=when,
+ branch=branch)
+
+ return change
# Bonsai mail parser by Stephen Davis.
#
@@ -231,106 +262,72 @@
# actually make a new directory part of the build process. That's my story
# and I'm sticking to it.
-def parseBonsaiMail(self, fd, prefix=None, sep="/"):
- """Parse mail sent by the Bonsai cvs loginfo script."""
-
- msg = Message(fd)
-
- # we don't care who the email came from b/c the cvs user is in the msg
- # text
-
- who = "unknown"
- timestamp = None
- files = []
- lines = msg.fp.readlines()
-
- # read the control lines (what/who/where/file/etc.)
- while lines:
- line = lines.pop(0)
- if line == "LOGCOMMENT\n":
- break;
- line = line.rstrip("\n")
-
- # we'd like to do the following but it won't work if the number of
- # items doesn't match so...
- # what, timestamp, user, repo, module, file = line.split( '|' )
- items = line.split('|')
- if len(items) < 6:
- # not a valid line, assume this isn't a bonsai message
- return None
-
- try:
- # just grab the bottom-most timestamp, they're probably all the
- # same. TODO: I'm assuming this is relative to the epoch, but
- # this needs testing.
- timestamp = int(items[1])
- except ValueError:
- pass
-
- user = items[2]
- if user:
- who = user
+class BonsaiMaildirSource(MaildirSource):
+ name = "Bonsai"
- module = items[4]
- file = items[5]
- if module and file:
- path = "%s/%s" % (module, file)
- files.append(path)
- sticky = items[7]
- branch = items[8]
+ def parse(self, fd, prefix=None, sep="/"):
+ """Parse mail sent by the Bonsai cvs loginfo script."""
- # if no files changed, return nothing
- if not files:
- return None
+ msg = Message(fd)
- # read the comments
- comments = ""
- while lines:
- line = lines.pop(0)
- if line == ":ENDLOGCOMMENT\n":
- break
- comments += line
- comments = comments.rstrip() + "\n"
+ # we don't care who the email came from b/c the cvs user is in the
+ # msg text
- # return buildbot Change object
- return changes.Change(who, files, comments, when=timestamp, branch=branch)
+ who = "unknown"
+ timestamp = None
+ files = []
+ lines = msg.fp.readlines()
+ # read the control lines (what/who/where/file/etc.)
+ while lines:
+ line = lines.pop(0)
+ if line == "LOGCOMMENT\n":
+ break;
+ line = line.rstrip("\n")
+ # we'd like to do the following but it won't work if the number of
+ # items doesn't match so...
+ # what, timestamp, user, repo, module, file = line.split( '|' )
+ items = line.split('|')
+ if len(items) < 6:
+ # not a valid line, assume this isn't a bonsai message
+ return None
-class MaildirSource(MaildirService, util.ComparableMixin):
- """This source will watch a maildir that is subscribed to a FreshCVS
- change-announcement mailing list.
- """
- implements(IChangeSource)
+ try:
+ # just grab the bottom-most timestamp, they're probably all the
+ # same. TODO: I'm assuming this is relative to the epoch, but
+ # this needs testing.
+ timestamp = int(items[1])
+ except ValueError:
+ pass
- compare_attrs = ["basedir", "pollinterval", "parser"]
- parser = None
- name = None
+ user = items[2]
+ if user:
+ who = user
- def __init__(self, maildir, prefix=None, sep="/"):
- MaildirService.__init__(self, maildir)
- self.prefix = prefix
- self.sep = sep
+ module = items[4]
+ file = items[5]
+ if module and file:
+ path = "%s/%s" % (module, file)
+ files.append(path)
+ sticky = items[7]
+ branch = items[8]
- def describe(self):
- return "%s mailing list in maildir %s" % (self.name, self.basedir)
+ # if no files changed, return nothing
+ if not files:
+ return None
- def messageReceived(self, filename):
- path = os.path.join(self.basedir, "new", filename)
- change = self.parser(open(path, "r"), self.prefix, self.sep)
- if change:
- self.parent.addChange(change)
- os.rename(os.path.join(self.basedir, "new", filename),
- os.path.join(self.basedir, "cur", filename))
+ # read the comments
+ comments = ""
+ while lines:
+ line = lines.pop(0)
+ if line == ":ENDLOGCOMMENT\n":
+ break
+ comments += line
+ comments = comments.rstrip() + "\n"
-class FCMaildirSource(MaildirSource):
- parser = parseFreshCVSMail
- name = "FreshCVS"
+ # return buildbot Change object
+ return changes.Change(who, files, comments, when=timestamp,
+ branch=branch)
-class SyncmailMaildirSource(MaildirSource):
- parser = parseSyncmail
- name = "Syncmail"
-class BonsaiMaildirSource(MaildirSource):
- parser = parseBonsaiMail
- name = "Bonsai"
More information about the Commits
mailing list