1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 from __future__ import with_statement
17
18 import os, time
19 from cPickle import dump
20
21 from zope.interface import implements
22 from twisted.python import log, runtime
23 from twisted.internet import defer
24 from twisted.web import html
25 from buildbot.util import datetime2epoch
26
27 from buildbot import interfaces, util
28 from buildbot.process.properties import Properties
31 """I represent a single change to the source tree. This may involve several
32 files, but they are all changed by the same person, and there is a change
33 comment for the group as a whole."""
34
35 implements(interfaces.IStatusEvent)
36
37 number = None
38 branch = None
39 category = None
40 revision = None
41 links = []
42
43 @classmethod
45 """
46 Class method to create a L{Change} from a dictionary as returned
47 by L{ChangesConnectorComponent.getChange}.
48
49 @param master: build master instance
50 @param ssdict: change dictionary
51
52 @returns: L{Change} via Deferred
53 """
54 cache = master.caches.get_cache("Changes", cls._make_ch)
55 return cache.get(chdict['changeid'], chdict=chdict, master=master)
56
57 @classmethod
58 - def _make_ch(cls, changeid, master, chdict):
59 change = cls(None, None, None, _fromChdict=True)
60 change.who = chdict['author']
61 change.comments = chdict['comments']
62 change.isdir = chdict['is_dir']
63 change.revision = chdict['revision']
64 change.branch = chdict['branch']
65 change.category = chdict['category']
66 change.revlink = chdict['revlink']
67 change.repository = chdict['repository']
68 change.codebase = chdict['codebase']
69 change.project = chdict['project']
70 change.number = chdict['changeid']
71
72 when = chdict['when_timestamp']
73 if when:
74 when = datetime2epoch(when)
75 change.when = when
76
77 change.files = chdict['files'][:]
78 change.files.sort()
79
80 change.properties = Properties()
81 for n, (v,s) in chdict['properties'].iteritems():
82 change.properties.setProperty(n, v, s)
83
84 return defer.succeed(change)
85
86 - def __init__(self, who, files, comments, isdir=0,
87 revision=None, when=None, branch=None, category=None,
88 revlink='', properties={}, repository='', codebase='',
89 project='', _fromChdict=False):
90
91 if _fromChdict:
92 return
93
94 self.who = who
95 self.comments = comments
96 self.isdir = isdir
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.codebase = codebase
120 self.project = project
121
122
123 self.files = (files or [])[:]
124 self.files.sort()
125
127 self.__dict__ = dict
128
129 if not hasattr(self, 'properties'):
130 self.properties = Properties()
131 if not hasattr(self, 'revlink'):
132 self.revlink = ""
133
135 return (u"Change(revision=%r, who=%r, branch=%r, comments=%r, " +
136 u"when=%r, category=%r, project=%r, repository=%r, " +
137 u"codebase=%r)") % (
138 self.revision, self.who, self.branch, self.comments,
139 self.when, self.category, self.project, self.repository,
140 self.codebase)
141
144
146 data = ""
147 data += self.getFileContents()
148 if self.repository:
149 data += "On: %s\n" % self.repository
150 if self.project:
151 data += "For: %s\n" % self.project
152 data += "At: %s\n" % self.getTime()
153 data += "Changed By: %s\n" % self.who
154 data += "Comments: %s" % self.comments
155 data += "Properties: \n%s\n\n" % self.getProperties()
156 return data
157
159 '''returns a dictonary with suitable info for html/mail rendering'''
160 result = {}
161
162 files = [ dict(name=f) for f in self.files ]
163 files.sort(cmp=lambda a, b: a['name'] < b['name'])
164
165
166 result['number'] = self.number
167 result['branch'] = self.branch
168 result['category'] = self.category
169 result['who'] = self.getShortAuthor()
170 result['comments'] = self.comments
171 result['revision'] = self.revision
172 result['rev'] = self.revision
173 result['when'] = self.when
174 result['at'] = self.getTime()
175 result['files'] = files
176 result['revlink'] = getattr(self, 'revlink', None)
177 result['properties'] = self.properties.asList()
178 result['repository'] = getattr(self, 'repository', None)
179 result['codebase'] = getattr(self, 'codebase', '')
180 result['project'] = getattr(self, 'project', None)
181 return result
182
185
187 if not self.when:
188 return "?"
189 return time.strftime("%a %d %b %Y %H:%M:%S",
190 time.localtime(self.when))
191
193 return (self.when, None)
194
196 return [html.escape(self.who)]
199
200 - def getFileContents(self):
201 data = ""
202 if len(self.files) == 1:
203 if self.isdir:
204 data += "Directory: %s\n" % self.files[0]
205 else:
206 data += "File: %s\n" % self.files[0]
207 else:
208 data += "Files:\n"
209 for f in self.files:
210 data += " %s\n" % f
211 return data
212
214 data = ""
215 for prop in self.properties.asList():
216 data += " %s: %s" % (prop[0], prop[1])
217 return data
218
221
222
223
224
225
226
228 self.changes = []
229
230 self.nextNumber = 1
231
233 filename = os.path.join(self.basedir, "changes.pck")
234 tmpfilename = filename + ".tmp"
235 try:
236 with open(tmpfilename, "wb") as f:
237 dump(self, f)
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