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 links = []
40
41 @classmethod
43 """
44 Class method to create a L{Change} from a dictionary as returned
45 by L{ChangesConnectorComponent.getChange}.
46
47 @param master: build master instance
48 @param ssdict: change dictionary
49
50 @returns: L{Change} via Deferred
51 """
52 cache = master.caches.get_cache("Changes", cls._make_ch)
53 return cache.get(chdict['changeid'], chdict=chdict, master=master)
54
55 @classmethod
56 - def _make_ch(cls, changeid, master, chdict):
57 change = cls(None, None, None, _fromChdict=True)
58 change.who = chdict['author']
59 change.comments = chdict['comments']
60 change.isdir = chdict['is_dir']
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,
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
95 def none_or_unicode(x):
96 if x is None: return x
97 return unicode(x)
98
99 self.revision = none_or_unicode(revision)
100 now = util.now()
101 if when is None:
102 self.when = now
103 elif when > now:
104
105
106 log.msg("received a Change with when > now; assuming the change happened now")
107 self.when = now
108 else:
109 self.when = when
110 self.branch = none_or_unicode(branch)
111 self.category = none_or_unicode(category)
112 self.revlink = revlink
113 self.properties = Properties()
114 self.properties.update(properties, "Change")
115 self.repository = repository
116 self.project = project
117
118
119 self.files = files[:]
120 self.files.sort()
121
123 self.__dict__ = dict
124
125 if not hasattr(self, 'properties'):
126 self.properties = Properties()
127 if not hasattr(self, 'revlink'):
128 self.revlink = ""
129
131 return (u"Change(revision=%r, who=%r, branch=%r, comments=%r, " +
132 u"when=%r, category=%r, project=%r, repository=%r)") % (
133 self.revision, self.who, self.branch, self.comments,
134 self.when, self.category, self.project, self.repository)
135
137 data = ""
138 data += self.getFileContents()
139 if self.repository:
140 data += "On: %s\n" % self.repository
141 if self.project:
142 data += "For: %s\n" % self.project
143 data += "At: %s\n" % self.getTime()
144 data += "Changed By: %s\n" % self.who
145 data += "Comments: %s" % self.comments
146 data += "Properties: \n%s\n\n" % self.getProperties()
147 return data
148
150 '''returns a dictonary with suitable info for html/mail rendering'''
151 result = {}
152
153 files = [ dict(name=f) for f in self.files ]
154 files.sort(cmp=lambda a, b: a['name'] < b['name'])
155
156
157 result['number'] = self.number
158 result['branch'] = self.branch
159 result['category'] = self.category
160 result['who'] = self.getShortAuthor()
161 result['comments'] = self.comments
162 result['revision'] = self.revision
163 result['rev'] = self.revision
164 result['when'] = self.when
165 result['at'] = self.getTime()
166 result['files'] = files
167 result['revlink'] = getattr(self, 'revlink', None)
168 result['properties'] = self.properties.asList()
169 result['repository'] = getattr(self, 'repository', None)
170 result['project'] = getattr(self, 'project', None)
171 return result
172
175
177 if not self.when:
178 return "?"
179 return time.strftime("%a %d %b %Y %H:%M:%S",
180 time.localtime(self.when))
181
183 return (self.when, None)
184
186 return [html.escape(self.who)]
189
190 - def getFileContents(self):
191 data = ""
192 if len(self.files) == 1:
193 if self.isdir:
194 data += "Directory: %s\n" % self.files[0]
195 else:
196 data += "File: %s\n" % self.files[0]
197 else:
198 data += "Files:\n"
199 for f in self.files:
200 data += " %s\n" % f
201 return data
202
204 data = ""
205 for prop in self.properties.asList():
206 data += " %s: %s" % (prop[0], prop[1])
207 return data
208
211
212
213
214
215
216
218 self.changes = []
219
220 self.nextNumber = 1
221
223 filename = os.path.join(self.basedir, "changes.pck")
224 tmpfilename = filename + ".tmp"
225 try:
226 dump(self, open(tmpfilename, "wb"))
227 if runtime.platformType == 'win32':
228
229 if os.path.exists(filename):
230 os.unlink(filename)
231 os.rename(tmpfilename, filename)
232 except Exception:
233 log.msg("unable to save changes")
234 log.err()
235
236
237
239 """Processes the list of changes, with the change attributes re-encoded
240 unicode objects"""
241 nconvert = 0
242 for c in self.changes:
243
244 if isinstance(c.revision, int):
245 c.revision = unicode(c.revision)
246
247 for attr in ("who", "comments", "revlink", "category", "branch", "revision"):
248 a = getattr(c, attr)
249 if isinstance(a, str):
250 try:
251 setattr(c, attr, a.decode(old_encoding))
252 nconvert += 1
253 except UnicodeDecodeError:
254 raise UnicodeError("Error decoding %s of change #%s as %s:\n%r" %
255 (attr, c.number, old_encoding, a))
256
257
258
259
260 newfiles = []
261 for filename in util.flatten(c.files):
262 if isinstance(filename, str):
263 try:
264 filename = filename.decode(old_encoding)
265 nconvert += 1
266 except UnicodeDecodeError:
267 raise UnicodeError("Error decoding filename '%s' of change #%s as %s:\n%r" %
268 (filename.decode('ascii', 'replace'),
269 c.number, old_encoding, a))
270 newfiles.append(filename)
271 c.files = newfiles
272 if not quiet:
273 print "converted %d strings" % nconvert
274
276
277 pass
278
279