diff options
| -rw-r--r-- | CHANGES.txt | 5 | ||||
| -rw-r--r-- | CONTRIBUTORS.txt | 2 | ||||
| -rw-r--r-- | README.rst | 6 | ||||
| m--------- | docs/_themes | 0 | ||||
| -rw-r--r-- | docs/narr/urldispatch.rst | 4 | ||||
| -rw-r--r-- | docs/narr/viewconfig.rst | 6 | ||||
| -rw-r--r-- | docs/tutorials/wiki/authorization.rst | 4 | ||||
| -rw-r--r-- | pyramid/config/views.py | 25 | ||||
| -rw-r--r-- | pyramid/tests/test_config/test_views.py | 26 | ||||
| -rw-r--r-- | pyramid/view.py | 32 |
10 files changed, 98 insertions, 12 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index 19d77eb68..639b9b802 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -119,6 +119,11 @@ Features explicitly different from ``request.response``. This does not change the API of a renderer. See https://github.com/Pylons/pyramid/pull/1563 +- The ``append_slash`` argument of ```Configurator().add_notfound_view()`` will + now accept anything that implements the ``IResponse`` interface and will use + that as the response class instead of the default ``HTTPFound``. See + https://github.com/Pylons/pyramid/pull/1610 + Bug Fixes --------- diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 4f9bd6e41..3d574f99d 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -244,3 +244,5 @@ Contributors - Geoffrey T. Dairiki, 2015/02/06 - David Glick, 2015/02/12 + +- Donald Stufft, 2015/03/15 diff --git a/README.rst b/README.rst index adf7eea5e..6de42ea40 100644 --- a/README.rst +++ b/README.rst @@ -6,7 +6,11 @@ Pyramid .. image:: https://readthedocs.org/projects/pyramid/badge/?version=master :target: http://docs.pylonsproject.org/projects/pyramid/en/master/ - :alt: Documentation Status + :alt: Master Documentation Status + +.. image:: https://readthedocs.org/projects/pyramid/badge/?version=latest + :target: http://docs.pylonsproject.org/projects/pyramid/en/latest/ + :alt: Latest Documentation Status Pyramid is a small, fast, down-to-earth, open source Python web framework. It makes real-world web application development and diff --git a/docs/_themes b/docs/_themes -Subproject b14bf8c2a0d95ae8e3d38d07ad3721370ae6f3f +Subproject 382cba80fbd6a7424818d17ec63ca520e485f10 diff --git a/docs/narr/urldispatch.rst b/docs/narr/urldispatch.rst index ca6a55164..fa3e734fe 100644 --- a/docs/narr/urldispatch.rst +++ b/docs/narr/urldispatch.rst @@ -842,7 +842,9 @@ route. When configured, along with at least one other route in your application, this view will be invoked if the value of ``PATH_INFO`` does not already end in a slash, and if the value of ``PATH_INFO`` *plus* a slash matches any route's pattern. In this case it does an HTTP redirect to the -slash-appended ``PATH_INFO``. +slash-appended ``PATH_INFO``. In addition you may pass anything that implements +:class:`pyramid.interfaces.IResponse` which will then be used in place of the +default class (:class:`pyramid.httpexceptions.HTTPFound`). Let's use an example. If the following routes are configured in your application: diff --git a/docs/narr/viewconfig.rst b/docs/narr/viewconfig.rst index a0feef8d7..d5203c6ba 100644 --- a/docs/narr/viewconfig.rst +++ b/docs/narr/viewconfig.rst @@ -325,7 +325,7 @@ configured view. ``match_param`` This param may be either a single string of the format "key=value" or a - dict of key/value pairs. + tuple containing one or more of these strings. This argument ensures that the view will only be called when the :term:`request` has key/value pairs in its :term:`matchdict` that equal @@ -334,8 +334,8 @@ configured view. hand side of the expression (``edit``) for the view to "match" the current request. - If the ``match_param`` is a dict, every key/value pair must match for the - predicate to pass. + If the ``match_param`` is a tuple, every key/value pair must match + for the predicate to pass. If ``match_param`` is not supplied, the view will be invoked without consideration of the keys and values in ``request.matchdict``. diff --git a/docs/tutorials/wiki/authorization.rst b/docs/tutorials/wiki/authorization.rst index 93cd0c18e..6c98b6f3a 100644 --- a/docs/tutorials/wiki/authorization.rst +++ b/docs/tutorials/wiki/authorization.rst @@ -197,9 +197,9 @@ Add the following import statements to the head of ``tutorial/tutorial/views.py``: .. literalinclude:: src/authorization/tutorial/views.py - :lines: 6-13,15-17 + :lines: 6-17 :linenos: - :emphasize-lines: 3,6-9,11 + :emphasize-lines: 3,6-11 :language: python (Only the highlighted lines, with other necessary modifications, diff --git a/pyramid/config/views.py b/pyramid/config/views.py index 4c927f5b5..e2da950be 100644 --- a/pyramid/config/views.py +++ b/pyramid/config/views.py @@ -1705,6 +1705,24 @@ class ViewsConfiguratorMixin(object): Pyramid will return the result of the view callable provided as ``view``, as normal. + If the argument provided as ``append_slash`` is not a boolean but + instead implements :class:`~pyramid.interfaces.IResponse`, the + append_slash logic will behave as if ``append_slash=True`` was passed, + but the provided class will be used as the response class instead of + the default :class:`~pyramid.httpexceptions.HTTPFound` response class + when a redirect is performed. For example: + + .. code-block:: python + + from pyramid.httpexceptions import HTTPMovedPermanently + config.add_notfound_view(append_slash=HTTPMovedPermanently) + + The above means that a redirect to a slash-appended route will be + attempted, but instead of :class:`~pyramid.httpexceptions.HTTPFound` + being used, :class:`~pyramid.httpexceptions.HTTPMovedPermanently will + be used` for the redirect response if a slash-appended route is found. + + .. versionchanged:: 1.6 .. versionadded:: 1.3 """ for arg in ('name', 'permission', 'context', 'for_', 'http_cache'): @@ -1739,7 +1757,12 @@ class ViewsConfiguratorMixin(object): settings.update(predicates) if append_slash: view = self._derive_view(view, attr=attr, renderer=renderer) - view = AppendSlashNotFoundViewFactory(view) + if IResponse.implementedBy(append_slash): + view = AppendSlashNotFoundViewFactory( + view, redirect_class=append_slash, + ) + else: + view = AppendSlashNotFoundViewFactory(view) settings['view'] = view else: settings['attr'] = attr diff --git a/pyramid/tests/test_config/test_views.py b/pyramid/tests/test_config/test_views.py index b4ad357ff..1c2d300a1 100644 --- a/pyramid/tests/test_config/test_views.py +++ b/pyramid/tests/test_config/test_views.py @@ -2013,7 +2013,7 @@ class TestViewsConfigurationMixin(unittest.TestCase): from pyramid.renderers import null_renderer from zope.interface import implementedBy from pyramid.interfaces import IRequest - from pyramid.httpexceptions import HTTPNotFound + from pyramid.httpexceptions import HTTPFound, HTTPNotFound config = self._makeOne(autocommit=True) config.add_route('foo', '/foo/') def view(request): return Response('OK') @@ -2026,6 +2026,30 @@ class TestViewsConfigurationMixin(unittest.TestCase): ctx_iface=implementedBy(HTTPNotFound), request_iface=IRequest) result = view(None, request) + self.assertTrue(isinstance(result, HTTPFound)) + self.assertEqual(result.location, '/scriptname/foo/?a=1&b=2') + + def test_add_notfound_view_append_slash_custom_response(self): + from pyramid.response import Response + from pyramid.renderers import null_renderer + from zope.interface import implementedBy + from pyramid.interfaces import IRequest + from pyramid.httpexceptions import HTTPMovedPermanently, HTTPNotFound + config = self._makeOne(autocommit=True) + config.add_route('foo', '/foo/') + def view(request): return Response('OK') + config.add_notfound_view( + view, renderer=null_renderer,append_slash=HTTPMovedPermanently + ) + request = self._makeRequest(config) + request.environ['PATH_INFO'] = '/foo' + request.query_string = 'a=1&b=2' + request.path = '/scriptname/foo' + view = self._getViewCallable(config, + ctx_iface=implementedBy(HTTPNotFound), + request_iface=IRequest) + result = view(None, request) + self.assertTrue(isinstance(result, HTTPMovedPermanently)) self.assertEqual(result.location, '/scriptname/foo/?a=1&b=2') def test_add_notfound_view_with_view_defaults(self): diff --git a/pyramid/view.py b/pyramid/view.py index ffa3fa791..fd020c7ea 100644 --- a/pyramid/view.py +++ b/pyramid/view.py @@ -255,10 +255,11 @@ class AppendSlashNotFoundViewFactory(object): .. deprecated:: 1.3 """ - def __init__(self, notfound_view=None): + def __init__(self, notfound_view=None, redirect_class=HTTPFound): if notfound_view is None: notfound_view = default_exceptionresponse_view self.notfound_view = notfound_view + self.redirect_class = redirect_class def __call__(self, context, request): path = decode_path_info(request.environ['PATH_INFO'] or '/') @@ -271,7 +272,7 @@ class AppendSlashNotFoundViewFactory(object): qs = request.query_string if qs: qs = '?' + qs - return HTTPFound(location=request.path+'/'+qs) + return self.redirect_class(location=request.path+'/'+qs) return self.notfound_view(context, request) append_slash_notfound_view = AppendSlashNotFoundViewFactory() @@ -334,6 +335,31 @@ class notfound_view_config(object): redirect to the URL implied by the route; if it does not, Pyramid will return the result of the view callable provided as ``view``, as normal. + If the argument provided as ``append_slash`` is not a boolean but + instead implements :class:`~pyramid.interfaces.IResponse`, the + append_slash logic will behave as if ``append_slash=True`` was passed, + but the provided class will be used as the response class instead of + the default :class:`~pyramid.httpexceptions.HTTPFound` response class + when a redirect is performed. For example: + + .. code-block:: python + + from pyramid.httpexceptions import ( + HTTPMovedPermanently, + HTTPNotFound + ) + + @notfound_view_config(append_slash=HTTPMovedPermanently) + def aview(request): + return HTTPNotFound('not found') + + The above means that a redirect to a slash-appended route will be + attempted, but instead of :class:`~pyramid.httpexceptions.HTTPFound` + being used, :class:`~pyramid.httpexceptions.HTTPMovedPermanently will + be used` for the redirect response if a slash-appended route is found. + + .. versionchanged:: 1.6 + See :ref:`changing_the_notfound_view` for detailed usage information. """ @@ -383,7 +409,7 @@ class forbidden_view_config(object): @forbidden_view_config() def forbidden(request): - return Response('You are not allowed', status='401 Unauthorized') + return Response('You are not allowed', status='403 Forbidden') All arguments passed to this function have the same meaning as :meth:`pyramid.view.view_config` and each predicate argument restricts |
