Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Fix for #18925 - Exception information should be localized and passed to got_request_exception signal handlers #332

Closed
wants to merge 2 commits into from

1 participant

Oliver Beattie
Oliver Beattie

See ticket for explanation.

added some commits September 07, 2012
Oliver Beattie Fixed #18925 -- Exception information should be localized
and passed to got_request_exception signal handlers

sys.exc_info() is now stored in a local variable before processing
got_request_exception signals, and passed to the signals as an argument,
to avoid issues seen with traceback information being lost if a greenlet
switch occurs during exception processing, when Django is running under
Gevent.
3be37da
Oliver Beattie Add test for #18925 63a760b
Oliver Beattie

In response to my posts on the mailing list, this has actually been fixed in gevent, so closing this.

Oliver Beattie obeattie closed this November 14, 2012
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 2 unique commits by 1 author.

Sep 07, 2012
Oliver Beattie Fixed #18925 -- Exception information should be localized
and passed to got_request_exception signal handlers

sys.exc_info() is now stored in a local variable before processing
got_request_exception signals, and passed to the signals as an argument,
to avoid issues seen with traceback information being lost if a greenlet
switch occurs during exception processing, when Django is running under
Gevent.
3be37da
Oliver Beattie Add test for #18925 63a760b
This page is out of date. Refresh to see the latest.
32  django/core/handlers/base.py
@@ -153,8 +153,13 @@ def get_response(self, request):
153 153
                         callback, param_dict = resolver.resolve404()
154 154
                         response = callback(request, **param_dict)
155 155
                     except:
156  
-                        signals.got_request_exception.send(sender=self.__class__, request=request)
157  
-                        response = self.handle_uncaught_exception(request, resolver, sys.exc_info())
  156
+                        exc_info = sys.exc_info() # See ticket #18925 for why this is localized
  157
+                        signals.got_request_exception.send(
  158
+                                sender=self.__class__, request=request,
  159
+                                exc_info=exc_info)
  160
+                        response = self.handle_uncaught_exception(request,
  161
+                                resolver, exc_info)
  162
+                        del exc_info
158 163
             except exceptions.PermissionDenied:
159 164
                 logger.warning(
160 165
                     'Forbidden (Permission denied): %s', request.path,
@@ -166,17 +171,24 @@ def get_response(self, request):
166 171
                     callback, param_dict = resolver.resolve403()
167 172
                     response = callback(request, **param_dict)
168 173
                 except:
  174
+                    exc_info = sys.exc_info() # See ticket #18925 for why this is localized
169 175
                     signals.got_request_exception.send(
170  
-                            sender=self.__class__, request=request)
  176
+                            sender=self.__class__, request=request,
  177
+                            exc_info=exc_info)
171 178
                     response = self.handle_uncaught_exception(request,
172  
-                            resolver, sys.exc_info())
  179
+                            resolver, exc_info)
  180
+                    del exc_info
173 181
             except SystemExit:
174 182
                 # Allow sys.exit() to actually exit. See tickets #1023 and #4701
175 183
                 raise
176 184
             except: # Handle everything else, including SuspiciousOperation, etc.
177 185
                 # Get the exception info now, in case another exception is thrown later.
178  
-                signals.got_request_exception.send(sender=self.__class__, request=request)
179  
-                response = self.handle_uncaught_exception(request, resolver, sys.exc_info())
  186
+                exc_info = sys.exc_info() # See ticket #18925 for why this is localized
  187
+                signals.got_request_exception.send(
  188
+                    sender=self.__class__, request=request,
  189
+                    exc_info=exc_info)
  190
+                response = self.handle_uncaught_exception(request, resolver, exc_info)
  191
+                del exc_info
180 192
         finally:
181 193
             # Reset URLconf for this thread on the way out for complete
182 194
             # isolation of request.urlconf
@@ -188,8 +200,12 @@ def get_response(self, request):
188 200
                 response = middleware_method(request, response)
189 201
             response = self.apply_response_fixes(request, response)
190 202
         except: # Any exception should be gathered and handled
191  
-            signals.got_request_exception.send(sender=self.__class__, request=request)
192  
-            response = self.handle_uncaught_exception(request, resolver, sys.exc_info())
  203
+            exc_info = sys.exc_info() # See ticket #18925 for why this is localized
  204
+            signals.got_request_exception.send(
  205
+                sender=self.__class__, request=request,
  206
+                exc_info=exc_info)
  207
+            response = self.handle_uncaught_exception(request, resolver, exc_info)
  208
+            del exc_info
193 209
 
194 210
         return response
195 211
 
17  tests/regressiontests/views/tests/debug.py
@@ -6,12 +6,14 @@
6 6
 
7 7
 from django.conf import settings
8 8
 from django.core.files.uploadedfile import SimpleUploadedFile
  9
+from django.core.signals import got_request_exception
9 10
 from django.test import TestCase, RequestFactory
10 11
 from django.test.utils import (setup_test_template_loader,
11 12
                                restore_template_loaders)
12 13
 from django.core.urlresolvers import reverse
13 14
 from django.views.debug import ExceptionReporter
14 15
 from django.core import mail
  16
+from django.core.handlers.wsgi import WSGIHandler
15 17
 
16 18
 from .. import BrokenException, except_args
17 19
 from ..views import (sensitive_view, non_sensitive_view, paranoid_view,
@@ -86,6 +88,21 @@ def test_template_loader_postmortem(self):
86 88
         template_path = os.path.join('templates', 'i_dont_exist.html')
87 89
         self.assertContains(response, template_path, status_code=500)
88 90
 
  91
+    def test_exception_preservation(self):
  92
+        "An exception is preserved and reported correctly, even if sys.exc_clear is called."
  93
+        def on_request_exception(sender, request, **kwargs):
  94
+            sys.exc_clear()
  95
+
  96
+        got_request_exception.connect(on_request_exception)
  97
+        try:
  98
+            with self.settings(DEBUG=True):
  99
+                handler = WSGIHandler()
  100
+                environ = RequestFactory().get(reverse('view_exception', args=(1,))).environ
  101
+                response = handler(environ, lambda *a, **k: None)
  102
+                self.assertContains(response, '<h2>Traceback ', status_code=500)
  103
+                self.assertContains(response, 'raise BrokenException(', status_code=500)
  104
+        finally:
  105
+            got_request_exception.disconnect(on_request_exception)
89 106
 
90 107
 class ExceptionReporterTests(TestCase):
91 108
     rf = RequestFactory()
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.