Merge branch 'exception_only'
| | |
| | | Enforce the ``permission`` defined on the view. This element is a no-op if no |
| | | permission is defined. Note there will always be a permission defined if a |
| | | default permission was assigned via |
| | | :meth:`pyramid.config.Configurator.set_default_permission`. |
| | | :meth:`pyramid.config.Configurator.set_default_permission` unless the |
| | | view is an :term:`exception view`. |
| | | |
| | | This element will also output useful debugging information when |
| | | ``pyramid.debug_authorization`` is enabled. |
| | |
| | | Used to check the CSRF token provided in the request. This element is a |
| | | no-op if ``require_csrf`` view option is not ``True``. Note there will |
| | | always be a ``require_csrf`` option if a default value was assigned via |
| | | :meth:`pyramid.config.Configurator.set_default_csrf_options`. |
| | | :meth:`pyramid.config.Configurator.set_default_csrf_options` unless |
| | | the view is an :term:`exception view`. |
| | | |
| | | ``owrapped_view`` |
| | | |
| | |
| | | deriver`, you should create a callable that conforms to the |
| | | :class:`pyramid.interfaces.IViewDeriver` interface, and then register it with |
| | | your application using :meth:`pyramid.config.Configurator.add_view_deriver`. |
| | | The callable should accept the ``view`` to be wrapped and the ``info`` object |
| | | which is an instance of :class:`pyramid.interfaces.IViewDeriverInfo`. |
| | | For example, below is a callable that can provide timing information for the |
| | | view pipeline: |
| | | |
| | |
| | | passed to :meth:`pyramid.config.Configurator.add_view` in order to decide what |
| | | to do, and they have a chance to affect every view in the application. |
| | | |
| | | .. _exception_view_derivers: |
| | | |
| | | Exception Views and View Derivers |
| | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| | | |
| | | A :term:`view deriver` has the opportunity to wrap any view, including |
| | | an :term:`exception view`. In general this is fine, but certain view derivers |
| | | may wish to avoid doing certain things when handling exceptions. For example, |
| | | the ``csrf_view`` and ``secured_view`` built-in view derivers will not perform |
| | | security checks on exception views unless explicitly told to do so. |
| | | |
| | | You can check for ``info.exception_only`` on the |
| | | :class:`pyramid.interfaces.IViewDeriverInfo` object when wrapping the view |
| | | to determine whether you are wrapping an exception view or a normal view. |
| | | |
| | | Ordering View Derivers |
| | | ~~~~~~~~~~~~~~~~~~~~~~ |
| | | |
| | |
| | | be invoked. |
| | | |
| | | A view configuration statement is made about information present in the |
| | | :term:`context` resource and the :term:`request`. |
| | | :term:`context` resource (or exception) and the :term:`request`. |
| | | |
| | | View configuration is performed in one of two ways: |
| | | |
| | |
| | | represented class or if the :term:`context` resource provides the represented |
| | | interface; it is otherwise false. |
| | | |
| | | It is possible to pass an exception class as the context if your context may |
| | | subclass an exception. In this case **two** views will be registered. One |
| | | will match normal incoming requests and the other will match as an |
| | | :term:`exception view` which only occurs when an exception is raised during |
| | | the normal request processing pipeline. |
| | | |
| | | If ``context`` is not supplied, the value ``None``, which matches any |
| | | resource, is used. |
| | | |
| | | ``exception_only`` |
| | | |
| | | When this value is ``True`` the ``context`` argument must be a subclass of |
| | | ``Exception``. This flag indicates that only an :term:`exception view` should |
| | | be created and that this view should not match if the traversal |
| | | :term:`context` matches the ``context`` argument. If the ``context`` is a |
| | | subclass of ``Exception`` and this value is ``False`` (the default) then a |
| | | view will be registered to match the traversal :term:`context` as well. |
| | | |
| | | .. versionadded:: 1.8 |
| | | |
| | | ``route_name`` |
| | | If ``route_name`` is supplied, the view callable will be invoked only when |
| | | the named route has matched. |
| | |
| | | also be used by application developers to convert arbitrary exceptions to |
| | | responses. |
| | | |
| | | To register a view that should be called whenever a particular exception is |
| | | raised from within :app:`Pyramid` view code, use the exception class (or one of |
| | | its superclasses) as the :term:`context` of a view configuration which points |
| | | at a view callable for which you'd like to generate a response. |
| | | To register a :term:`exception view` that should be called whenever a |
| | | particular exception is raised from within :app:`Pyramid` view code, use |
| | | :meth:`pyramid.config.Configurator.add_exception_view` to register a view |
| | | configuration which matches the exception (or a subclass of the exception) and |
| | | points at a view callable for which you'd like to generate a response. The |
| | | exception will be passed as the ``context`` argument to any |
| | | :term:`view predicate` registered with the view as well as to the view itself. |
| | | For convenience a new decorator exists, |
| | | :class:`pyramid.views.exception_view_config`, which may be used to easily |
| | | register exception views. |
| | | |
| | | For example, given the following exception class in a module named |
| | | ``helloworld.exceptions``: |
| | |
| | | def __init__(self, msg): |
| | | self.msg = msg |
| | | |
| | | |
| | | You can wire a view callable to be called whenever any of your *other* code |
| | | raises a ``helloworld.exceptions.ValidationFailure`` exception: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | from pyramid.view import view_config |
| | | from pyramid.view import exception_view_config |
| | | from helloworld.exceptions import ValidationFailure |
| | | |
| | | @view_config(context=ValidationFailure) |
| | | @exception_view_config(ValidationFailure) |
| | | def failed_validation(exc, request): |
| | | response = Response('Failed validation: %s' % exc.msg) |
| | | response.status_int = 500 |
| | |
| | | from pyramid.view import view_config |
| | | from helloworld.exceptions import ValidationFailure |
| | | |
| | | @view_config(context=ValidationFailure, route_name='home') |
| | | @exception_view_config(ValidationFailure, route_name='home') |
| | | def failed_validation(exc, request): |
| | | response = Response('Failed validation: %s' % exc.msg) |
| | | response.status_int = 500 |
| | |
| | | |
| | | .. note:: |
| | | |
| | | Normal (i.e., non-exception) views registered against a context resource type |
| | | which inherits from :exc:`Exception` will work normally. When an exception |
| | | view configuration is processed, *two* views are registered. One as a |
| | | "normal" view, the other as an "exception" view. This means that you can use |
| | | an exception as ``context`` for a normal view. |
| | | In most cases, you should register an :term:`exception view` by using |
| | | :meth:`pyramid.config.Configurator.add_exception_view`. However, it is |
| | | possible to register 'normal' (i.e., non-exception) views against a context |
| | | resource type which inherits from :exc:`Exception` (i.e., |
| | | ``config.add_view(context=Exception)``). When the view configuration is |
| | | processed, *two* views are registered. One as a "normal" view, the other |
| | | as an :term:`exception view`. This means that you can use an exception as |
| | | ``context`` for a normal view. |
| | | |
| | | The view derivers that wrap these two views may behave differently. |
| | | See :ref:`exception_view_derivers` for more information about this. |
| | | |
| | | Exception views can be configured with any view registration mechanism: |
| | | ``@view_config`` decorator or imperative ``add_view`` styles. |
| | | ``@exception_view_config`` decorator or imperative ``add_exception_view`` |
| | | styles. |
| | | |
| | | .. note:: |
| | | |
| | |
| | | implementedBy, |
| | | implementer, |
| | | ) |
| | | |
| | | from zope.interface.interfaces import IInterface |
| | | |
| | | from pyramid.interfaces import ( |
| | | IException, |
| | | IExceptionViewClassifier, |
| | | IException, |
| | | IMultiView, |
| | | IPackageOverrides, |
| | | IRendererFactory, |
| | |
| | | match_param=None, |
| | | check_csrf=None, |
| | | require_csrf=None, |
| | | exception_only=False, |
| | | **view_options): |
| | | """ Add a :term:`view configuration` to the current |
| | | configuration state. Arguments to ``add_view`` are broken |
| | |
| | | if the :term:`context` provides the represented interface; |
| | | it is otherwise false. This argument may also be provided |
| | | to ``add_view`` as ``for_`` (an older, still-supported |
| | | spelling). |
| | | spelling). If the view should **only** match when handling |
| | | exceptions then set the ``exception_only`` to ``True``. |
| | | |
| | | exception_only |
| | | |
| | | .. versionadded:: 1.8 |
| | | |
| | | When this value is ``True`` the ``context`` argument must be |
| | | a subclass of ``Exception``. This flag indicates that only an |
| | | :term:`exception view` should be created and that this view should |
| | | not match if the traversal :term:`context` matches the ``context`` |
| | | argument. If the ``context`` is a subclass of ``Exception`` and |
| | | this value is ``False`` (the default) then a view will be |
| | | registered to match the traversal :term:`context` as well. |
| | | |
| | | route_name |
| | | |
| | |
| | | obsoletes this argument, but it is kept around for backwards |
| | | compatibility. |
| | | |
| | | view_options: |
| | | view_options |
| | | |
| | | Pass a key/value pair here to use a third-party predicate or set a |
| | | value for a view deriver. See |
| | |
| | | if context is None: |
| | | context = for_ |
| | | |
| | | isexc = isexception(context) |
| | | if exception_only and not isexc: |
| | | raise ConfigurationError( |
| | | 'view "context" must be an exception type when ' |
| | | '"exception_only" is True') |
| | | |
| | | r_context = context |
| | | if r_context is None: |
| | | r_context = Interface |
| | |
| | | # is. It can't be computed any sooner because thirdparty |
| | | # predicates/view derivers may not yet exist when add_view is |
| | | # called. |
| | | predlist = self.get_predlist('view') |
| | | valid_predicates = predlist.names() |
| | | pvals = {} |
| | | dvals = {} |
| | |
| | | view_intr.update(dict( |
| | | name=name, |
| | | context=context, |
| | | exception_only=exception_only, |
| | | containment=containment, |
| | | request_param=request_param, |
| | | request_methods=request_method, |
| | |
| | | )) |
| | | view_intr.update(view_options) |
| | | introspectables.append(view_intr) |
| | | predlist = self.get_predlist('view') |
| | | |
| | | def register(permission=permission, renderer=renderer): |
| | | request_iface = IRequest |
| | |
| | | registry=self.registry |
| | | ) |
| | | |
| | | renderer_type = getattr(renderer, 'type', None) |
| | | intrspc = self.introspector |
| | | if ( |
| | | renderer_type is not None and |
| | | tmpl_intr is not None and |
| | | intrspc is not None and |
| | | intrspc.get('renderer factories', renderer_type) is not None |
| | | ): |
| | | # allow failure of registered template factories to be deferred |
| | | # until view execution, like other bad renderer factories; if |
| | | # we tried to relate this to an existing renderer factory |
| | | # without checking if it the factory actually existed, we'd end |
| | | # up with a KeyError at startup time, which is inconsistent |
| | | # with how other bad renderer registrations behave (they throw |
| | | # a ValueError at view execution time) |
| | | tmpl_intr.relate('renderer factories', renderer.type) |
| | | |
| | | # make a new view separately for normal and exception paths |
| | | if not exception_only: |
| | | derived_view = derive_view(False, renderer) |
| | | register_view(IViewClassifier, request_iface, derived_view) |
| | | if isexc: |
| | | derived_exc_view = derive_view(True, renderer) |
| | | register_view(IExceptionViewClassifier, request_iface, |
| | | derived_exc_view) |
| | | |
| | | if exception_only: |
| | | derived_view = derived_exc_view |
| | | |
| | | # if there are two derived views, combine them into one for |
| | | # introspection purposes |
| | | if not exception_only and isexc: |
| | | derived_view = runtime_exc_view(derived_view, derived_exc_view) |
| | | |
| | | derived_view.__discriminator__ = lambda *arg: discriminator |
| | | # __discriminator__ is used by superdynamic systems |
| | | # that require it for introspection after manual view lookup; |
| | | # see also MultiView.__discriminator__ |
| | | view_intr['derived_callable'] = derived_view |
| | | |
| | | self.registry._clear_view_lookup_cache() |
| | | |
| | | def derive_view(isexc_only, renderer): |
| | | # added by discrim_func above during conflict resolving |
| | | preds = view_intr['predicates'] |
| | | order = view_intr['order'] |
| | | phash = view_intr['phash'] |
| | | |
| | | # __no_permission_required__ handled by _secure_view |
| | | derived_view = self._derive_view( |
| | | view, |
| | | route_name=route_name, |
| | |
| | | predicates=preds, |
| | | attr=attr, |
| | | context=context, |
| | | exception_only=isexc_only, |
| | | renderer=renderer, |
| | | wrapper_viewname=wrapper, |
| | | viewname=name, |
| | |
| | | require_csrf=require_csrf, |
| | | extra_options=ovals, |
| | | ) |
| | | derived_view.__discriminator__ = lambda *arg: discriminator |
| | | # __discriminator__ is used by superdynamic systems |
| | | # that require it for introspection after manual view lookup; |
| | | # see also MultiView.__discriminator__ |
| | | view_intr['derived_callable'] = derived_view |
| | | return derived_view |
| | | |
| | | registered = self.registry.adapters.registered |
| | | |
| | | def register_view(classifier, request_iface, derived_view): |
| | | # A multiviews is a set of views which are registered for |
| | | # exactly the same context type/request type/name triad. Each |
| | | # consituent view in a multiview differs only by the |
| | |
| | | # matches on all the arguments it receives. |
| | | |
| | | old_view = None |
| | | order, phash = view_intr['order'], view_intr['phash'] |
| | | registered = self.registry.adapters.registered |
| | | |
| | | for view_type in (IView, ISecuredView, IMultiView): |
| | | old_view = registered((IViewClassifier, request_iface, |
| | | r_context), view_type, name) |
| | | old_view = registered( |
| | | (classifier, request_iface, r_context), |
| | | view_type, name) |
| | | if old_view is not None: |
| | | break |
| | | |
| | | isexc = isexception(context) |
| | | |
| | | def regclosure(): |
| | | if hasattr(derived_view, '__call_permissive__'): |
| | |
| | | view_iface = IView |
| | | self.registry.registerAdapter( |
| | | derived_view, |
| | | (IViewClassifier, request_iface, context), view_iface, name |
| | | (classifier, request_iface, context), |
| | | view_iface, |
| | | name |
| | | ) |
| | | if isexc: |
| | | self.registry.registerAdapter( |
| | | derived_view, |
| | | (IExceptionViewClassifier, request_iface, context), |
| | | view_iface, name) |
| | | |
| | | is_multiview = IMultiView.providedBy(old_view) |
| | | old_phash = getattr(old_view, '__phash__', DEFAULT_PHASH) |
| | |
| | | for view_type in (IView, ISecuredView): |
| | | # unregister any existing views |
| | | self.registry.adapters.unregister( |
| | | (IViewClassifier, request_iface, r_context), |
| | | (classifier, request_iface, r_context), |
| | | view_type, name=name) |
| | | if isexc: |
| | | self.registry.adapters.unregister( |
| | | (IExceptionViewClassifier, request_iface, |
| | | r_context), view_type, name=name) |
| | | self.registry.registerAdapter( |
| | | multiview, |
| | | (IViewClassifier, request_iface, context), |
| | | (classifier, request_iface, context), |
| | | IMultiView, name=name) |
| | | if isexc: |
| | | self.registry.registerAdapter( |
| | | multiview, |
| | | (IExceptionViewClassifier, request_iface, context), |
| | | IMultiView, name=name) |
| | | |
| | | self.registry._clear_view_lookup_cache() |
| | | renderer_type = getattr(renderer, 'type', None) # gard against None |
| | | intrspc = self.introspector |
| | | if ( |
| | | renderer_type is not None and |
| | | tmpl_intr is not None and |
| | | intrspc is not None and |
| | | intrspc.get('renderer factories', renderer_type) is not None |
| | | ): |
| | | # allow failure of registered template factories to be deferred |
| | | # until view execution, like other bad renderer factories; if |
| | | # we tried to relate this to an existing renderer factory |
| | | # without checking if it the factory actually existed, we'd end |
| | | # up with a KeyError at startup time, which is inconsistent |
| | | # with how other bad renderer registrations behave (they throw |
| | | # a ValueError at view execution time) |
| | | tmpl_intr.relate('renderer factories', renderer.type) |
| | | |
| | | if mapper: |
| | | mapper_intr = self.introspectable( |
| | |
| | | viewname=None, accept=None, order=MAX_ORDER, |
| | | phash=DEFAULT_PHASH, decorator=None, route_name=None, |
| | | mapper=None, http_cache=None, context=None, |
| | | require_csrf=None, extra_options=None): |
| | | require_csrf=None, exception_only=False, |
| | | extra_options=None): |
| | | view = self.maybe_dotted(view) |
| | | mapper = self.maybe_dotted(mapper) |
| | | if isinstance(renderer, string_types): |
| | |
| | | registry=self.registry, |
| | | package=self.package, |
| | | predicates=predicates, |
| | | exception_only=exception_only, |
| | | options=options, |
| | | ) |
| | | |
| | |
| | | argument restricts the set of circumstances under which this forbidden |
| | | view will be invoked. Unlike |
| | | :meth:`pyramid.config.Configurator.add_view`, this method will raise |
| | | an exception if passed ``name``, ``permission``, ``context``, |
| | | ``for_``, or ``http_cache`` keyword arguments. These argument values |
| | | make no sense in the context of a forbidden view. |
| | | an exception if passed ``name``, ``permission``, ``require_csrf``, |
| | | ``context``, ``for_`` or ``exception_only`` keyword arguments. These |
| | | argument values make no sense in the context of a forbidden |
| | | :term:`exception view`. |
| | | |
| | | .. versionadded:: 1.3 |
| | | |
| | | .. versionchanged:: 1.8 |
| | | |
| | | The view is created using ``exception_only=True``. |
| | | """ |
| | | for arg in ( |
| | | 'name', 'permission', 'context', 'for_', 'http_cache', |
| | | 'require_csrf', |
| | | 'name', 'permission', 'context', 'for_', 'require_csrf', |
| | | 'exception_only', |
| | | ): |
| | | if arg in view_options: |
| | | raise ConfigurationError( |
| | | '%s may not be used as an argument to add_forbidden_view' |
| | | % arg |
| | | ) |
| | | % (arg,)) |
| | | |
| | | if view is None: |
| | | view = default_exceptionresponse_view |
| | |
| | | settings = dict( |
| | | view=view, |
| | | context=HTTPForbidden, |
| | | exception_only=True, |
| | | wrapper=wrapper, |
| | | request_type=request_type, |
| | | request_method=request_method, |
| | |
| | | append_slash=False, |
| | | **view_options |
| | | ): |
| | | """ Add a default Not Found View to the current configuration state. |
| | | The view will be called when Pyramid or application code raises an |
| | | :exc:`pyramid.httpexceptions.HTTPNotFound` exception (e.g. when a |
| | | """ Add a default :term:`Not Found View` to the current configuration |
| | | state. The view will be called when Pyramid or application code raises |
| | | an :exc:`pyramid.httpexceptions.HTTPNotFound` exception (e.g. when a |
| | | view cannot be found for the request). The simplest example is: |
| | | |
| | | .. code-block:: python |
| | |
| | | argument restricts the set of circumstances under which this notfound |
| | | view will be invoked. Unlike |
| | | :meth:`pyramid.config.Configurator.add_view`, this method will raise |
| | | an exception if passed ``name``, ``permission``, ``context``, |
| | | ``for_``, or ``http_cache`` keyword arguments. These argument values |
| | | make no sense in the context of a Not Found View. |
| | | an exception if passed ``name``, ``permission``, ``require_csrf``, |
| | | ``context``, ``for_``, or ``exception_only`` keyword arguments. These |
| | | argument values make no sense in the context of a Not Found View. |
| | | |
| | | If ``append_slash`` is ``True``, when this Not Found View is invoked, |
| | | and the current path info does not end in a slash, the notfound logic |
| | |
| | | being used, :class:`~pyramid.httpexceptions.HTTPMovedPermanently will |
| | | be used` for the redirect response if a slash-appended route is found. |
| | | |
| | | .. versionadded:: 1.3 |
| | | |
| | | .. versionchanged:: 1.6 |
| | | |
| | | The ``append_slash`` argument was modified to allow any object that |
| | | implements the ``IResponse`` interface to specify the response class |
| | | used when a redirect is performed. |
| | | |
| | | .. versionadded:: 1.3 |
| | | .. versionchanged:: 1.8 |
| | | |
| | | The view is created using ``exception_only=True``. |
| | | """ |
| | | for arg in ( |
| | | 'name', 'permission', 'context', 'for_', 'http_cache', |
| | | 'require_csrf', |
| | | 'name', 'permission', 'context', 'for_', 'require_csrf', |
| | | 'exception_only', |
| | | ): |
| | | if arg in view_options: |
| | | raise ConfigurationError( |
| | | '%s may not be used as an argument to add_notfound_view' |
| | | % arg |
| | | ) |
| | | % (arg,)) |
| | | |
| | | if view is None: |
| | | view = default_exceptionresponse_view |
| | |
| | | settings = dict( |
| | | view=view, |
| | | context=HTTPNotFound, |
| | | exception_only=True, |
| | | wrapper=wrapper, |
| | | request_type=request_type, |
| | | request_method=request_method, |
| | |
| | | return self.add_view(**settings) |
| | | |
| | | set_notfound_view = add_notfound_view # deprecated sorta-bw-compat alias |
| | | |
| | | @viewdefaults |
| | | @action_method |
| | | def add_exception_view( |
| | | self, |
| | | view=None, |
| | | context=None, |
| | | # force all other arguments to be specified as key=value |
| | | **view_options |
| | | ): |
| | | """ Add an :term:`exception view` for the specified ``exception`` to |
| | | the current configuration state. The view will be called when Pyramid |
| | | or application code raises the given exception. |
| | | |
| | | This method accepts accepts almost all of the same arguments as |
| | | :meth:`pyramid.config.Configurator.add_view` except for ``name``, |
| | | ``permission``, ``for_``, ``require_csrf`` and ``exception_only``. |
| | | |
| | | By default, this method will set ``context=Exception`` thus |
| | | registering for most default Python exceptions. Any subclass of |
| | | ``Exception`` may be specified. |
| | | |
| | | .. versionadded:: 1.8 |
| | | """ |
| | | for arg in ( |
| | | 'name', 'for_', 'exception_only', 'require_csrf', 'permission', |
| | | ): |
| | | if arg in view_options: |
| | | raise ConfigurationError( |
| | | '%s may not be used as an argument to add_exception_view' |
| | | % (arg,)) |
| | | if context is None: |
| | | context = Exception |
| | | view_options.update(dict( |
| | | view=view, |
| | | context=context, |
| | | exception_only=True, |
| | | permission=NO_PERMISSION_REQUIRED, |
| | | require_csrf=False, |
| | | )) |
| | | return self.add_view(**view_options) |
| | | |
| | | @action_method |
| | | def set_view_mapper(self, mapper): |
| | |
| | | (inspect.isclass(o) and (issubclass(o, Exception))) |
| | | ) |
| | | |
| | | def runtime_exc_view(view, excview): |
| | | # create a view callable which can pretend to be both a normal view |
| | | # and an exception view, dispatching to the appropriate one based |
| | | # on the state of request.exception |
| | | def wrapper_view(context, request): |
| | | if getattr(request, 'exception', None): |
| | | return excview(context, request) |
| | | return view(context, request) |
| | | |
| | | # these constants are the same between the two views |
| | | wrapper_view.__wraps__ = wrapper_view |
| | | wrapper_view.__original_view__ = getattr(view, '__original_view__', view) |
| | | wrapper_view.__module__ = view.__module__ |
| | | wrapper_view.__doc__ = view.__doc__ |
| | | wrapper_view.__name__ = view.__name__ |
| | | |
| | | wrapper_view.__accept__ = getattr(view, '__accept__', None) |
| | | wrapper_view.__order__ = getattr(view, '__order__', MAX_ORDER) |
| | | wrapper_view.__phash__ = getattr(view, '__phash__', DEFAULT_PHASH) |
| | | wrapper_view.__view_attr__ = getattr(view, '__view_attr__', None) |
| | | wrapper_view.__permission__ = getattr(view, '__permission__', None) |
| | | |
| | | def wrap_fn(attr): |
| | | def wrapper(context, request): |
| | | if getattr(request, 'exception', None): |
| | | selected_view = excview |
| | | else: |
| | | selected_view = view |
| | | fn = getattr(selected_view, attr, None) |
| | | if fn is not None: |
| | | return fn(context, request) |
| | | return wrapper |
| | | |
| | | # these methods are dynamic per-request and should dispatch to their |
| | | # respective views based on whether it's an exception or not |
| | | wrapper_view.__call_permissive__ = wrap_fn('__call_permissive__') |
| | | wrapper_view.__permitted__ = wrap_fn('__permitted__') |
| | | wrapper_view.__predicated__ = wrap_fn('__predicated__') |
| | | wrapper_view.__predicates__ = wrap_fn('__predicates__') |
| | | return wrapper_view |
| | | |
| | | @implementer(IViewDeriverInfo) |
| | | class ViewDeriverInfo(object): |
| | | def __init__(self, view, registry, package, predicates, options): |
| | | def __init__(self, |
| | | view, |
| | | registry, |
| | | package, |
| | | predicates, |
| | | exception_only, |
| | | options, |
| | | ): |
| | | self.original_view = view |
| | | self.registry = registry |
| | | self.package = package |
| | | self.predicates = predicates or [] |
| | | self.options = options or {} |
| | | self.exception_only = exception_only |
| | | |
| | | @reify |
| | | def settings(self): |
| | |
| | | def __str__(self): |
| | | return "%s: %s\n in:\n %s" % (self.etype, self.evalue, self.info) |
| | | |
| | | |
| | | class CyclicDependencyError(Exception): |
| | | """ The exception raised when the Pyramid topological sorter detects a |
| | | cyclic dependency.""" |
| | |
| | | 'default values that were not overriden') |
| | | predicates = Attribute('The list of predicates active on the view') |
| | | original_view = Attribute('The original view object being wrapped') |
| | | exception_only = Attribute('The view will only be invoked for exceptions') |
| | | |
| | | class IViewDerivers(Interface): |
| | | """ Interface for view derivers list """ |
| | |
| | | config = Configurator(*arg, **kw) |
| | | return config |
| | | |
| | | def _getViewCallable(self, config, ctx_iface=None, request_iface=None, |
| | | name='', exception_view=False): |
| | | def _getViewCallable(self, config, ctx_iface=None, exc_iface=None, |
| | | request_iface=None, name=''): |
| | | from zope.interface import Interface |
| | | from pyramid.interfaces import IRequest |
| | | from pyramid.interfaces import IView |
| | | from pyramid.interfaces import IViewClassifier |
| | | from pyramid.interfaces import IExceptionViewClassifier |
| | | if exception_view: |
| | | if exc_iface: |
| | | classifier = IExceptionViewClassifier |
| | | ctx_iface = exc_iface |
| | | else: |
| | | classifier = IViewClassifier |
| | | if ctx_iface is None: |
| | |
| | | config.add_view(view=newview, xhr=True, context=RuntimeError, |
| | | renderer=null_renderer) |
| | | wrapper = self._getViewCallable( |
| | | config, ctx_iface=implementedBy(RuntimeError), exception_view=True) |
| | | config, exc_iface=implementedBy(RuntimeError)) |
| | | self.assertFalse(IMultiView.providedBy(wrapper)) |
| | | request = DummyRequest() |
| | | request.is_xhr = True |
| | |
| | | config.add_view(view=newview, context=RuntimeError, |
| | | renderer=null_renderer) |
| | | wrapper = self._getViewCallable( |
| | | config, ctx_iface=implementedBy(RuntimeError), exception_view=True) |
| | | config, exc_iface=implementedBy(RuntimeError)) |
| | | self.assertFalse(IMultiView.providedBy(wrapper)) |
| | | request = DummyRequest() |
| | | request.is_xhr = True |
| | |
| | | config.add_view(view=newview, context=RuntimeError, |
| | | renderer=null_renderer) |
| | | wrapper = self._getViewCallable( |
| | | config, ctx_iface=implementedBy(RuntimeError), exception_view=True) |
| | | config, exc_iface=implementedBy(RuntimeError)) |
| | | self.assertFalse(IMultiView.providedBy(wrapper)) |
| | | request = DummyRequest() |
| | | request.is_xhr = True |
| | |
| | | config.add_view(view=view, context=RuntimeError, |
| | | renderer=null_renderer) |
| | | wrapper = self._getViewCallable( |
| | | config, ctx_iface=implementedBy(RuntimeError), exception_view=True) |
| | | config, exc_iface=implementedBy(RuntimeError)) |
| | | self.assertTrue(IMultiView.providedBy(wrapper)) |
| | | self.assertEqual(wrapper(None, None), 'OK') |
| | | |
| | |
| | | ISecuredView, name='') |
| | | config.add_view(view=view, context=RuntimeError, renderer=null_renderer) |
| | | wrapper = self._getViewCallable( |
| | | config, ctx_iface=implementedBy(RuntimeError), exception_view=True) |
| | | config, exc_iface=implementedBy(RuntimeError)) |
| | | self.assertTrue(IMultiView.providedBy(wrapper)) |
| | | self.assertEqual(wrapper(None, None), 'OK') |
| | | |
| | |
| | | config.add_view(view=view2, accept='text/html', context=RuntimeError, |
| | | renderer=null_renderer) |
| | | wrapper = self._getViewCallable( |
| | | config, ctx_iface=implementedBy(RuntimeError), exception_view=True) |
| | | config, exc_iface=implementedBy(RuntimeError)) |
| | | self.assertTrue(IMultiView.providedBy(wrapper)) |
| | | self.assertEqual(len(wrapper.views), 1) |
| | | self.assertEqual(len(wrapper.media_views), 1) |
| | |
| | | config.add_view(view=view2, context=RuntimeError, |
| | | renderer=null_renderer) |
| | | wrapper = self._getViewCallable( |
| | | config, ctx_iface=implementedBy(RuntimeError), exception_view=True) |
| | | config, exc_iface=implementedBy(RuntimeError)) |
| | | self.assertTrue(IMultiView.providedBy(wrapper)) |
| | | self.assertEqual(len(wrapper.views), 1) |
| | | self.assertEqual(len(wrapper.media_views), 1) |
| | |
| | | self.assertEqual([x[:2] for x in wrapper.views], [(view2, None)]) |
| | | self.assertEqual(wrapper(None, None), 'OK1') |
| | | |
| | | def test_add_view_exc_multiview_replaces_multiview(self): |
| | | def test_add_view_exc_multiview_replaces_multiviews(self): |
| | | from pyramid.renderers import null_renderer |
| | | from zope.interface import implementedBy |
| | | from pyramid.interfaces import IRequest |
| | | from pyramid.interfaces import IMultiView |
| | | from pyramid.interfaces import IViewClassifier |
| | | from pyramid.interfaces import IExceptionViewClassifier |
| | | view = DummyMultiView() |
| | | hot_view = DummyMultiView() |
| | | exc_view = DummyMultiView() |
| | | config = self._makeOne(autocommit=True) |
| | | config.registry.registerAdapter( |
| | | view, |
| | | hot_view, |
| | | (IViewClassifier, IRequest, implementedBy(RuntimeError)), |
| | | IMultiView, name='') |
| | | config.registry.registerAdapter( |
| | | view, |
| | | exc_view, |
| | | (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), |
| | | IMultiView, name='') |
| | | view2 = lambda *arg: 'OK2' |
| | | config.add_view(view=view2, context=RuntimeError, |
| | | renderer=null_renderer) |
| | | wrapper = self._getViewCallable( |
| | | config, ctx_iface=implementedBy(RuntimeError), exception_view=True) |
| | | self.assertTrue(IMultiView.providedBy(wrapper)) |
| | | self.assertEqual([x[:2] for x in wrapper.views], [(view2, None)]) |
| | | self.assertEqual(wrapper(None, None), 'OK1') |
| | | hot_wrapper = self._getViewCallable( |
| | | config, ctx_iface=implementedBy(RuntimeError)) |
| | | self.assertTrue(IMultiView.providedBy(hot_wrapper)) |
| | | self.assertEqual([x[:2] for x in hot_wrapper.views], [(view2, None)]) |
| | | self.assertEqual(hot_wrapper(None, None), 'OK1') |
| | | |
| | | exc_wrapper = self._getViewCallable( |
| | | config, exc_iface=implementedBy(RuntimeError)) |
| | | self.assertTrue(IMultiView.providedBy(exc_wrapper)) |
| | | self.assertEqual([x[:2] for x in exc_wrapper.views], [(view2, None)]) |
| | | self.assertEqual(exc_wrapper(None, None), 'OK1') |
| | | |
| | | def test_add_view_exc_multiview_replaces_only_exc_multiview(self): |
| | | from pyramid.renderers import null_renderer |
| | | from zope.interface import implementedBy |
| | | from pyramid.interfaces import IRequest |
| | | from pyramid.interfaces import IMultiView |
| | | from pyramid.interfaces import IViewClassifier |
| | | from pyramid.interfaces import IExceptionViewClassifier |
| | | hot_view = DummyMultiView() |
| | | exc_view = DummyMultiView() |
| | | config = self._makeOne(autocommit=True) |
| | | config.registry.registerAdapter( |
| | | hot_view, |
| | | (IViewClassifier, IRequest, implementedBy(RuntimeError)), |
| | | IMultiView, name='') |
| | | config.registry.registerAdapter( |
| | | exc_view, |
| | | (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), |
| | | IMultiView, name='') |
| | | view2 = lambda *arg: 'OK2' |
| | | config.add_view(view=view2, context=RuntimeError, exception_only=True, |
| | | renderer=null_renderer) |
| | | hot_wrapper = self._getViewCallable( |
| | | config, ctx_iface=implementedBy(RuntimeError)) |
| | | self.assertTrue(IMultiView.providedBy(hot_wrapper)) |
| | | self.assertEqual(len(hot_wrapper.views), 0) |
| | | self.assertEqual(hot_wrapper(None, None), 'OK1') |
| | | |
| | | exc_wrapper = self._getViewCallable( |
| | | config, exc_iface=implementedBy(RuntimeError)) |
| | | self.assertTrue(IMultiView.providedBy(exc_wrapper)) |
| | | self.assertEqual([x[:2] for x in exc_wrapper.views], [(view2, None)]) |
| | | self.assertEqual(exc_wrapper(None, None), 'OK1') |
| | | |
| | | def test_add_view_multiview_context_superclass_then_subclass(self): |
| | | from pyramid.renderers import null_renderer |
| | |
| | | config.registry.registerAdapter( |
| | | view, (IViewClassifier, IRequest, ISuper), IView, name='') |
| | | config.add_view(view=view2, for_=ISub, renderer=null_renderer) |
| | | wrapper = self._getViewCallable(config, ISuper, IRequest) |
| | | wrapper = self._getViewCallable(config, ctx_iface=ISuper, |
| | | request_iface=IRequest) |
| | | self.assertFalse(IMultiView.providedBy(wrapper)) |
| | | self.assertEqual(wrapper(None, None), 'OK') |
| | | wrapper = self._getViewCallable(config, ISub, IRequest) |
| | | wrapper = self._getViewCallable(config, ctx_iface=ISub, |
| | | request_iface=IRequest) |
| | | self.assertFalse(IMultiView.providedBy(wrapper)) |
| | | self.assertEqual(wrapper(None, None), 'OK2') |
| | | |
| | |
| | | view, (IExceptionViewClassifier, IRequest, Super), IView, name='') |
| | | config.add_view(view=view2, for_=Sub, renderer=null_renderer) |
| | | wrapper = self._getViewCallable( |
| | | config, implementedBy(Super), IRequest) |
| | | config, ctx_iface=implementedBy(Super), request_iface=IRequest) |
| | | wrapper_exc_view = self._getViewCallable( |
| | | config, implementedBy(Super), IRequest, exception_view=True) |
| | | config, exc_iface=implementedBy(Super), request_iface=IRequest) |
| | | self.assertEqual(wrapper_exc_view, wrapper) |
| | | self.assertFalse(IMultiView.providedBy(wrapper_exc_view)) |
| | | self.assertEqual(wrapper_exc_view(None, None), 'OK') |
| | | wrapper = self._getViewCallable( |
| | | config, implementedBy(Sub), IRequest) |
| | | config, ctx_iface=implementedBy(Sub), request_iface=IRequest) |
| | | wrapper_exc_view = self._getViewCallable( |
| | | config, implementedBy(Sub), IRequest, exception_view=True) |
| | | config, exc_iface=implementedBy(Sub), request_iface=IRequest) |
| | | self.assertEqual(wrapper_exc_view, wrapper) |
| | | self.assertFalse(IMultiView.providedBy(wrapper_exc_view)) |
| | | self.assertEqual(wrapper_exc_view(None, None), 'OK2') |
| | |
| | | renderer=null_renderer) |
| | | request_iface = self._getRouteRequestIface(config, 'foo') |
| | | wrapper_exc_view = self._getViewCallable( |
| | | config, ctx_iface=implementedBy(RuntimeError), |
| | | request_iface=request_iface, exception_view=True) |
| | | config, exc_iface=implementedBy(RuntimeError), |
| | | request_iface=request_iface) |
| | | self.assertNotEqual(wrapper_exc_view, None) |
| | | wrapper = self._getViewCallable( |
| | | config, ctx_iface=implementedBy(RuntimeError), |
| | |
| | | |
| | | self.assertRaises(ConfigurationError, configure_view) |
| | | |
| | | def test_add_view_exception_only_no_regular_view(self): |
| | | from zope.interface import implementedBy |
| | | from pyramid.renderers import null_renderer |
| | | view1 = lambda *arg: 'OK' |
| | | config = self._makeOne(autocommit=True) |
| | | config.add_view(view=view1, context=Exception, exception_only=True, |
| | | renderer=null_renderer) |
| | | view = self._getViewCallable(config, ctx_iface=implementedBy(Exception)) |
| | | self.assertTrue(view is None) |
| | | |
| | | def test_add_view_exception_only(self): |
| | | from zope.interface import implementedBy |
| | | from pyramid.renderers import null_renderer |
| | | view1 = lambda *arg: 'OK' |
| | | config = self._makeOne(autocommit=True) |
| | | config.add_view(view=view1, context=Exception, exception_only=True, |
| | | renderer=null_renderer) |
| | | view = self._getViewCallable( |
| | | config, exc_iface=implementedBy(Exception)) |
| | | self.assertEqual(view1, view) |
| | | |
| | | def test_add_view_exception_only_misconfiguration(self): |
| | | view = lambda *arg: 'OK' |
| | | config = self._makeOne(autocommit=True) |
| | | class NotAnException(object): |
| | | pass |
| | | self.assertRaises( |
| | | ConfigurationError, |
| | | config.add_view, view, context=NotAnException, exception_only=True) |
| | | |
| | | def test_add_exception_view(self): |
| | | from zope.interface import implementedBy |
| | | from pyramid.renderers import null_renderer |
| | | view1 = lambda *arg: 'OK' |
| | | config = self._makeOne(autocommit=True) |
| | | config.add_exception_view(view=view1, renderer=null_renderer) |
| | | wrapper = self._getViewCallable( |
| | | config, exc_iface=implementedBy(Exception)) |
| | | context = Exception() |
| | | request = self._makeRequest(config) |
| | | self.assertEqual(wrapper(context, request), 'OK') |
| | | |
| | | def test_add_exception_view_with_subclass(self): |
| | | from zope.interface import implementedBy |
| | | from pyramid.renderers import null_renderer |
| | | view1 = lambda *arg: 'OK' |
| | | config = self._makeOne(autocommit=True) |
| | | config.add_exception_view(view=view1, context=ValueError, |
| | | renderer=null_renderer) |
| | | wrapper = self._getViewCallable( |
| | | config, exc_iface=implementedBy(ValueError)) |
| | | context = ValueError() |
| | | request = self._makeRequest(config) |
| | | self.assertEqual(wrapper(context, request), 'OK') |
| | | |
| | | def test_add_exception_view_disallows_name(self): |
| | | config = self._makeOne(autocommit=True) |
| | | self.assertRaises(ConfigurationError, |
| | | config.add_exception_view, |
| | | context=Exception(), |
| | | name='foo') |
| | | |
| | | def test_add_exception_view_disallows_permission(self): |
| | | config = self._makeOne(autocommit=True) |
| | | self.assertRaises(ConfigurationError, |
| | | config.add_exception_view, |
| | | context=Exception(), |
| | | permission='foo') |
| | | |
| | | def test_add_exception_view_disallows_require_csrf(self): |
| | | config = self._makeOne(autocommit=True) |
| | | self.assertRaises(ConfigurationError, |
| | | config.add_exception_view, |
| | | context=Exception(), |
| | | require_csrf=True) |
| | | |
| | | def test_add_exception_view_disallows_for_(self): |
| | | config = self._makeOne(autocommit=True) |
| | | self.assertRaises(ConfigurationError, |
| | | config.add_exception_view, |
| | | context=Exception(), |
| | | for_='foo') |
| | | |
| | | def test_add_exception_view_disallows_exception_only(self): |
| | | config = self._makeOne(autocommit=True) |
| | | self.assertRaises(ConfigurationError, |
| | | config.add_exception_view, |
| | | context=Exception(), |
| | | exception_only=True) |
| | | |
| | | def test_add_exception_view_with_view_defaults(self): |
| | | from pyramid.renderers import null_renderer |
| | | from pyramid.exceptions import PredicateMismatch |
| | | from zope.interface import directlyProvides |
| | | from zope.interface import implementedBy |
| | | class view(object): |
| | | __view_defaults__ = { |
| | | 'containment': 'pyramid.tests.test_config.IDummy' |
| | | } |
| | | def __init__(self, request): |
| | | pass |
| | | def __call__(self): |
| | | return 'OK' |
| | | config = self._makeOne(autocommit=True) |
| | | config.add_exception_view( |
| | | view=view, |
| | | context=Exception, |
| | | renderer=null_renderer) |
| | | wrapper = self._getViewCallable( |
| | | config, exc_iface=implementedBy(Exception)) |
| | | context = DummyContext() |
| | | directlyProvides(context, IDummy) |
| | | request = self._makeRequest(config) |
| | | self.assertEqual(wrapper(context, request), 'OK') |
| | | context = DummyContext() |
| | | request = self._makeRequest(config) |
| | | self.assertRaises(PredicateMismatch, wrapper, context, request) |
| | | |
| | | def test_derive_view_function(self): |
| | | from pyramid.renderers import null_renderer |
| | | def view(request): |
| | |
| | | config.add_forbidden_view(view, renderer=null_renderer) |
| | | request = self._makeRequest(config) |
| | | view = self._getViewCallable(config, |
| | | ctx_iface=implementedBy(HTTPForbidden), |
| | | exc_iface=implementedBy(HTTPForbidden), |
| | | request_iface=IRequest) |
| | | result = view(None, request) |
| | | self.assertEqual(result, 'OK') |
| | |
| | | config.add_forbidden_view() |
| | | request = self._makeRequest(config) |
| | | view = self._getViewCallable(config, |
| | | ctx_iface=implementedBy(HTTPForbidden), |
| | | exc_iface=implementedBy(HTTPForbidden), |
| | | request_iface=IRequest) |
| | | context = HTTPForbidden() |
| | | result = view(context, request) |
| | |
| | | self.assertRaises(ConfigurationError, |
| | | config.add_forbidden_view, permission='foo') |
| | | |
| | | def test_add_forbidden_view_disallows_require_csrf(self): |
| | | config = self._makeOne(autocommit=True) |
| | | self.assertRaises(ConfigurationError, |
| | | config.add_forbidden_view, require_csrf=True) |
| | | |
| | | def test_add_forbidden_view_disallows_context(self): |
| | | config = self._makeOne(autocommit=True) |
| | | self.assertRaises(ConfigurationError, |
| | |
| | | config = self._makeOne(autocommit=True) |
| | | self.assertRaises(ConfigurationError, |
| | | config.add_forbidden_view, for_='foo') |
| | | |
| | | def test_add_forbidden_view_disallows_http_cache(self): |
| | | config = self._makeOne(autocommit=True) |
| | | self.assertRaises(ConfigurationError, |
| | | config.add_forbidden_view, http_cache='foo') |
| | | |
| | | def test_add_forbidden_view_with_view_defaults(self): |
| | | from pyramid.interfaces import IRequest |
| | |
| | | view=view, |
| | | renderer=null_renderer) |
| | | wrapper = self._getViewCallable( |
| | | config, ctx_iface=implementedBy(HTTPForbidden), |
| | | config, exc_iface=implementedBy(HTTPForbidden), |
| | | request_iface=IRequest) |
| | | context = DummyContext() |
| | | directlyProvides(context, IDummy) |
| | |
| | | config.add_notfound_view(view, renderer=null_renderer) |
| | | request = self._makeRequest(config) |
| | | view = self._getViewCallable(config, |
| | | ctx_iface=implementedBy(HTTPNotFound), |
| | | exc_iface=implementedBy(HTTPNotFound), |
| | | request_iface=IRequest) |
| | | result = view(None, request) |
| | | self.assertEqual(result, (None, request)) |
| | |
| | | config.add_notfound_view() |
| | | request = self._makeRequest(config) |
| | | view = self._getViewCallable(config, |
| | | ctx_iface=implementedBy(HTTPNotFound), |
| | | exc_iface=implementedBy(HTTPNotFound), |
| | | request_iface=IRequest) |
| | | context = HTTPNotFound() |
| | | result = view(context, request) |
| | |
| | | self.assertRaises(ConfigurationError, |
| | | config.add_notfound_view, permission='foo') |
| | | |
| | | def test_add_notfound_view_disallows_require_csrf(self): |
| | | config = self._makeOne(autocommit=True) |
| | | self.assertRaises(ConfigurationError, |
| | | config.add_notfound_view, require_csrf=True) |
| | | |
| | | def test_add_notfound_view_disallows_context(self): |
| | | config = self._makeOne(autocommit=True) |
| | | self.assertRaises(ConfigurationError, |
| | |
| | | config = self._makeOne(autocommit=True) |
| | | self.assertRaises(ConfigurationError, |
| | | config.add_notfound_view, for_='foo') |
| | | |
| | | def test_add_notfound_view_disallows_http_cache(self): |
| | | config = self._makeOne(autocommit=True) |
| | | self.assertRaises(ConfigurationError, |
| | | config.add_notfound_view, http_cache='foo') |
| | | |
| | | def test_add_notfound_view_append_slash(self): |
| | | from pyramid.response import Response |
| | |
| | | request.query_string = 'a=1&b=2' |
| | | request.path = '/scriptname/foo' |
| | | view = self._getViewCallable(config, |
| | | ctx_iface=implementedBy(HTTPNotFound), |
| | | exc_iface=implementedBy(HTTPNotFound), |
| | | request_iface=IRequest) |
| | | result = view(None, request) |
| | | self.assertTrue(isinstance(result, HTTPFound)) |
| | |
| | | request.query_string = 'a=1&b=2' |
| | | request.path = '/scriptname/foo' |
| | | view = self._getViewCallable(config, |
| | | ctx_iface=implementedBy(HTTPNotFound), |
| | | exc_iface=implementedBy(HTTPNotFound), |
| | | request_iface=IRequest) |
| | | result = view(None, request) |
| | | self.assertTrue(isinstance(result, HTTPMovedPermanently)) |
| | |
| | | view=view, |
| | | renderer=null_renderer) |
| | | wrapper = self._getViewCallable( |
| | | config, ctx_iface=implementedBy(HTTPNotFound), |
| | | config, exc_iface=implementedBy(HTTPNotFound), |
| | | request_iface=IRequest) |
| | | context = DummyContext() |
| | | directlyProvides(context, IDummy) |
| | |
| | | renderer='json') |
| | | request = self._makeRequest(config) |
| | | view = self._getViewCallable(config, |
| | | ctx_iface=implementedBy(HTTPNotFound), |
| | | exc_iface=implementedBy(HTTPNotFound), |
| | | request_iface=IRequest) |
| | | result = view(None, request) |
| | | self._assertBody(result, '{}') |
| | |
| | | renderer='json') |
| | | request = self._makeRequest(config) |
| | | view = self._getViewCallable(config, |
| | | ctx_iface=implementedBy(HTTPForbidden), |
| | | exc_iface=implementedBy(HTTPForbidden), |
| | | request_iface=IRequest) |
| | | result = view(None, request) |
| | | self._assertBody(result, '{}') |
| | |
| | | from pyramid.tests import test_config |
| | | self.assertEqual(result, test_config) |
| | | |
| | | def test_add_normal_and_exception_view_intr_derived_callable(self): |
| | | from pyramid.renderers import null_renderer |
| | | from pyramid.exceptions import BadCSRFToken |
| | | config = self._makeOne(autocommit=True) |
| | | introspector = DummyIntrospector() |
| | | config.introspector = introspector |
| | | view = lambda r: 'OK' |
| | | config.set_default_csrf_options(require_csrf=True) |
| | | config.add_view(view, context=Exception, renderer=null_renderer) |
| | | view_intr = introspector.introspectables[1] |
| | | self.assertTrue(view_intr.type_name, 'view') |
| | | self.assertEqual(view_intr['callable'], view) |
| | | derived_view = view_intr['derived_callable'] |
| | | |
| | | request = self._makeRequest(config) |
| | | request.method = 'POST' |
| | | request.scheme = 'http' |
| | | request.POST = {} |
| | | request.headers = {} |
| | | request.session = DummySession({'csrf_token': 'foo'}) |
| | | self.assertRaises(BadCSRFToken, lambda: derived_view(None, request)) |
| | | request.exception = Exception() |
| | | self.assertEqual(derived_view(None, request), 'OK') |
| | | |
| | | class Test_runtime_exc_view(unittest.TestCase): |
| | | def _makeOne(self, view1, view2): |
| | | from pyramid.config.views import runtime_exc_view |
| | | return runtime_exc_view(view1, view2) |
| | | |
| | | def test_call(self): |
| | | def view1(context, request): return 'OK' |
| | | def view2(context, request): raise AssertionError |
| | | result_view = self._makeOne(view1, view2) |
| | | request = DummyRequest() |
| | | result = result_view(None, request) |
| | | self.assertEqual(result, 'OK') |
| | | |
| | | def test_call_dispatches_on_exception(self): |
| | | def view1(context, request): raise AssertionError |
| | | def view2(context, request): return 'OK' |
| | | result_view = self._makeOne(view1, view2) |
| | | request = DummyRequest() |
| | | request.exception = Exception() |
| | | result = result_view(None, request) |
| | | self.assertEqual(result, 'OK') |
| | | |
| | | def test_permitted(self): |
| | | def errfn(context, request): raise AssertionError |
| | | def view1(context, request): raise AssertionError |
| | | view1.__permitted__ = lambda c, r: 'OK' |
| | | def view2(context, request): raise AssertionError |
| | | view2.__permitted__ = errfn |
| | | result_view = self._makeOne(view1, view2) |
| | | request = DummyRequest() |
| | | result = result_view.__permitted__(None, request) |
| | | self.assertEqual(result, 'OK') |
| | | |
| | | def test_permitted_dispatches_on_exception(self): |
| | | def errfn(context, request): raise AssertionError |
| | | def view1(context, request): raise AssertionError |
| | | view1.__permitted__ = errfn |
| | | def view2(context, request): raise AssertionError |
| | | view2.__permitted__ = lambda c, r: 'OK' |
| | | result_view = self._makeOne(view1, view2) |
| | | request = DummyRequest() |
| | | request.exception = Exception() |
| | | result = result_view.__permitted__(None, request) |
| | | self.assertEqual(result, 'OK') |
| | | |
| | | class Test_requestonly(unittest.TestCase): |
| | | def _callFUT(self, view, attr=None): |
| | | from pyramid.config.views import requestonly |
| | |
| | | result = str(exc) |
| | | self.assertTrue("'a' sorts before ['c', 'd']" in result) |
| | | self.assertTrue("'c' sorts before ['a']" in result) |
| | | |
| | | |
| | |
| | | self.assertEqual(settings[0]['view'], None) # comes from call_venusian |
| | | self.assertEqual(settings[0]['attr'], 'view') |
| | | self.assertEqual(settings[0]['_info'], 'codeinfo') |
| | | |
| | | |
| | | class Test_exception_view_config(BaseTest, unittest.TestCase): |
| | | def _makeOne(self, *args, **kw): |
| | | from pyramid.view import exception_view_config |
| | | return exception_view_config(*args, **kw) |
| | | |
| | | def test_ctor(self): |
| | | inst = self._makeOne(context=Exception, path_info='path_info') |
| | | self.assertEqual(inst.__dict__, |
| | | {'context':Exception, 'path_info':'path_info'}) |
| | | |
| | | def test_ctor_positional_exception(self): |
| | | inst = self._makeOne(Exception, path_info='path_info') |
| | | self.assertEqual(inst.__dict__, |
| | | {'context':Exception, 'path_info':'path_info'}) |
| | | |
| | | def test_ctor_positional_extras(self): |
| | | from pyramid.exceptions import ConfigurationError |
| | | self.assertRaises(ConfigurationError, lambda: self._makeOne(Exception, True)) |
| | | |
| | | def test_it_function(self): |
| | | def view(request): pass |
| | | decorator = self._makeOne(context=Exception, renderer='renderer') |
| | | venusian = DummyVenusian() |
| | | decorator.venusian = venusian |
| | | wrapped = decorator(view) |
| | | self.assertTrue(wrapped is view) |
| | | config = call_venusian(venusian) |
| | | settings = config.settings |
| | | self.assertEqual( |
| | | settings, |
| | | [{'venusian': venusian, 'context': Exception, |
| | | '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]), 4) |
| | | 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): |
| | | from pyramid.view import render_view_to_response |
| | |
| | | def add_view(self, **kw): |
| | | self.settings.append(kw) |
| | | |
| | | add_notfound_view = add_forbidden_view = add_view |
| | | add_notfound_view = add_forbidden_view = add_exception_view = add_view |
| | | |
| | | def with_package(self, pkg): |
| | | self.pkg = pkg |
| | |
| | | "'view_name' against context None): " |
| | | "Allowed (NO_PERMISSION_REQUIRED)") |
| | | |
| | | def test_debug_auth_permission_authpol_permitted_excview(self): |
| | | response = DummyResponse() |
| | | view = lambda *arg: response |
| | | self.config.registry.settings = dict( |
| | | debug_authorization=True, reload_templates=True) |
| | | logger = self._registerLogger() |
| | | self._registerSecurityPolicy(True) |
| | | result = self.config._derive_view( |
| | | view, context=Exception, permission='view') |
| | | self.assertEqual(view.__module__, result.__module__) |
| | | self.assertEqual(view.__doc__, result.__doc__) |
| | | self.assertEqual(view.__name__, result.__name__) |
| | | self.assertEqual(result.__call_permissive__.__wraps__, view) |
| | | request = self._makeRequest() |
| | | request.view_name = 'view_name' |
| | | request.url = 'url' |
| | | self.assertEqual(result(Exception(), request), response) |
| | | self.assertEqual(len(logger.messages), 1) |
| | | self.assertEqual(logger.messages[0], |
| | | "debug_authorization of url url (view name " |
| | | "'view_name' against context Exception()): True") |
| | | |
| | | def test_secured_view_authn_policy_no_authz_policy(self): |
| | | response = DummyResponse() |
| | | view = lambda *arg: response |
| | |
| | | |
| | | from pyramid.compat import decode_path_info |
| | | |
| | | from pyramid.exceptions import PredicateMismatch |
| | | from pyramid.exceptions import ( |
| | | ConfigurationError, |
| | | PredicateMismatch, |
| | | ) |
| | | |
| | | from pyramid.httpexceptions import ( |
| | | HTTPFound, |
| | |
| | | :class:`pyramid.view.bfg_view`. |
| | | |
| | | :class:`pyramid.view.view_config` supports the following keyword |
| | | arguments: ``context``, ``permission``, ``name``, |
| | | arguments: ``context``, ``exception``, ``permission``, ``name``, |
| | | ``request_type``, ``route_name``, ``request_method``, ``request_param``, |
| | | ``containment``, ``xhr``, ``accept``, ``header``, ``path_info``, |
| | | ``custom_predicates``, ``decorator``, ``mapper``, ``http_cache``, |
| | |
| | | .. versionadded:: 1.3 |
| | | |
| | | An analogue of :class:`pyramid.view.view_config` which registers a |
| | | :term:`Not Found View`. |
| | | :term:`Not Found View` using |
| | | :meth:`pyramid.config.Configurator.add_notfound_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 |
| | |
| | | .. versionadded:: 1.3 |
| | | |
| | | An analogue of :class:`pyramid.view.view_config` which registers a |
| | | :term:`forbidden view`. |
| | | :term:`forbidden view` using |
| | | :meth:`pyramid.config.Configurator.add_forbidden_view`. |
| | | |
| | | The forbidden_view_config constructor accepts most of the same arguments |
| | | as the constructor of :class:`pyramid.view.view_config`. It can be used |
| | |
| | | settings['_info'] = info.codeinfo # fbo "action_method" |
| | | return wrapped |
| | | |
| | | class exception_view_config(object): |
| | | """ |
| | | .. versionadded:: 1.8 |
| | | |
| | | An analogue of :class:`pyramid.view.view_config` which registers an |
| | | :term:`exception view` using |
| | | :meth:`pyramid.config.Configurator.add_exception_view`. |
| | | |
| | | The ``exception_view_config`` constructor requires an exception context, |
| | | and additionally 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 an |
| | | exception view instead of a 'normal' view that dispatches on the request |
| | | :term:`context`. |
| | | |
| | | Example: |
| | | |
| | | .. code-block:: python |
| | | |
| | | from pyramid.view import exception_view_config |
| | | from pyramid.response import Response |
| | | |
| | | @exception_view_config(ValueError, renderer='json') |
| | | def error_view(request): |
| | | return {'error': str(request.exception)} |
| | | |
| | | All arguments passed to this function have the same meaning as |
| | | :meth:`pyramid.view.view_config` and each predicate argument restricts |
| | | the set of circumstances under which this exception view will be invoked. |
| | | |
| | | """ |
| | | venusian = venusian |
| | | |
| | | def __init__(self, *args, **settings): |
| | | if 'context' not in settings and len(args) > 0: |
| | | exception, args = args[0], args[1:] |
| | | settings['context'] = exception |
| | | if len(args) > 0: |
| | | raise ConfigurationError('unknown positional arguments') |
| | | self.__dict__.update(settings) |
| | | |
| | | def __call__(self, wrapped): |
| | | settings = self.__dict__.copy() |
| | | |
| | | def callback(context, name, ob): |
| | | config = context.config.with_package(info.module) |
| | | config.add_exception_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 _find_views( |
| | | registry, |
| | | request_iface, |
| | |
| | | authn_policy = info.registry.queryUtility(IAuthenticationPolicy) |
| | | authz_policy = info.registry.queryUtility(IAuthorizationPolicy) |
| | | |
| | | # no-op on exception-only views without an explicit permission |
| | | if explicit_val is None and info.exception_only: |
| | | return view |
| | | |
| | | if authn_policy and authz_policy and (permission is not None): |
| | | def _permitted(context, request): |
| | | def permitted(context, request): |
| | | principals = authn_policy.effective_principals(request) |
| | | return authz_policy.permits(context, principals, permission) |
| | | def _secured_view(context, request): |
| | | if ( |
| | | getattr(request, 'exception', None) is not None and |
| | | explicit_val is None |
| | | ): |
| | | return view(context, request) |
| | | |
| | | result = _permitted(context, request) |
| | | def secured_view(context, request): |
| | | result = permitted(context, request) |
| | | if result: |
| | | return view(context, request) |
| | | view_name = getattr(view, '__name__', view) |
| | |
| | | request, 'authdebug_message', |
| | | 'Unauthorized: %s failed permission check' % view_name) |
| | | raise HTTPForbidden(msg, result=result) |
| | | _secured_view.__call_permissive__ = view |
| | | _secured_view.__permitted__ = _permitted |
| | | _secured_view.__permission__ = permission |
| | | wrapped_view = _secured_view |
| | | wrapped_view = secured_view |
| | | wrapped_view.__call_permissive__ = view |
| | | wrapped_view.__permitted__ = permitted |
| | | wrapped_view.__permission__ = permission |
| | | |
| | | return wrapped_view |
| | | |
| | |
| | | authn_policy = info.registry.queryUtility(IAuthenticationPolicy) |
| | | authz_policy = info.registry.queryUtility(IAuthorizationPolicy) |
| | | logger = info.registry.queryUtility(IDebugLogger) |
| | | if settings and settings.get('debug_authorization', False): |
| | | def _authdebug_view(context, request): |
| | | if ( |
| | | getattr(request, 'exception', None) is not None and |
| | | explicit_val is None |
| | | ): |
| | | return view(context, request) |
| | | |
| | | # no-op on exception-only views without an explicit permission |
| | | if explicit_val is None and info.exception_only: |
| | | return view |
| | | |
| | | if settings and settings.get('debug_authorization', False): |
| | | def authdebug_view(context, request): |
| | | view_name = getattr(request, 'view_name', None) |
| | | |
| | | if authn_policy and authz_policy: |
| | |
| | | if request is not None: |
| | | request.authdebug_message = msg |
| | | return view(context, request) |
| | | |
| | | wrapped_view = _authdebug_view |
| | | wrapped_view = authdebug_view |
| | | |
| | | return wrapped_view |
| | | |
| | |
| | | token = defaults.token |
| | | header = defaults.header |
| | | safe_methods = defaults.safe_methods |
| | | |
| | | enabled = ( |
| | | explicit_val is True or |
| | | (explicit_val is not False and default_val) |
| | | # fallback to the default val if not explicitly enabled |
| | | # but only if the view is not an exception view |
| | | ( |
| | | explicit_val is not False and default_val and |
| | | not info.exception_only |
| | | ) |
| | | ) |
| | | # disable if both header and token are disabled |
| | | enabled = enabled and (token or header) |
| | | wrapped_view = view |
| | | if enabled: |
| | | def csrf_view(context, request): |
| | | if ( |
| | | request.method not in safe_methods and |
| | | ( |
| | | # skip exception views unless value is explicitly defined |
| | | getattr(request, 'exception', None) is None or |
| | | explicit_val is not None |
| | | ) |
| | | ): |
| | | if request.method not in safe_methods: |
| | | check_csrf_origin(request, raises=True) |
| | | check_csrf_token(request, token, header, raises=True) |
| | | return view(context, request) |