Merge lp:~elachuni/ubuntu-webcatalog/amqp-oopses into lp:ubuntu-webcatalog
- amqp-oopses
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 178 |
Proposed branch: | lp:~elachuni/ubuntu-webcatalog/amqp-oopses |
Merge into: | lp:ubuntu-webcatalog |
Diff against target: |
338 lines (+153/-24) 11 files modified
.bzrignore (+1/-0) django_project/config/main.cfg (+2/-12) django_project/urls.py (+4/-0) setup.py (+1/-1) src/webcatalog/management/commands/runserver.py (+36/-0) src/webcatalog/schema.py (+2/-0) src/webcatalog/templates/500.html (+3/-2) src/webcatalog/tests/test_oops_wsgi_config.py (+12/-4) src/webcatalog/urls.py (+1/-0) src/webcatalog/views.py (+58/-2) src/webcatalog/wsgi.py (+33/-3) |
To merge this branch: | bzr merge lp:~elachuni/ubuntu-webcatalog/amqp-oopses |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Łukasz Czyżykowski (community) | Approve | ||
Review via email: mp+151051@code.launchpad.net |
Commit message
Display the oops id on every error page, and generate oops reports in dev.
Description of the change
This branch includes several improvements related to ampq oops publishing:
- A custom wsgi handler was added that generates an oops id for every exception.
- Switched to oops-dictconfig 0.0.5 to use the 'inherit_id' and 'new_only' settings.
- A custom runserver command was added that uses webcatalog's wsgi stack in dev (to generate oopses in dev)
Łukasz Czyżykowski (lukasz-czyzykowski) wrote : | # |
Łukasz Czyżykowski (lukasz-czyzykowski) : | # |
Ubuntu One Auto Pilot (otto-pilot) wrote : | # |
The attempt to merge lp:~elachuni/ubuntu-webcatalog/amqp-oopses into lp:ubuntu-webcatalog failed. Below is the output from the failed tests.
[localhost] local: /usr/bin/python /usr/bin/virtualenv --no-site-packages virtualenv
The --no-site-packages flag is deprecated; it is now the default behavior.
New python executable in virtualenv/
Installing distribute.
Installing pip....
[localhost] local: virtualenv/bin/pip install -r test_requiremen
Downloading/
Running setup.py egg_info for package django
Downloading/
Running setup.py egg_info for package coverage
warning: no previously-included files matching '*.pyc' found anywhere in distribution
Downloading/
Running setup.py egg_info for package mock
warning: no files found matching '*.png' under directory 'docs'
warning: no files found matching '*.css' under directory 'docs'
warning: no files found matching '*.html' under directory 'docs'
warning: no files found matching '*.js' under directory 'docs'
Downloading/
Downloading piston-
Running setup.py egg_info for package piston-mini-client
Downloading/
Running setup.py egg_info for package oauthlib
Downloading/
Running setup.py egg_info for package httplib2
Installing collected packages: django, coverage, mock, piston-mini-client, oauthlib, httplib2
Running setup.py install for django
changing mode of build/scripts-
changing mode of /mnt/tarmac/
Running setup.py install for coverage
building 'coverage.tracer' extension
gcc -pthread -fno-strict-
gcc -pthread -shared -Wl,-O1 -Wl,-Bsymbolic-
warning: no previously-included files matching '*.pyc' found anywhere in distribution
Installing coverage2 script to /mnt/tarmac/
Installing coverage-2.7 script to /mnt/tarmac/
Installing coverage script to /mnt/tarmac/
Running setup.py install for mock
warning: no files found matching '*.png' under directory 'docs'
warning: no files found matching '*.css' under directory ...
Preview Diff
1 | === modified file '.bzrignore' | |||
2 | --- .bzrignore 2012-04-18 09:37:51 +0000 | |||
3 | +++ .bzrignore 2013-03-01 12:52:22 +0000 | |||
4 | @@ -15,3 +15,4 @@ | |||
5 | 15 | django_project/adminaudit | 15 | django_project/adminaudit |
6 | 16 | django_project/local.cfg | 16 | django_project/local.cfg |
7 | 17 | django_project/sreclient.py | 17 | django_project/sreclient.py |
8 | 18 | oopses/ | ||
9 | 18 | 19 | ||
10 | === modified file 'django_project/config/main.cfg' | |||
11 | --- django_project/config/main.cfg 2012-08-23 15:12:54 +0000 | |||
12 | +++ django_project/config/main.cfg 2013-03-01 12:52:22 +0000 | |||
13 | @@ -11,14 +11,14 @@ | |||
14 | 11 | debug = true | 11 | debug = true |
15 | 12 | media_root = django_project/media_root_dev/ | 12 | media_root = django_project/media_root_dev/ |
16 | 13 | 13 | ||
19 | 14 | installed_apps = webcatalog | 14 | installed_apps = django.contrib.contenttypes |
18 | 15 | django.contrib.contenttypes | ||
20 | 16 | django.contrib.sessions | 15 | django.contrib.sessions |
21 | 17 | django.contrib.sites | 16 | django.contrib.sites |
22 | 18 | django.contrib.messages | 17 | django.contrib.messages |
23 | 19 | django.contrib.staticfiles | 18 | django.contrib.staticfiles |
24 | 20 | django.contrib.markup | 19 | django.contrib.markup |
25 | 21 | django.contrib.admin | 20 | django.contrib.admin |
26 | 21 | webcatalog | ||
27 | 22 | django_openid_auth | 22 | django_openid_auth |
28 | 23 | django_configglue | 23 | django_configglue |
29 | 24 | django.contrib.auth | 24 | django.contrib.auth |
30 | @@ -106,16 +106,6 @@ | |||
31 | 106 | canonical-losas = admin | 106 | canonical-losas = admin |
32 | 107 | canonical-ca-hackers = developers | 107 | canonical-ca-hackers = developers |
33 | 108 | 108 | ||
34 | 109 | [oops_wsgi] | ||
35 | 110 | oopses = oops_publishers | ||
36 | 111 | |||
37 | 112 | [oops_publishers] | ||
38 | 113 | publishers = oops_datedir_publisher | ||
39 | 114 | |||
40 | 115 | [oops_datedir_publisher] | ||
41 | 116 | type = datedir | ||
42 | 117 | error_dir = oopses | ||
43 | 118 | |||
44 | 119 | [webcatalog] | 109 | [webcatalog] |
45 | 120 | serve_site_media = True | 110 | serve_site_media = True |
46 | 121 | sca_api_url = https://sc.staging.ubuntu.com/api/2.0/ | 111 | sca_api_url = https://sc.staging.ubuntu.com/api/2.0/ |
47 | 122 | 112 | ||
48 | === modified file 'django_project/urls.py' | |||
49 | --- django_project/urls.py 2012-06-21 20:18:44 +0000 | |||
50 | +++ django_project/urls.py 2013-03-01 12:52:22 +0000 | |||
51 | @@ -25,6 +25,10 @@ | |||
52 | 25 | admin.autodiscover() | 25 | admin.autodiscover() |
53 | 26 | preflight.autodiscover() | 26 | preflight.autodiscover() |
54 | 27 | 27 | ||
55 | 28 | handler500 = 'webcatalog.views.server_error' | ||
56 | 29 | handler404 = 'webcatalog.views.page_not_found' | ||
57 | 30 | |||
58 | 31 | |||
59 | 28 | urlpatterns = patterns('', | 32 | urlpatterns = patterns('', |
60 | 29 | url(r'^cat/', include('webcatalog.urls')), | 33 | url(r'^cat/', include('webcatalog.urls')), |
61 | 30 | url(r'^admin/', include(admin.site.urls)), | 34 | url(r'^admin/', include(admin.site.urls)), |
62 | 31 | 35 | ||
63 | === modified file 'setup.py' | |||
64 | --- setup.py 2012-09-28 15:02:24 +0000 | |||
65 | +++ setup.py 2013-03-01 12:52:22 +0000 | |||
66 | @@ -53,7 +53,7 @@ | |||
67 | 53 | 'celery==2.5.0', | 53 | 'celery==2.5.0', |
68 | 54 | 'django-celery==2.5.0', | 54 | 'django-celery==2.5.0', |
69 | 55 | 'oops-wsgi==0.0.10', | 55 | 'oops-wsgi==0.0.10', |
71 | 56 | 'oops-dictconfig==0.0.2', | 56 | 'oops-dictconfig==0.0.5', |
72 | 57 | 'oops-datedir_repo==0.0.17', | 57 | 'oops-datedir_repo==0.0.17', |
73 | 58 | ], | 58 | ], |
74 | 59 | package_data = find_packages_data('src'), | 59 | package_data = find_packages_data('src'), |
75 | 60 | 60 | ||
76 | === added file 'src/webcatalog/management/commands/runserver.py' | |||
77 | --- src/webcatalog/management/commands/runserver.py 1970-01-01 00:00:00 +0000 | |||
78 | +++ src/webcatalog/management/commands/runserver.py 2013-03-01 12:52:22 +0000 | |||
79 | @@ -0,0 +1,36 @@ | |||
80 | 1 | # -*- coding: utf-8 -*- | ||
81 | 2 | # This file is part of the Apps Directory | ||
82 | 3 | # Copyright (C) 2011-2013 Canonical Ltd. | ||
83 | 4 | # | ||
84 | 5 | # This program is free software: you can redistribute it and/or modify | ||
85 | 6 | # it under the terms of the GNU Affero General Public License as | ||
86 | 7 | # published by the Free Software Foundation, either version 3 of the | ||
87 | 8 | # License, or (at your option) any later version. | ||
88 | 9 | # | ||
89 | 10 | # This program is distributed in the hope that it will be useful, | ||
90 | 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
91 | 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
92 | 13 | # GNU Affero General Public License for more details. | ||
93 | 14 | # | ||
94 | 15 | # You should have received a copy of the GNU Affero General Public License | ||
95 | 16 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
96 | 17 | |||
97 | 18 | """Customized runserver command that runs ubuntu-webcatalog's wsgi stack.""" | ||
98 | 19 | |||
99 | 20 | from optparse import make_option | ||
100 | 21 | from django.core.management.commands.runserver import BaseRunserverCommand | ||
101 | 22 | from django.core.servers.basehttp import AdminMediaHandler | ||
102 | 23 | |||
103 | 24 | |||
104 | 25 | class Command(BaseRunserverCommand): | ||
105 | 26 | option_list = BaseRunserverCommand.option_list + ( | ||
106 | 27 | make_option( | ||
107 | 28 | '--adminmedia', dest='admin_media_path', default='', | ||
108 | 29 | help='Specifies the directory from which to serve admin media.'), | ||
109 | 30 | ) | ||
110 | 31 | |||
111 | 32 | def get_handler(self, *args, **options): | ||
112 | 33 | """Serves admin media like old-school (deprecation pending).""" | ||
113 | 34 | from webcatalog.wsgi import make_app | ||
114 | 35 | handler = make_app() | ||
115 | 36 | return AdminMediaHandler(handler, options.get('admin_media_path', '')) | ||
116 | 0 | 37 | ||
117 | === modified file 'src/webcatalog/schema.py' | |||
118 | --- src/webcatalog/schema.py 2013-02-22 21:02:05 +0000 | |||
119 | +++ src/webcatalog/schema.py 2013-03-01 12:52:22 +0000 | |||
120 | @@ -38,6 +38,8 @@ | |||
121 | 38 | 'publishers': [{ | 38 | 'publishers': [{ |
122 | 39 | 'type': 'datedir', | 39 | 'type': 'datedir', |
123 | 40 | 'error_dir': 'oopses', | 40 | 'error_dir': 'oopses', |
124 | 41 | 'inherit_id': True, | ||
125 | 42 | 'new_only': False, | ||
126 | 41 | }], | 43 | }], |
127 | 42 | }) | 44 | }) |
128 | 43 | 45 | ||
129 | 44 | 46 | ||
130 | === modified file 'src/webcatalog/templates/500.html' | |||
131 | --- src/webcatalog/templates/500.html 2012-08-23 14:59:18 +0000 | |||
132 | +++ src/webcatalog/templates/500.html 2013-03-01 12:52:22 +0000 | |||
133 | @@ -1,10 +1,11 @@ | |||
135 | 1 | {% extends "light/index.1col.html" %} | 1 | {% extends "webcatalog/base.html" %} |
136 | 2 | {% load i18n %} | 2 | {% load i18n %} |
137 | 3 | 3 | ||
138 | 4 | {% block title %}{% trans 'Oops!' %}{% endblock %} | 4 | {% block title %}{% trans 'Oops!' %}{% endblock %} |
139 | 5 | 5 | ||
140 | 6 | {% block content %} | 6 | {% block content %} |
142 | 7 | <h1>{% trans 'Server Error <em>(500)</em>' %}</h1> | 7 | <h1>{% trans 'Server Error' %}</h1> |
143 | 8 | {% if errormsg %}<p>{{ errormsg }}</p>{% endif %} | 8 | {% if errormsg %}<p>{{ errormsg }}</p>{% endif %} |
144 | 9 | <p>{% blocktrans %}An error occurred which prevented the page you requested from loading. If this problem continues, please consider reporting this problem by sending an e-mail to <a href="mailto:webmaster@canonical.com">webmaster@canonical.com</a>{% endblocktrans %}</p> | 9 | <p>{% blocktrans %}An error occurred which prevented the page you requested from loading. If this problem continues, please consider reporting this problem by sending an e-mail to <a href="mailto:webmaster@canonical.com">webmaster@canonical.com</a>{% endblocktrans %}</p> |
145 | 10 | <p id="oops-id">{{ oops_id }}</p> | ||
146 | 10 | {% endblock %} | 11 | {% endblock %} |
147 | 11 | 12 | ||
148 | === modified file 'src/webcatalog/tests/test_oops_wsgi_config.py' | |||
149 | --- src/webcatalog/tests/test_oops_wsgi_config.py 2012-08-23 14:59:18 +0000 | |||
150 | +++ src/webcatalog/tests/test_oops_wsgi_config.py 2013-03-01 12:52:22 +0000 | |||
151 | @@ -26,6 +26,7 @@ | |||
152 | 26 | 26 | ||
153 | 27 | import bson | 27 | import bson |
154 | 28 | import os | 28 | import os |
155 | 29 | import re | ||
156 | 29 | from shutil import rmtree | 30 | from shutil import rmtree |
157 | 30 | from tempfile import mkdtemp | 31 | from tempfile import mkdtemp |
158 | 31 | 32 | ||
159 | @@ -67,13 +68,15 @@ | |||
160 | 67 | 68 | ||
161 | 68 | def request_wsgi_response(self, environ): | 69 | def request_wsgi_response(self, environ): |
162 | 69 | start_response = Mock() | 70 | start_response = Mock() |
164 | 70 | with patch_settings(OOPSES={ | 71 | oops_config = { |
165 | 71 | 'publishers': [{ | 72 | 'publishers': [{ |
166 | 72 | 'type': 'datedir', | 73 | 'type': 'datedir', |
167 | 73 | 'error_dir': self.oops_dir, | 74 | 'error_dir': self.oops_dir, |
169 | 74 | 'instance_id': 'dev', | 75 | 'inherit_id': True, |
170 | 76 | 'new_only': False, | ||
171 | 75 | }], | 77 | }], |
173 | 76 | }): | 78 | } |
174 | 79 | with patch_settings(OOPSES=oops_config): | ||
175 | 77 | app = make_app() | 80 | app = make_app() |
176 | 78 | response = app(environ, start_response).next() | 81 | response = app(environ, start_response).next() |
177 | 79 | return response | 82 | return response |
178 | @@ -105,6 +108,11 @@ | |||
179 | 105 | def test_oops_on_disk_on_error(self): | 108 | def test_oops_on_disk_on_error(self): |
180 | 106 | response = self.request_with_exception() | 109 | response = self.request_with_exception() |
181 | 107 | 110 | ||
183 | 108 | self.assertIn('Server Error <em>(500)</em>', response) | 111 | self.assertIn('Server Error', response) |
184 | 112 | oops_id_re = re.compile('<p id="oops-id">(OOPS-[0-9a-f]{32})</p>') | ||
185 | 113 | match = oops_id_re.search(response) | ||
186 | 114 | self.assertTrue(match) | ||
187 | 115 | oops_id = match.groups()[0] | ||
188 | 109 | oops_data = self.read_oops_from_disk() | 116 | oops_data = self.read_oops_from_disk() |
189 | 110 | self.assertEqual('ZeroDivisionError', oops_data['type']) | 117 | self.assertEqual('ZeroDivisionError', oops_data['type']) |
190 | 118 | self.assertEqual(oops_id, oops_data['id']) | ||
191 | 111 | 119 | ||
192 | === modified file 'src/webcatalog/urls.py' | |||
193 | --- src/webcatalog/urls.py 2012-07-02 21:25:30 +0000 | |||
194 | +++ src/webcatalog/urls.py 2013-03-01 12:52:22 +0000 | |||
195 | @@ -63,4 +63,5 @@ | |||
196 | 63 | name='wc-task-status'), | 63 | name='wc-task-status'), |
197 | 64 | 64 | ||
198 | 65 | (r'^api/', include('webcatalog.api.urls')), | 65 | (r'^api/', include('webcatalog.api.urls')), |
199 | 66 | url(r'^die/$', 'die', name='debug-die'), | ||
200 | 66 | ) | 67 | ) |
201 | 67 | 68 | ||
202 | === modified file 'src/webcatalog/views.py' | |||
203 | --- src/webcatalog/views.py 2012-09-06 10:38:18 +0000 | |||
204 | +++ src/webcatalog/views.py 2013-03-01 12:52:22 +0000 | |||
205 | @@ -34,7 +34,6 @@ | |||
206 | 34 | from django.http import ( | 34 | from django.http import ( |
207 | 35 | HttpResponse, | 35 | HttpResponse, |
208 | 36 | HttpResponseForbidden, | 36 | HttpResponseForbidden, |
209 | 37 | HttpResponseNotFound, | ||
210 | 38 | HttpResponseRedirect, | 37 | HttpResponseRedirect, |
211 | 39 | ) | 38 | ) |
212 | 40 | from django.shortcuts import ( | 39 | from django.shortcuts import ( |
213 | @@ -42,7 +41,10 @@ | |||
214 | 42 | render_to_response, | 41 | render_to_response, |
215 | 43 | ) | 42 | ) |
216 | 44 | from django.template import RequestContext | 43 | from django.template import RequestContext |
218 | 45 | from django.template.loader import render_to_string | 44 | from django.template.loader import ( |
219 | 45 | render_to_string, | ||
220 | 46 | get_template, | ||
221 | 47 | ) | ||
222 | 46 | from django.utils.translation import ugettext as _ | 48 | from django.utils.translation import ugettext as _ |
223 | 47 | from django.views.decorators.cache import never_cache | 49 | from django.views.decorators.cache import never_cache |
224 | 48 | from django.views.decorators.http import require_GET | 50 | from django.views.decorators.http import require_GET |
225 | @@ -346,3 +348,57 @@ | |||
226 | 346 | def forbidden(request): | 348 | def forbidden(request): |
227 | 347 | return HttpResponseForbidden( | 349 | return HttpResponseForbidden( |
228 | 348 | render_to_string('forbidden.html', RequestContext(request))) | 350 | render_to_string('forbidden.html', RequestContext(request))) |
229 | 351 | |||
230 | 352 | |||
231 | 353 | class ErrorPage(object): | ||
232 | 354 | def __init__(self, status, errormsg=None): | ||
233 | 355 | self.template_name = '%s.html' % status | ||
234 | 356 | self.status_code = status | ||
235 | 357 | self.errormsg = errormsg | ||
236 | 358 | |||
237 | 359 | def __call__(self, request): | ||
238 | 360 | accept = request.META.get('ACCEPT', '') | ||
239 | 361 | if 'application/json' in accept and not 'text/html' in accept: | ||
240 | 362 | return self.render_json_error(request) | ||
241 | 363 | else: | ||
242 | 364 | return self.render_html_error(request) | ||
243 | 365 | |||
244 | 366 | def render_html_error(self, request): | ||
245 | 367 | oops_id = self.get_oops_id(request) | ||
246 | 368 | template = get_template(self.template_name) | ||
247 | 369 | atts = { | ||
248 | 370 | 'oops_id': oops_id, | ||
249 | 371 | 'errormsg': self.errormsg, | ||
250 | 372 | } | ||
251 | 373 | context = RequestContext(request, atts) | ||
252 | 374 | return HttpResponse(template.render(context), status=self.status_code) | ||
253 | 375 | |||
254 | 376 | def render_json_error(self, request): | ||
255 | 377 | oops_id = self.get_oops_id(request) | ||
256 | 378 | oops_context = request.META.get('oops.context', {}) | ||
257 | 379 | exc_info = oops_context.get('exc_info', (None, None, None)) | ||
258 | 380 | exception_type = unicode(exc_info[0]) | ||
259 | 381 | response = json.dumps({ | ||
260 | 382 | 'oops_id': oops_id, | ||
261 | 383 | 'error_type': exception_type, | ||
262 | 384 | 'error_msg': "An error occurred and has been logged.", | ||
263 | 385 | }) | ||
264 | 386 | return HttpResponse(response, status=self.status_code, | ||
265 | 387 | content_type='application/json') | ||
266 | 388 | |||
267 | 389 | def get_oops_id(self, request): | ||
268 | 390 | oops_report = request.environ.get('oops.report', {}) | ||
269 | 391 | return oops_report.get('id', 'OOPS ID not available.') | ||
270 | 392 | |||
271 | 393 | |||
272 | 394 | server_error = ErrorPage(500) | ||
273 | 395 | page_not_found = ErrorPage(404) | ||
274 | 396 | |||
275 | 397 | |||
276 | 398 | class ArtificialOopsException(Exception): | ||
277 | 399 | pass | ||
278 | 400 | |||
279 | 401 | |||
280 | 402 | def die(request): | ||
281 | 403 | """Just trigger an oops report""" | ||
282 | 404 | raise ArtificialOopsException() | ||
283 | 349 | 405 | ||
284 | === modified file 'src/webcatalog/wsgi.py' | |||
285 | --- src/webcatalog/wsgi.py 2012-08-23 14:59:18 +0000 | |||
286 | +++ src/webcatalog/wsgi.py 2013-03-01 12:52:22 +0000 | |||
287 | @@ -23,8 +23,40 @@ | |||
288 | 23 | import oops_wsgi.django | 23 | import oops_wsgi.django |
289 | 24 | import os | 24 | import os |
290 | 25 | import platform | 25 | import platform |
291 | 26 | import uuid | ||
292 | 26 | 27 | ||
293 | 27 | from django.conf import settings | 28 | from django.conf import settings |
294 | 29 | from django.core.handlers import wsgi | ||
295 | 30 | |||
296 | 31 | |||
297 | 32 | class EagerOOPSWSGIHandler(wsgi.WSGIHandler): | ||
298 | 33 | """Pre-generates an oops id on exception.""" | ||
299 | 34 | def handle_uncaught_exception(self, request, resolver, exc_info): | ||
300 | 35 | # This will pass the exception information back to oops. It | ||
301 | 36 | # should not be necessary once | ||
302 | 37 | # https://code.djangoproject.com/ticket/16674 has been merged. | ||
303 | 38 | if 'oops.context' in request.environ: | ||
304 | 39 | request.environ['oops.context']['exc_info'] = exc_info | ||
305 | 40 | # Generate OOPS id early so that we can render the error page right | ||
306 | 41 | # away and not depend on passing thread locals around. | ||
307 | 42 | if 'oops.report' in request.environ: | ||
308 | 43 | unique_id = uuid.uuid4().hex | ||
309 | 44 | request.environ['oops.report']['id'] = "OOPS-%s" % unique_id | ||
310 | 45 | |||
311 | 46 | return super(EagerOOPSWSGIHandler, self).handle_uncaught_exception( | ||
312 | 47 | request, resolver, exc_info) | ||
313 | 48 | |||
314 | 49 | def __call__(self, environ, start_response): | ||
315 | 50 | def start_response_with_exc_info(status, headers, exc_info=None): | ||
316 | 51 | """Custom start_response callback for wsgi.""" | ||
317 | 52 | # This will pass the exception information back to oops_wgi. It | ||
318 | 53 | # should not be necessary once | ||
319 | 54 | # https://code.djangoproject.com/ticket/16674 has been merged. | ||
320 | 55 | if exc_info is None: | ||
321 | 56 | exc_info = environ['oops.context'].get('exc_info', None) | ||
322 | 57 | return start_response(status, headers, exc_info) | ||
323 | 58 | return super(EagerOOPSWSGIHandler, self).__call__( | ||
324 | 59 | environ, start_response_with_exc_info) | ||
325 | 28 | 60 | ||
326 | 29 | 61 | ||
327 | 30 | def make_app(): | 62 | def make_app(): |
328 | @@ -38,9 +70,7 @@ | |||
329 | 38 | if logging_config: | 70 | if logging_config: |
330 | 39 | logging.config.fileConfig(logging_config) | 71 | logging.config.fileConfig(logging_config) |
331 | 40 | 72 | ||
335 | 41 | # This is needed to workaround a bug in Django, see the file it is | 73 | non_oops_app = EagerOOPSWSGIHandler() |
333 | 42 | # implemented in for more details. | ||
334 | 43 | non_oops_app = oops_wsgi.django.OOPSWSGIHandler() | ||
336 | 44 | 74 | ||
337 | 45 | config = oops_dictconfig.config_from_dict(settings.OOPSES) | 75 | config = oops_dictconfig.config_from_dict(settings.OOPSES) |
338 | 46 | oops_wsgi.install_hooks(config) | 76 | oops_wsgi.install_hooks(config) |
Why there's render_html_error but no render_json_error (which should be extracted from __call__)?