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 if when is None: 61 when = util.now() 62 self.when = when 63 self.branch = none_or_unicode(branch) 64 self.category = none_or_unicode(category) 65 self.revlink = revlink 66 self.properties = Properties() 67 self.properties.update(properties, "Change") 68 self.repository = repository 69 self.project = project 70 71 # keep a sorted list of the files, for easier display 72 self.files = files[:] 73 self.files.sort()
74
75 - def __setstate__(self, dict):
76 self.__dict__ = dict 77 # Older Changes won't have a 'properties' attribute in them 78 if not hasattr(self, 'properties'): 79 self.properties = Properties() 80 if not hasattr(self, 'revlink'): 81 self.revlink = ""
82
83 - def asText(self):
84 data = "" 85 data += self.getFileContents() 86 if self.repository: 87 data += "On: %s\n" % self.repository 88 if self.project: 89 data += "For: %s\n" % self.project 90 data += "At: %s\n" % self.getTime() 91 data += "Changed By: %s\n" % self.who 92 data += "Comments: %s" % self.comments 93 data += "Properties: \n%s\n\n" % self.getProperties() 94 return data
95
96 - def asDict(self):
97 '''returns a dictonary with suitable info for html/mail rendering''' 98 result = {} 99 100 files = [] 101 for file in self.files: 102 link = filter(lambda s: s.find(file) != -1, self.links) 103 if len(link) == 1: 104 url = link[0] 105 else: 106 url = None 107 files.append(dict(url=url, name=file)) 108 109 files = sorted(files, cmp=lambda a, b: a['name'] < b['name']) 110 111 # Constant 112 result['number'] = self.number 113 result['branch'] = self.branch 114 result['category'] = self.category 115 result['who'] = self.getShortAuthor() 116 result['comments'] = self.comments 117 result['revision'] = self.revision 118 result['rev'] = self.revision 119 result['when'] = self.when 120 result['at'] = self.getTime() 121 result['files'] = files 122 result['revlink'] = getattr(self, 'revlink', None) 123 result['properties'] = self.properties.asList() 124 result['repository'] = getattr(self, 'repository', None) 125 result['project'] = getattr(self, 'project', None) 126 return result
127
128 - def getShortAuthor(self):
129 return self.who
130
131 - def getTime(self):
132 if not self.when: 133 return "?" 134 return time.strftime("%a %d %b %Y %H:%M:%S", 135 time.localtime(self.when))
136
137 - def getTimes(self):
138 return (self.when, None)
139
140 - def getText(self):
141 return [html.escape(self.who)]
142 - def getLogs(self):
143 return {}
144
145 - def getFileContents(self):
146 data = "" 147 if len(self.files) == 1: 148 if self.isdir: 149 data += "Directory: %s\n" % self.files[0] 150 else: 151 data += "File: %s\n" % self.files[0] 152 else: 153 data += "Files:\n" 154 for f in self.files: 155 data += " %s\n" % f 156 return data
157
158 - def getProperties(self):
159 data = "" 160 for prop in self.properties.asList(): 161 data += " %s: %s" % (prop[0], prop[1]) 162 return data
163 164
165 -class ChangeMaster:
166 # this is a stub, retained to allow the "buildbot upgrade-master" tool to 167 # read old changes.pck pickle files and convert their contents into the 168 # new database format. This is only instantiated by that tool, or by 169 # test_db.py which tests that tool. The functionality that previously 170 # lived here has been moved into buildbot.changes.manager.ChangeManager 171
172 - def __init__(self):
173 self.changes = [] 174 # self.basedir must be filled in by the parent 175 self.nextNumber = 1
176
177 - def addChange(self, change):
178 change.number = self.nextNumber 179 self.nextNumber += 1 180 self.changes.append(change)
181
182 - def saveYourself(self):
183 filename = os.path.join(self.basedir, "changes.pck") 184 tmpfilename = filename + ".tmp" 185 try: 186 dump(self, open(tmpfilename, "wb")) 187 if runtime.platformType == 'win32': 188 # windows cannot rename a file on top of an existing one 189 if os.path.exists(filename): 190 os.unlink(filename) 191 os.rename(tmpfilename, filename) 192 except Exception: 193 log.msg("unable to save changes") 194 log.err()
195 196 # This method is used by contrib/fix_changes_pickle_encoding.py to recode all 197 # bytestrings in an old changes.pck into unicode strings
198 - def recode_changes(self, old_encoding, quiet=False):
199 """Processes the list of changes, with the change attributes re-encoded 200 as UTF-8 bytestrings""" 201 nconvert = 0 202 for c in self.changes: 203 # give revision special handling, in case it is an integer 204 if isinstance(c.revision, int): 205 c.revision = unicode(c.revision) 206 207 for attr in ("who", "comments", "revlink", "category", "branch", "revision"): 208 a = getattr(c, attr) 209 if isinstance(a, str): 210 try: 211 setattr(c, attr, a.decode(old_encoding)) 212 nconvert += 1 213 except UnicodeDecodeError: 214 raise UnicodeError("Error decoding %s of change #%s as %s:\n%r" % 215 (attr, c.number, old_encoding, a)) 216 if not quiet: 217 print "converted %d strings" % nconvert
218
219 -class OldChangeMaster(ChangeMaster):
220 # this is a reminder that the ChangeMaster class is old 221 pass
222 # vim: set ts=4 sts=4 sw=4 et: 223