summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris McDonough <chrism@plope.com>2011-08-13 14:58:52 -0400
committerChris McDonough <chrism@plope.com>2011-08-13 14:58:52 -0400
commit85093dba5ad0b606d8998e3708e4581901cf6dcc (patch)
treefe8ac7645d040f1df3c37c132ca04bc22fe46f3b
parent157c29002377b65834a960fd2d59c40bdd43f417 (diff)
downloadpyramid-85093dba5ad0b606d8998e3708e4581901cf6dcc.tar.gz
pyramid-85093dba5ad0b606d8998e3708e4581901cf6dcc.tar.bz2
pyramid-85093dba5ad0b606d8998e3708e4581901cf6dcc.zip
- 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.
-rw-r--r--CHANGES.txt8
-rw-r--r--pyramid/httpexceptions.py89
-rw-r--r--pyramid/interfaces.py2
-rw-r--r--pyramid/tests/test_httpexceptions.py10
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 = '<br/>'
- if comment:
- html_comment = '<!-- %s -->' % 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 = '<br/>'
+ if comment:
+ html_comment = '<!-- %s -->' % 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()