Merge lp:~james-w/udd/storm-unicode-fixes into lp:udd
- storm-unicode-fixes
- Merge into import-scripts
Status: | Merged |
---|---|
Merged at revision: | 601 |
Proposed branch: | lp:~james-w/udd/storm-unicode-fixes |
Merge into: | lp:udd |
Prerequisite: | lp:~james-w/udd/storm |
Diff against target: |
505 lines (+208/-29) 5 files modified
udd/icommon.py (+186/-21) udd/scripts/mass_import.py (+6/-2) udd/tests/test_commit_database.py (+1/-1) udd/tests/test_revid_database.py (+1/-1) udd/tests/test_status_database.py (+14/-4) |
To merge this branch: | bzr merge lp:~james-w/udd/storm-unicode-fixes |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Martin Packman | Approve | ||
Vincent Ladeuil | Needs Information | ||
John A Meinel | Approve | ||
James Westby | Pending | ||
Review via email: mp+113004@code.launchpad.net |
This proposal supersedes a proposal from 2012-07-02.
Commit message
Description of the change
Hi,
This fixes the issues that Max found where we weren't using unicode for
TEXT content, which meant the info was stored as blobs and problems
happened.
Thanks,
James
Martin Packman (gz) wrote : | # |
For background for this change see UDD list thread:
<https:/
I'd like to see here is documentation for why SHAs and testaments are being stored as text, and whether it's just to avoid rewriting existing data. From experience it doesn't actually matter much storing hashes as hex digest text rather than their raw form.
My main complaint is this branch and its prereq make it harder to tell whether a variable should be bytes or unicode, tending to just add casts in various places without documenting which methods expect what or sticking to one clear rule.
def check(self, version, revid, suite, branch):
+ revid = unicode(revid)
sha = self.get_
This is the kind of thing I mean. This change casts the arg to unicode, but before passing back to the bzr layer which expects bytes, so relies on bzrlib autocasting back. Only switching to unicode at db insert and retrieve would be less confusing.
=== modified file 'udd/tests/
Test fix not related to unicode usage?
-import sqlite3
+from pysqlite2.dbapi2 import OperationalError
So UDD now depends on having external pysqlite2 package?
=== modified file 'udd/tests/
Test fix not related to unicode usage?
Vincent Ladeuil (vila) wrote : | # |
15 FAILURES_
16 (package text constraint nonull NOT NULL,
17 - reason blob,
18 + reason text,
24 OLD_FAILURES_
25 (package text constraint nonull NOT NULL,
26 - reason blob,
27 + reason text,
34 RETRY_TABLE_CREATE = '''create table if not exists %s
35 - (signature blob constraint nonull NOT NULL,
36 + (signature text constraint nonull NOT NULL,
Don't the above changes requires some db migration ?
If not, can you explain why ?
Same remark as gz regarding pysqlite2: is that a new dependency ?
Also, why is it the only place you made this change when sqlite3 is used in
other places ?
James Westby (james-w) wrote : | # |
On Mon, 02 Jul 2012 11:23:18 -0000, Martin Packman <email address hidden> wrote:
> I'd like to see here is documentation for why SHAs and testaments are
> being stored as text, and whether it's just to avoid rewriting
> existing data. From experience it doesn't actually matter much storing
> hashes as hex digest text rather than their raw form.
Yeah, it's because the db has them as TEXT. I agree that BLOB would make
more sense, but wanted to avoid a data transition.
> My main complaint is this branch and its prereq make it harder to tell
> whether a variable should be bytes or unicode, tending to just add
> casts in various places without documenting which methods expect what
> or sticking to one clear rule.
I agree that the docs could be better, I did have a rule (which
admittedly may not be all that clear):
If it's text require unicode in the apis, if not then do it in the db
layer.
> def check(self, version, revid, suite, branch):
> + revid = unicode(revid)
> sha = self.get_
>
> This is the kind of thing I mean. This change casts the arg to
> unicode, but before passing back to the bzr layer which expects bytes,
> so relies on bzrlib autocasting back. Only switching to unicode at db
> insert and retrieve would be less confusing.
Yeah, I should do the cast later when it hits the db, rather than before
bzrlib gets it.
> === modified file 'udd/tests/
>
> Test fix not related to unicode usage?
>
> -import sqlite3
> +from pysqlite2.dbapi2 import OperationalError
>
> So UDD now depends on having external pysqlite2 package?
That's the error that storm raises. I mistakenly assumed it was the
built in version.
I'll change it to import sqlite from storm, so that it uses whichever
one storm has found.
> === modified file 'udd/tests/
>
> Test fix not related to unicode usage?
Yep, I assume the test was added after the storm branch was reverted,
but didn't cause a test conflict. I can move it in to the earlier branch
if you like?
Thanks,
James
James Westby (james-w) wrote : | # |
On Mon, 02 Jul 2012 16:12:20 -0000, Vincent Ladeuil <email address hidden> wrote:
> Review: Needs Information
>
> 15 FAILURES_
> 16 (package text constraint nonull NOT NULL,
> 17 - reason blob,
> 18 + reason text,
>
> 24 OLD_FAILURES_
> 25 (package text constraint nonull NOT NULL,
> 26 - reason blob,
> 27 + reason text,
>
> 34 RETRY_TABLE_CREATE = '''create table if not exists %s
> 35 - (signature blob constraint nonull NOT NULL,
> 36 + (signature text constraint nonull NOT NULL,
>
>
> Don't the above changes requires some db migration ?
>
> If not, can you explain why ?
Because as I understand it they are only there to use up characters in
sqlite. Defining the columns with those types didn't stop the data being
stored in another type, and didn't stop me from assuming that they
were correct and not fixing the things in this branch first time around.
They are purely for documentation (hence changing them to match what is
stored) but as such a db migration isn't really needed.
It's probably the case that the dbs on production will keep the wrong
schema, but I don't care enough about that when the point is to delete
them.
> Also, why is it the only place you made this change when sqlite3 is used in
> other places ?
It's the only one where the test failed, because it's storm that is
generating the error.
Thanks,
James
- 601. By James Westby
-
Import sqlite from storm so we are using the same one, thanks Max.
Martin Packman (gz) wrote : | # |
> Yeah, it's because the db has them as TEXT. I agree that BLOB would make
> more sense, but wanted to avoid a data transition.
Great, a comment to that effect by the table creation should do.
> I agree that the docs could be better, I did have a rule (which
> admittedly may not be all that clear):
>
> If it's text require unicode in the apis, if not then do it in the db
> layer.
That rule makes sense, but there are currently some exceptions to it.
> Yeah, I should do the cast later when it hits the db, rather than before
> bzrlib gets it.
Right. Having outstanding_marks deliberately use the db form is a source of some of the pain here.
> That's the error that storm raises. I mistakenly assumed it was the
> built in version.
>
> I'll change it to import sqlite from storm, so that it uses whichever
> one storm has found.
Yup, we just want the import fallback logic in one place then to use the bound name from there. As storm has it, using that everywhere sounds good.
> Yep, I assume the test was added after the storm branch was reverted,
> but didn't cause a test conflict. I can move it in to the earlier branch
> if you like?
No need, just checking that was the case.
James Westby (james-w) wrote : | # |
On Mon, 02 Jul 2012 17:07:23 -0000, Martin Packman <email address hidden> wrote:
> Great, a comment to that effect by the table creation should do.
Done.
> That rule makes sense, but there are currently some exceptions to it.
If you can point them out I'll see about fixing them.
> > Yeah, I should do the cast later when it hits the db, rather than before
> > bzrlib gets it.
>
> Right. Having outstanding_marks deliberately use the db form is a source of some of the pain here.
Yeah, it does make some things a bit odd. Running on postgres will allow
us to delete that code though, because it was a way to avoid sqlite
locking.
> Yup, we just want the import fallback logic in one place then to use the bound name from there. As storm has it, using that everywhere sounds good.
Done.
> > Yep, I assume the test was added after the storm branch was reverted,
> > but didn't cause a test conflict. I can move it in to the earlier branch
> > if you like?
>
> No need, just checking that was the case.
Ok.
Thanks,
James
- 602. By James Westby
-
Document some of the db methods.
- 603. By James Westby
-
Add a comment as to why revid and testament are TEXT. Thanks Martin.
- 604. By James Westby
-
Merged storm into storm-unicode-
fixes. - 605. By James Westby
-
Merged storm into storm-unicode-
fixes.
Preview Diff
1 | === modified file 'udd/icommon.py' |
2 | --- udd/icommon.py 2012-07-03 17:10:24 +0000 |
3 | +++ udd/icommon.py 2012-07-03 17:10:24 +0000 |
4 | @@ -42,7 +42,7 @@ |
5 | # below. Some way to share the config is needed -- vila 2011-12-07 |
6 | conf = iconfig.ImporterStack() |
7 | |
8 | -running_sentinel = "Apparently the supervisor died\n" |
9 | +running_sentinel = u"Apparently the supervisor died\n" |
10 | no_lock_returncode = 139 |
11 | |
12 | |
13 | @@ -453,7 +453,7 @@ |
14 | FAILURES_TABLE = "failures" |
15 | FAILURES_TABLE_CREATE = '''create table if not exists %s |
16 | (package text constraint nonull NOT NULL, |
17 | - reason blob, |
18 | + reason text, |
19 | when_failed timestamp, |
20 | emailed integer default 0, |
21 | constraint isprimary PRIMARY KEY |
22 | @@ -464,7 +464,7 @@ |
23 | OLD_FAILURES_TABLE = "old_failures" |
24 | OLD_FAILURES_TABLE_CREATE = '''create table if not exists %s |
25 | (package text constraint nonull NOT NULL, |
26 | - reason blob, |
27 | + reason text, |
28 | when_failed timestamp, |
29 | last_failed timestamp, |
30 | failure_count integer, |
31 | @@ -499,7 +499,7 @@ |
32 | |
33 | RETRY_TABLE = "should_retry" |
34 | RETRY_TABLE_CREATE = '''create table if not exists %s |
35 | - (signature blob constraint nonull NOT NULL, |
36 | + (signature text constraint nonull NOT NULL, |
37 | constraint isprimary PRIMARY KEY |
38 | (signature))''' % RETRY_TABLE |
39 | RETRY_TABLE_SELECT = '''select * from %s where signature=?''' % RETRY_TABLE |
40 | @@ -545,6 +545,7 @@ |
41 | self._add_job(c, row[0], self.JOB_TYPE_PRIORITY) |
42 | |
43 | def add_jobs_for_interrupted(self): |
44 | + """Add all interrupted jobs to the queue.""" |
45 | with Cursor(self.conn) as c: |
46 | rows = c.execute('select * from %s where reason=?' |
47 | % self.FAILURES_TABLE, (running_sentinel,)) |
48 | @@ -578,10 +579,31 @@ |
49 | return ret |
50 | |
51 | def start_package(self, package): |
52 | + """Note that the import of `package` is being started. |
53 | + |
54 | + :param package: the package name that is being imported |
55 | + :type package: unicode |
56 | + :return: the id assigned to the job. |
57 | + :rtype: int |
58 | + """ |
59 | with Cursor(self.conn) as c: |
60 | return self._start_package(c, package) |
61 | |
62 | def finish_job(self, package, job_id, success, output, when=None): |
63 | + """Note that the import of `package` has finished |
64 | + |
65 | + :param package: the package name that was imported |
66 | + :type package: unicode |
67 | + :param job_id: the id of the job |
68 | + :type job_id: int |
69 | + :param success: whether the job was successful |
70 | + :type success: boolean |
71 | + :param output: the output from the job |
72 | + :type output: unicode |
73 | + :param when: the datetime at which the job failed. The default |
74 | + is None which means that the current time should be used. |
75 | + :type when: datetime |
76 | + """ |
77 | if when is None: |
78 | when = datetime.utcnow() |
79 | with Cursor(self.conn) as c: |
80 | @@ -606,11 +628,25 @@ |
81 | datetime.utcnow(), None, None)) |
82 | |
83 | def add_jobs(self, packages, job_type): |
84 | + """Add a set of packages to the queue. |
85 | + |
86 | + :param packages: the package names to be added |
87 | + :type package: list(unicode) |
88 | + :param job_type: the type of the job, one of JOB_TYPE_NEW, |
89 | + JOB_TYPE_RETRY, JOB_TYPE_PRIORITY. |
90 | + :type job_type: int |
91 | + """ |
92 | with Cursor(self.conn) as c: |
93 | for package in packages: |
94 | self._add_job(c, package, job_type) |
95 | |
96 | def last_import_time(self): |
97 | + """Get the time that the last Launchpad import was done. |
98 | + |
99 | + :return: the datetime at which the Launchpad import last |
100 | + ran successfully. |
101 | + :rtype: datetime |
102 | + """ |
103 | with Cursor(self.conn) as c: |
104 | row = c.execute('select * from %s order by import desc' |
105 | % self.IMPORT_TABLE).get_one() |
106 | @@ -619,6 +655,17 @@ |
107 | return row |
108 | |
109 | def add_import_jobs(self, packages, newest_published): |
110 | + """Add a set of jobs from the Launchpad import. |
111 | + |
112 | + The time reported as `newest_published` will also be stored |
113 | + as the last time the import successfully ran. |
114 | + |
115 | + :param packages: the list of packages to import |
116 | + :type packages: list(unicode) |
117 | + :param newest_published: the last publishing time seen for |
118 | + any of those packages. |
119 | + :type packages: datetime |
120 | + """ |
121 | with Cursor(self.conn) as c: |
122 | for package in packages: |
123 | self._add_job(c, package, self.JOB_TYPE_NEW) |
124 | @@ -626,6 +673,12 @@ |
125 | (newest_published,)) |
126 | |
127 | def next_job(self): |
128 | + """Get the next job to run. |
129 | + |
130 | + :return: the next job to run, as a tuple of (package_name, job_id). |
131 | + If either is None then there is no job to run currently. |
132 | + :rtype: (Maybe unicode, Maybe int) |
133 | + """ |
134 | with Cursor(self.conn) as c: |
135 | job_id = 0 |
136 | package = None |
137 | @@ -648,6 +701,13 @@ |
138 | return (job_id, package) |
139 | |
140 | def failure_reason(self, package): |
141 | + """Get the reason (signature) why a package failed. |
142 | + |
143 | + :param package: the package to get the reason for |
144 | + :type package: unicode |
145 | + :return: the failure reason, or None if the package hasn't failed. |
146 | + :rtype: unicode |
147 | + """ |
148 | with Cursor(self.conn) as c: |
149 | row = c.execute(self.FAILURES_TABLE_FIND, (package,)).get_one() |
150 | if row is None: |
151 | @@ -655,6 +715,13 @@ |
152 | return row[1] |
153 | |
154 | def failure_signature(self, raw_reason): |
155 | + """Construct a failure signature from the output of a run. |
156 | + |
157 | + :param raw_reason: the output of the job that failed |
158 | + :type raw_reason: str |
159 | + :return: the failure signature |
160 | + :rtype: unicode |
161 | + """ |
162 | trace = raw_reason.splitlines() |
163 | if len(trace) == 1: |
164 | if trace[0] == running_sentinel[:-1]: # Get rid of the final '\n' |
165 | @@ -662,24 +729,24 @@ |
166 | # sometimes, Python exceptions do not have file references |
167 | m = re.match('(\w+): ', trace[0]) |
168 | if m: |
169 | - return m.group(1) |
170 | + return unicode(m.group(1)) |
171 | else: |
172 | - return trace[0].strip() |
173 | + return unicode(trace[0].strip()) |
174 | elif len(trace) < 3: |
175 | - return " ".join(trace).strip() |
176 | + return u" ".join(trace).strip() |
177 | |
178 | # If the failure reason is a traceback (which should always be the |
179 | # case, the running_sentinel check above taking care of the still |
180 | # running imports), we build the traceback signature and capture the |
181 | # exception type |
182 | - sig = '' |
183 | - exc_type = '' |
184 | + sig = u'' |
185 | + exc_type = u'' |
186 | exc_type_coming = False |
187 | file_line_seen = False |
188 | for l in trace: |
189 | if l.startswith(' File'): |
190 | # Keep the method/function name |
191 | - sig += ':' + l.split()[-1] |
192 | + sig += u':' + unicode(l.split()[-1]) |
193 | file_line_seen = True |
194 | elif file_line_seen: |
195 | # We've seen the 'File...' line, so we are now seeing the code |
196 | @@ -704,11 +771,52 @@ |
197 | return c.execute(self.RETRY_TABLE_SELECT, (sig,)).get_one() |
198 | |
199 | def known_auto_retry(self, sig): |
200 | - with Cursor(self.conn) as c: |
201 | - return self._known_auto_retry(c, sig) |
202 | + """Is the signature marked for auto-retry? |
203 | + |
204 | + :param sig: the signature to check |
205 | + :type sig: unicode |
206 | + :return: whether the failure should be auto-retried. |
207 | + :rtype: boolean |
208 | + """ |
209 | + with Cursor(self.conn) as c: |
210 | + return self._known_auto_retry(c, sig) is not None |
211 | + |
212 | + def _add_known_auto_retry(self, c, sig): |
213 | + row = self._known_auto_retry(c, sig) |
214 | + if not row: |
215 | + c.execute(self.RETRY_TABLE_INSERT, (sig,)) |
216 | + |
217 | + def add_known_auto_retry(self, sig): |
218 | + """Mark a signature as being one that should be automatically retried. |
219 | + |
220 | + :param sig: the signature to check |
221 | + :type sig: unicode |
222 | + """ |
223 | + with Cursor(self.conn) as c: |
224 | + return self._add_known_auto_retry(c, sig) |
225 | |
226 | def retry(self, package, force=False, priority=False, auto=False, |
227 | all=False): |
228 | + """Retry a package. |
229 | + |
230 | + :param package: the package to retry |
231 | + :type package: unicode |
232 | + :param force: whether to retry even if the package isn't marked as |
233 | + failed (default is False). |
234 | + :type force: boolean |
235 | + :param priority: whether the job should be retried with high |
236 | + priority (default is False). |
237 | + :type priority: boolean |
238 | + :param auto: whether to failures with that signature should |
239 | + be retried automatically in future (default is False). |
240 | + :type auto: boolean |
241 | + :param all: whether to retry all current failures with that |
242 | + signature (default is False). |
243 | + :type all: boolean |
244 | + :return: whether the job was actually retried (see force for |
245 | + why it might not be) |
246 | + :rtype: boolean |
247 | + """ |
248 | with Cursor(self.conn) as c: |
249 | row = c.execute(self.FAILURES_TABLE_FIND, (package,)).get_one() |
250 | if row is None: |
251 | @@ -724,9 +832,7 @@ |
252 | sig = self.failure_signature(raw_reason) |
253 | self._retry(c, package, sig, row[2], priority=priority) |
254 | if auto and sig != None: |
255 | - row = self._known_auto_retry(c, sig) |
256 | - if row is None: |
257 | - c.execute(self.RETRY_TABLE_INSERT, (sig,)) |
258 | + self._add_known_auto_retry(c, sig) |
259 | if all and sig != None: |
260 | rows = c.execute('select * from %s' |
261 | % self.FAILURES_TABLE) |
262 | @@ -765,7 +871,7 @@ |
263 | |
264 | def _attempt_retry(self, c, info): |
265 | row = self._known_auto_retry(c, info.signature) |
266 | - if row is None: |
267 | + if row: |
268 | # A failure that is not auto-retried |
269 | return False |
270 | info.auto_retry = True |
271 | @@ -954,6 +1060,9 @@ |
272 | |
273 | class RevidDatabase(object): |
274 | |
275 | + # revid and testament are stored as TEXT when BLOB might more sense |
276 | + # for these bytestrings, because that's what the original code |
277 | + # stored them as, and keeping that avoids a data migration. |
278 | REVID_TABLE = "revids" |
279 | REVID_TABLE_CREATE = '''create table if not exists %s |
280 | (package text constraint nonull NOT NULL, |
281 | @@ -1005,6 +1114,15 @@ |
282 | % self.REVID_TABLE, (self.package,))) |
283 | |
284 | def is_marked(self, version, suite): |
285 | + """Whether that version is marked in the suite. |
286 | + |
287 | + :param version: the version to check |
288 | + :type version: changelog.Version |
289 | + :param suite: suite to check |
290 | + :type suite: unicode |
291 | + :return: Whether a mark is recorded for that pair. |
292 | + :rtype: boolean |
293 | + """ |
294 | with Cursor(self.conn) as c: |
295 | rows = list(c.execute(self.REVID_TABLE_FIND % self.REVID_TABLE, |
296 | (unicode(version), self.package, suite))) |
297 | @@ -1018,6 +1136,14 @@ |
298 | return False |
299 | |
300 | def last_marked_version(self, suite): |
301 | + """Get the last version to be marked. |
302 | + |
303 | + :param suite: suite to check |
304 | + :type suite: unicode |
305 | + :return: the versionto be marked, or None if there are no marked |
306 | + versions in the suite. |
307 | + :rtype: Maybe str |
308 | + """ |
309 | with Cursor(self.conn) as c: |
310 | versions = [a[0] for a in c.execute(self.REVID_TABLE_FIND_SUITE |
311 | % self.REVID_WORKING_TABLE, |
312 | @@ -1033,6 +1159,19 @@ |
313 | return None |
314 | |
315 | def check(self, version, revid, suite, branch): |
316 | + """Check whether the (version, revid, suite) for a branch matches the db. |
317 | + |
318 | + :param version: the version to check |
319 | + :type version: changelog.Version |
320 | + :param revid: the revid to check |
321 | + :type revid: str |
322 | + :param suite: suite to check |
323 | + :type suite: unicode |
324 | + :param branch: the branch to check |
325 | + :type branch: bzrlib.branch.Branch |
326 | + :return: whether the info matches the bd |
327 | + :rtype: boolean |
328 | + """ |
329 | sha = self.get_testament_sha1(branch.repository, revid) |
330 | with Cursor(self.conn) as c: |
331 | rows = list(c.execute(self.REVID_TABLE_FIND % self.REVID_TABLE, |
332 | @@ -1040,19 +1179,28 @@ |
333 | rows += list(c.execute(self.REVID_TABLE_FIND % self.REVID_WORKING_TABLE, |
334 | (unicode(version), self.package, suite))) |
335 | if (version, suite) in self.outstanding_marks: |
336 | - revid, tment = self.outstanding_marks[(unicode(version), suite)] |
337 | - rows += (None, None, None, revid, tment) |
338 | + rev, tment = self.outstanding_marks[(unicode(version), suite)] |
339 | + rows += (None, None, None, rev, tment) |
340 | if len(rows) < 1: |
341 | return False |
342 | assert len(rows) < 2, "Multiple versions for a package/suite?" |
343 | row = rows[0] |
344 | - if (revid, sha) != (row[3], row[4]): |
345 | + if (unicode(revid), unicode(sha)) != (row[3], row[4]): |
346 | assert False, ("%s != %s for %s %s in %s, something has changed" |
347 | % (unicode((revid, sha)), unicode((row[3], row[4])), |
348 | self.package, unicode(version), suite)) |
349 | return True |
350 | |
351 | def get_testament_sha1(self, repo, revid): |
352 | + """Get the sha1 of the testament for revid in repo. |
353 | + |
354 | + :param repo: the repository to consult |
355 | + :type repo: bzrlib.repository.Repository |
356 | + :param revid: the revid to get the testament for |
357 | + :type revid: str |
358 | + :return: the sha1 of the testament |
359 | + :rtype: unicode |
360 | + """ |
361 | op = cleanup.OperationWithCleanups(self._get_testament_sha1) |
362 | return op.run(repo, revid) |
363 | |
364 | @@ -1078,6 +1226,12 @@ |
365 | self._commit(c) |
366 | |
367 | def cleanup_last_run(self, cleanup_cb, push_cb): |
368 | + """Cleanup any leftovers from the last run. |
369 | + |
370 | + :param cleanup_cb: a callable to call when the cleanup is done. |
371 | + :param push_cb: a callable to call if the db indicates that there |
372 | + was work-in-progress when the last run terminated. |
373 | + """ |
374 | with Cursor(self.conn) as c: |
375 | rows = list(c.execute(self.REVID_TABLE_FIND_PACKAGE |
376 | % self.REVID_WORKING_TABLE, (self.package,))) |
377 | @@ -1094,8 +1248,19 @@ |
378 | c.execute(self.DELETE_WORKING, (self.package,)) |
379 | |
380 | def mark(self, version, revid, suite, branch): |
381 | + """Mark a (version, revision, suite) for a branch. |
382 | + |
383 | + :param version: the version to mark. |
384 | + :type version: changelog.Version |
385 | + :param revid: the revision id |
386 | + :type revid: str |
387 | + :param suite: the suite to mark |
388 | + :type suite: unicode |
389 | + :param branch: the branh to mark |
390 | + :type branch: bzrlib.branch.Branch |
391 | + """ |
392 | sha = self.get_testament_sha1(branch.repository, revid) |
393 | - self.outstanding_marks[(unicode(version), suite)] = (revid, sha) |
394 | + self.outstanding_marks[(unicode(version), suite)] = (unicode(revid), unicode(sha)) |
395 | |
396 | def commit_outstanding(self): |
397 | with Cursor(self.conn) as c: |
398 | @@ -1116,7 +1281,7 @@ |
399 | rows = list(c.execute("select * from %s where package=?" |
400 | % self.SUFFIX_TABLE, (self.package,))) |
401 | if len(rows) < 1: |
402 | - return "" |
403 | + return u"" |
404 | return rows[0][1] |
405 | |
406 | |
407 | |
408 | === modified file 'udd/scripts/mass_import.py' |
409 | --- udd/scripts/mass_import.py 2012-07-03 17:10:24 +0000 |
410 | +++ udd/scripts/mass_import.py 2012-07-03 17:10:24 +0000 |
411 | @@ -161,6 +161,9 @@ |
412 | # We mostly care about self.err which contains the traceback here |
413 | output = self.out + self.err |
414 | unicode_output = output.decode("utf-8", "replace") |
415 | + # Ecode to ascii with 'replace' as that's what the failure |
416 | + # signature code expects to get, even though we now deal |
417 | + # with the value as a unicode string throughout. |
418 | ascii_output = unicode_output.encode("ascii", "replace") |
419 | self.success = self.retcode == 0 |
420 | if self.success: |
421 | @@ -172,7 +175,8 @@ |
422 | "Importing %s failed:\n%s" % ( |
423 | self.package_name, ascii_output)) |
424 | self.status_db.finish_job( |
425 | - self.package_name, self.job_id, self.success, ascii_output) |
426 | + self.package_name, self.job_id, self.success, |
427 | + unicode(ascii_output)) |
428 | if not self.success: |
429 | reason = self.status_db.failure_reason(self.package_name) |
430 | self.failure_sig = self.status_db.failure_signature(reason) |
431 | @@ -334,7 +338,7 @@ |
432 | # We can't blame launchpad downtimes for all failures, we rely on |
433 | # the ones declared in RETRY_TABLE (created when |
434 | # 'requeue_package.py --auto' is used) |
435 | - if self.status_db.known_auto_retry(imp.failure_sig) is not None: |
436 | + if self.status_db.known_auto_retry(imp.failure_sig): |
437 | self.see_failure() |
438 | # We want to retry asap ('priority') |
439 | self.status_db.retry(package_name, priority=True) |
440 | |
441 | === modified file 'udd/tests/test_commit_database.py' |
442 | --- udd/tests/test_commit_database.py 2012-07-03 17:10:24 +0000 |
443 | +++ udd/tests/test_commit_database.py 2012-07-03 17:10:24 +0000 |
444 | @@ -8,7 +8,7 @@ |
445 | def setUp(self): |
446 | super(TestCommitDb, self).setUp() |
447 | db_conn, db_type = idb.get_memory_db_connection() |
448 | - self.db = icommon.CommitDatabase(db_conn, db_type, 'foo') |
449 | + self.db = icommon.CommitDatabase(db_conn, db_type, u'foo') |
450 | |
451 | def test_has_commit(self): |
452 | self.assertEquals(False, self.db.has_commit_started()) |
453 | |
454 | === modified file 'udd/tests/test_revid_database.py' |
455 | --- udd/tests/test_revid_database.py 2012-07-03 17:10:24 +0000 |
456 | +++ udd/tests/test_revid_database.py 2012-07-03 17:10:24 +0000 |
457 | @@ -28,7 +28,7 @@ |
458 | |
459 | def get_db(self): |
460 | db_conn, db_type = idb.get_memory_db_connection() |
461 | - return icommon.RevidDatabase(db_conn, db_type, "pkg") |
462 | + return icommon.RevidDatabase(db_conn, db_type, u"pkg") |
463 | |
464 | def check_rows(self, memory, working, saved): |
465 | self.assertEqual(memory, len(self.db._memory_rows())) |
466 | |
467 | === modified file 'udd/tests/test_status_database.py' |
468 | --- udd/tests/test_status_database.py 2012-07-03 17:10:24 +0000 |
469 | +++ udd/tests/test_status_database.py 2012-07-03 17:10:24 +0000 |
470 | @@ -112,13 +112,20 @@ |
471 | class TestRetry(TestStatusDb): |
472 | |
473 | def test_known_auto_retry_empty(self): |
474 | - self.assertEquals(None, self.db.known_auto_retry('whatever')) |
475 | + self.assertEquals(False, self.db.known_auto_retry('whatever')) |
476 | + |
477 | + def test_known_auto_retry_known(self): |
478 | + sig = u'whatever' |
479 | + self.db.add_known_auto_retry(sig) |
480 | + self.assertEquals(True, self.db.known_auto_retry(sig)) |
481 | + |
482 | + def test_known_auto_retry_unknown(self): |
483 | + self.db.add_known_auto_retry(u'something') |
484 | + self.assertEquals(False, self.db.known_auto_retry(u'something else')) |
485 | |
486 | # FIXME: Need more tests for the following but they are too hard to test for |
487 | # now -- vila 20111020 |
488 | |
489 | -# _known_auto_retry(self, c, sig): |
490 | -# known_auto_retry(self, sig): |
491 | # retry(self, package, force=False, priority=False, auto=False, |
492 | # _retry(self, c, package, signature, timestamp, priority=False): |
493 | # _attempt_retry(self, c, info): |
494 | @@ -127,7 +134,10 @@ |
495 | class TestSignatures(TestStatusDb): |
496 | |
497 | def assertSignature(self, expected, raw): |
498 | - self.assertEquals(expected, self.db.failure_signature(raw)) |
499 | + failure_signature = self.db.failure_signature(raw) |
500 | + self.assertEquals(expected, failure_signature) |
501 | + if failure_signature is not None: |
502 | + self.assertIsInstance(failure_signature, unicode) |
503 | |
504 | def test_running_signature(self): |
505 | # Running imports use a special failure signature |
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
On 7/2/2012 12:03 PM, James Westby wrote: /code.launchpad .net/~james- w/udd/storm- unicode- fixes/+ merge/113004
> James Westby has proposed merging
> lp:~james-w/udd/storm-unicode-fixes into lp:udd with
> lp:~james-w/udd/storm as a prerequisite.
>
> Requested reviews: James Westby (james-w)
>
> For more details, see:
> https:/
>
> Hi,
>
> This fixes the issues that Max found where we weren't using unicode
> for TEXT content, which meant the info was stored as blobs and
> problems happened.
>
> Thanks,
>
> James
>
This looks pretty good to me.
review: approve
John
=:->
-----BEGIN PGP SIGNATURE----- enigmail. mozdev. org/
xc+EACgkQJdeBCY SNAAPAjACdFDMf3 zAcN6xOdBjxMI53 0Rjk TZnjqS4YpYUpTua NV
Version: GnuPG v1.4.9 (Cygwin)
Comment: Using GnuPG with Mozilla - http://
iEYEARECAAYFAk/
FGUAn0GISTMu57D
=B8FQ
-----END PGP SIGNATURE-----