From 85093dba5ad0b606d8998e3708e4581901cf6dcc Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Sat, 13 Aug 2011 14:58:52 -0400 Subject: - WSGIHTTPException (HTTPFound, HTTPNotFound, etc) now has a new API named "prepare" which renders the body and content type when it is provided with a WSGI environ. Required for debug toolbar. - Once ``__call__`` or ``prepare`` is called on a WSGIHTTPException, the body will be set, and subsequent calls to ``__call__`` will always return the same body. Delete the body attribute to rerender the exception body. --- CHANGES.txt | 8 ++++ pyramid/httpexceptions.py | 89 ++++++++++++++++++------------------ pyramid/interfaces.py | 2 + pyramid/tests/test_httpexceptions.py | 10 ++++ 4 files changed, 65 insertions(+), 44 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 995e64e0c..8a14a82de 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -58,6 +58,14 @@ Internal - The Pyramid "exception view" machinery is now implemented as a "tween" (``pyramid.tweens.excview_tween_factory``). +- WSGIHTTPException (HTTPFound, HTTPNotFound, etc) now has a new API named + "prepare" which renders the body and content type when it is provided with + a WSGI environ. Required for debug toolbar. + +- Once ``__call__`` or ``prepare`` is called on a WSGIHTTPException, the body + will be set, and subsequent calls to ``__call__`` will always return the + same body. Delete the body attribute to rerender the exception body. + Deprecations ------------ diff --git a/pyramid/httpexceptions.py b/pyramid/httpexceptions.py index 4d23db8d2..8c49889be 100644 --- a/pyramid/httpexceptions.py +++ b/pyramid/httpexceptions.py @@ -207,48 +207,50 @@ ${body}''') def __str__(self): return self.detail or self.explanation - def _set_default_attrs(self, environ): - html_comment = '' - comment = self.comment or '' - accept = environ.get('HTTP_ACCEPT', '') - if accept and 'html' in accept or '*/*' in accept: - self.content_type = 'text/html' - escape = _html_escape - page_template = self.html_template_obj - br = '
' - if comment: - html_comment = '' % escape(comment) - else: - self.content_type = 'text/plain' - escape = _no_escape - page_template = self.plain_template_obj - br = '\n' - if comment: - html_comment = escape(comment) - args = { - 'br':br, - 'explanation': escape(self.explanation), - 'detail': escape(self.detail or ''), - 'comment': escape(comment), - 'html_comment':html_comment, - } - body_tmpl = self.body_template_obj - if WSGIHTTPException.body_template_obj is not body_tmpl: - # Custom template; add headers to args - for k, v in environ.items(): - if (not k.startswith('wsgi.')) and ('.' in k): - # omit custom environ variables, stringifying them may - # trigger code that should not be executed here; see - # https://github.com/Pylons/pyramid/issues/239 - continue - args[k] = escape(v) - for k, v in self.headers.items(): - args[k.lower()] = escape(v) - body = body_tmpl.substitute(args) - page = page_template.substitute(status=self.status, body=body) - if isinstance(page, unicode): - page = page.encode(self.charset) - self.app_iter = [page] + def prepare(self, environ): + if not self.body and not self.empty_body: + html_comment = '' + comment = self.comment or '' + accept = environ.get('HTTP_ACCEPT', '') + if accept and 'html' in accept or '*/*' in accept: + self.content_type = 'text/html' + escape = _html_escape + page_template = self.html_template_obj + br = '
' + if comment: + html_comment = '' % escape(comment) + else: + self.content_type = 'text/plain' + escape = _no_escape + page_template = self.plain_template_obj + br = '\n' + if comment: + html_comment = escape(comment) + args = { + 'br':br, + 'explanation': escape(self.explanation), + 'detail': escape(self.detail or ''), + 'comment': escape(comment), + 'html_comment':html_comment, + } + body_tmpl = self.body_template_obj + if WSGIHTTPException.body_template_obj is not body_tmpl: + # Custom template; add headers to args + for k, v in environ.items(): + if (not k.startswith('wsgi.')) and ('.' in k): + # omit custom environ variables, stringifying them may + # trigger code that should not be executed here; see + # https://github.com/Pylons/pyramid/issues/239 + continue + args[k] = escape(v) + for k, v in self.headers.items(): + args[k.lower()] = escape(v) + body = body_tmpl.substitute(args) + page = page_template.substitute(status=self.status, body=body) + if isinstance(page, unicode): + page = page.encode(self.charset) + self.app_iter = [page] + self.body = page @property def wsgi_response(self): @@ -265,8 +267,7 @@ ${body}''') # - does not manufacture a new response object when generating # the default response # - if not self.body and not self.empty_body: - self._set_default_attrs(environ) + self.prepare(environ) return Response.__call__(self, environ, start_response) class HTTPError(WSGIHTTPException): diff --git a/pyramid/interfaces.py b/pyramid/interfaces.py index 11721d23a..22d879506 100644 --- a/pyramid/interfaces.py +++ b/pyramid/interfaces.py @@ -265,6 +265,8 @@ class IExceptionResponse(IException, IResponse): :class:`pyramid.response.Response`, including :class:`pyramid.httpexceptions.HTTPNotFound` and :class:`pyramid.httpexceptions.HTTPForbidden`).""" + def prepare(environ): + """ Prepares the response for being called as a WSGI application """ class IBeforeRender(Interface): """ diff --git a/pyramid/tests/test_httpexceptions.py b/pyramid/tests/test_httpexceptions.py index 7db071d03..9c7b38e27 100644 --- a/pyramid/tests/test_httpexceptions.py +++ b/pyramid/tests/test_httpexceptions.py @@ -182,6 +182,16 @@ class TestWSGIHTTPException(unittest.TestCase): self.assertTrue(start_response.headerlist) self.assertEqual(start_response.status, '200 OK') + def test_call_returns_same_body_called_twice(self): + # optimization + cls = self._getTargetSubclass() + exc = cls() + environ = _makeEnviron() + environ['HTTP_ACCEPT'] = '*/*' + start_response = DummyStartResponse() + app_iter = exc(environ, start_response) + self.assertEqual(app_iter[0], exc.body) + def test__default_app_iter_no_comment_plain(self): cls = self._getTargetSubclass() exc = cls() -- cgit v1.2.3