Package buildbot :: Package changes :: Module changes
[frames] | no frames]

Source Code for Module buildbot.changes.changes

  1  import sys, os, time 
  2  from cPickle import dump 
  3   
  4  from zope.interface import implements 
  5  from twisted.python import log, runtime 
  6  from twisted.web import html 
  7   
  8  from buildbot import interfaces, util 
  9  from buildbot.process.properties import Properties 
 10   
11 -class Change:
12 """I represent a single change to the source tree. This may involve 13 several files, but they are all changed by the same person, and there is 14 a change comment for the group as a whole. 15 16 If the version control system supports sequential repository- (or 17 branch-) wide change numbers (like SVN, P4, and Bzr), then revision= 18 should be set to that number. The highest such number will be used at 19 checkout time to get the correct set of files. 20 21 If it does not (like CVS), when= should be set to the timestamp (seconds 22 since epoch, as returned by time.time()) when the change was made. when= 23 will be filled in for you (to the current time) if you omit it, which is 24 suitable for ChangeSources which have no way of getting more accurate 25 timestamps. 26 27 The revision= and branch= values must be ASCII bytestrings, since they 28 will eventually be used in a ShellCommand and passed to os.exec(), which 29 requires bytestrings. These values will also be stored in a database, 30 possibly as unicode, so they must be safely convertable back and forth. 31 This restriction may be relaxed in the future. 32 33 Changes should be submitted to ChangeMaster.addChange() in 34 chronologically increasing order. Out-of-order changes will probably 35 cause the web status displays to be corrupted.""" 36 37 implements(interfaces.IStatusEvent) 38 39 number = None 40 41 branch = None 42 category = None 43 revision = None # used to create a source-stamp 44
45 - def __init__(self, who, files, comments, isdir=0, links=None, 46 revision=None, when=None, branch=None, category=None, 47 revlink='', properties={}, repository='', project=''):
48 self.who = who 49 self.comments = comments 50 self.isdir = isdir 51 if links is None: 52 links = [] 53 self.links = links 54 55 def none_or_unicode(x): 56 if x is None: return x 57 return unicode(x)
58 59 self.revision = none_or_unicode(revision) 60 now = util.now() 61 if when is None: 62 self.when = now 63 elif when > now: 64 # this happens when the committing system has an incorrect clock, for example. 65 # handle it gracefully 66 log.msg("received a Change with when > now; assuming the change happened now") 67 self.when = now 68 else: 69 self.when = when 70 self.branch = none_or_unicode(branch) 71 self.category = none_or_unicode(category) 72 self.revlink = revlink 73 self.properties = Properties() 74 self.properties.update(properties, "Change") 75 self.repository = repository 76 self.project = project 77 78 # keep a sorted list of the files, for easier display 79 self.files = files[:] 80 self.files.sort()
81
82 - def __setstate__(self, dict):
83 self.__dict__ = dict 84 # Older Changes won't have a 'properties' attribute in them 85 if not hasattr(self, 'properties'): 86 self.properties = Properties() 87 if not hasattr(self, 'revlink'): 88 self.revlink = ""
89
90 - def asText(self):
91 data = "" 92 data += self.getFileContents() 93 if self.repository: 94 data += "On: %s\n" % self.repository 95 if self.project: 96 data += "For: %s\n" % self.project 97 data += "At: %s\n" % self.getTime() 98 data += "Changed By: %s\n" % self.who 99 data += "Comments: %s" % self.comments 100 data += "Properties: \n%s\n\n" % self.getProperties() 101 return data
102
103 - def asDict(self):
104 '''returns a dictonary with suitable info for html/mail rendering''' 105 result = {} 106 107 files = [] 108 for file in self.files: 109 link = filter(lambda s: s.find(file) != -1, self.links) 110 if len(link) == 1: 111 url = link[0] 112 else: 113 url = None 114 files.append(dict(url=url, name=file)) 115 116 files = sorted(files, cmp=lambda a, b: a['name'] < b['name']) 117 118 # Constant 119 result['number'] = self.number 120 result['branch'] = self.branch 121 result['category'] = self.category 122 result['who'] = self.getShortAuthor() 123 result['comments'] = self.comments 124 result['revision'] = self.revision 125 result['rev'] = self.revision 126 result['when'] = self.when 127 result['at'] = self.getTime() 128 result['files'] = files 129 result['revlink'] = getattr(self, 'revlink', None) 130 result['properties'] = self.properties.asList() 131 result['repository'] = getattr(self, 'repository', None) 132 result['project'] = getattr(self, 'project', None) 133 return result
134
135 - def getShortAuthor(self):
136 return self.who
137
138 - def getTime(self):
139 if not self.when: 140 return "?" 141 return time.strftime("%a %d %b %Y %H:%M:%S", 142 time.localtime(self.when))
143
144 - def getTimes(self):
145 return (self.when, None)
146
147 - def getText(self):
148 return [html.escape(self.who)]
149 - def getLogs(self):
150 return {}
151
152 - def getFileContents(self):
153 data = "" 154 if len(self.files) == 1: 155 if self.isdir: 156 data += "Directory: %s\n" % self.files[0] 157 else: 158 data += "File: %s\n" % self.files[0] 159 else: 160 data += "Files:\n" 161 for f in self.files: 162 data += " %s\n" % f 163 return data
164
165 - def getProperties(self):
166 data = "" 167 for prop in self.properties.asList(): 168 data += " %s: %s" % (prop[0], prop[1]) 169 return data
170 171
172 -class ChangeMaster:
173 # this is a stub, retained to allow the "buildbot upgrade-master" tool to 174 # read old changes.pck pickle files and convert their contents into the 175 # new database format. This is only instantiated by that tool, or by 176 # test_db.py which tests that tool. The functionality that previously 177 # lived here has been moved into buildbot.changes.manager.ChangeManager 178
179 - def __init__(self):
180 self.changes = [] 181 # self.basedir must be filled in by the parent 182 self.nextNumber = 1
183
184 - def addChange(self, change):
185 change.number = self.nextNumber 186 self.nextNumber += 1 187 self.changes.append(change)
188
189 - def saveYourself(self):
190 filename = os.path.join(self.basedir, "changes.pck") 191 tmpfilename = filename + ".tmp" 192 try: 193 dump(self, open(tmpfilename, "wb")) 194 if runtime.platformType == 'win32': 195 # windows cannot rename a file on top of an existing one 196 if os.path.exists(filename): 197 os.unlink(filename) 198 os.rename(tmpfilename, filename) 199 except Exception: 200 log.msg("unable to save changes") 201 log.err()
202 203 # This method is used by contrib/fix_changes_pickle_encoding.py to recode all 204 # bytestrings in an old changes.pck into unicode strings
205 - def recode_changes(self, old_encoding, quiet=False):
206 """Processes the list of changes, with the change attributes re-encoded 207 as UTF-8 bytestrings""" 208 nconvert = 0 209 for c in self.changes: 210 # give revision special handling, in case it is an integer 211 if isinstance(c.revision, int): 212 c.revision = unicode(c.revision) 213 214 for attr in ("who", "comments", "revlink", "category", "branch", "revision"): 215 a = getattr(c, attr) 216 if isinstance(a, str): 217 try: 218 setattr(c, attr, a.decode(old_encoding)) 219 nconvert += 1 220 except UnicodeDecodeError: 221 raise UnicodeError("Error decoding %s of change #%s as %s:\n%r" % 222 (attr, c.number, old_encoding, a)) 223 if not quiet: 224 print "converted %d strings" % nconvert
225
226 -class OldChangeMaster(ChangeMaster):
227 # this is a reminder that the ChangeMaster class is old 228 pass
229 # vim: set ts=4 sts=4 sw=4 et: 230