version from testing.py, which works better with 2to3.
Closes #508
import sys
import threading
+from tornado.util import raise_exc_info
class _State(threading.local):
def __init__(self):
# Don't rely on sys.exc_info() still containing
# the right information. Another exception may
# have been raised and caught by an exit method
- raise exc[0], exc[1], exc[2]
+ raise_exc_info(exc)
'tornado.test.template_test',
'tornado.test.testing_test',
'tornado.test.twisted_test',
+ 'tornado.test.util_test',
'tornado.test.web_test',
'tornado.test.wsgi_test',
]
--- /dev/null
+from __future__ import absolute_import, division, with_statement
+import sys
+import unittest
+
+from tornado.util import raise_exc_info
+
+class RaiseExcInfoTest(unittest.TestCase):
+ def test_two_arg_exception(self):
+ # This test would fail on python 3 if raise_exc_info were simply
+ # a three-argument raise statement, because TwoArgException
+ # doesn't have a "copy constructor"
+ class TwoArgException(Exception):
+ def __init__(self, a, b):
+ super(TwoArgException, self).__init__()
+ self.a, self.b = a, b
+
+ try:
+ raise TwoArgException(1, 2)
+ except TwoArgException:
+ exc_info = sys.exc_info()
+ try:
+ raise_exc_info(exc_info)
+ self.fail("didn't get expected exception")
+ except TwoArgException, e:
+ self.assertTrue(e is exc_info[1])
HTTPServer = None
IOLoop = None
from tornado.stack_context import StackContext, NullContext
+from tornado.util import raise_exc_info
import contextlib
import logging
import signal
if self.__failure is not None:
failure = self.__failure
self.__failure = None
- # 2to3 isn't smart enough to convert three-argument raise
- # statements correctly in some cases.
- if isinstance(failure[1], failure[0]):
- raise failure[1], None, failure[2]
- else:
- raise failure[0], failure[1], failure[2]
+ raise_exc_info(failure)
def run(self, result=None):
bytes_type = str
+def raise_exc_info(exc_info):
+ """Re-raise an exception (with original traceback) from an exc_info tuple.
+
+ The argument is a ``(type, value, traceback)`` tuple as returned by
+ `sys.exc_info`.
+ """
+ # 2to3 isn't smart enough to convert three-argument raise
+ # statements correctly in some cases.
+ if isinstance(exc_info[1], exc_info[0]):
+ raise exc_info[1], None, exc_info[2]
+ # After 2to3: raise exc_info[1].with_traceback(exc_info[2])
+ else:
+ # I think this branch is only taken for string exceptions,
+ # which were removed in Python 2.6.
+ raise exc_info[0], exc_info[1], exc_info[2]
+ # After 2to3: raise exc_info[0](exc_info[1]).with_traceback(exc_info[2])
+
+
def doctests():
import doctest
return doctest.DocTestSuite()
from tornado import stack_context
from tornado import template
from tornado.escape import utf8, _unicode
-from tornado.util import b, bytes_type, import_object, ObjectDict
+from tornado.util import b, bytes_type, import_object, ObjectDict, raise_exc_info
try:
from io import BytesIO # python 3
kwargs['exception'] = exc_info[1]
try:
# Put the traceback into sys.exc_info()
- raise exc_info[0], exc_info[1], exc_info[2]
+ raise_exc_info(exc_info)
except Exception:
self.finish(self.get_error_html(status_code, **kwargs))
else:
# the exception value instead of the full triple,
# so re-raise the exception to ensure that it's in
# sys.exc_info()
- raise type, value, traceback
+ raise_exc_info((type, value, traceback))
except Exception:
self._handle_request_exception(value)
return True