Merge lp:~lifeless/python-oops-wsgi/foru1 into lp:python-oops-wsgi

Proposed by Robert Collins
Status: Merged
Merged at revision: 19
Proposed branch: lp:~lifeless/python-oops-wsgi/foru1
Merge into: lp:python-oops-wsgi
Diff against target: 248 lines (+88/-21)
5 files modified
oops_wsgi/__init__.py (+12/-0)
oops_wsgi/hooks.py (+7/-2)
oops_wsgi/middleware.py (+11/-9)
oops_wsgi/tests/test_hooks.py (+17/-0)
oops_wsgi/tests/test_middleware.py (+41/-10)
To merge this branch: bzr merge lp:~lifeless/python-oops-wsgi/foru1
Reviewer Review Type Date Requested Status
James Henstridge Approve
Steve Kowalik (community) code Approve
Review via email: mp+72649@code.launchpad.net

Description of the change

Another (perhaps final!) tweak for u1: allow injecting things into the report from deep down the stack.

To post a comment you must log in.
Revision history for this message
Steve Kowalik (stevenk) :
review: Approve (code)
Revision history for this message
James Henstridge (jamesh) wrote :

Looks good.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'oops_wsgi/__init__.py'
2--- oops_wsgi/__init__.py 2011-08-24 00:56:00 +0000
3+++ oops_wsgi/__init__.py 2011-08-24 03:48:22 +0000
4@@ -76,6 +76,18 @@
5 to gather stats on the number of 404's occuring without doing log processing).
6
7 >>> app = oops_wsgi.make_app(app, config, oops_on_status=['404'])
8+
9+The oops middleware injects two variables into the WSGI environ to make it easy
10+for cooperating code to report additional data.
11+
12+The `oops.report` variable is a dict which is copied into the report. See the
13+`oops` package documentation for documentation on what should be present in an
14+oops report. This requires the update_report hook to be installed (which
15+`install_hooks` will do for you).
16+
17+The `oops.context` variable is a dict used for generating the report - keys and
18+values added to that can be used in the `config.on_create` hooks to populate
19+custom data without needing to resort to global variables.
20 """
21
22
23
24=== modified file 'oops_wsgi/hooks.py'
25--- oops_wsgi/hooks.py 2011-08-24 01:21:57 +0000
26+++ oops_wsgi/hooks.py 2011-08-24 03:48:22 +0000
27@@ -20,6 +20,7 @@
28 'copy_environ',
29 'hide_cookie',
30 'install_hooks',
31+ 'update_report',
32 ]
33
34 _wsgi_standard_env_keys = set([
35@@ -79,5 +80,9 @@
36 def install_hooks(config):
37 """Install the default wsgi hooks into config."""
38 config.on_create.extend([copy_environ, hide_cookie])
39-
40-
41+ config.on_create.insert(0, update_report)
42+
43+
44+def update_report(report, context):
45+ """Copy the oops.report contents from the wsgi environment to report."""
46+ report.update(context.get('wsgi_environ', {}).get('oops.report', {}))
47
48=== modified file 'oops_wsgi/middleware.py'
49--- oops_wsgi/middleware.py 2011-08-24 01:56:49 +0000
50+++ oops_wsgi/middleware.py 2011-08-24 03:48:22 +0000
51@@ -81,7 +81,15 @@
52 * socket errors and GeneratorExit errors are passed through without
53 * being forward to the oops system.
54 """
55+ environ['oops.report'] = {}
56+ environ['oops.context'] = {}
57 state = {}
58+ def make_context(exc_info=None):
59+ context = dict(url=construct_url(environ), wsgi_environ=environ)
60+ context.update(environ.get('oops.context', {}))
61+ if exc_info is not None:
62+ context['exc_info'] = exc_info
63+ return context
64 def oops_write(bytes):
65 write = state.get('write')
66 if write is None:
67@@ -97,9 +105,7 @@
68 # forward to the containing element verbatim. In future we might
69 # choose to add the OOPS id to the headers (but we can't fiddle
70 # the body as it is being generated by the contained app.
71- report = config.create(
72- dict(exc_info=exc_info, url=construct_url(environ),
73- wsgi_environ=environ))
74+ report = config.create(make_context(exc_info=exc_info))
75 ids = config.publish(report)
76 try:
77 if ids:
78@@ -113,9 +119,7 @@
79 if oops_on_status:
80 for sniff_status in oops_on_status:
81 if status.startswith(sniff_status):
82- report = config.create(
83- dict(url=construct_url(environ),
84- wsgi_environ=environ))
85+ report = config.create(make_context())
86 report['HTTP_STATUS'] = status.split(' ')[0]
87 config.publish(report)
88 state['response'] = (status, headers)
89@@ -134,9 +138,7 @@
90 raise
91 except Exception:
92 exc_info = sys.exc_info()
93- report = config.create(
94- dict(exc_info=exc_info, url=construct_url(environ),
95- wsgi_environ=environ))
96+ report = config.create(make_context(exc_info=exc_info))
97 ids = config.publish(report)
98 if not ids or 'write' in state:
99 # No OOPS generated, no oops publisher, or we have already
100
101=== modified file 'oops_wsgi/tests/test_hooks.py'
102--- oops_wsgi/tests/test_hooks.py 2011-08-24 01:43:38 +0000
103+++ oops_wsgi/tests/test_hooks.py 2011-08-24 03:48:22 +0000
104@@ -26,6 +26,7 @@
105 from oops_wsgi.hooks import (
106 copy_environ,
107 hide_cookie,
108+ update_report,
109 )
110
111
112@@ -40,6 +41,9 @@
113 config.on_create.index(copy_environ),
114 LessThan(config.on_create.index(hide_cookie)),
115 )
116+ # update report wants to be at the start - its closer to a template
117+ # than anything.
118+ self.assertEqual(config.on_create[0], update_report)
119
120
121 class TestHooks(TestCase):
122@@ -94,3 +98,16 @@
123 }.items())
124 expected_report = {'req_vars': expected_vars}
125 self.assertEqual(expected_report, report)
126+
127+ def test_update_report_no_wsgi_report(self):
128+ report = {}
129+ update_report(report, {})
130+ update_report(report, {'wsgi_environ': {}})
131+ self.assertEqual({}, report)
132+
133+ def test_update_report_copies_wsgi_report_variables(self):
134+ report = {}
135+ update_report(
136+ report,
137+ {'wsgi_environ': {'oops.report': {'foo':'bar'}}})
138+ self.assertEqual({'foo': 'bar'}, report)
139
140=== modified file 'oops_wsgi/tests/test_middleware.py'
141--- oops_wsgi/tests/test_middleware.py 2011-08-24 01:56:49 +0000
142+++ oops_wsgi/tests/test_middleware.py 2011-08-24 03:48:22 +0000
143@@ -82,7 +82,7 @@
144 super(TestMakeApp, self).setUp()
145 self.calls = []
146
147- def make_environ(self):
148+ def make_outer_environ(self):
149 environ = {}
150 # Shove enough stuff in to let url reconstruction work:
151 environ['wsgi.url_scheme'] = 'http'
152@@ -91,12 +91,18 @@
153 environ['QUERY_STRING'] = 'param=value'
154 return environ
155
156+ def make_inner_environ(self, context={}):
157+ environ = self.make_outer_environ()
158+ environ['oops.report'] = {}
159+ environ['oops.context'] = context
160+ return environ
161+
162 def wrap_and_run(self, inner, config, failing_write=False,
163 failing_start=False, failing_server_write=False, params=None):
164 if not params:
165 params = {}
166 app = make_app(inner, config, **params)
167- environ = self.make_environ()
168+ environ = self.make_outer_environ()
169 def start_response(status, headers, exc_info=None):
170 if exc_info:
171 # If an exception is raised after we have written (via the app
172@@ -343,13 +349,18 @@
173 ValueError, ('boom, yo',))),
174 ]))
175
176- def test_error_in_app_context_includes_wsgi_environ(self):
177- # When the app blows up we attach the environment.
178+ def test_error_in_app_context_sets_oops_context(self):
179+ # When the app blows up we attach the environment and the oops.context
180+ # wsgi variable injects straight into the context.
181 def inner(environ, start_response):
182+ environ['oops.context']['foo'] = 'bar'
183 raise ValueError('boom, yo')
184 config = self.config_for_oopsing(capture_create=True)
185 self.wrap_and_run(inner, config)
186- self.assertEqual(self.make_environ(), self.calls[0]['wsgi_environ'])
187+ self.assertEqual(
188+ self.make_inner_environ({'foo':'bar'}),
189+ self.calls[0]['wsgi_environ'])
190+ self.assertEqual('bar', self.calls[0]['foo'])
191
192 def test_start_response_with_just_exc_info_generates_oops(self):
193 def inner(environ, start_response):
194@@ -378,9 +389,12 @@
195 Equals(expected_start_response),
196 ]))
197
198- def test_start_response_exc_info_context_includes_wsgi_environ(self):
199- # When the app handles its own error we still attach the environment.
200+ def test_start_response_exc_info_includes_environ_and_context(self):
201+ # When the app handles its own error we still attach the environment
202+ # and the wsgi.context wsgi variable.
203 def inner(environ, start_response):
204+ # Set a custom variable for the context
205+ environ['oops.context']['foo'] = 'bar'
206 try:
207 raise ValueError('boom, yo')
208 except ValueError:
209@@ -390,7 +404,10 @@
210 yield 'body'
211 config = self.config_for_oopsing(capture_create=True)
212 self.wrap_and_run(inner, config)
213- self.assertEqual(self.make_environ(), self.calls[0]['wsgi_environ'])
214+ self.assertEqual(
215+ self.make_inner_environ({'foo':'bar'}),
216+ self.calls[0]['wsgi_environ'])
217+ self.assertEqual('bar', self.calls[0]['foo'])
218
219 def test_sniff_404_not_published_does_not_error(self):
220 def inner(environ, start_response):
221@@ -420,11 +437,25 @@
222 Equals(expected_start_response),
223 ]))
224
225- def test_sniff_oops_context_includes_wsgi_environ(self):
226+ def test_sniff_oops_context_includes_wsgi_environ_and_context(self):
227 def inner(environ, start_response):
228+ environ['oops.context']['foo'] = 'bar'
229 start_response('404 MISSING', [])
230 yield 'pretty 404'
231 config = self.config_for_oopsing(capture_create=True)
232 self.wrap_and_run(
233 inner, config, params=dict(oops_on_status=['404']))
234- self.assertEqual(self.make_environ(), self.calls[0]['wsgi_environ'])
235+ self.assertEqual(
236+ self.make_inner_environ({'foo':'bar'}),
237+ self.calls[0]['wsgi_environ'])
238+ self.assertEqual('bar', self.calls[0]['foo'])
239+
240+ def test_inner_environ_has_oops_report_and_oops_context_variables(self):
241+ def inner(environ, start_response):
242+ self.assertEqual({}, environ['oops.report'])
243+ self.assertEqual({}, environ['oops.context'])
244+ start_response('200 OK', [])
245+ yield 'success'
246+ config = self.config_for_oopsing()
247+ body = self.wrap_and_run(inner, config)
248+ self.assertEqual('success', body)

Subscribers

People subscribed via source and target branches

to all changes: