Merge lp:~stevenk/launchpad/destroy-librarianformatter into lp:launchpad
- destroy-librarianformatter
- Merge into devel
Status: | Merged | ||||||||
---|---|---|---|---|---|---|---|---|---|
Approved by: | Steve Kowalik | ||||||||
Approved revision: | no longer in the source branch. | ||||||||
Merged at revision: | 16300 | ||||||||
Proposed branch: | lp:~stevenk/launchpad/destroy-librarianformatter | ||||||||
Merge into: | lp:launchpad | ||||||||
Diff against target: |
775 lines (+83/-430) 13 files modified
cronscripts/expire-bugtasks.py (+4/-13) lib/lp/hardwaredb/doc/hwdb-submission.txt (+3/-0) lib/lp/services/scripts/base.py (+4/-4) lib/lp/services/scripts/doc/launchpad-scripts.txt (+4/-0) lib/lp/services/scripts/logger.py (+10/-75) lib/lp/services/scripts/tests/librarianformatter.txt (+0/-142) lib/lp/services/scripts/tests/librarianformatter_noca.txt (+0/-66) lib/lp/services/scripts/tests/raiseexception.py (+0/-35) lib/lp/services/scripts/tests/test_librarianformatter.py (+0/-45) lib/lp/services/scripts/tests/test_librarianformatter_noca.py (+0/-22) lib/lp/soyuz/doc/gina-multiple-arch.txt (+3/-1) lib/lp/soyuz/doc/gina.txt (+54/-27) lib/lp/translations/doc/distroseries-translations-copy.txt (+1/-0) |
||||||||
To merge this branch: | bzr merge lp:~stevenk/launchpad/destroy-librarianformatter | ||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Stuart Bishop (community) | Approve | ||
Review via email: mp+135311@code.launchpad.net |
Commit message
Kill LibrarianFormatter, we now log the entire traceback and the OOPS id.
Description of the change
Kill LibrarianFormatter and its two doctests entirely.
The new plan is that OopsHandler will log the OOPS created (at level INFO) after it has done so.
With a script raising an uncaught Exception:
2012-11-21 03:36:40 ERROR Unhandled exception
2012-11-21 03:36:40 INFO OOPS-432e7c3e29181f82f8352b061429d202
And with a script logging something at ERROR:
2012-11-21 03:38:22 ERROR Causing OOPS
2012-11-21 03:38:22 INFO OOPS-ba8ec0f55e9b71f94ce43b65eee2d6c5
I have removed the catch-all exception from cronscripts/
Preview Diff
1 | === modified file 'cronscripts/expire-bugtasks.py' |
2 | --- cronscripts/expire-bugtasks.py 2012-01-01 03:14:54 +0000 |
3 | +++ cronscripts/expire-bugtasks.py 2012-11-22 00:17:20 +0000 |
4 | @@ -1,10 +1,8 @@ |
5 | #!/usr/bin/python -S |
6 | # |
7 | -# Copyright 2009 Canonical Ltd. This software is licensed under the |
8 | +# Copyright 2009-2012 Canonical Ltd. This software is licensed under the |
9 | # GNU Affero General Public License version 3 (see the file LICENSE). |
10 | |
11 | -# pylint: disable-msg=C0103,W0403 |
12 | - |
13 | """Expire all old, Incomplete bugs tasks that are unassigned in Malone. |
14 | |
15 | Only bug task for project that use Malone may be expired. The expiration |
16 | @@ -47,16 +45,9 @@ |
17 | # Avoid circular import. |
18 | from lp.registry.interfaces.distribution import IDistributionSet |
19 | target = getUtility(IDistributionSet).getByName('ubuntu') |
20 | - try: |
21 | - janitor = BugJanitor( |
22 | - log=self.logger, target=target, limit=self.options.limit) |
23 | - janitor.expireBugTasks(self.txn) |
24 | - except Exception: |
25 | - # We use a catchall here because we don't know (and don't care) |
26 | - # about the particular error--we'll just log it to as an Oops. |
27 | - self.logger.error( |
28 | - 'An error occured trying to expire bugtasks.', exc_info=1) |
29 | - raise |
30 | + janitor = BugJanitor( |
31 | + log=self.logger, target=target, limit=self.options.limit) |
32 | + janitor.expireBugTasks(self.txn) |
33 | |
34 | |
35 | if __name__ == '__main__': |
36 | |
37 | === modified file 'lib/lp/hardwaredb/doc/hwdb-submission.txt' |
38 | --- lib/lp/hardwaredb/doc/hwdb-submission.txt 2011-12-30 01:48:17 +0000 |
39 | +++ lib/lp/hardwaredb/doc/hwdb-submission.txt 2012-11-22 00:17:20 +0000 |
40 | @@ -273,6 +273,7 @@ |
41 | INFO Creating lockfile: /var/lock/launchpad-hwdbsubmissions.lock |
42 | ERROR Parsing submission test_submission_id_1: syntax error: |
43 | line 1, column 0 |
44 | + INFO OOPS-... |
45 | INFO Processed 0 valid and 1 invalid HWDB submissions |
46 | <BLANKLINE> |
47 | >>> print out |
48 | @@ -308,7 +309,9 @@ |
49 | >>> print err |
50 | INFO Creating lockfile: /var/lock/launchpad-hwdbsubmissions.lock |
51 | ERROR Parsing submission unique-id-1: syntax error: line 1, column 0 |
52 | + INFO OOPS-... |
53 | INFO Processed 1 valid and 1 invalid HWDB submissions |
54 | + <BLANKLINE> |
55 | >>> print out |
56 | <BLANKLINE> |
57 | |
58 | |
59 | === modified file 'lib/lp/services/scripts/base.py' |
60 | --- lib/lp/services/scripts/base.py 2012-06-29 08:40:05 +0000 |
61 | +++ lib/lp/services/scripts/base.py 2012-11-22 00:17:20 +0000 |
62 | @@ -1,4 +1,4 @@ |
63 | -# Copyright 2009-2011 Canonical Ltd. This software is licensed under the |
64 | +# Copyright 2009-2012 Canonical Ltd. This software is licensed under the |
65 | # GNU Affero General Public License version 3 (see the file LICENSE). |
66 | |
67 | __metaclass__ = type |
68 | @@ -384,8 +384,7 @@ |
69 | """ |
70 | self.lock_or_die(blocking=blocking) |
71 | try: |
72 | - self.run( |
73 | - use_web_security=use_web_security, isolation=isolation) |
74 | + self.run(use_web_security=use_web_security, isolation=isolation) |
75 | finally: |
76 | self.unlock(skip_delete=skip_delete) |
77 | |
78 | @@ -413,7 +412,8 @@ |
79 | # self.name is used instead of the name argument, since it may have |
80 | # have been overridden by command-line parameters or by |
81 | # overriding the name property. |
82 | - logging.getLogger().addHandler(OopsHandler(self.name)) |
83 | + oops_hdlr = OopsHandler(self.name, logger=self.logger) |
84 | + logging.getLogger().addHandler(oops_hdlr) |
85 | |
86 | def get_last_activity(self): |
87 | """Return the last activity, if any.""" |
88 | |
89 | === modified file 'lib/lp/services/scripts/doc/launchpad-scripts.txt' |
90 | --- lib/lp/services/scripts/doc/launchpad-scripts.txt 2011-12-29 05:29:36 +0000 |
91 | +++ lib/lp/services/scripts/doc/launchpad-scripts.txt 2012-11-22 00:17:20 +0000 |
92 | @@ -28,12 +28,16 @@ |
93 | >>> print p.communicate()[0] |
94 | INFO Creating lockfile: ... |
95 | WARNING This is a warning |
96 | + INFO None |
97 | INFO New OOPS detected |
98 | CRITICAL This is critical |
99 | + INFO None |
100 | INFO New OOPS detected |
101 | ERROR Unhandled exception |
102 | ... |
103 | NotImplementedError: Whoops |
104 | + INFO None |
105 | + <BLANKLINE> |
106 | >>> p.returncode |
107 | 1 |
108 | |
109 | |
110 | === modified file 'lib/lp/services/scripts/logger.py' |
111 | --- lib/lp/services/scripts/logger.py 2012-10-12 11:49:10 +0000 |
112 | +++ lib/lp/services/scripts/logger.py 2012-11-22 00:17:20 +0000 |
113 | @@ -1,8 +1,6 @@ |
114 | -# Copyright 2009-2011 Canonical Ltd. This software is licensed under the |
115 | +# Copyright 2009-2012 Canonical Ltd. This software is licensed under the |
116 | # GNU Affero General Public License version 3 (see the file LICENSE). |
117 | |
118 | -# pylint: disable-msg=W0702 |
119 | - |
120 | """Logging setup for scripts. |
121 | |
122 | Don't import from this module. Import it from lp.services.scripts. |
123 | @@ -10,7 +8,7 @@ |
124 | |
125 | __metaclass__ = type |
126 | |
127 | -# Don't import stuff from this module. Import it from canonical.scripts |
128 | +# Don't import stuff from this module. Import it from lp.services.scripts |
129 | __all__ = [ |
130 | 'DEBUG2', |
131 | 'DEBUG3', |
132 | @@ -30,9 +28,6 @@ |
133 | |
134 | |
135 | from contextlib import contextmanager |
136 | -from cStringIO import StringIO |
137 | -from datetime import timedelta |
138 | -import hashlib |
139 | import logging |
140 | from logging.handlers import WatchedFileHandler |
141 | from optparse import OptionParser |
142 | @@ -42,19 +37,10 @@ |
143 | import time |
144 | from traceback import format_exception_only |
145 | |
146 | -from zope.component import getUtility |
147 | from zope.exceptions.log import Formatter |
148 | |
149 | from lp.services.config import config |
150 | -from lp.services.librarian.interfaces.client import ( |
151 | - ILibrarianClient, |
152 | - UploadFailed, |
153 | - ) |
154 | from lp.services.log import loglevels |
155 | -from lp.services.utils import ( |
156 | - compress_hash, |
157 | - utc_now, |
158 | - ) |
159 | from lp.services.webapp.errorlog import ( |
160 | globalErrorUtility, |
161 | ScriptRequest, |
162 | @@ -75,12 +61,13 @@ |
163 | class OopsHandler(logging.Handler): |
164 | """Handler to log to the OOPS system.""" |
165 | |
166 | - def __init__(self, script_name, level=logging.WARN): |
167 | + def __init__(self, script_name, level=logging.WARN, logger=None): |
168 | logging.Handler.__init__(self, level) |
169 | # Context for OOPS reports. |
170 | self.request = ScriptRequest( |
171 | [('script_name', script_name), ('path', sys.argv[0])]) |
172 | self.setFormatter(LaunchpadFormatter()) |
173 | + self.logger = logger |
174 | |
175 | def emit(self, record): |
176 | """Emit a record as an OOPS.""" |
177 | @@ -91,6 +78,8 @@ |
178 | msg = record.getMessage() |
179 | with globalErrorUtility.oopsMessage(msg): |
180 | globalErrorUtility.raising(info, self.request) |
181 | + if self.logger: |
182 | + self.logger.info(self.request.oopsid) |
183 | except Exception: |
184 | self.handleError(record) |
185 | |
186 | @@ -110,59 +99,6 @@ |
187 | self.converter = time.gmtime |
188 | |
189 | |
190 | -class LibrarianFormatter(LaunchpadFormatter): |
191 | - """A logging.Formatter that stores tracebacks in the Librarian and emits |
192 | - a URL rather than emitting the traceback directly. |
193 | - |
194 | - The traceback will be emitted as a fallback if the Librarian cannot be |
195 | - contacted. |
196 | - |
197 | - XXX bug=641103 StuartBishop -- This class should die. Remove it and |
198 | - replace with LaunchpadFormatter, fixing the test fallout. |
199 | - """ |
200 | - |
201 | - def formatException(self, ei): |
202 | - """Format the exception and store it in the Librian. |
203 | - |
204 | - Returns the URL, or the formatted exception if the Librarian is |
205 | - not available. |
206 | - """ |
207 | - traceback = LaunchpadFormatter.formatException(self, ei) |
208 | - # Uncomment this line to stop exception storage in the librarian. |
209 | - # Useful for debugging tests. |
210 | - # return traceback |
211 | - try: |
212 | - librarian = getUtility(ILibrarianClient) |
213 | - except LookupError: |
214 | - return traceback |
215 | - |
216 | - exception_string = '' |
217 | - try: |
218 | - exception_string = str(ei[1]).encode('ascii') |
219 | - except: |
220 | - pass |
221 | - if not exception_string: |
222 | - exception_string = ei[0].__name__ |
223 | - |
224 | - expiry = utc_now() + timedelta(days=90) |
225 | - try: |
226 | - filename = compress_hash(hashlib.sha1(traceback)) + '.txt' |
227 | - url = librarian.remoteAddFile( |
228 | - filename, len(traceback), StringIO(traceback), |
229 | - 'text/plain;charset=%s' % sys.getdefaultencoding(), |
230 | - expires=expiry) |
231 | - return ' -> %s (%s)' % (url, exception_string) |
232 | - except UploadFailed: |
233 | - return traceback |
234 | - except Exception: |
235 | - # Exceptions raised by the Formatter get swallowed, but we want |
236 | - # to know about them. Since we are already spitting out exception |
237 | - # information, we can stuff our own problems in there too. |
238 | - return '%s\n\nException raised in formatter:\n%s\n' % ( |
239 | - traceback, |
240 | - LaunchpadFormatter.formatException(self, sys.exc_info())) |
241 | - |
242 | - |
243 | class LogLevelNudger: |
244 | """Callable to adjust the global log level. |
245 | |
246 | @@ -393,9 +329,8 @@ |
247 | root_logger.removeHandler(hdlr) |
248 | |
249 | |
250 | -def _logger( |
251 | - level, out_stream, name=None, |
252 | - log_file=None, log_file_level=logging.DEBUG, milliseconds=False): |
253 | +def _logger(level, out_stream, name=None, log_file=None, |
254 | + log_file_level=logging.DEBUG, milliseconds=False): |
255 | """Create the actual logger instance, logging at the given level |
256 | |
257 | if name is None, it will get args[0] without the extension (e.g. gina). |
258 | @@ -425,10 +360,10 @@ |
259 | hdlr.setLevel(level) |
260 | if milliseconds: |
261 | # Python default datefmt includes milliseconds. |
262 | - formatter = LibrarianFormatter(datefmt=None) |
263 | + formatter = LaunchpadFormatter(datefmt=None) |
264 | else: |
265 | # Launchpad default datefmt does not include milliseconds. |
266 | - formatter = LibrarianFormatter() |
267 | + formatter = LaunchpadFormatter() |
268 | hdlr.setFormatter(formatter) |
269 | root_logger.addHandler(hdlr) |
270 | |
271 | |
272 | === removed file 'lib/lp/services/scripts/tests/librarianformatter.txt' |
273 | --- lib/lp/services/scripts/tests/librarianformatter.txt 2011-12-28 17:03:06 +0000 |
274 | +++ lib/lp/services/scripts/tests/librarianformatter.txt 1970-01-01 00:00:00 +0000 |
275 | @@ -1,142 +0,0 @@ |
276 | - |
277 | -The LibrarianFormatter works just like a normal logger.Formatter if there |
278 | -is no Librarian available or active |
279 | - |
280 | ->>> from lp.services.scripts.logger import LibrarianFormatter |
281 | - |
282 | -This is a helper to quickly construct a Logger instance with a particular |
283 | -formatter. Each call generates a unique Logger. |
284 | - |
285 | ->>> _count = 0 |
286 | ->>> def make_logger(formatter_class): |
287 | -... global _count |
288 | -... _count += 1 |
289 | -... logger = logging.getLogger('log%d' % _count) |
290 | -... output = StringIO() |
291 | -... handler = logging.StreamHandler(output) |
292 | -... # Note - no timestamp so we can make valid comparisons! |
293 | -... formatter = formatter_class(fmt='%(levelname)s %(message)s') |
294 | -... handler.setFormatter(formatter) |
295 | -... logger.addHandler(handler) |
296 | -... logger.setLevel(logging.DEBUG) |
297 | -... logger.propagate = False |
298 | -... return logger, output |
299 | - |
300 | -Create an exception we can reuse |
301 | - |
302 | ->>> try: |
303 | -... raise RuntimeError('An Exception') |
304 | -... except RuntimeError: |
305 | -... exception = sys.exc_info() |
306 | - |
307 | -Because no Librarian is running, output from the two loggers should be |
308 | -identical. Note the overhead involved in attempting to contact the |
309 | -Librarian is not significant. |
310 | - |
311 | ->>> from lp.testing.layers import LibrarianLayer |
312 | ->>> LibrarianLayer.hide() |
313 | - |
314 | ->>> from lp.services.utils import utc_now |
315 | - |
316 | ->>> normal_log, normal_out= make_logger(logging.Formatter) |
317 | ->>> librarian_log, librarian_out = make_logger(LibrarianFormatter) |
318 | ->>> import time |
319 | ->>> start_time = time.time() |
320 | ->>> for log in (normal_log, librarian_log): |
321 | -... log.error('Blah!', exc_info=exception) |
322 | ->>> end_time = time.time() |
323 | ->>> (end_time - start_time) < 5.0 |
324 | -True |
325 | - |
326 | ->>> print normal_out.getvalue() |
327 | -ERROR Blah! |
328 | -Traceback (most recent call last): |
329 | -... |
330 | -RuntimeError: An Exception |
331 | -<BLANKLINE> |
332 | - |
333 | ->>> print librarian_out.getvalue() |
334 | -ERROR Blah! |
335 | -Traceback (most recent call last): |
336 | -... |
337 | -RuntimeError: An Exception |
338 | -<BLANKLINE> |
339 | - |
340 | ->>> normal_out.getvalue() == librarian_out.getvalue() |
341 | -True |
342 | - |
343 | -Now if we fire up the Librarian, the LibrarianFormatter should start |
344 | -storing tracebacks there instead, reducing the verbosity of spam from |
345 | -cronjobs |
346 | - |
347 | ->>> LibrarianLayer.reveal() |
348 | ->>> librarian_log, librarian_out = make_logger(LibrarianFormatter) |
349 | ->>> librarian_log.error('Oops', exc_info=exception) |
350 | ->>> print librarian_out.getvalue() |
351 | -ERROR Oops |
352 | - -> http://.../....txt (An Exception) |
353 | ->>> url = librarian_out.getvalue().splitlines()[-1].split()[1:2][0] |
354 | ->>> print url |
355 | -http://.../....txt |
356 | ->>> print urlopen(url).read() |
357 | -Traceback (most recent call last): |
358 | - ... |
359 | -RuntimeError: An Exception |
360 | - |
361 | -As the librarian commits to the test database in a subprocess, we need |
362 | -to force the DatabaseLayer to fully tear down and restore the database |
363 | -after this test. |
364 | - |
365 | ->>> from lp.testing.layers import DatabaseLayer |
366 | ->>> DatabaseLayer.force_dirty_database() |
367 | - |
368 | -We keep exceptions from expiring for about 3 months. |
369 | - |
370 | ->>> match = re.search('/(\d+)/', url) |
371 | ->>> alias_id = match.group(1) |
372 | ->>> from lp.services.librarian.model import LibraryFileAlias |
373 | ->>> transaction.abort() # To see db changes made by the librarian |
374 | ->>> alias = LibraryFileAlias.get(alias_id) |
375 | ->>> alias.expires > utc_now() + timedelta(days=89) |
376 | -True |
377 | - |
378 | -Note that we also need to remain informative with dud exceptions, such as |
379 | -those with a non-ASCII string representation or no string representation |
380 | -at all |
381 | - |
382 | ->>> librarian_log, librarian_out = make_logger(LibrarianFormatter) |
383 | ->>> class Dud(Exception): |
384 | -... pass |
385 | ->>> try: |
386 | -... raise Dud() |
387 | -... except Dud: |
388 | -... librarian_log.exception('Dud1') |
389 | ->>> try: |
390 | -... raise Dud(u'\N{BIOHAZARD SIGN}'.encode('utf8')) |
391 | -... except Dud: |
392 | -... librarian_log.exception('Dud2') |
393 | ->>> print librarian_out.getvalue() |
394 | -ERROR Dud1 |
395 | - -> http://.../....txt (Dud) |
396 | -ERROR Dud2 |
397 | - -> http://.../....txt (Dud) |
398 | -<BLANKLINE> |
399 | - |
400 | -The end result of this is to not have scripts display exceptions to |
401 | -stderr, instead reporting URLs and greatly reducing the verbosity. |
402 | - |
403 | ->>> script = os.path.join(this_directory, 'raiseexception.py') |
404 | ->>> import subprocess |
405 | ->>> p = subprocess.Popen([sys.executable, script], stdin=subprocess.PIPE, |
406 | -... stdout=subprocess.PIPE, stderr=subprocess.STDOUT) |
407 | ->>> (out, err) = p.communicate() |
408 | ->>> p.returncode |
409 | -0 |
410 | ->>> print out |
411 | -Script Output |
412 | -ERROR Oops |
413 | - -> http://.../....txt (Aargh) |
414 | -ERROR Root oops |
415 | - -> http://.../....txt (Aargh) |
416 | - |
417 | - |
418 | |
419 | === removed file 'lib/lp/services/scripts/tests/librarianformatter_noca.txt' |
420 | --- lib/lp/services/scripts/tests/librarianformatter_noca.txt 2011-12-21 19:44:48 +0000 |
421 | +++ lib/lp/services/scripts/tests/librarianformatter_noca.txt 1970-01-01 00:00:00 +0000 |
422 | @@ -1,66 +0,0 @@ |
423 | ->>> import logging |
424 | ->>> import sys |
425 | ->>> from cStringIO import StringIO |
426 | - |
427 | -The LibrarianFormatter works just like a normal logger.Formatter if there |
428 | -is no Librarian available or active |
429 | - |
430 | ->>> from lp.services.scripts.logger import LibrarianFormatter |
431 | - |
432 | -This is a helper to quickly construct a Logger instance with a particular |
433 | -formatter. Each call generates a unique Logger. |
434 | - |
435 | ->>> _count = 0 |
436 | ->>> def make_logger(formatter_class): |
437 | -... global _count |
438 | -... _count += 1 |
439 | -... logger = logging.getLogger('log%d' % _count) |
440 | -... output = StringIO() |
441 | -... handler = logging.StreamHandler(output) |
442 | -... # Note - no timestamp so we can make valid comparisons! |
443 | -... formatter = formatter_class(fmt='%(levelname)s %(message)s') |
444 | -... handler.setFormatter(formatter) |
445 | -... logger.addHandler(handler) |
446 | -... logger.setLevel(logging.DEBUG) |
447 | -... return logger, output |
448 | - |
449 | -Now setup two identical Logger instances except for their formatter |
450 | - |
451 | ->>> normal_log, normal_out = make_logger(logging.Formatter) |
452 | ->>> librarian_log, librarian_out = make_logger(LibrarianFormatter) |
453 | - |
454 | -Output from the standard formatter should match the output from |
455 | -LibrianFormatter at this stage, as we have no component architecture |
456 | -loaded and no Librarian running. |
457 | - |
458 | -First, create an exception we can reuse |
459 | - |
460 | ->>> try: |
461 | -... raise RuntimeError('An Exception') |
462 | -... except RuntimeError: |
463 | -... exception = sys.exc_info() |
464 | -... for log in (normal_log, librarian_log): |
465 | -... log.info('Some crap') |
466 | -... log.error('Oops!', exc_info=exception) |
467 | -... log.error('Error %d occurred', 42) |
468 | ->>> normal_out.getvalue() == librarian_out.getvalue() |
469 | -True |
470 | ->>> print normal_out.getvalue() |
471 | -INFO Some crap |
472 | -ERROR Oops! |
473 | -Traceback (most recent call last): |
474 | - File "<doctest librarianformatter_noca.txt[...]>", line 2, in <module> |
475 | - raise RuntimeError('An Exception') |
476 | -RuntimeError: An Exception |
477 | -ERROR Error 42 occurred |
478 | -<BLANKLINE> |
479 | ->>> print librarian_out.getvalue() |
480 | -INFO Some crap |
481 | -ERROR Oops! |
482 | -Traceback (most recent call last): |
483 | - File "<doctest librarianformatter_noca.txt[...]>", line 2, in <module> |
484 | - raise RuntimeError('An Exception') |
485 | -RuntimeError: An Exception |
486 | -ERROR Error 42 occurred |
487 | -<BLANKLINE> |
488 | - |
489 | |
490 | === removed file 'lib/lp/services/scripts/tests/raiseexception.py' |
491 | --- lib/lp/services/scripts/tests/raiseexception.py 2011-12-21 20:23:01 +0000 |
492 | +++ lib/lp/services/scripts/tests/raiseexception.py 1970-01-01 00:00:00 +0000 |
493 | @@ -1,35 +0,0 @@ |
494 | -# Copyright 2009 Canonical Ltd. This software is licensed under the |
495 | -# GNU Affero General Public License version 3 (see the file LICENSE). |
496 | - |
497 | -"""This script is called from librarianformatter.txt to |
498 | -demonstrate a script using the LibrarianFormatter |
499 | -""" |
500 | - |
501 | -__metaclass__ = type |
502 | - |
503 | -import logging |
504 | -from optparse import OptionParser |
505 | -import sys |
506 | - |
507 | -from lp.services.scripts import ( |
508 | - execute_zcml_for_scripts, |
509 | - logger, |
510 | - logger_options, |
511 | - ) |
512 | - |
513 | - |
514 | -if __name__ == '__main__': |
515 | - parser = OptionParser() |
516 | - logger_options(parser) |
517 | - (options, args) = parser.parse_args() |
518 | - log = logger(options) |
519 | - # Test the root logger too, because some code is using it |
520 | - root_log = logging.getLogger() |
521 | - execute_zcml_for_scripts() |
522 | - print >> sys.stderr, 'Script Output' |
523 | - try: |
524 | - raise RuntimeError('Aargh') |
525 | - except RuntimeError: |
526 | - log.exception('Oops') |
527 | - root_log.exception('Root oops') |
528 | - |
529 | |
530 | === removed file 'lib/lp/services/scripts/tests/test_librarianformatter.py' |
531 | --- lib/lp/services/scripts/tests/test_librarianformatter.py 2011-12-28 17:03:06 +0000 |
532 | +++ lib/lp/services/scripts/tests/test_librarianformatter.py 1970-01-01 00:00:00 +0000 |
533 | @@ -1,45 +0,0 @@ |
534 | -# Copyright 2009 Canonical Ltd. This software is licensed under the |
535 | -# GNU Affero General Public License version 3 (see the file LICENSE). |
536 | - |
537 | -"""Module docstring goes here.""" |
538 | - |
539 | -__metaclass__ = type |
540 | - |
541 | -from datetime import ( |
542 | - datetime, |
543 | - timedelta, |
544 | - ) |
545 | -import logging |
546 | -import os.path |
547 | -import re |
548 | -from StringIO import StringIO |
549 | -import sys |
550 | -import time |
551 | -from urllib2 import urlopen |
552 | - |
553 | -from pytz import utc |
554 | -import transaction |
555 | - |
556 | -from lp.testing import ( |
557 | - ANONYMOUS, |
558 | - login, |
559 | - logout, |
560 | - ) |
561 | -from lp.testing.layers import LaunchpadFunctionalLayer |
562 | -from lp.testing.systemdocs import LayeredDocFileSuite |
563 | - |
564 | - |
565 | -this_directory = os.path.dirname(__file__) |
566 | - |
567 | -def setUp(test): |
568 | - # Suck this modules environment into the test environment |
569 | - test.globs.update(globals()) |
570 | - login(ANONYMOUS) |
571 | - |
572 | -def tearDown(test): |
573 | - logout() |
574 | - |
575 | -def test_suite(): |
576 | - return LayeredDocFileSuite( |
577 | - 'librarianformatter.txt', setUp=setUp, tearDown=tearDown, |
578 | - layer=LaunchpadFunctionalLayer) |
579 | |
580 | === removed file 'lib/lp/services/scripts/tests/test_librarianformatter_noca.py' |
581 | --- lib/lp/services/scripts/tests/test_librarianformatter_noca.py 2011-12-28 17:03:06 +0000 |
582 | +++ lib/lp/services/scripts/tests/test_librarianformatter_noca.py 1970-01-01 00:00:00 +0000 |
583 | @@ -1,22 +0,0 @@ |
584 | -# Copyright 2009 Canonical Ltd. This software is licensed under the |
585 | -# GNU Affero General Public License version 3 (see the file LICENSE). |
586 | - |
587 | -"""Python harness for librarianformatter_noca.txt.""" |
588 | - |
589 | -__metaclass__ = type |
590 | - |
591 | -from lp.testing import reset_logging |
592 | -from lp.testing.systemdocs import LayeredDocFileSuite |
593 | - |
594 | - |
595 | -def setUp(test): |
596 | - # Suck this modules environment into the test environment |
597 | - reset_logging() |
598 | - |
599 | -def tearDown(test): |
600 | - reset_logging() |
601 | - |
602 | -def test_suite(): |
603 | - return LayeredDocFileSuite( |
604 | - 'librarianformatter_noca.txt', |
605 | - setUp=setUp, tearDown=tearDown, stdout_logging=False) |
606 | |
607 | === modified file 'lib/lp/soyuz/doc/gina-multiple-arch.txt' |
608 | --- lib/lp/soyuz/doc/gina-multiple-arch.txt 2012-07-06 19:53:17 +0000 |
609 | +++ lib/lp/soyuz/doc/gina-multiple-arch.txt 2012-11-22 00:17:20 +0000 |
610 | @@ -93,7 +93,9 @@ |
611 | >>> print proc.stderr.read() |
612 | WARNING ... |
613 | ERROR Database setup required for run on powerpc |
614 | - -> http://... (Unable to find a processor...dapper/powerpc) |
615 | + Traceback (most recent call last): |
616 | + ... |
617 | + DataSetupError: Unable to find a processor from the processor family... |
618 | <BLANKLINE> |
619 | >>> proc.wait() |
620 | 1 |
621 | |
622 | === modified file 'lib/lp/soyuz/doc/gina.txt' |
623 | --- lib/lp/soyuz/doc/gina.txt 2012-07-06 19:53:17 +0000 |
624 | +++ lib/lp/soyuz/doc/gina.txt 2012-11-22 00:17:20 +0000 |
625 | @@ -140,41 +140,54 @@ |
626 | |
627 | >>> print proc.stderr.read() |
628 | ERROR Error processing package files for clearlooks |
629 | - -> http://...Error 2 unpacking source) |
630 | + ... |
631 | + ExecutionError: Error 2 unpacking source |
632 | WARNING Invalid format in db1-compat, assumed '1.0' |
633 | WARNING Source package ed lacks section, assumed 'misc' |
634 | ERROR Unable to create SourcePackageData for mkvmlinuz |
635 | - -> http://...version: None) |
636 | + ... |
637 | + InvalidVersionError: mkvmlinuz has an invalid version: None |
638 | WARNING Invalid urgency in python-pam, None, assumed 'low' |
639 | ERROR Error processing package files for util-linux |
640 | - -> http://...dsc not in archive) |
641 | + ... |
642 | + PoolFileNotFound: File util-linux_2.12p-2ubuntu2.2.dsc not in archive |
643 | ERROR Error processing package files for bsdutils |
644 | - -> http://...deb not found) |
645 | + ... |
646 | + PoolFileNotFound: .../bsdutils_2.12p-2ubuntu2_i386.deb not found |
647 | WARNING Binary package ed lacks valid priority, assumed 'extra' |
648 | ERROR Unable to create BinaryPackageData for mount |
649 | - -> http://...invalid version...) |
650 | + ... |
651 | + InvalidVersionError: mount has an invalid version: -ewePP2.12p-2ubuntu2 |
652 | WARNING Binary package python-pam lacks a section, assumed 'misc' |
653 | ERROR Error processing package files for python2.4-pam |
654 | - -> http://...deb not found) |
655 | + ... |
656 | + PoolFileNotFound: .../python2.4-pam_0.4.2-10.1ubuntu3_i386.deb not found |
657 | ERROR Error processing package files for python2.4-sqlite |
658 | - -> http://...deb not found) |
659 | + ... |
660 | + PoolFileNotFound: .../python2.4-sqlite_1.0.1-1ubuntu1_i386.deb not found |
661 | WARNING No source package rioutil (1.4.4-1.0.1) listed for rioutil (1.4.4-1.0.1), scrubbing archive... |
662 | WARNING Nope, couldn't find it. Could it be a bin-only-NMU? Checking... |
663 | ERROR Error processing package files for util-linux |
664 | - -> http://...deb not found) |
665 | + ... |
666 | + PoolFileNotFound: .../util-linux_2.12p-2ubuntu2_i386.deb not found |
667 | ERROR Unable to create BinaryPackageData for util-linux-locales |
668 | - -> http://...installed_size']) |
669 | + ... |
670 | + MissingRequiredArguments: ['installed_size'] |
671 | ERROR Invalid Sources stanza in /tmp/tmp... |
672 | - -> http://...bogus\n') |
673 | + ... |
674 | + KeyError: 'Bogus, bogus, bogus\n' |
675 | WARNING No changelog file found for mkvmlinuz in mkvmlinuz-14ubuntu1 |
676 | WARNING No copyright file found for mkvmlinuz in mkvmlinuz-14ubuntu1 |
677 | WARNING Invalid urgency in mkvmlinuz, None, assumed 'low' |
678 | ERROR Error processing package files for python-sqlite |
679 | - -> http://...dsc not in archive) |
680 | + ... |
681 | + PoolFileNotFound: File python-sqlite_1.0.1-2ubuntu1.dsc not in archive |
682 | ERROR Error processing package files for util-linux |
683 | - -> http://...dsc not in archive) |
684 | + ... |
685 | + PoolFileNotFound: File util-linux_2.12p-6ubuntu5.dsc not in archive |
686 | ERROR Error processing package files for python-sqlite |
687 | - -> http://...deb not found) |
688 | + ... |
689 | + PoolFileNotFound: .../python-sqlite_1.0.1-2ubuntu1_all.deb not found |
690 | WARNING No source package ubuntu-meta (0.80) listed for ubuntu-base (0.80), scrubbing archive... |
691 | <BLANKLINE> |
692 | |
693 | @@ -515,34 +528,47 @@ |
694 | >>> proc = subprocess.Popen(gina_proc, stderr=subprocess.PIPE) |
695 | >>> print proc.stderr.read() |
696 | ERROR Error processing package files for clearlooks |
697 | - -> http://...Error 2 unpacking source) |
698 | + ... |
699 | + ExecutionError: Error 2 unpacking source |
700 | WARNING Source package ed lacks section, assumed 'misc' |
701 | ERROR Unable to create SourcePackageData for mkvmlinuz |
702 | - -> http://...version: None) |
703 | + ... |
704 | + InvalidVersionError: mkvmlinuz has an invalid version: None |
705 | ERROR Error processing package files for util-linux |
706 | - -> http://...dsc not in archive) |
707 | + ... |
708 | + PoolFileNotFound: File util-linux_2.12p-2ubuntu2.2.dsc not in archive |
709 | ERROR Error processing package files for bsdutils |
710 | - -> http://...deb not found) |
711 | + ... |
712 | + PoolFileNotFound: .../bsdutils_2.12p-2ubuntu2_i386.deb not found |
713 | WARNING Binary package ed lacks valid priority, assumed 'extra' |
714 | ERROR Unable to create BinaryPackageData for mount |
715 | - -> http://...invalid version...) |
716 | + ... |
717 | + InvalidVersionError: mount has an invalid version: -ewePP2.12p-2ubuntu2 |
718 | WARNING Binary package python-pam lacks a section, assumed 'misc' |
719 | ERROR Error processing package files for python2.4-pam |
720 | - -> http://...deb not found) |
721 | + ... |
722 | + PoolFileNotFound: .../python2.4-pam_0.4.2-10.1ubuntu3_i386.deb not found |
723 | ERROR Error processing package files for python2.4-sqlite |
724 | - -> http://...deb not found) |
725 | + ... |
726 | + PoolFileNotFound: .../python2.4-sqlite_1.0.1-1ubuntu1_i386.deb not found |
727 | ERROR Error processing package files for util-linux |
728 | - -> http://...deb not found) |
729 | + ... |
730 | + PoolFileNotFound: .../util-linux_2.12p-2ubuntu2_i386.deb not found |
731 | ERROR Unable to create BinaryPackageData for util-linux-locales |
732 | - -> http://...installed_size']) |
733 | + ... |
734 | + MissingRequiredArguments: ['installed_size'] |
735 | ERROR Invalid Sources stanza in /tmp/tmp... |
736 | - -> http://...bogus\n') |
737 | + ... |
738 | + KeyError: 'Bogus, bogus, bogus\n' |
739 | ERROR Error processing package files for python-sqlite |
740 | - -> http://...dsc not in archive) |
741 | + ... |
742 | + PoolFileNotFound: File python-sqlite_1.0.1-2ubuntu1.dsc not in archive |
743 | ERROR Error processing package files for util-linux |
744 | - -> http://...dsc not in archive) |
745 | + ... |
746 | + PoolFileNotFound: File util-linux_2.12p-6ubuntu5.dsc not in archive |
747 | ERROR Error processing package files for python-sqlite |
748 | - -> http://...deb not found) |
749 | + ... |
750 | + PoolFileNotFound: .../python-sqlite_1.0.1-2ubuntu1_all.deb not found |
751 | <BLANKLINE> |
752 | >>> proc.wait() |
753 | 0 |
754 | @@ -787,7 +813,8 @@ |
755 | >>> proc = subprocess.Popen(gina_proc, stderr=subprocess.PIPE) |
756 | >>> print proc.stderr.read() |
757 | ERROR Failed to analyze archive for bogoland |
758 | - -> http://...) |
759 | + ... |
760 | + MangledArchiveError: No archive directory for bogoland/main |
761 | <BLANKLINE> |
762 | >>> proc.wait() |
763 | 1 |
764 | |
765 | === modified file 'lib/lp/translations/doc/distroseries-translations-copy.txt' |
766 | --- lib/lp/translations/doc/distroseries-translations-copy.txt 2012-05-05 06:08:43 +0000 |
767 | +++ lib/lp/translations/doc/distroseries-translations-copy.txt 2012-11-22 00:17:20 +0000 |
768 | @@ -201,6 +201,7 @@ |
769 | defer_translation_imports flags for distribution foobuntu, series |
770 | darty; or use the --force option to make it happen |
771 | automatically. |
772 | + INFO OOPS-... |
773 | <BLANKLINE> |
774 | |
775 | >>> transaction.abort() |
Looks good.