Skip to content

Commit

Permalink
Merge pull request #5374 from hypothesis/fix-exception-capture
Browse files Browse the repository at this point in the history
Fix Sentry reporting in `h.util.view.handle_exception`
  • Loading branch information
robertknight committed Oct 16, 2018
2 parents 020c2f8 + 72b377b commit cec86d9
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 6 deletions.
22 changes: 21 additions & 1 deletion h/util/view.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,16 @@

from __future__ import unicode_literals

import sys

from pyramid.view import view_config


# Test seam. Patching `sys.exc_info` directly causes problems with pytest.
def _exc_info():
return sys.exc_info()


def handle_exception(request, exception):
"""
Handle an uncaught exception for the passed request.
Expand All @@ -13,7 +20,20 @@ def handle_exception(request, exception):
:param exception: The exception passed as context to the exception-handling view.
"""
request.response.status_int = 500
request.sentry.captureException(exception)

# There are two code paths here depending on whether this is the most recent
# exception in the current thread. If it is, we can let Raven capture
# the details under Python 2 + 3. If not, we need to construct the
# exc_info tuple manually and the stacktrace is only available in Python 3.
last_exc_info = _exc_info()
if exception is last_exc_info[1]:
request.sentry.captureException()
else:
# `__traceback__` is a Python 3-only property.
traceback = getattr(exception, '__traceback__', None)
exc_info = (type(exception), exception, traceback)
request.sentry.captureException(exc_info)

# In debug mode we should just reraise, so that the exception is caught by
# the debug toolbar.
if request.debug:
Expand Down
39 changes: 34 additions & 5 deletions tests/h/util/view_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,27 @@
from h.util.view import handle_exception, json_view


@pytest.mark.usefixtures("sys_exc_info")
class TestHandleException(object):
def test_sets_response_status_500(self, pyramid_request):
handle_exception(pyramid_request, Mock())

assert pyramid_request.response.status_int == 500

def test_triggers_sentry_capture(self, pyramid_request):
exception = Mock()
handle_exception(pyramid_request, exception)

pyramid_request.sentry.captureException.assert_called_once_with(exception)
def test_triggers_sentry_capture_with_latest_exception(
self, pyramid_request, latest_exception
):
handle_exception(pyramid_request, latest_exception)
pyramid_request.sentry.captureException.assert_called_once_with()

def test_triggers_sentry_capture_with_old_exception(
self, pyramid_request, old_exception
):
handle_exception(pyramid_request, old_exception)
traceback = getattr(old_exception, "__traceback__", None)
pyramid_request.sentry.captureException.assert_called_once_with(
(type(old_exception), old_exception, traceback)
)

def test_reraises_in_debug_mode(self, pyramid_request):
pyramid_request.debug = True
Expand All @@ -38,6 +48,25 @@ def pyramid_request(self, pyramid_request):
pyramid_request.debug = False
return pyramid_request

@pytest.fixture
def latest_exception(self):
return Exception("Last exception raised in thread")

@pytest.fixture
def old_exception(self):
try:
# Create exception and populate `__traceback__` in Python 3.
raise Exception("An earlier exception raised in thread")
except Exception as exc:
result = exc
return result

@pytest.fixture
def sys_exc_info(self, patch, latest_exception):
sys_exc_info = patch("h.util.view._exc_info")
sys_exc_info.return_value = (type(latest_exception), latest_exception, None)
return sys_exc_info


@pytest.mark.usefixtures('view_config')
class TestJsonView(object):
Expand Down
1 change: 1 addition & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ passenv =
CONFIG_URI
MODEL_CREATE_ALL
SEARCH_AUTOCONFIG
SENTRY_DSN
USE_HTTPS
WEBSOCKET_URL
whitelist_externals = sh
Expand Down

0 comments on commit cec86d9

Please sign in to comment.