launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #04705
[Merge] lp:~lifeless/python-oops-wsgi/extraction into lp:python-oops-wsgi
Robert Collins has proposed merging lp:~lifeless/python-oops-wsgi/extraction into lp:python-oops-wsgi.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~lifeless/python-oops-wsgi/extraction/+merge/72380
I fail at implementing wsgi right. This fixes exc_info handling AFAICT.
--
https://code.launchpad.net/~lifeless/python-oops-wsgi/extraction/+merge/72380
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~lifeless/python-oops-wsgi/extraction into lp:python-oops-wsgi.
=== modified file 'oops_wsgi/middleware.py'
--- oops_wsgi/middleware.py 2011-08-18 00:45:58 +0000
+++ oops_wsgi/middleware.py 2011-08-22 07:04:24 +0000
@@ -85,8 +85,23 @@
state['write'] = start_response(status, headers)
write = state['write']
write(bytes)
- def oops_start_response(status, headers):
- state['response'] = (status, headers)
+ def oops_start_response(status, headers, exc_info=None):
+ if exc_info is not None:
+ # The app is explicitly signalling an error (rather than
+ # returning a page describing the error). Capture that and then
+ # forward to the containing element verbatim. In future we might
+ # choose to add the OOPS id to the headers (but we can't fiddle
+ # the body as it is being generated by the contained app.
+ report = config.create(
+ dict(exc_info=exc_info, url=construct_url(environ)))
+ ids = config.publish(report)
+ try:
+ state['write'] = start_response(status, headers, exc_info)
+ return state['write']
+ finally:
+ del exc_info
+ else:
+ state['response'] = (status, headers)
return oops_write
try:
iterator = iter(app(environ, oops_start_response))
@@ -112,7 +127,8 @@
# server figure it out.
raise
start_response('500 Internal Server Error',
- [('Content-Type', content_type)])
+ [('Content-Type', content_type)], exc_info=exc_info)
+ del exc_info
if error_render is not None:
yield error_render(report)
else:
=== modified file 'oops_wsgi/tests/test_middleware.py'
--- oops_wsgi/tests/test_middleware.py 2011-08-18 22:44:58 +0000
+++ oops_wsgi/tests/test_middleware.py 2011-08-22 07:04:24 +0000
@@ -20,6 +20,7 @@
from doctest import ELLIPSIS
import gc
import socket
+import sys
from textwrap import dedent
from oops import (
@@ -52,7 +53,21 @@
environ['HTTP_HOST'] = 'example.com'
environ['PATH_INFO'] = '/demo'
environ['QUERY_STRING'] = 'param=value'
- def start_response(status, headers):
+ def start_response(status, headers, exc_info=None):
+ if exc_info:
+ # If an exception is raised after we have written (via the app
+ # calling our oops_write) or after we have yielded data, we
+ # must supply exc_info in the call to start_response, per the
+ # wsgi spec. The easiest way to do that is to always supply
+ # exc_info when reporting an exception - that will DTRT if
+ # start_response upstream had already written data. Thus
+ # our test start_response which lives above the oops middleware
+ # captures the fact exc_info was called and all our tests that
+ # expect oopses raised expect the exc_info to be present.
+ self.calls.append(
+ ('start error', status, headers, exc_info[0],
+ exc_info[1].args))
+ return self.calls.append
if failing_start:
raise socket.error(errno.EPIPE, 'Connection closed')
self.calls.append((status, headers))
@@ -191,7 +206,8 @@
'url': 'http://example.com/demo?param=value',
'value': u'boom, yo'},
# Then the middleware responses
- ('500 Internal Server Error', [('Content-Type', 'text/html')]),
+ ('start error', '500 Internal Server Error',
+ [('Content-Type', 'text/html')], ValueError, ('boom, yo',)),
], self.calls)
self.assertThat(iterated, DocTestMatches(dedent("""\
<html>
@@ -219,7 +235,8 @@
'url': 'http://example.com/demo?param=value',
'value': u'boom, yo'},
# Then the middleware responses
- ('500 Internal Server Error', [('Content-Type', 'text/json')]),
+ ('start error', '500 Internal Server Error',
+ [('Content-Type', 'text/json')], ValueError, ('boom, yo',)),
], self.calls)
self.assertThat(iterated, DocTestMatches(dedent("""\
{"oopsid" : "..."}"""), ELLIPSIS))
@@ -239,7 +256,8 @@
'url': 'http://example.com/demo?param=value',
'value': u'boom, yo'},
# Then the middleware responses
- ('500 Internal Server Error', [('Content-Type', 'text/html')]),
+ ('start error', '500 Internal Server Error',
+ [('Content-Type', 'text/html')], ValueError, ('boom, yo',)),
], self.calls)
self.assertEqual(iterated, 'woo')
@@ -250,3 +268,20 @@
self.wrap_and_run(inner, config)
self.assertEqual('http://example.com/demo?param=value',
self.calls[0]['url'])
+
+ def test_start_response_with_just_exc_info_generates_oops(self):
+ def inner(environ, start_response):
+ try:
+ raise ValueError('boom, yo')
+ except ValueError:
+ exc_info = sys.exc_info()
+ start_response('500 FAIL', [], exc_info)
+ yield 'body'
+ config = self.config_for_oopsing()
+ self.wrap_and_run(inner, config)
+ self.assertEqual([{'id': 1,
+ 'type': 'ValueError',
+ 'url': 'http://example.com/demo?param=value',
+ 'value': u'boom, yo'},
+ ('start error', '500 FAIL', [], ValueError, ('boom, yo',))],
+ self.calls)