Merge lp:~benji/lazr.restful/bug-854695 into lp:lazr.restful

Proposed by Benji York
Status: Merged
Approved by: Benji York
Approved revision: 196
Merged at revision: 196
Proposed branch: lp:~benji/lazr.restful/bug-854695
Merge into: lp:lazr.restful
Diff against target: 110 lines (+33/-11)
2 files modified
src/lazr/restful/_resource.py (+3/-1)
src/lazr/restful/tests/test_error.py (+30/-10)
To merge this branch: bzr merge lp:~benji/lazr.restful/bug-854695
Reviewer Review Type Date Requested Status
Graham Binns (community) code Approve
Review via email: mp+76230@code.launchpad.net

Description of the change

Bug 854695 describes several OOPS scenarios caused by lazr.restful
requiring an __traceback__ attribute on exceptions when
LaunchpadTimeoutError doesn't have one.

The fix was to extract the traceback and other bits using sys.exc_info()
instead (as well as adding a test that demonstrated the problem).

To post a comment you must log in.
Revision history for this message
Graham Binns (gmb) wrote :

Hi Benji,

Nice branch, one really minor nitpick:

97 + self.fail('The resource should not have generated an '
98 + 'AttributeError. This is probably because something was '
99 + 'expecting the exception to have a __traceback__ attribute.')

should be formatted as:

            self.fail(
                'The resource should not have generated an '
                'AttributeError. This is probably because something was '
                'expecting the exception to have a __traceback__ attribute.')

(It also has a double-space in there before the "This" that it doesn't need).

review: Approve (code)
lp:~benji/lazr.restful/bug-854695 updated
197. By Benji York

change formatting as requested in review

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/lazr/restful/_resource.py'
2--- src/lazr/restful/_resource.py 2011-09-08 20:18:30 +0000
3+++ src/lazr/restful/_resource.py 2011-09-20 15:44:24 +0000
4@@ -35,6 +35,7 @@
5 import os
6 import simplejson
7 import simplejson.encoder
8+import sys
9 import time
10
11 # Import SHA in a way compatible with both Python 2.4 and Python 2.6.
12@@ -928,6 +929,7 @@
13 self.request.response.setStatus(405)
14 self.request.response.setHeader("Allow", allow_string)
15 except Exception, e:
16+ exception_info = sys.exc_info()
17 view = getMultiAdapter((e, self.request), name="index.html")
18 try:
19 ws_view = IWebServiceExceptionView(view)
20@@ -938,7 +940,7 @@
21 # re-raise the exception, let the publisher look up
22 # the view, and hopefully handle it better. Note the careful
23 # reraising that ensures the original trackabck is preserved.
24- raise e, None, e.__traceback__
25+ raise exception_info[0], exception_info[1], exception_info[2]
26
27 self.request.response.setStatus(ws_view.status)
28 if ws_view.status / 100 == 4:
29
30=== modified file 'src/lazr/restful/tests/test_error.py'
31--- src/lazr/restful/tests/test_error.py 2011-09-07 20:46:50 +0000
32+++ src/lazr/restful/tests/test_error.py 2011-09-20 15:44:24 +0000
33@@ -53,13 +53,8 @@
34 class ErrorsTestCase(unittest.TestCase):
35
36 def setUp(self):
37- try:
38- raise RuntimeError('my traceback')
39- except:
40- fake_traceback = sys.exc_info()[2]
41-
42 class MyException(Exception):
43- __traceback__ = fake_traceback
44+ pass
45
46 self.exception_class = MyException
47 super(ErrorsTestCase, self).setUp()
48@@ -133,10 +128,13 @@
49 self.assertRaises(RuntimeError, resource)
50 self.assertEquals(request.response.getStatus(), 200)
51
52- def _setup_non_web_service_exception(self):
53+ def _setup_non_web_service_exception(self, exception_class=None):
54+ if exception_class is None:
55+ exception_class = self.exception_class
56+
57 # Define a method that raises the exception.
58 def has_a_view():
59- raise self.exception_class()
60+ raise exception_class()
61
62 # Register a view for the exception that does not subclass
63 # WebServiceExceptionView and provides no information about
64@@ -145,7 +143,7 @@
65 def __init__(*args):
66 pass
67 getGlobalSiteManager().registerAdapter(
68- ViewHasNoStatus, (self.exception_class, IWebServiceLayer),
69+ ViewHasNoStatus, (exception_class, IWebServiceLayer),
70 Interface, name="index.html")
71
72 # The exception is re-raised, even though a view is registered
73@@ -175,7 +173,7 @@
74 resource()
75 except:
76 pass
77- self.assertTrue('my traceback' in traceback.format_exc())
78+ self.assertTrue('raise exception_class()' in traceback.format_exc())
79
80 def test_passing_bad_things_to_expose(self):
81 # The expose function only accepts instances of exceptions. It
82@@ -230,6 +228,28 @@
83 self.assertTrue(
84 "raise RuntimeError('something broke')" in traceback.format_exc())
85
86+ def test_reporting_original_exception_with_no_trackeback(self):
87+ # Sometimes an exception won't have an __traceback__ attribute. The
88+ # re-raising should still work (bug 854695).
89+ class TracebacklessException(Exception):
90+ pass
91+
92+ resource = self._setup_non_web_service_exception(
93+ exception_class=TracebacklessException)
94+ try:
95+ resource()
96+ except AttributeError:
97+ self.fail(
98+ 'The resource should not have generated an AttributeError. '
99+ 'This is probably because something was expecting the '
100+ 'exception to have a __traceback__ attribute.')
101+ except (MemoryError, KeyboardInterrupt, SystemExit):
102+ raise
103+ except:
104+ pass
105+
106+ self.assertTrue("TracebacklessException" in traceback.format_exc())
107+
108
109 class FunctionalLayer:
110 allow_teardown = False

Subscribers

People subscribed via source and target branches