Commits

Walt Woods  committed a96f196

Better exception reporting for tools - show original stack trace

  • Participants
  • Parent commits ac23a50

Comments (0)

Files changed (2)

File cherrypy/_cprequest.py

 import os
 import sys
 import time
+import traceback
 import warnings
 
 import cherrypy
-from cherrypy._cpcompat import basestring, copykeys, ntob, unicodestr
+from cherrypy._cpcompat import basestring, copykeys, ntob, ntou, unicodestr
 from cherrypy._cpcompat import SimpleCookie, CookieError, py3k
 from cherrypy import _cpreqbody, _cpconfig
-from cherrypy._cperror import format_exc, bare_error
+from cherrypy._cperror import format_exc, bare_error, CherryPyException
 from cherrypy.lib import httputil, file_generator
 
 
                     raise
                 except (cherrypy.HTTPError, cherrypy.HTTPRedirect,
                         cherrypy.InternalRedirect):
-                    exc = sys.exc_info()[1]
+                    exc = sys.exc_info()
                 except:
-                    exc = sys.exc_info()[1]
+                    exc = sys.exc_info()
                     cherrypy.log(traceback=True, severity=40)
         if exc:
-            raise exc
-    
+            if isinstance(exc[1], CherryPyException):
+                raise exc[1]
+            else:
+                # For compatibility with python 2 and 3, we can neither use the
+                # three-argument raise operator, nor the "from" operator of
+                # python 3.
+                raise Exception(ntou("Hook exception:\n\n") + ntou('').join(
+                        traceback.format_exception(*exc)))
+        
     def __copy__(self):
         newmap = self.__class__()
         # We can't just use 'update' because we want copies of the

File cherrypy/test/test_tools.py

             finally:
                 o.close()
         cherrypy.tools.streamer = cherrypy._cptools.HandlerWrapperTool(stream_handler)
+
+        def raise_value_error():
+            """Raise a ValueError to test that user hooks raising exceptions
+            display a useful traceback.
+            """
+            raise ValueError("raise_value_error errored!")
+        cherrypy.tools.raise_value_error = cherrypy.Tool('on_start_resource', 
+                raise_value_error)
         
         class Root:
             def index(self):
                     yield str(x)
             stream._cp_config = {'response.stream': True}
         
+            def user_tool_tb(self):
+                """Check tracebacks from tools"""
+                return "you should never see this!"
+            user_tool_tb._cp_config = {'tools.raise_value_error.on': True}
         
         conf = {
             # METHOD THREE:
         # Test compile-time decorator with kwargs from config.
         self.getPage("/demo/userid")
         self.assertBody("Welcome!")
+
+        # Test user tool tracebacks
+        self.getPage("/demo/user_tool_tb")
+        self.assertTrue(ntob("in raise_value_error") in self.body,
+                "Traceback did not appear to include tool traceback")
+        self.assertTrue(ntob("Hook exception:") in self.body,
+                "Traceback did not appear to include tool traceback")
     
     def testEndRequestOnDrop(self):
         old_timeout = None