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