1   
  2   
  3   
  4   
  5   
  6   
  7   
  8   
  9   
 10   
 11   
 12   
 13   
 14   
 15   
 16  import os, time 
 17  from cPickle import dump 
 18   
 19  from zope.interface import implements 
 20  from twisted.python import log, runtime 
 21  from twisted.internet import defer 
 22  from twisted.web import html 
 23  from buildbot.util import datetime2epoch 
 24   
 25  from buildbot import interfaces, util 
 26  from buildbot.process.properties import Properties 
 29      """I represent a single change to the source tree. This may involve several 
 30      files, but they are all changed by the same person, and there is a change 
 31      comment for the group as a whole.""" 
 32   
 33      implements(interfaces.IStatusEvent) 
 34   
 35      number = None 
 36      branch = None 
 37      category = None 
 38      revision = None  
 39   
 40      @classmethod 
 42          """ 
 43          Class method to create a L{Change} from a dictionary as returned 
 44          by L{ChangesConnectorComponent.getChange}. 
 45   
 46          @param master: build master instance 
 47          @param ssdict: change dictionary 
 48   
 49          @returns: L{Change} via Deferred 
 50          """ 
 51          cache = master.caches.get_cache("Changes", cls._make_ch) 
 52          return cache.get(chdict['changeid'], chdict=chdict, master=master) 
  53   
 54      @classmethod 
 55 -    def _make_ch(cls, changeid, master, chdict): 
  56          change = cls(None, None, None, _fromChdict=True) 
 57          change.who = chdict['author'] 
 58          change.comments = chdict['comments'] 
 59          change.isdir = chdict['is_dir'] 
 60          change.links = chdict['links'] 
 61          change.revision = chdict['revision'] 
 62          change.branch = chdict['branch'] 
 63          change.category = chdict['category'] 
 64          change.revlink = chdict['revlink'] 
 65          change.repository = chdict['repository'] 
 66          change.project = chdict['project'] 
 67          change.number = chdict['changeid'] 
 68   
 69          when = chdict['when_timestamp'] 
 70          if when: 
 71              when = datetime2epoch(when) 
 72          change.when = when 
 73   
 74          change.files = chdict['files'][:] 
 75          change.files.sort() 
 76   
 77          change.properties = Properties() 
 78          for n, (v,s) in chdict['properties'].iteritems(): 
 79              change.properties.setProperty(n, v, s) 
 80   
 81          return defer.succeed(change) 
  82   
 83 -    def __init__(self, who, files, comments, isdir=0, links=None, 
 84                   revision=None, when=None, branch=None, category=None, 
 85                   revlink='', properties={}, repository='', project='', 
 86                   _fromChdict=False): 
  87           
 88          if _fromChdict: 
 89              return 
 90   
 91          self.who = who 
 92          self.comments = comments 
 93          self.isdir = isdir 
 94          if links is None: 
 95              links = [] 
 96          self.links = links 
 97   
 98          def none_or_unicode(x): 
 99              if x is None: return x 
100              return unicode(x) 
 101   
102          self.revision = none_or_unicode(revision) 
103          now = util.now() 
104          if when is None: 
105              self.when = now 
106          elif when > now: 
107               
108               
109              log.msg("received a Change with when > now; assuming the change happened now") 
110              self.when = now 
111          else: 
112              self.when = when 
113          self.branch = none_or_unicode(branch) 
114          self.category = none_or_unicode(category) 
115          self.revlink = revlink 
116          self.properties = Properties() 
117          self.properties.update(properties, "Change") 
118          self.repository = repository 
119          self.project = project 
120   
121           
122          self.files = files[:] 
123          self.files.sort() 
 124   
126          self.__dict__ = dict 
127           
128          if not hasattr(self, 'properties'): 
129              self.properties = Properties() 
130          if not hasattr(self, 'revlink'): 
131              self.revlink = "" 
 132   
134          return (u"Change(revision=%r, who=%r, branch=%r, comments=%r, " + 
135                  u"when=%r, category=%r, project=%r, repository=%r)") % ( 
136                  self.revision, self.who, self.branch, self.comments, 
137                  self.when, self.category, self.project, self.repository) 
 138   
140          data = "" 
141          data += self.getFileContents() 
142          if self.repository: 
143              data += "On: %s\n" % self.repository 
144          if self.project: 
145              data += "For: %s\n" % self.project 
146          data += "At: %s\n" % self.getTime() 
147          data += "Changed By: %s\n" % self.who 
148          data += "Comments: %s" % self.comments 
149          data += "Properties: \n%s\n\n" % self.getProperties() 
150          return data 
 151   
153          '''returns a dictonary with suitable info for html/mail rendering''' 
154          result = {} 
155   
156          files = [] 
157          for file in self.files: 
158              link = filter(lambda s: s.find(file) != -1, self.links) 
159              if len(link) == 1: 
160                  url = link[0] 
161              else: 
162                  url = None 
163              files.append(dict(url=url, name=file)) 
164   
165          files = sorted(files, cmp=lambda a, b: a['name'] < b['name']) 
166   
167           
168          result['number'] = self.number 
169          result['branch'] = self.branch 
170          result['category'] = self.category 
171          result['who'] = self.getShortAuthor() 
172          result['comments'] = self.comments 
173          result['revision'] = self.revision 
174          result['rev'] = self.revision 
175          result['when'] = self.when 
176          result['at'] = self.getTime() 
177          result['files'] = files 
178          result['revlink'] = getattr(self, 'revlink', None) 
179          result['properties'] = self.properties.asList() 
180          result['repository'] = getattr(self, 'repository', None) 
181          result['project'] = getattr(self, 'project', None) 
182          return result 
 183   
186   
188          if not self.when: 
189              return "?" 
190          return time.strftime("%a %d %b %Y %H:%M:%S", 
191                               time.localtime(self.when)) 
 192   
194          return (self.when, None) 
 195   
197          return [html.escape(self.who)] 
 200   
201 -    def getFileContents(self): 
 202          data = "" 
203          if len(self.files) == 1: 
204              if self.isdir: 
205                  data += "Directory: %s\n" % self.files[0] 
206              else: 
207                  data += "File: %s\n" % self.files[0] 
208          else: 
209              data += "Files:\n" 
210              for f in self.files: 
211                  data += " %s\n" % f 
212          return data 
 213   
215          data = "" 
216          for prop in self.properties.asList(): 
217              data += "  %s: %s" % (prop[0], prop[1]) 
218          return data 
 219   
222       
223       
224       
225       
226       
227   
229          self.changes = [] 
230           
231          self.nextNumber = 1 
 232   
234          filename = os.path.join(self.basedir, "changes.pck") 
235          tmpfilename = filename + ".tmp" 
236          try: 
237              dump(self, open(tmpfilename, "wb")) 
238              if runtime.platformType  == 'win32': 
239                   
240                  if os.path.exists(filename): 
241                      os.unlink(filename) 
242              os.rename(tmpfilename, filename) 
243          except Exception: 
244              log.msg("unable to save changes") 
245              log.err() 
 246   
247       
248       
250          """Processes the list of changes, with the change attributes re-encoded 
251          unicode objects""" 
252          nconvert = 0 
253          for c in self.changes: 
254               
255              if isinstance(c.revision, int): 
256                  c.revision = unicode(c.revision) 
257   
258              for attr in ("who", "comments", "revlink", "category", "branch", "revision"): 
259                  a = getattr(c, attr) 
260                  if isinstance(a, str): 
261                      try: 
262                          setattr(c, attr, a.decode(old_encoding)) 
263                          nconvert += 1 
264                      except UnicodeDecodeError: 
265                          raise UnicodeError("Error decoding %s of change #%s as %s:\n%r" % 
266                                          (attr, c.number, old_encoding, a)) 
267   
268               
269               
270               
271              newfiles = [] 
272              for filename in util.flatten(c.files): 
273                  if isinstance(filename, str): 
274                      try: 
275                          filename = filename.decode(old_encoding) 
276                          nconvert += 1 
277                      except UnicodeDecodeError: 
278                          raise UnicodeError("Error decoding filename '%s' of change #%s as %s:\n%r" % 
279                                          (filename.decode('ascii', 'replace'), 
280                                           c.number, old_encoding, a)) 
281                  newfiles.append(filename) 
282              c.files = newfiles 
283          if not quiet: 
284              print "converted %d strings" % nconvert 
 285   
287       
288      pass 
289   
290