summaryrefslogtreecommitdiff
path: root/pecan
diff options
context:
space:
mode:
authorRyan Petrello <lists@ryanpetrello.com>2014-06-25 11:19:43 -0400
committerRyan Petrello <lists@ryanpetrello.com>2014-06-25 11:41:53 -0400
commit21f70bbba74ab940b3bf24bfb4cc859fe8926492 (patch)
treeab41f4d7fbc115680355babfbc05049d14984acd /pecan
parent6c7a8b3ee9658c4bb2a2d72f241720861d1da56f (diff)
downloadpecan-21f70bbba74ab940b3bf24bfb4cc859fe8926492.tar.gz
Add support for specifying custom request and response implementations.
I noticed that people using pecan have taken to writing custom webob req/resp subclasses and monkeypatching onto `pecan.request` and `pecan.response`. Let's give them what they need to do this properly. Change-Id: If0ac953e381cec3a744388000a3b3afc0ea2525c
Diffstat (limited to 'pecan')
-rw-r--r--pecan/__init__.py8
-rw-r--r--pecan/core.py32
-rw-r--r--pecan/tests/test_base.py39
3 files changed, 65 insertions, 14 deletions
diff --git a/pecan/__init__.py b/pecan/__init__.py
index c294572..4c52713 100644
--- a/pecan/__init__.py
+++ b/pecan/__init__.py
@@ -1,6 +1,6 @@
from .core import (
- abort, override_template, Pecan, load_app, redirect, render,
- request, response
+ abort, override_template, Pecan, Request, Response, load_app,
+ redirect, render, request, response
)
from .decorators import expose
from .hooks import RequestViewerHook
@@ -21,8 +21,8 @@ import warnings
__all__ = [
- 'make_app', 'load_app', 'Pecan', 'request', 'response',
- 'override_template', 'expose', 'conf', 'set_config', 'render',
+ 'make_app', 'load_app', 'Pecan', 'Request', 'Response', 'request',
+ 'response', 'override_template', 'expose', 'conf', 'set_config', 'render',
'abort', 'redirect'
]
diff --git a/pecan/core.py b/pecan/core.py
index 44bad0e..c2f1dab 100644
--- a/pecan/core.py
+++ b/pecan/core.py
@@ -10,7 +10,8 @@ import operator
import six
-from webob import Request, Response, exc, acceptparse
+from webob import (Request as WebObRequest, Response as WebObResponse, exc,
+ acceptparse)
from .compat import urlparse, unquote_plus, izip
from .secure import handle_security
@@ -37,6 +38,14 @@ class RoutingState(object):
self.controller = controller
+class Request(WebObRequest):
+ pass
+
+
+class Response(WebObResponse):
+ pass
+
+
def proxy(key):
class ObjectProxy(object):
@@ -120,7 +129,7 @@ def redirect(location=None, internal=False, code=None, headers={},
:param code: The HTTP status code to use for the redirect. Defaults to 302.
:param headers: Any HTTP headers to send with the response, as a
dictionary.
- :param request: The :class:`webob.request.BaseRequest` instance to use.
+ :param request: The :class:`pecan.Request` instance to use.
'''
request = request or state.request
@@ -200,11 +209,14 @@ class PecanBase(object):
template_path='templates', hooks=lambda: [],
custom_renderers={}, extra_template_vars={},
force_canonical=True, guess_content_type_from_ext=True,
- context_local_factory=None, **kw):
+ context_local_factory=None, request_cls=Request,
+ response_cls=Response, **kw):
if isinstance(root, six.string_types):
root = self.__translate_root__(root)
self.root = root
+ self.request_cls = request_cls
+ self.response_cls = response_cls
self.renderers = RendererFactory(custom_renderers, extra_template_vars)
self.default_renderer = default_renderer
@@ -304,7 +316,7 @@ class PecanBase(object):
result = getattr(hook, hook_type)(*args)
# on_error hooks can choose to return a Response, which will
# be used instead of the standard error pages.
- if hook_type == 'on_error' and isinstance(result, Response):
+ if hook_type == 'on_error' and isinstance(result, WebObResponse):
return result
def get_args(self, state, all_params, remainder, argspec, im_self):
@@ -516,7 +528,7 @@ class PecanBase(object):
# care of filling it out
if result is response:
return
- elif isinstance(result, Response):
+ elif isinstance(result, WebObResponse):
state.response = result
return
@@ -567,8 +579,8 @@ class PecanBase(object):
'''
# create the request and response object
- req = Request(environ)
- resp = Response()
+ req = self.request_cls(environ)
+ resp = self.response_cls()
state = RoutingState(req, resp, self)
controller = None
@@ -597,7 +609,7 @@ class PecanBase(object):
)
# if the on_error handler returned a Response, use it.
- if isinstance(on_error_result, Response):
+ if isinstance(on_error_result, WebObResponse):
state.response = on_error_result
else:
if not isinstance(e, exc.HTTPException):
@@ -670,6 +682,10 @@ class Pecan(PecanBase):
:param use_context_locals: When `True`, `pecan.request` and
`pecan.response` will be available as
thread-local references.
+ :param request_cls: Can be used to specify a custom `pecan.request` object.
+ Defaults to `pecan.Request`.
+ :param response_cls: Can be used to specify a custom `pecan.response`
+ object. Defaults to `pecan.Response`.
'''
def __new__(cls, *args, **kw):
diff --git a/pecan/tests/test_base.py b/pecan/tests/test_base.py
index 81471ec..89b7a43 100644
--- a/pecan/tests/test_base.py
+++ b/pecan/tests/test_base.py
@@ -14,8 +14,8 @@ from six import b as b_
from six.moves import cStringIO as StringIO
from pecan import (
- Pecan, expose, request, response, redirect, abort, make_app,
- override_template, render
+ Pecan, Request, Response, expose, request, response, redirect,
+ abort, make_app, override_template, render
)
from pecan.templating import (
_builtin_renderers as builtin_renderers, error_formatters
@@ -954,6 +954,41 @@ class TestManualResponse(PecanTestCase):
assert r.body == b_('Hello, World!')
+class TestCustomResponseandRequest(PecanTestCase):
+
+ def test_custom_objects(self):
+
+ class CustomRequest(Request):
+
+ @property
+ def headers(self):
+ headers = super(CustomRequest, self).headers
+ headers['X-Custom-Request'] = 'ABC'
+ return headers
+
+ class CustomResponse(Response):
+
+ @property
+ def headers(self):
+ headers = super(CustomResponse, self).headers
+ headers['X-Custom-Response'] = 'XYZ'
+ return headers
+
+ class RootController(object):
+ @expose()
+ def index(self):
+ return request.headers.get('X-Custom-Request')
+
+ app = TestApp(Pecan(
+ RootController(),
+ request_cls=CustomRequest,
+ response_cls=CustomResponse
+ ))
+ r = app.get('/')
+ assert r.body == b_('ABC')
+ assert r.headers.get('X-Custom-Response') == 'XYZ'
+
+
class TestThreadLocalState(PecanTestCase):
def test_thread_local_dir(self):