summaryrefslogtreecommitdiff
path: root/repoze/bfg/view.py
diff options
context:
space:
mode:
authorChris McDonough <chrism@plope.com>2010-10-25 10:29:31 -0400
committerChris McDonough <chrism@plope.com>2010-10-25 10:29:31 -0400
commit64372401084889a440c9d990a0febc221e3e4b5c (patch)
treec8939a341505d19f19fa6918d264b4e1d95326f8 /repoze/bfg/view.py
parentc8e78c2037806f3e5dab57de635bf80865b7061d (diff)
downloadpyramid-64372401084889a440c9d990a0febc221e3e4b5c.tar.gz
pyramid-64372401084889a440c9d990a0febc221e3e4b5c.tar.bz2
pyramid-64372401084889a440c9d990a0febc221e3e4b5c.zip
first pass at converting bfg to pyramid namespace
Diffstat (limited to 'repoze/bfg/view.py')
-rw-r--r--repoze/bfg/view.py551
1 files changed, 0 insertions, 551 deletions
diff --git a/repoze/bfg/view.py b/repoze/bfg/view.py
deleted file mode 100644
index c5fd60623..000000000
--- a/repoze/bfg/view.py
+++ /dev/null
@@ -1,551 +0,0 @@
-import mimetypes
-import os
-
-# See http://bugs.python.org/issue5853 which is a recursion bug
-# that seems to effect Python 2.6, Python 2.6.1, and 2.6.2 (a fix
-# has been applied on the Python 2 trunk). This workaround should
-# really be in Paste if anywhere, but it's easiest to just do it
-# here and get it over with to avoid needing to deal with any
-# fallout.
-
-if hasattr(mimetypes, 'init'):
- mimetypes.init()
-
-from webob.exc import HTTPFound
-
-import venusian
-
-from zope.deprecation import deprecated
-from zope.interface import providedBy
-
-from repoze.bfg.interfaces import IRoutesMapper
-from repoze.bfg.interfaces import IView
-from repoze.bfg.interfaces import IViewClassifier
-
-from repoze.bfg.path import package_path
-from repoze.bfg.resource import resource_spec_from_abspath
-from repoze.bfg.static import static_view as static # B/C
-from repoze.bfg.threadlocal import get_current_registry
-
-# b/c imports
-from repoze.bfg.security import view_execution_permitted
-
-view_execution_permitted # prevent PyFlakes from complaining
-
-deprecated('view_execution_permitted',
- "('from repoze.bfg.view import view_execution_permitted' was "
- "deprecated as of repoze.bfg 1.0; instead use 'from "
- "repoze.bfg.security import view_execution_permitted')",
- )
-
-deprecated('NotFound',
- "('from repoze.bfg.view import NotFound' was "
- "deprecated as of repoze.bfg 1.1; instead use 'from "
- "repoze.bfg.exceptions import NotFound')",
- )
-
-static = static # dont yet deprecate this (ever?)
-
-_marker = object()
-
-def render_view_to_response(context, request, name='', secure=True):
- """ Call the :term:`view callable` configured with a :term:`view
- configuration` that matches the :term:`view name` ``name``
- registered against the specified ``context`` and ``request`` and
- return a :term:`response` object. This function will return
- ``None`` if a corresponding :term:`view callable` cannot be found
- (when no :term:`view configuration` matches the combination of
- ``name`` / ``context`` / and ``request``).
-
- If `secure`` is ``True``, and the :term:`view callable` found is
- protected by a permission, the permission will be checked before
- calling the view function. If the permission check disallows view
- execution (based on the current :term:`authorization policy`), a
- :exc:`repoze.bfg.exceptions.Forbidden` exception will be raised.
- The exception's ``args`` attribute explains why the view access
- was disallowed.
-
- If ``secure`` is ``False``, no permission checking is done."""
- provides = [IViewClassifier] + map(providedBy, (request, context))
- try:
- reg = request.registry
- except AttributeError:
- reg = get_current_registry()
- view = reg.adapters.lookup(provides, IView, name=name)
- if view is None:
- return None
-
- if not secure:
- # the view will have a __call_permissive__ attribute if it's
- # secured; otherwise it won't.
- view = getattr(view, '__call_permissive__', view)
-
- # if this view is secured, it will raise a Forbidden
- # appropriately if the executing user does not have the proper
- # permission
- return view(context, request)
-
-def render_view_to_iterable(context, request, name='', secure=True):
- """ Call the :term:`view callable` configured with a :term:`view
- configuration` that matches the :term:`view name` ``name``
- registered against the specified ``context`` and ``request`` and
- return an iterable object which represents the body of a response.
- This function will return ``None`` if a corresponding :term:`view
- callable` cannot be found (when no :term:`view configuration`
- matches the combination of ``name`` / ``context`` / and
- ``request``). Additionally, this function will raise a
- :exc:`ValueError` if a view function is found and called but the
- view function's result does not have an ``app_iter`` attribute.
-
- You can usually get the string representation of the return value
- of this function by calling ``''.join(iterable)``, or just use
- :func:`repoze.bfg.view.render_view` instead.
-
- If ``secure`` is ``True``, and the view is protected by a
- permission, the permission will be checked before the view
- function is invoked. If the permission check disallows view
- execution (based on the current :term:`authentication policy`), a
- :exc:`repoze.bfg.exceptions.Forbidden` exception will be raised;
- its ``args`` attribute explains why the view access was
- disallowed.
-
- If ``secure`` is ``False``, no permission checking is
- done."""
- response = render_view_to_response(context, request, name, secure)
- if response is None:
- return None
- return response.app_iter
-
-def render_view(context, request, name='', secure=True):
- """ Call the :term:`view callable` configured with a :term:`view
- configuration` that matches the :term:`view name` ``name``
- registered against the specified ``context`` and ``request``
- and unwind the view response's ``app_iter`` (see
- :ref:`the_response`) into a single string. This function will
- return ``None`` if a corresponding :term:`view callable` cannot be
- found (when no :term:`view configuration` matches the combination
- of ``name`` / ``context`` / and ``request``). Additionally, this
- function will raise a :exc:`ValueError` if a view function is
- found and called but the view function's result does not have an
- ``app_iter`` attribute. This function will return ``None`` if a
- corresponding view cannot be found.
-
- If ``secure`` is ``True``, and the view is protected by a
- permission, the permission will be checked before the view is
- invoked. If the permission check disallows view execution (based
- on the current :term:`authorization policy`), a
- :exc:`repoze.bfg.exceptions.Forbidden` exception will be raised;
- its ``args`` attribute explains why the view access was
- disallowed.
-
- If ``secure`` is ``False``, no permission checking is done."""
- iterable = render_view_to_iterable(context, request, name, secure)
- if iterable is None:
- return None
- return ''.join(iterable)
-
-def is_response(ob):
- """ Return ``True`` if ``ob`` implements the interface implied by
- :ref:`the_response`. ``False`` if not.
-
- .. note:: this isn't a true interface check (in Zope terms), it's a
- duck-typing check, as response objects are not obligated to
- actually implement a Zope interface."""
- # response objects aren't obligated to implement a Zope interface,
- # so we do it the hard way
- if ( hasattr(ob, 'app_iter') and hasattr(ob, 'headerlist') and
- hasattr(ob, 'status') ):
- if ( hasattr(ob.app_iter, '__iter__') and
- hasattr(ob.headerlist, '__iter__') and
- isinstance(ob.status, basestring) ) :
- return True
- return False
-
-class bfg_view(object):
- """ A function, class or method :term:`decorator` which allows a
- developer to create view registrations nearer to a :term:`view
- callable` definition than use of :term:`ZCML` or :term:`imperative
- configuration` to do the same.
-
- For example, this code in a module ``views.py``::
-
- from models import MyModel
-
- @bfg_view(name='my_view', context=MyModel, permission='read',
- route_name='site1')
- def my_view(context, request):
- return 'OK'
-
- Might replace the following call to the
- :meth:`repoze.bfg.configuration.Configurator.add_view` method::
-
- import views
- import models
- config.add_view(views.my_view, context=models.MyModel, name='my_view',
- permission='read', 'route_name='site1')
-
- Or might replace the following ZCML ``view`` declaration::
-
- <view
- for='.models.MyModel'
- view='.views.my_view'
- name='my_view'
- permission='read'
- route_name='site1'
- />
-
- The following arguments are supported as arguments to
- ``bfg_view``: ``context``, ``permission``, ``name``,
- ``request_type``, ``route_name``, ``request_method``,
- ``request_param``, ``containment``, ``xhr``, ``accept``,
- ``header`` and ``path_info``.
-
- ``context`` should be a Python object or :term:`dotted Python
- name` representing the context type that must be found for this
- view to be called. If ``context`` is not supplied, the interface
- ``zope.interface.Interface`` (matching any context) is used. An
- alias for ``context`` is ``for_``.
-
- If ``permission`` is not supplied, no permission is registered for
- this view (it's accessible by any caller).
-
- If ``name`` is not supplied, the empty string is used (implying
- the default view name).
-
- If ``attr`` is not supplied, ``None`` is used (implying the
- function itself if the view is a function, or the ``__call__``
- callable attribute if the view is a class).
-
- If ``renderer`` is not supplied, ``None`` is used (meaning that no
- renderer is associated with this view).
-
- If ``wrapper`` is not supplied, ``None`` is used (meaning that no
- view wrapper is associated with this view).
-
- If ``request_type`` is not supplied, the interface
- :class:`repoze.bfg.interfaces.IRequest` is used, implying the
- standard request interface type.
-
- If ``route_name`` is not supplied, the view configuration is
- considered to be made against a URL that doesn't match any defined
- :term:`route`. The use of a ``route_name`` is an advanced
- feature, useful only if you're also using :term:`url dispatch`.
-
- If ``request_method`` is not supplied, this view will match a
- request with any HTTP ``REQUEST_METHOD``
- (GET/POST/PUT/HEAD/DELETE). If this parameter *is* supplied, it
- must be a string naming an HTTP ``REQUEST_METHOD``, indicating
- that this view will only match when the current request has a
- ``REQUEST_METHOD`` that matches this value.
-
- If ``request_param`` is not supplied, this view will be called
- when a request with any (or no) request GET or POST parameters is
- encountered. If the value is present, it must be a string. If
- the value supplied to the parameter has no ``=`` sign in it, it
- implies that the key must exist in the ``request.params``
- dictionary for this view to 'match' the current request. If the value
- supplied to the parameter has a ``=`` sign in it, e.g.
- ``request_params="foo=123"``, then the key (``foo``) must both exist
- in the ``request.params`` dictionary, and the value must match the
- right hand side of the expression (``123``) for the view to "match" the
- current request.
-
- ``containment`` should be a Python object or :term:`dotted Python
- name` representing a class or interface type which must be found
- as one of the context's location parents for this view to be
- called. If ``containment`` is not supplied, this view will be
- called when the context of the request has any (or no)
- :term:`lineage`. If ``containment`` *is* supplied, it must be a
- class or :term:`interface`, denoting that the view'matches' the
- current request only if any graph :term:`lineage` node possesses
- this class or interface.
-
- If ``xhr`` is specified, it must be a boolean value. If the value
- is ``True``, the view will only be invoked if the request's
- ``X-Requested-With`` header has the value ``XMLHttpRequest``.
-
- If ``accept`` is specified, it must be a mimetype value. If
- ``accept`` is specified, the view will only be invoked if the
- ``Accept`` HTTP header matches the value requested. See the
- description of ``accept`` in :ref:`view_directive` for information
- about the allowable composition and matching behavior of this
- value.
-
- If ``header`` is specified, it must be a header name or a
- ``headername:headervalue`` pair. If ``header`` is specified, and
- possesses a value the view will only be invoked if an HTTP header
- matches the value requested. If ``header`` is specified without a
- value (a bare header name only), the view will only be invoked if
- the HTTP header exists with any value in the request. See the
- description of ``header`` in :ref:`view_directive` for information
- about the allowable composition and matching behavior of this
- value.
-
- If ``path_info`` is specified, it must be a regular
- expression. The view will only be invoked if the ``PATH_INFO``
- WSGI environment variable matches the expression.
-
- If ``custom_predicates`` is specified, it must be a sequence of
- :term:`predicate` callables (a predicate callable accepts two
- arguments: ``context`` and ``request`` and returns ``True`` or
- ``False``). The view will only be invoked if all custom
- predicates return ``True``.
-
- Any individual or all parameters can be omitted. The simplest
- ``bfg_view`` declaration is::
-
- @bfg_view()
- def my_view(...):
- ...
-
- Such a registration implies that the view name will be
- ``my_view``, registered for any :term:`context` object, using no
- permission, registered against all non-URL-dispatch-based
- requests, with any ``REQUEST_METHOD``, any set of request.params
- values, without respect to any object in the :term:`lineage`.
-
- The ``bfg_view`` decorator can also be used as a class decorator
- in Python 2.6 and better (Python 2.5 and below do not support
- class decorators)::
-
- from webob import Response
- from repoze.bfg.view import bfg_view
-
- @bfg_view()
- class MyView(object):
- def __init__(self, context, request):
- self.context = context
- self.request = request
- def __call__(self):
- return Response('hello from %s!' % self.context)
-
- In Python 2.5 and below, the ``bfg_view`` decorator can still be
- used against a class, although not in decorator form::
-
- from webob import Response
- from repoze.bfg.view import bfg_view
-
- class MyView(object):
- def __init__(self, context, request):
- self.context = context
- self.request = request
- def __call__(self):
- return Response('hello from %s!' % self.context)
-
- MyView = bfg_view()(MyView)
-
- .. note:: When a view is a class, the calling semantics are
- different than when it is a function or another
- non-class callable. See :ref:`class_as_view` for more
- information.
-
- .. warning:: Using a class as a view is a new feature in 0.8.1+.
-
- The bfg_view decorator can also be used against a class method::
-
- from webob import Response
- from repoze.bfg.view import bfg_view
-
- class MyView(object):
- def __init__(self, context, request):
- self.context = context
- self.request = request
-
- @bfg_view(name='hello')
- def amethod(self):
- return Response('hello from %s!' % self.context)
-
- When the ``bfg_view`` decorator is used against a class method, a
- view is registered for the *class* (as described above), so the
- class constructor must accept either ``request`` or ``context,
- request``. The method which is decorated must return a response
- (or rely on a :term:`renderer` to generate one). Using the
- decorator against a particular method of a class is equivalent to
- using the ``attr`` parameter in a decorator attached to the class
- itself. For example, the above registration implied by the
- decorator being used against the ``amethod`` method could be
- spelled equivalently as::
-
- from webob import Response
- from repoze.bfg.view import bfg_view
-
- @bfg_view(attr='amethod', name='hello')
- class MyView(object):
- def __init__(self, context, request):
- self.context = context
- self.request = request
-
- def amethod(self):
- return Response('hello from %s!' % self.context)
-
- .. warning:: The ability to use the ``bfg_view`` decorator as a
- method decorator is new in :mod:`repoze.bfg` version
- 1.1.
-
- To make use of any ``bfg_view`` declaration, you must perform a
- :term:`scan`. To do so, either insert the following boilerplate
- into your application registry's ZCML::
-
- <scan package="."/>
-
- See :ref:`scan_directive` for more information about the ZCML
- ``scan`` directive.
-
- Or, if you don't use ZCML, use the
- :meth:`repoze.bfg.configuration.Configurator.scan` method::
-
- config.scan()
- """
- venusian = venusian # for testing injection
- def __init__(self, name='', request_type=None, for_=None, permission=None,
- route_name=None, request_method=None, request_param=None,
- containment=None, attr=None, renderer=None, wrapper=None,
- xhr=False, accept=None, header=None, path_info=None,
- custom_predicates=(), context=None):
- self.name = name
- self.request_type = request_type
- self.context = context or for_
- self.permission = permission
- self.route_name = route_name
- self.request_method = request_method
- self.request_param = request_param
- self.containment = containment
- self.attr = attr
- self.renderer = renderer
- self.wrapper = wrapper
- self.xhr = xhr
- self.accept = accept
- self.header = header
- self.path_info = path_info
- self.custom_predicates = custom_predicates
-
- def __call__(self, wrapped):
- settings = self.__dict__.copy()
-
- def callback(context, name, ob):
- context.config.add_view(view=ob, **settings)
-
- info = self.venusian.attach(wrapped, callback, category='bfg')
-
- 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['attr'] is None:
- settings['attr'] = wrapped.__name__
-
- # try to convert the renderer provided into a fully qualified
- # resource specification
- abspath = settings.get('renderer')
- if abspath is not None and '.' in abspath:
- isabs = os.path.isabs(abspath)
- if not (':' in abspath and not isabs):
- # not already a resource spec
- if not isabs:
- pp = package_path(info.module)
- abspath = os.path.join(pp, abspath)
- resource = resource_spec_from_abspath(abspath, info.module)
- settings['renderer'] = resource
-
- return wrapped
-
-def default_exceptionresponse_view(context, request):
- if not isinstance(context, Exception):
- # backwards compat for an exception response view registered via
- # config.set_notfound_view or config.set_forbidden_view
- # instead of as a proper exception view
- context = request.exception or context
- return context
-
-class AppendSlashNotFoundViewFactory(object):
- """ There can only be one :term:`Not Found view` in any
- :mod:`repoze.bfg` application. Even if you use
- :func:`repoze.bfg.view.append_slash_notfound_view` as the Not
- Found view, :mod:`repoze.bfg` 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 you only
- need redirections to slash-appended route URLs, you may use the
- :func:`repoze.bfg.view.append_slash_notfound_view` object as the
- Not Found view. 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 this 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
-
- from repoze.bfg.exceptions import NotFound
- from repoze.bfg.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=NotFound)
-
- The ``notfound_view`` supplied must adhere to the two-argument
- view callable calling convention of ``(context, request)``
- (``context`` will be the exception object).
-
- .. note:: This class is new as of :mod:`repoze.bfg` version 1.3.
-
- """
- def __init__(self, notfound_view=None):
- if notfound_view is None:
- notfound_view = default_exceptionresponse_view
- 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 = request.exception
- path = request.environ.get('PATH_INFO', '/')
- registry = request.registry
- mapper = registry.queryUtility(IRoutesMapper)
- if mapper is not None and not path.endswith('/'):
- slashpath = path + '/'
- for route in mapper.get_routes():
- if route.match(slashpath) is not None:
- return HTTPFound(location=slashpath)
- return self.notfound_view(context, request)
-
-append_slash_notfound_view = AppendSlashNotFoundViewFactory()
-append_slash_notfound_view.__doc__ = """\
-For behavior like Django's ``APPEND_SLASH=True``, use this view as the
-:term:`Not Found view` in your application.
-
-When this view is the Not Found view (indicating that no view was
-found), and any routes have been defined in the configuration of your
-application, if the value of the ``PATH_INFO`` WSGI environment
-variable does not already end in a slash, and if the value of
-``PATH_INFO`` *plus* a slash matches any route's path, do an HTTP
-redirect to the slash-appended PATH_INFO. Note that this will *lose*
-``POST`` data information (turning it into a GET), so you shouldn't
-rely on this to redirect POST requests.
-
-If you use :term:`ZCML`, add the following to your application's
-``configure.zcml`` to use this view as the Not Found view::
-
- <view
- context="repoze.bfg.exceptions.NotFound"
- view="repoze.bfg.view.append_slash_notfound_view"/>
-
-Or use the
-:meth:`repoze.bfg.configuration.Configurator.add_view`
-method if you don't use ZCML::
-
- from repoze.bfg.exceptions import NotFound
- from repoze.bfg.view import append_slash_notfound_view
- config.add_view(append_slash_notfound_view, context=NotFound)
-
-See also :ref:`changing_the_notfound_view`.
-
-.. note:: This function is new as of :mod:`repoze.bfg` version 1.1.
-"""
-
-
-