Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

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

@obeattie

See ticket for explanation.

obeattie added some commits
@obeattie obeattie 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
@obeattie obeattie Add test for #18925 63a760b
@obeattie

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

@obeattie obeattie closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Sep 7, 2012
  1. @obeattie

    Fixed #18925 -- Exception information should be localized

    obeattie authored
    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.
  2. @obeattie

    Add test for #18925

    obeattie authored
This page is out of date. Refresh to see the latest.
View
32 django/core/handlers/base.py
@@ -153,8 +153,13 @@ def get_response(self, request):
callback, param_dict = resolver.resolve404()
response = callback(request, **param_dict)
except:
- signals.got_request_exception.send(sender=self.__class__, request=request)
- response = self.handle_uncaught_exception(request, resolver, sys.exc_info())
+ exc_info = sys.exc_info() # See ticket #18925 for why this is localized
+ signals.got_request_exception.send(
+ sender=self.__class__, request=request,
+ exc_info=exc_info)
+ response = self.handle_uncaught_exception(request,
+ resolver, exc_info)
+ del exc_info
except exceptions.PermissionDenied:
logger.warning(
'Forbidden (Permission denied): %s', request.path,
@@ -166,17 +171,24 @@ def get_response(self, request):
callback, param_dict = resolver.resolve403()
response = callback(request, **param_dict)
except:
+ exc_info = sys.exc_info() # See ticket #18925 for why this is localized
signals.got_request_exception.send(
- sender=self.__class__, request=request)
+ sender=self.__class__, request=request,
+ exc_info=exc_info)
response = self.handle_uncaught_exception(request,
- resolver, sys.exc_info())
+ resolver, exc_info)
+ del exc_info
except SystemExit:
# Allow sys.exit() to actually exit. See tickets #1023 and #4701
raise
except: # Handle everything else, including SuspiciousOperation, etc.
# Get the exception info now, in case another exception is thrown later.
- signals.got_request_exception.send(sender=self.__class__, request=request)
- response = self.handle_uncaught_exception(request, resolver, sys.exc_info())
+ exc_info = sys.exc_info() # See ticket #18925 for why this is localized
+ signals.got_request_exception.send(
+ sender=self.__class__, request=request,
+ exc_info=exc_info)
+ response = self.handle_uncaught_exception(request, resolver, exc_info)
+ del exc_info
finally:
# Reset URLconf for this thread on the way out for complete
# isolation of request.urlconf
@@ -188,8 +200,12 @@ def get_response(self, request):
response = middleware_method(request, response)
response = self.apply_response_fixes(request, response)
except: # Any exception should be gathered and handled
- signals.got_request_exception.send(sender=self.__class__, request=request)
- response = self.handle_uncaught_exception(request, resolver, sys.exc_info())
+ exc_info = sys.exc_info() # See ticket #18925 for why this is localized
+ signals.got_request_exception.send(
+ sender=self.__class__, request=request,
+ exc_info=exc_info)
+ response = self.handle_uncaught_exception(request, resolver, exc_info)
+ del exc_info
return response
View
17 tests/regressiontests/views/tests/debug.py
@@ -6,12 +6,14 @@
from django.conf import settings
from django.core.files.uploadedfile import SimpleUploadedFile
+from django.core.signals import got_request_exception
from django.test import TestCase, RequestFactory
from django.test.utils import (setup_test_template_loader,
restore_template_loaders)
from django.core.urlresolvers import reverse
from django.views.debug import ExceptionReporter
from django.core import mail
+from django.core.handlers.wsgi import WSGIHandler
from .. import BrokenException, except_args
from ..views import (sensitive_view, non_sensitive_view, paranoid_view,
@@ -86,6 +88,21 @@ def test_template_loader_postmortem(self):
template_path = os.path.join('templates', 'i_dont_exist.html')
self.assertContains(response, template_path, status_code=500)
+ def test_exception_preservation(self):
+ "An exception is preserved and reported correctly, even if sys.exc_clear is called."
+ def on_request_exception(sender, request, **kwargs):
+ sys.exc_clear()
+
+ got_request_exception.connect(on_request_exception)
+ try:
+ with self.settings(DEBUG=True):
+ handler = WSGIHandler()
+ environ = RequestFactory().get(reverse('view_exception', args=(1,))).environ
+ response = handler(environ, lambda *a, **k: None)
+ self.assertContains(response, '<h2>Traceback ', status_code=500)
+ self.assertContains(response, 'raise BrokenException(', status_code=500)
+ finally:
+ got_request_exception.disconnect(on_request_exception)
class ExceptionReporterTests(TestCase):
rf = RequestFactory()
Something went wrong with that request. Please try again.