summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.txt5
-rw-r--r--CONTRIBUTORS.txt2
-rw-r--r--README.rst6
m---------docs/_themes0
-rw-r--r--docs/narr/urldispatch.rst4
-rw-r--r--docs/narr/viewconfig.rst6
-rw-r--r--docs/tutorials/wiki/authorization.rst4
-rw-r--r--pyramid/config/views.py25
-rw-r--r--pyramid/tests/test_config/test_views.py26
-rw-r--r--pyramid/view.py32
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