From d3159f1d6bec06845b152de836793f4454d95804 Mon Sep 17 00:00:00 2001 From: John Vrbanac Date: Mon, 1 Jun 2015 18:24:22 -0500 Subject: Allowing tracebacks to be surfaced through abort Currently, anytime abort(...) is called, It raises a new exception which then suppresses any existing tracebacks. This causes error monitoring systems like NewRelic to report back worthless surface-level tracebacks that have nothing to do with the application. This change allows for the exception type to change, but keep the existing traceback (if one exists). This allows for monitoring systems to see a real traceback that can be correlated to logged information. Change-Id: Ibe6eb37e25b74d1dcfca76dfc1f5bbce28d34e85 --- pecan/core.py | 25 +++++++++++++++++++------ pecan/tests/test_base.py | 15 +++++++++++++++ 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/pecan/core.py b/pecan/core.py index a6525ca..14b9ad3 100644 --- a/pecan/core.py +++ b/pecan/core.py @@ -8,6 +8,7 @@ from mimetypes import guess_type, add_type from os.path import splitext import logging import operator +import sys import types import six @@ -123,12 +124,24 @@ def abort(status_code=None, detail='', headers=None, comment=None, **kw): :param comment: A comment to include in the response. ''' - raise exc.status_map[status_code]( - detail=detail, - headers=headers, - comment=comment, - **kw - ) + # If there is a traceback, we need to catch it for a re-raise + try: + _, _, traceback = sys.exc_info() + webob_exception = exc.status_map[status_code]( + detail=detail, + headers=headers, + comment=comment, + **kw + ) + + if six.PY3: + raise webob_exception.with_traceback(traceback) + else: + # Using exec to avoid python 3 parsers from crashing + exec('raise webob_exception, None, traceback') + finally: + # Per the suggestion of the Python docs, delete the traceback object + del traceback def redirect(location=None, internal=False, code=None, headers={}, diff --git a/pecan/tests/test_base.py b/pecan/tests/test_base.py index e6adf0e..785b522 100644 --- a/pecan/tests/test_base.py +++ b/pecan/tests/test_base.py @@ -3,6 +3,7 @@ import sys import os import json +import traceback import warnings import webob @@ -1121,6 +1122,20 @@ class TestAbort(PecanTestCase): r = app.get('/', status=401) assert r.status_int == 401 + def test_abort_keeps_traceback(self): + last_exc, last_traceback = None, None + + try: + try: + raise Exception('Bottom Exception') + except: + abort(404) + except Exception: + last_exc, _, last_traceback = sys.exc_info() + + assert last_exc is HTTPNotFound + assert 'Bottom Exception' in traceback.format_tb(last_traceback)[-1] + class TestScriptName(PecanTestCase): -- cgit v1.2.1