Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update pyramid's default execution policy to attempt to render any uncaught exception #3053

Closed
mmerickel opened this issue May 22, 2017 · 1 comment
Assignees

Comments

@mmerickel
Copy link
Member

If an exception propagates all the way to the execution policy there should be a last-ditch effort to render a response... this is part of the larger consistency change to pyramid_tm and pyramid_retry. With the addition of request.invoke_exception_view it's hopefully clear that exception views should be callable from various parts of the lifecycle and pyramid_retry will attempt to invoke exception views at the end if an exception is uncaught. Also pyramid_tm will attempt to invoke exceptions on commit/abort to handle those specifically.

I've documented the process here in a little ascii diagram[1] reproduced here:

   pyramid_retry   +-------->    if exc not retryable, render response
                                 if exc retryable, ignore exc and try again
        +
        |
        |
        v

pyramid_debugtoolbar

        +
        |
        |
        v

    pyramid_tm     +-------->    if exc caught then abort and reraise
                                 if request.exception then abort and return response
        +                        if abort or commit raises, render response
        |
        |
        v

     excview       +-------->    if exc caught, render response and set request.exception
                                                if no exception then reraise
        +
        |
        |
        v

       app

[1] https://gist.github.com/mmerickel/2e83f4af6c7d5eae34947b8f1f075f61

@mmerickel mmerickel self-assigned this May 23, 2017
@digitalresistor
Copy link
Member

    def test_it_supports_alternate_requests(self):
        def exc_view(exc, request):
            self.assertTrue(request is other_req)

# Next two lines are new
            from pyramid.threadlocal import get_current_request
            self.assertTrue(get_current_request() is other_req)

            return DummyResponse(b'foo')
        self.config.add_view(exc_view, context=RuntimeError)
        request = self._makeOne()
        other_req = self._makeOne()
        try:
            raise RuntimeError
        except RuntimeError:
            response = request.invoke_exception_view(request=other_req)
            self.assertEqual(response.app_iter, [b'foo'])
        else: # pragma: no cover
            self.fail()

This test, changed from the original in test_view.py will fail:

======================================================================
FAIL: test_it_supports_alternate_requests (pyramid.tests.test_view.TestViewMethodsMixin)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/xistence/Projects/pyramid/pyramid/tests/test_view.py", line 800, in test_it_supports_alternate_requests
    raise RuntimeError
RuntimeError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/xistence/Projects/pyramid/pyramid/tests/test_view.py", line 802, in test_it_supports_alternate_requests
    response = request.invoke_exception_view(request=other_req)
  File "/Users/xistence/Projects/pyramid/pyramid/view.py", line 702, in invoke_exception_view
    request_iface=request_iface.combined,
  File "/Users/xistence/Projects/pyramid/pyramid/view.py", line 612, in _call_view
    response = view_callable(context, request)
  File "/Users/xistence/Projects/pyramid/pyramid/viewderivers.py", line 409, in viewresult_to_response
    result = view(context, request)
  File "/Users/xistence/Projects/pyramid/pyramid/tests/test_view.py", line 794, in exc_view
    self.assertTrue(get_current_request() is other_req)
AssertionError: False is not true

----------------------------------------------------------------------
Ran 2612 tests in 8.742s

This is due to the fact that the threadlocals are not pushed when invoking the exception view, this means that the request in the view is different and potentially has different behaviour than the get_current_request() request object.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants