- New API: ``pyramid.config.Configurator.add_notfound_view``. This is a
wrapper for ``pyramid.Config.configurator.add_view`` which provides easy
append_slash support. It should be preferred over calling ``add_view``
directly with ``context=HTTPNotFound`` as was previously recommended.
- New API: ``pyramid.view.notfound_view_config``. This is a decorator
constructor like ``pyramid.view.view_config`` that calls
``pyramid.config.Configurator.add_notfound_view`` when scanned. It should
be preferred over using ``pyramid.view.view_config`` with
``context=HTTPNotFound`` as was previously recommended.
- The older deprecated ``set_notfound_view`` Configurator method is now an
alias for the new ``add_notfound_view`` Configurator method. This has the
following impact: the ``context`` sent to views with a ``(context,
request)`` call signature registered via the deprecated
``add_notfound_view``/``set_notfound_view`` will now be the HTTPNotFound
exception object instead of the actual resource context found. Use
``request.context`` to get the actual resource context. It's also
recommended to disuse ``set_notfound_view`` in favor of
``add_notfound_view``, despite the aliasing.
- The API documentation for ``pyramid.view.append_slash_notfound_view`` and
``pyramid.view.AppendSlashNotFoundViewFactory`` was removed. These names
still exist and are still importable, but they are no longer APIs. Use
``pyramid.config.Configurator.add_notfound_view(append_slash=True)`` or
``pyramid.view.notfound_view_config(append_slash=True)`` to get the same
behavior.
- The ``set_forbidden_view`` method of the Configurator was removed from the
documentation. It has been deprecated since Pyramid 1.1.
- The AppendSlashNotFoundViewFactory used request.path to match routes. This
was wrong because request.path contains the script name, and this would
cause it to fail in circumstances where the script name was not empty. It
should have used request.path_info, and now does.
- Updated the "Registering a Not Found View" section of the "Hooks" chapter,
replacing explanations of registering a view using ``add_view`` or
``view_config`` with ones using ``add_notfound_view`` or
``notfound_view_config``.
- Updated the "Redirecting to Slash-Appended Routes" section of the "URL
Dispatch" chapter, replacing explanations of registering a view using
``add_view`` or ``view_config`` with ones using ``add_notfound_view`` or
``notfound_view_config``
1 files added
13 files modified
| | |
| | | requirement that the debug toolbar's own views and methods not show up in |
| | | the introspector. |
| | | |
| | | - New API: ``pyramid.config.Configurator.add_notfound_view``. This is a |
| | | wrapper for ``pyramid.Config.configurator.add_view`` which provides easy |
| | | append_slash support. It should be preferred over calling ``add_view`` |
| | | directly with ``context=HTTPNotFound`` as was previously recommended. |
| | | |
| | | - New API: ``pyramid.view.notfound_view_config``. This is a decorator |
| | | constructor like ``pyramid.view.view_config`` that calls |
| | | ``pyramid.config.Configurator.add_notfound_view`` when scanned. It should |
| | | be preferred over using ``pyramid.view.view_config`` with |
| | | ``context=HTTPNotFound`` as was previously recommended. |
| | | |
| | | Backwards Incompatibilities |
| | | --------------------------- |
| | | |
| | |
| | | |
| | | - The ``pyramid.registry.noop_introspector`` API object has been removed. |
| | | |
| | | - The older deprecated ``set_notfound_view`` Configurator method is now an |
| | | alias for the new ``add_notfound_view`` Configurator method. This has the |
| | | following impact: the ``context`` sent to views with a ``(context, |
| | | request)`` call signature registered via the deprecated |
| | | ``add_notfound_view``/``set_notfound_view`` will now be the HTTPNotFound |
| | | exception object instead of the actual resource context found. Use |
| | | ``request.context`` to get the actual resource context. It's also |
| | | recommended to disuse ``set_notfound_view`` in favor of |
| | | ``add_notfound_view``, despite the aliasing. |
| | | |
| | | Deprecations |
| | | ------------ |
| | | |
| | | - The API documentation for ``pyramid.view.append_slash_notfound_view`` and |
| | | ``pyramid.view.AppendSlashNotFoundViewFactory`` was removed. These names |
| | | still exist and are still importable, but they are no longer APIs. Use |
| | | ``pyramid.config.Configurator.add_notfound_view(append_slash=True)`` or |
| | | ``pyramid.view.notfound_view_config(append_slash=True)`` to get the same |
| | | behavior. |
| | | |
| | | - The ``set_forbidden_view`` method of the Configurator was removed from the |
| | | documentation. It has been deprecated since Pyramid 1.1. |
| | | |
| | | Bug Fixes |
| | | --------- |
| | | |
| | | - The static file response object used by ``config.add_static_view`` opened |
| | | the static file twice, when it only needed to open it once. |
| | | |
| | | - The AppendSlashNotFoundViewFactory used request.path to match routes. This |
| | | was wrong because request.path contains the script name, and this would |
| | | cause it to fail in circumstances where the script name was not empty. It |
| | | should have used request.path_info, and now does. |
| | | |
| | | Documentation |
| | | ------------- |
| | | |
| | | - Updated the "Registering a Not Found View" section of the "Hooks" chapter, |
| | | replacing explanations of registering a view using ``add_view`` or |
| | | ``view_config`` with ones using ``add_notfound_view`` or |
| | | ``notfound_view_config``. |
| | | |
| | | - Updated the "Redirecting to Slash-Appended Routes" section of the "URL |
| | | Dispatch" chapter, replacing explanations of registering a view using |
| | | ``add_view`` or ``view_config`` with ones using ``add_notfound_view`` or |
| | | ``notfound_view_config`` |
| | | |
| | | 1.3a8 (2012-02-19) |
| | | ================== |
| | | |
| | |
| | | Pyramid TODOs |
| | | ============= |
| | | |
| | | Must-Have |
| | | --------- |
| | | |
| | | - Fix scaffolds and tutorials to use notfound_view_config rather than |
| | | view_config. |
| | | |
| | | Nice-to-Have |
| | | ------------ |
| | | |
| | | - Add forbidden_view_config? |
| | | |
| | | - Add docs about upgrading between Pyramid versions (e.g. how to see |
| | | deprecation warnings). |
| | |
| | | |
| | | with config.partial(introspection=False) as c: |
| | | c.add_view(..) |
| | | |
| | | - Decorator for append_slash_notfound_view_factory. |
| | | |
| | | - Introspection: |
| | | |
| | |
| | | .. automethod:: add_route |
| | | .. automethod:: add_static_view(name, path, cache_max_age=3600, permission=NO_PERMISSION_REQUIRED) |
| | | .. automethod:: add_view |
| | | .. automethod:: set_forbidden_view |
| | | .. automethod:: set_notfound_view |
| | | .. automethod:: add_notfound_view |
| | | |
| | | :methodcategory:`Adding an Event Subscriber` |
| | | |
| | |
| | | .. autoclass:: view_defaults |
| | | :members: |
| | | |
| | | .. autoclass:: notfound_view_config |
| | | :members: |
| | | |
| | | .. autoclass:: static |
| | | :members: |
| | | :inherited-members: |
| | | |
| | | .. autofunction:: append_slash_notfound_view(context, request) |
| | | |
| | | .. autoclass:: AppendSlashNotFoundViewFactory |
| | | |
| | |
| | | exists. The default not found view can be overridden through application |
| | | configuration. |
| | | |
| | | The :term:`not found view` callable is a view callable like any other. The |
| | | :term:`view configuration` which causes it to be a "not found" view consists |
| | | only of naming the :exc:`pyramid.httpexceptions.HTTPNotFound` class as the |
| | | ``context`` of the view configuration. |
| | | |
| | | If your application uses :term:`imperative configuration`, you can replace |
| | | the Not Found view by using the :meth:`pyramid.config.Configurator.add_view` |
| | | method to register an "exception view": |
| | | the Not Found view by using the |
| | | :meth:`pyramid.config.Configurator.add_notfound_view` method: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | from pyramid.httpexceptions import HTTPNotFound |
| | | from helloworld.views import notfound_view |
| | | config.add_view(notfound_view, context=HTTPNotFound) |
| | | from helloworld.views import notfound |
| | | config.add_notfound_view(notfound) |
| | | |
| | | Replace ``helloworld.views.notfound_view`` with a reference to the |
| | | :term:`view callable` you want to use to represent the Not Found view. |
| | | Replace ``helloworld.views.notfound`` with a reference to the :term:`view |
| | | callable` you want to use to represent the Not Found view. The :term:`not |
| | | found view` callable is a view callable like any other. |
| | | |
| | | If your application instead uses :class:`pyramid.view.view_config` decorators |
| | | and a :term:`scan`, you can replace the Not Found view by using the |
| | | :class:`pyramid.view.notfound_view_config` decorator: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | from pyramid.view import notfound_view_config |
| | | |
| | | notfound_view_config() |
| | | def notfound(request): |
| | | return Response('Not Found, dude', status='404 Not Found') |
| | | |
| | | def main(globals, **settings): |
| | | config = Configurator() |
| | | config.scan() |
| | | |
| | | This does exactly what the imperative example above showed. |
| | | |
| | | Your application can define *multiple* not found views if necessary. Both |
| | | :meth:`pyramid.config.Configurator.add_notfound_view` and |
| | | :class:`pyramid.view.notfound_view_config` take most of the same arguments as |
| | | :class:`pyramid.config.Configurator.add_view` and |
| | | :class:`pyramid.view.view_config`, respectively. This means that not found |
| | | views can carry predicates limiting their applicability. For example: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | from pyramid.view import notfound_view_config |
| | | |
| | | notfound_view_config(request_method='GET') |
| | | def notfound_get(request): |
| | | return Response('Not Found during GET, dude', status='404 Not Found') |
| | | |
| | | notfound_view_config(request_method='POST') |
| | | def notfound_post(request): |
| | | return Response('Not Found during POST, dude', status='404 Not Found') |
| | | |
| | | def main(globals, **settings): |
| | | config = Configurator() |
| | | config.scan() |
| | | |
| | | The ``notfound_get`` view will be called when a view could not be found and |
| | | the request method was ``GET``. The ``notfound_post`` view will be called |
| | | when a view could not be found and the request method was ``POST``. |
| | | |
| | | Like any other view, the notfound view must accept at least a ``request`` |
| | | parameter, or both ``context`` and ``request``. The ``request`` is the |
| | |
| | | :exc:`~pyramid.httpexceptions.HTTPNotFound` exception that caused the view to |
| | | be called. |
| | | |
| | | Both :meth:`pyramid.config.Configurator.add_notfound_view` and |
| | | :class:`pyramid.view.notfound_view_config` can be used to automatically |
| | | redirect requests to slash-appended routes. See |
| | | :ref:`redirecting_to_slash_appended_routes` for examples. |
| | | |
| | | Here's some sample code that implements a minimal NotFound view callable: |
| | | |
| | | .. code-block:: python |
| | |
| | | |
| | | from pyramid.httpexceptions import HTTPNotFound |
| | | |
| | | def notfound_view(request): |
| | | def notfound(request): |
| | | return HTTPNotFound() |
| | | |
| | | .. note:: |
| | |
| | | ``pyramid.debug_notfound`` environment setting is true than it is when it |
| | | is false. |
| | | |
| | | .. note:: |
| | | |
| | | Both :meth:`pyramid.config.Configurator.add_notfound_view` and |
| | | :class:`pyramid.view.notfound_view_config` are new as of Pyramid 1.3. |
| | | Older Pyramid documentation instructed users to use ``add_view`` instead, |
| | | with a ``context`` of ``HTTPNotFound``. This still works; the convenience |
| | | method and decorator are just wrappers around this functionality. |
| | | |
| | | .. warning:: |
| | | |
| | | When a NotFound view callable accepts an argument list as |
| | |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | from pyramid.httpexceptions import HTTPNotFound |
| | | from pyramid.httpexceptions import HTTPFound |
| | | from pyramid.view import view_config |
| | | |
| | | @view_config(renderer='json') |
| | |
| | | Redirecting to Slash-Appended Routes |
| | | ------------------------------------ |
| | | |
| | | For behavior like Django's ``APPEND_SLASH=True``, use the |
| | | :func:`~pyramid.view.append_slash_notfound_view` view as the :term:`Not Found |
| | | view` in your application. Defining this view as the :term:`Not Found view` |
| | | is a way to automatically redirect requests where the URL lacks a trailing |
| | | slash, but requires one to match the proper 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``. |
| | | For behavior like Django's ``APPEND_SLASH=True``, use the ``append_slash`` |
| | | argument to :meth:`pyramid.config.Configurator.add_notfound_view` or the |
| | | equivalent ``append_slash`` argument to the |
| | | :class:`pyramid.view.notfound_view_config` decorator. |
| | | |
| | | Let's use an example, because this behavior is a bit magical. If the |
| | | ``append_slash_notfound_view`` is configured in your application and your |
| | | route configuration looks like so: |
| | | Adding ``append_slash=True`` is a way to automatically redirect requests |
| | | where the URL lacks a trailing slash, but requires one to match the proper |
| | | 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``. |
| | | |
| | | To configure the slash-appending not found view in your application, change |
| | | the application's startup configuration, adding the following stanza: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | config.add_route('noslash', 'no_slash') |
| | | config.add_route('hasslash', 'has_slash/') |
| | | Let's use an example. If the following routes are configured in your |
| | | application: |
| | | |
| | | config.add_view('myproject.views.no_slash', route_name='noslash') |
| | | config.add_view('myproject.views.has_slash', route_name='hasslash') |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | from pyramid.httpexceptions import HTTPNotFound |
| | | |
| | | def notfound(request): |
| | | return HTTPNotFound('Not found, bro.') |
| | | |
| | | def no_slash(request): |
| | | return Response('No slash') |
| | | |
| | | def has_slash(request): |
| | | return Response('Has slash') |
| | | |
| | | def main(g, **settings): |
| | | config = Configurator() |
| | | config.add_route('noslash', 'no_slash') |
| | | config.add_route('hasslash', 'has_slash/') |
| | | config.add_view(no_slash, route_name='noslash') |
| | | config.add_view(has_slash, route_name='hasslash') |
| | | config.add_notfound_view(notfound, append_slash=True) |
| | | |
| | | If a request enters the application with the ``PATH_INFO`` value of |
| | | ``/no_slash``, the first route will match and the browser will show "No |
| | | slash". However, if a request enters the application with the ``PATH_INFO`` |
| | | value of ``/no_slash/``, *no* route will match, and the slash-appending not |
| | | found view will not find a matching route with an appended slash. As a |
| | | result, the ``notfound`` view will be called and it will return a "Not found, |
| | | bro." body. |
| | | |
| | | If a request enters the application with the ``PATH_INFO`` value of |
| | | ``/has_slash/``, the second route will match. If a request enters the |
| | | application with the ``PATH_INFO`` value of ``/has_slash``, a route *will* be |
| | | found by the slash-appending not found view. An HTTP redirect to |
| | | ``/has_slash/`` will be returned to the user's browser. |
| | | ``/has_slash/`` will be returned to the user's browser. As a result, the |
| | | ``notfound`` view will never actually be called. |
| | | |
| | | If a request enters the application with the ``PATH_INFO`` value of |
| | | ``/no_slash``, the first route will match. However, if a request enters the |
| | | application with the ``PATH_INFO`` value of ``/no_slash/``, *no* route will |
| | | match, and the slash-appending not found view will *not* find a matching |
| | | route with an appended slash. |
| | | The following application uses the :class:`pyramid.view.notfound_view_config` |
| | | and :class:`pyramid.view.view_config` decorators and a :term:`scan` to do |
| | | exactly the same job: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | from pyramid.httpexceptions import HTTPNotFound |
| | | from pyramid.view import notfound_view_config, view_config |
| | | |
| | | @notfound_view_config(append_slash=True) |
| | | def notfound(request): |
| | | return HTTPNotFound('Not found, bro.') |
| | | |
| | | @view_config(route_name='noslash') |
| | | def no_slash(request): |
| | | return Response('No slash') |
| | | |
| | | @view_config(route_name='hasslash') |
| | | def has_slash(request): |
| | | return Response('Has slash') |
| | | |
| | | def main(g, **settings): |
| | | config = Configurator() |
| | | config.add_route('noslash', 'no_slash') |
| | | config.add_route('hasslash', 'has_slash/') |
| | | config.scan() |
| | | |
| | | .. warning:: |
| | | |
| | |
| | | request into a ``GET``, losing any ``POST`` data in the original |
| | | request. |
| | | |
| | | To configure the slash-appending not found view in your application, change |
| | | the application's startup configuration, adding the following stanza: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | config.add_view('pyramid.view.append_slash_notfound_view', |
| | | context='pyramid.httpexceptions.HTTPNotFound') |
| | | |
| | | See :ref:`view_module` and :ref:`changing_the_notfound_view` for more |
| | | information about the slash-appending not found view and for a more general |
| | | description of how to configure a not found view. |
| | | |
| | | Custom Not Found View With Slash Appended Routes |
| | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| | | |
| | | There can only be one :term:`Not Found view` in any :app:`Pyramid` |
| | | application. Even if you use :func:`~pyramid.view.append_slash_notfound_view` |
| | | as the Not Found view, :app:`Pyramid` still must generate a ``404 Not Found`` |
| | | response when it cannot redirect to a slash-appended URL; this not found |
| | | response will be visible to site users. |
| | | |
| | | If you don't care what this 404 response looks like, and only you need |
| | | redirections to slash-appended route URLs, you may use the |
| | | :func:`~pyramid.view.append_slash_notfound_view` object as the Not Found view |
| | | as described above. However, if you wish to use a *custom* notfound view |
| | | callable when a URL cannot be redirected to a slash-appended URL, you may |
| | | wish to use an instance of the |
| | | :class:`~pyramid.view.AppendSlashNotFoundViewFactory` class as the Not Found |
| | | view, supplying a :term:`view callable` to be used as the custom notfound |
| | | view as the first argument to its constructor. For instance: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | from pyramid.httpexceptions import HTTPNotFound |
| | | from pyramid.view import AppendSlashNotFoundViewFactory |
| | | |
| | | def notfound_view(context, request): |
| | | return HTTPNotFound('It aint there, stop trying!') |
| | | |
| | | custom_append_slash = AppendSlashNotFoundViewFactory(notfound_view) |
| | | config.add_view(custom_append_slash, context=HTTPNotFound) |
| | | |
| | | The ``notfound_view`` supplied must adhere to the two-argument view callable |
| | | calling convention of ``(context, request)`` (``context`` will be the |
| | | exception object). |
| | | See :ref:`view_module` and :ref:`changing_the_notfound_view` for for a more |
| | | general description of how to configure a view and/or a not found view. |
| | | |
| | | .. index:: |
| | | pair: debugging; route matching |
| | |
| | | |
| | | if containment is not None: |
| | | def containment_predicate(context, request): |
| | | return find_interface(context, containment) is not None |
| | | ctx = getattr(request, 'context', context) |
| | | return find_interface(ctx, containment) is not None |
| | | containment_predicate.__text__ = "containment = %s" % containment |
| | | weights.append(1 << 7) |
| | | predicates.append(containment_predicate) |
| | |
| | | from pyramid.security import NO_PERMISSION_REQUIRED |
| | | from pyramid.static import static_view |
| | | from pyramid.threadlocal import get_current_registry |
| | | from pyramid.view import render_view_to_response |
| | | |
| | | from pyramid.view import ( |
| | | render_view_to_response, |
| | | AppendSlashNotFoundViewFactory, |
| | | ) |
| | | |
| | | from pyramid.util import object_description |
| | | |
| | | from pyramid.config.util import ( |
| | |
| | | The ``wrapper`` argument should be the name of another view |
| | | which will wrap this view when rendered (see the ``add_view`` |
| | | method's ``wrapper`` argument for a description).""" |
| | | if isinstance(renderer, string_types): |
| | | renderer = renderers.RendererHelper( |
| | | name=renderer, package=self.package, |
| | | registry = self.registry) |
| | | view = self._derive_view(view, attr=attr, renderer=renderer) |
| | | def bwcompat_view(context, request): |
| | | context = getattr(request, 'context', None) |
| | |
| | | wrapper=wrapper, renderer=renderer) |
| | | |
| | | @action_method |
| | | def set_notfound_view(self, view=None, attr=None, renderer=None, |
| | | wrapper=None): |
| | | """ Add a default not found view to the current configuration |
| | | state. |
| | | def add_notfound_view( |
| | | self, view=None, attr=None, renderer=None, wrapper=None, |
| | | route_name=None, request_type=None, request_method=None, |
| | | request_param=None, containment=None, xhr=None, accept=None, |
| | | header=None, path_info=None, custom_predicates=(), decorator=None, |
| | | mapper=None, match_param=None, append_slash=False): |
| | | """ Add a default notfound view to the current configuration state. |
| | | The view will be called when a view cannot otherwise be found for the |
| | | set of circumstances implied by the predicates provided. The |
| | | simplest example is: |
| | | |
| | | .. warning:: |
| | | .. code-block:: python |
| | | |
| | | This method has been deprecated in :app:`Pyramid` 1.0. *Do not use |
| | | it for new development; it should only be used to support older code |
| | | bases which depend upon it.* See :ref:`changing_the_notfound_view` to |
| | | see how a not found view should be registered in new projects. |
| | | config.add_notfound_view(someview) |
| | | |
| | | The ``view`` argument should be a :term:`view callable` or a |
| | | :term:`dotted Python name` which refers to a view callable. |
| | | All arguments except ``append_slash`` have the same meaning as |
| | | :meth:`pyramid.config.Configurator.add_view` and each predicate |
| | | argument restricts the set of circumstances under which this notfound |
| | | view will be invoked. |
| | | |
| | | The ``attr`` argument should be the attribute of the view |
| | | callable used to retrieve the response (see the ``add_view`` |
| | | method's ``attr`` argument for a description). |
| | | If ``append_slash`` is ``True``, when this notfound view is invoked, |
| | | and the current path info does not end in a slash, the notfound logic |
| | | will attempt to find a :term:`route` that matches the request's path |
| | | info suffixed with a slash. If such a route exists, Pyramid will |
| | | issue a 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. |
| | | |
| | | The ``renderer`` argument should be the name of (or path to) a |
| | | :term:`renderer` used to generate a response for this view |
| | | (see the |
| | | :meth:`pyramid.config.Configurator.add_view` |
| | | method's ``renderer`` argument for information about how a |
| | | configurator relates to a renderer). |
| | | .. note:: |
| | | |
| | | The ``wrapper`` argument should be the name of another view |
| | | which will wrap this view when rendered (see the ``add_view`` |
| | | method's ``wrapper`` argument for a description). |
| | | This method is new as of Pyramid 1.3. |
| | | """ |
| | | if isinstance(renderer, string_types): |
| | | renderer = renderers.RendererHelper( |
| | | name=renderer, package=self.package, |
| | | registry=self.registry) |
| | | view = self._derive_view(view, attr=attr, renderer=renderer) |
| | | def bwcompat_view(context, request): |
| | | context = getattr(request, 'context', None) |
| | | return view(context, request) |
| | | return self.add_view(bwcompat_view, context=HTTPNotFound, |
| | | wrapper=wrapper, renderer=renderer) |
| | | settings = dict( |
| | | view=view, |
| | | context=HTTPNotFound, |
| | | wrapper=wrapper, |
| | | request_type=request_type, |
| | | request_method=request_method, |
| | | request_param=request_param, |
| | | containment=containment, |
| | | xhr=xhr, |
| | | accept=accept, |
| | | header=header, |
| | | path_info=path_info, |
| | | custom_predicates=custom_predicates, |
| | | decorator=decorator, |
| | | mapper=mapper, |
| | | match_param=match_param, |
| | | route_name=route_name |
| | | ) |
| | | if append_slash: |
| | | view = self._derive_view(view, attr=attr, renderer=renderer) |
| | | view = AppendSlashNotFoundViewFactory(view) |
| | | settings['view'] = view |
| | | else: |
| | | settings['attr'] = attr |
| | | settings['renderer'] = renderer |
| | | return self.add_view(**settings) |
| | | |
| | | set_notfound_view = add_notfound_view # deprecated sorta-bw-compat alias |
| | | |
| | | @action_method |
| | | def set_view_mapper(self, mapper): |
New file |
| | |
| | | from pyramid.view import notfound_view_config, view_config |
| | | from pyramid.response import Response |
| | | |
| | | @notfound_view_config(route_name='foo', append_slash=True) |
| | | def foo_notfound(request): # pragma: no cover |
| | | return Response('foo_notfound') |
| | | |
| | | @notfound_view_config(route_name='baz') |
| | | def baz_notfound(request): |
| | | return Response('baz_notfound') |
| | | |
| | | @notfound_view_config(append_slash=True) |
| | | def notfound(request): |
| | | return Response('generic_notfound') |
| | | |
| | | @view_config(route_name='bar') |
| | | def bar(request): |
| | | return Response('OK bar') |
| | | |
| | | @view_config(route_name='foo2') |
| | | def foo2(request): |
| | | return Response('OK foo2') |
| | | |
| | | def includeme(config): |
| | | config.add_route('foo', '/foo') |
| | | config.add_route('foo2', '/foo/') |
| | | config.add_route('bar', '/bar/') |
| | | config.add_route('baz', '/baz') |
| | | config.scan('pyramid.tests.pkgs.notfoundview') |
| | | |
| | |
| | | |
| | | |
| | | |
| | | def test_set_notfound_view(self): |
| | | def test_add_notfound_view(self): |
| | | from pyramid.renderers import null_renderer |
| | | from zope.interface import implementedBy |
| | | from pyramid.interfaces import IRequest |
| | | from pyramid.httpexceptions import HTTPNotFound |
| | | config = self._makeOne(autocommit=True) |
| | | view = lambda *arg: arg |
| | | config.set_notfound_view(view, renderer=null_renderer) |
| | | config.add_notfound_view(view, renderer=null_renderer) |
| | | request = self._makeRequest(config) |
| | | view = self._getViewCallable(config, |
| | | ctx_iface=implementedBy(HTTPNotFound), |
| | |
| | | result = view(None, request) |
| | | self.assertEqual(result, (None, request)) |
| | | |
| | | def test_set_notfound_view_request_has_context(self): |
| | | def test_add_notfound_view_append_slash(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 HTTPNotFound |
| | | config = self._makeOne(autocommit=True) |
| | | view = lambda *arg: arg |
| | | config.set_notfound_view(view, renderer=null_renderer) |
| | | config.add_route('foo', '/foo/') |
| | | def view(request): return Response('OK') |
| | | config.add_notfound_view(view, renderer=null_renderer,append_slash=True) |
| | | request = self._makeRequest(config) |
| | | request.context = 'abc' |
| | | 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.assertEqual(result, ('abc', request)) |
| | | |
| | | @testing.skip_on('java') |
| | | def test_set_notfound_view_with_renderer(self): |
| | | self.assertEqual(result.location, '/scriptname/foo/?a=1&b=2') |
| | | |
| | | def test_add_notfound_view_with_renderer(self): |
| | | from zope.interface import implementedBy |
| | | from pyramid.interfaces import IRequest |
| | | from pyramid.httpexceptions import HTTPNotFound |
| | | config = self._makeOne(autocommit=True) |
| | | view = lambda *arg: {} |
| | | config.set_notfound_view( |
| | | config.add_notfound_view( |
| | | view, |
| | | renderer='pyramid.tests.test_config:files/minimal.pt') |
| | | config.begin() |
| | |
| | | config.end() |
| | | self.assertTrue(b'div' in result.body) |
| | | |
| | | @testing.skip_on('java') |
| | | def test_set_forbidden_view_with_renderer(self): |
| | | def test_add_forbidden_view_with_renderer(self): |
| | | from zope.interface import implementedBy |
| | | from pyramid.interfaces import IRequest |
| | | from pyramid.httpexceptions import HTTPForbidden |
| | |
| | | res = self.testapp.get('/second', status=200) |
| | | self.assertTrue(b'OK2' in res.body) |
| | | |
| | | class TestNotFoundView(IntegrationBase, unittest.TestCase): |
| | | package = 'pyramid.tests.pkgs.notfoundview' |
| | | |
| | | def test_it(self): |
| | | res = self.testapp.get('/wontbefound', status=200) |
| | | self.assertTrue(b'generic_notfound' in res.body) |
| | | res = self.testapp.get('/bar', status=302) |
| | | self.assertEqual(res.location, 'http://localhost/bar/') |
| | | res = self.testapp.get('/bar/', status=200) |
| | | self.assertTrue(b'OK bar' in res.body) |
| | | res = self.testapp.get('/foo', status=302) |
| | | self.assertEqual(res.location, 'http://localhost/foo/') |
| | | res = self.testapp.get('/foo/', status=200) |
| | | self.assertTrue(b'OK foo2' in res.body) |
| | | res = self.testapp.get('/baz', status=200) |
| | | self.assertTrue(b'baz_notfound' in res.body) |
| | | |
| | | class TestViewPermissionBug(IntegrationBase, unittest.TestCase): |
| | | # view_execution_permitted bug as reported by Shane at http://lists.repoze.org/pipermail/repoze-dev/2010-October/003603.html |
| | | package = 'pyramid.tests.pkgs.permbugapp' |
| | |
| | | context = DummyContext() |
| | | directlyProvides(context, IContext) |
| | | return context |
| | | |
| | | |
| | | class Test_notfound_view_config(BaseTest, unittest.TestCase): |
| | | def _makeOne(self, **kw): |
| | | from pyramid.view import notfound_view_config |
| | | return notfound_view_config(**kw) |
| | | |
| | | def test_ctor(self): |
| | | inst = self._makeOne(attr='attr', path_info='path_info', |
| | | append_slash=True) |
| | | self.assertEqual(inst.__dict__, |
| | | {'attr':'attr', 'path_info':'path_info', |
| | | 'append_slash':True}) |
| | | |
| | | def test_it_function(self): |
| | | def view(request): pass |
| | | decorator = self._makeOne(attr='attr', renderer='renderer', |
| | | append_slash=True) |
| | | venusian = DummyVenusian() |
| | | decorator.venusian = venusian |
| | | wrapped = decorator(view) |
| | | self.assertTrue(wrapped is view) |
| | | config = call_venusian(venusian) |
| | | settings = config.settings |
| | | self.assertEqual( |
| | | settings, |
| | | [{'attr': 'attr', 'venusian': venusian, 'append_slash': True, |
| | | 'renderer': 'renderer', '_info': 'codeinfo', 'view': None}] |
| | | ) |
| | | |
| | | def test_it_class(self): |
| | | decorator = self._makeOne() |
| | | venusian = DummyVenusian() |
| | | decorator.venusian = venusian |
| | | decorator.venusian.info.scope = 'class' |
| | | class view(object): pass |
| | | wrapped = decorator(view) |
| | | self.assertTrue(wrapped is view) |
| | | config = call_venusian(venusian) |
| | | settings = config.settings |
| | | self.assertEqual(len(settings), 1) |
| | | self.assertEqual(len(settings[0]), 5) |
| | | self.assertEqual(settings[0]['venusian'], venusian) |
| | | self.assertEqual(settings[0]['view'], None) # comes from call_venusian |
| | | self.assertEqual(settings[0]['attr'], 'view') |
| | | self.assertEqual(settings[0]['_info'], 'codeinfo') |
| | | |
| | | class RenderViewToResponseTests(BaseTest, unittest.TestCase): |
| | | def _callFUT(self, *arg, **kw): |
| | |
| | | def add_view(self, **kw): |
| | | self.settings.append(kw) |
| | | |
| | | add_notfound_view = add_view |
| | | |
| | | def with_package(self, pkg): |
| | | self.pkg = pkg |
| | | return self |
| | |
| | | IViewClassifier, |
| | | ) |
| | | |
| | | from pyramid.compat import map_ |
| | | from pyramid.compat import ( |
| | | map_, |
| | | decode_path_info, |
| | | ) |
| | | |
| | | from pyramid.httpexceptions import ( |
| | | HTTPFound, |
| | | default_exceptionresponse_view, |
| | | ) |
| | | |
| | | from pyramid.path import caller_package |
| | | from pyramid.static import static_view |
| | | from pyramid.threadlocal import get_current_registry |
| | |
| | | self.notfound_view = notfound_view |
| | | |
| | | def __call__(self, context, request): |
| | | if not isinstance(context, Exception): |
| | | # backwards compat for an append_notslash_view registered via |
| | | # config.set_notfound_view instead of as a proper exception view |
| | | context = getattr(request, 'exception', None) or context |
| | | path = request.path |
| | | path = decode_path_info(request.environ['PATH_INFO'] or '/') |
| | | registry = request.registry |
| | | mapper = registry.queryUtility(IRoutesMapper) |
| | | if mapper is not None and not path.endswith('/'): |
| | |
| | | if route.match(slashpath) is not None: |
| | | qs = request.query_string |
| | | if qs: |
| | | slashpath += '?' + qs |
| | | return HTTPFound(location=slashpath) |
| | | qs = '?' + qs |
| | | return HTTPFound(location=request.path+'/'+qs) |
| | | return self.notfound_view(context, request) |
| | | |
| | | append_slash_notfound_view = AppendSlashNotFoundViewFactory() |
| | |
| | | |
| | | """ |
| | | |
| | | class notfound_view_config(object): |
| | | """ |
| | | |
| | | An analogue of :class:`pyramid.view.view_config` which registers a |
| | | :term:`not found view`. |
| | | |
| | | The notfound_view_config constructor accepts most of the same arguments |
| | | as the constructor of :class:`pyramid.view.view_config`. It can be used |
| | | in the same places, and behaves in largely the same way, except it always |
| | | registers a not found exception view instead of a "normal" view. |
| | | |
| | | Example: |
| | | |
| | | .. code-block:: python |
| | | |
| | | from pyramid.view import notfound_view_config |
| | | from pyramid.response import Response |
| | | |
| | | notfound_view_config() |
| | | def notfound(request): |
| | | return Response('Not found, dude!', status='404 Not Found') |
| | | |
| | | All arguments except ``append_slash`` have the same meaning as |
| | | :meth:`pyramid.view.view_config` and each predicate |
| | | argument restricts the set of circumstances under which this notfound |
| | | view will be invoked. |
| | | |
| | | If ``append_slash`` is ``True``, when the notfound view is invoked, and |
| | | the current path info does not end in a slash, the notfound logic will |
| | | attempt to find a :term:`route` that matches the request's path info |
| | | suffixed with a slash. If such a route exists, Pyramid will issue a |
| | | 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. |
| | | |
| | | See :ref:`changing_the_notfound_view` for detailed usage information. |
| | | |
| | | .. note:: |
| | | |
| | | This class is new as of Pyramid 1.3. |
| | | """ |
| | | |
| | | venusian = venusian |
| | | |
| | | def __init__(self, request_type=default, request_method=default, |
| | | route_name=default, request_param=default, attr=default, |
| | | renderer=default, containment=default, wrapper=default, |
| | | xhr=default, accept=default, header=default, |
| | | path_info=default, custom_predicates=default, |
| | | decorator=default, mapper=default, match_param=default, |
| | | append_slash=False): |
| | | L = locals() |
| | | for k, v in L.items(): |
| | | if k not in ('self', 'L') and v is not default: |
| | | self.__dict__[k] = v |
| | | |
| | | def __call__(self, wrapped): |
| | | settings = self.__dict__.copy() |
| | | |
| | | def callback(context, name, ob): |
| | | config = context.config.with_package(info.module) |
| | | config.add_notfound_view(view=ob, **settings) |
| | | |
| | | info = self.venusian.attach(wrapped, callback, category='pyramid') |
| | | |
| | | if info.scope == 'class': |
| | | # if the decorator was attached to a method in a class, or |
| | | # otherwise executed at class scope, we need to set an |
| | | # 'attr' into the settings if one isn't already in there |
| | | if settings.get('attr') is None: |
| | | settings['attr'] = wrapped.__name__ |
| | | |
| | | settings['_info'] = info.codeinfo # fbo "action_method" |
| | | return wrapped |
| | | |
| | | def is_response(ob): |
| | | """ Return ``True`` if ``ob`` implements the interface implied by |
| | | :ref:`the_response`. ``False`` if not. |