import inspect
import os
import re
import sys
import types
import traceback
import warnings
from hashlib import md5
import venusian
from translationstring import ChameleonTranslate
from zope.configuration.config import GroupingContextDecorator
from zope.configuration.config import ConfigurationMachine
from zope.configuration.xmlconfig import registerCommonDirectives
from zope.interface import Interface
from zope.interface import implementedBy
from zope.interface.interfaces import IInterface
from zope.interface import implements
from zope.interface import classProvides
from pyramid.interfaces import IAuthenticationPolicy
from pyramid.interfaces import IAuthorizationPolicy
from pyramid.interfaces import IChameleonTranslate
from pyramid.interfaces import IDebugLogger
from pyramid.interfaces import IDefaultPermission
from pyramid.interfaces import IDefaultRootFactory
from pyramid.interfaces import IException
from pyramid.interfaces import IExceptionResponse
from pyramid.interfaces import IExceptionViewClassifier
from pyramid.interfaces import ILocaleNegotiator
from pyramid.interfaces import IMultiView
from pyramid.interfaces import IPackageOverrides
from pyramid.interfaces import IRendererFactory
from pyramid.interfaces import IRendererGlobalsFactory
from pyramid.interfaces import IRequest
from pyramid.interfaces import IRequestFactory
from pyramid.interfaces import IResponse
from pyramid.interfaces import IRootFactory
from pyramid.interfaces import IRouteRequest
from pyramid.interfaces import IRoutesMapper
from pyramid.interfaces import ISecuredView
from pyramid.interfaces import ISessionFactory
from pyramid.interfaces import IStaticURLInfo
from pyramid.interfaces import ITranslationDirectories
from pyramid.interfaces import ITraverser
from pyramid.interfaces import IView
from pyramid.interfaces import IViewClassifier
from pyramid.interfaces import IViewMapper
from pyramid.interfaces import IViewMapperFactory
from pyramid import renderers
from pyramid.authorization import ACLAuthorizationPolicy
from pyramid.events import ApplicationCreated
from pyramid.exceptions import ConfigurationError
from pyramid.exceptions import PredicateMismatch
from pyramid.httpexceptions import default_exceptionresponse_view
from pyramid.httpexceptions import HTTPForbidden
from pyramid.httpexceptions import HTTPNotFound
from pyramid.i18n import get_localizer
from pyramid.log import make_stream_logger
from pyramid.mako_templating import renderer_factory as mako_renderer_factory
from pyramid.path import caller_package
from pyramid.path import package_path
from pyramid.path import package_of
from pyramid.registry import Registry
from pyramid.renderers import RendererHelper
from pyramid.request import route_request_iface
from pyramid.asset import PackageOverrides
from pyramid.asset import resolve_asset_spec
from pyramid.settings import Settings
from pyramid.static import StaticURLInfo
from pyramid.threadlocal import get_current_registry
from pyramid.threadlocal import get_current_request
from pyramid.threadlocal import manager
from pyramid.traversal import DefaultRootFactory
from pyramid.traversal import find_interface
from pyramid.traversal import traversal_path
from pyramid.urldispatch import RoutesMapper
from pyramid.util import DottedNameResolver
from pyramid.util import WeakOrderedSet
from pyramid.view import render_view_to_response
DEFAULT_RENDERERS = (
('.mak', mako_renderer_factory),
('.mako', mako_renderer_factory),
('json', renderers.json_renderer_factory),
('string', renderers.string_renderer_factory),
)
try:
from pyramid import chameleon_text
DEFAULT_RENDERERS += (('.txt', chameleon_text.renderer_factory),)
except TypeError: # pragma: no cover
pass # pypy
try:
from pyramid import chameleon_zpt
DEFAULT_RENDERERS += (('.pt', chameleon_zpt.renderer_factory),)
except TypeError: # pragma: no cover
pass #pypy
MAX_ORDER = 1 << 30
DEFAULT_PHASH = md5().hexdigest()
def action_method(wrapped):
""" Wrapper to provide the right conflict info report data when a method
that calls Configurator.action calls another that does the same"""
def wrapper(self, *arg, **kw):
if self._ainfo is None:
self._ainfo = []
info = kw.pop('_info', None)
if info is None:
try:
f = traceback.extract_stack(limit=3)
info = f[-2]
except: # pragma: no cover
info = ''
self._ainfo.append(info)
try:
result = wrapped(self, *arg, **kw)
finally:
self._ainfo.pop()
return result
wrapper.__name__ = wrapped.__name__
wrapper.__doc__ = wrapped.__doc__
wrapper.__docobj__ = wrapped # for sphinx
return wrapper
[docs]class Configurator(object):
"""
A Configurator is used to configure a :app:`Pyramid`
:term:`application registry`.
The Configurator accepts a number of arguments: ``registry``,
``package``, ``settings``, ``root_factory``, ``authentication_policy``,
``authorization_policy``, ``renderers`` ``debug_logger``,
``locale_negotiator``, ``request_factory``, ``renderer_globals_factory``,
``default_permission``, ``session_factory``, ``default_view_mapper``,
``autocommit``, and ``exceptionresponse_view``.
If the ``registry`` argument is passed as a non-``None`` value, it
must be an instance of the :class:`pyramid.registry.Registry`
class representing the registry to configure. If ``registry`` is
``None``, the configurator will create a
:class:`pyramid.registry.Registry` instance itself; it will
also perform some default configuration that would not otherwise
be done. After construction, the configurator may be used to add
configuration to the registry. The overall state of a registry is
called the 'configuration state'.
.. warning:: If a ``registry`` is passed to the Configurator
constructor, all other constructor arguments except ``package``
are ignored.
If the ``package`` argument is passed, it must be a reference to a
Python :term:`package` (e.g. ``sys.modules['thepackage']``) or a
:term:`dotted Python name` to same. This value is used as a basis
to convert relative paths passed to various configuration methods,
such as methods which accept a ``renderer`` argument, into
absolute paths. If ``None`` is passed (the default), the package
is assumed to be the Python package in which the *caller* of the
``Configurator`` constructor lives.
If the ``settings`` argument is passed, it should be a Python dictionary
representing the deployment settings for this application. These are
later retrievable using the :attr:`pyramid.registry.Registry.settings`
attribute (aka ``request.registry.settings``).
If the ``root_factory`` argument is passed, it should be an object
representing the default :term:`root factory` for your application
or a :term:`dotted Python name` to same. If it is ``None``, a
default root factory will be used.
If ``authentication_policy`` is passed, it should be an instance
of an :term:`authentication policy` or a :term:`dotted Python
name` to same.
If ``authorization_policy`` is passed, it should be an instance of
an :term:`authorization policy` or a :term:`dotted Python name` to
same.
.. note:: A ``ConfigurationError`` will be raised when an
authorization policy is supplied without also supplying an
authentication policy (authorization requires authentication).
If ``renderers`` is passed, it should be a list of tuples
representing a set of :term:`renderer` factories which should be
configured into this application (each tuple representing a set of
positional values that should be passed to
:meth:`pyramid.config.Configurator.add_renderer`). If
it is not passed, a default set of renderer factories is used.
If ``debug_logger`` is not passed, a default debug logger that
logs to stderr will be used. If it is passed, it should be an
instance of the :class:`logging.Logger` (PEP 282) standard library
class or a :term:`dotted Python name` to same. The debug logger
is used by :app:`Pyramid` itself to log warnings and
authorization debugging information.
If ``locale_negotiator`` is passed, it should be a :term:`locale
negotiator` implementation or a :term:`dotted Python name` to
same. See :ref:`custom_locale_negotiator`.
If ``request_factory`` is passed, it should be a :term:`request
factory` implementation or a :term:`dotted Python name` to same.
See :ref:`changing_the_request_factory`. By default it is ``None``,
which means use the default request factory.
If ``renderer_globals_factory`` is passed, it should be a :term:`renderer
globals` factory implementation or a :term:`dotted Python name` to same.
See :ref:`adding_renderer_globals`. By default, it is ``None``, which
means use no renderer globals factory.
.. warning::
as of Pyramid 1.1, ``renderer_globals_factory`` is deprecated. Instead,
use a BeforeRender event subscriber as per :ref:`beforerender_event`.
If ``default_permission`` is passed, it should be a
:term:`permission` string to be used as the default permission for
all view configuration registrations performed against this
Configurator. An example of a permission string:``'view'``.
Adding a default permission makes it unnecessary to protect each
view configuration with an explicit permission, unless your
application policy requires some exception for a particular view.
By default, ``default_permission`` is ``None``, meaning that view
configurations which do not explicitly declare a permission will
always be executable by entirely anonymous users (any
authorization policy in effect is ignored). See also
:ref:`setting_a_default_permission`.
If ``session_factory`` is passed, it should be an object which
implements the :term:`session factory` interface. If a nondefault
value is passed, the ``session_factory`` will be used to create a
session object when ``request.session`` is accessed. Note that
the same outcome can be achieved by calling
:meth:`pyramid.config.Configurator.set_session_factory`. By
default, this argument is ``None``, indicating that no session
factory will be configured (and thus accessing ``request.session``
will throw an error) unless ``set_session_factory`` is called later
during configuration.
If ``autocommit`` is ``True``, every method called on the configurator
will cause an immediate action, and no configuration conflict detection
will be used. If ``autocommit`` is ``False``, most methods of the
configurator will defer their action until
:meth:`pyramid.config.Configurator.commit` is called. When
:meth:`pyramid.config.Configurator.commit` is called, the actions implied
by the called methods will be checked for configuration conflicts unless
``autocommit`` is ``True``. If a conflict is detected a
``ConfigurationConflictError`` will be raised. Calling
:meth:`pyramid.config.Configurator.make_wsgi_app` always implies a final
commit.
If ``default_view_mapper`` is passed, it will be used as the default
:term:`view mapper` factory for view configurations that don't otherwise
specify one (see :class:`pyramid.interfaces.IViewMapperFactory`). If a
default_view_mapper is not passed, a superdefault view mapper will be
used.
If ``exceptionresponse_view`` is passed, it must be a :term:`view
callable` or ``None``. If it is a view callable, it will be used as an
exception view callable when an :term:`exception response` is raised. If
``exceptionresponse_view`` is ``None``, no exception response view will
be registered, and all raised exception responses will be bubbled up to
Pyramid's caller. By
default, the ``pyramid.httpexceptions.default_exceptionresponse_view``
function is used as the ``exceptionresponse_view``. This argument is new
in Pyramid 1.1. """
manager = manager # for testing injection
venusian = venusian # for testing injection
_ctx = None
_ainfo = None
def __init__(self,
registry=None,
package=None,
settings=None,
root_factory=None,
authentication_policy=None,
authorization_policy=None,
renderers=DEFAULT_RENDERERS,
debug_logger=None,
locale_negotiator=None,
request_factory=None,
renderer_globals_factory=None,
default_permission=None,
session_factory=None,
default_view_mapper=None,
autocommit=False,
exceptionresponse_view=default_exceptionresponse_view,
):
if package is None:
package = caller_package()
name_resolver = DottedNameResolver(package)
self.name_resolver = name_resolver
self.package_name = name_resolver.package_name
self.package = name_resolver.package
self.registry = registry
self.autocommit = autocommit
if registry is None:
registry = Registry(self.package_name)
self.registry = registry
self.setup_registry(
settings=settings,
root_factory=root_factory,
authentication_policy=authentication_policy,
authorization_policy=authorization_policy,
renderers=renderers,
debug_logger=debug_logger,
locale_negotiator=locale_negotiator,
request_factory=request_factory,
renderer_globals_factory=renderer_globals_factory,
default_permission=default_permission,
session_factory=session_factory,
default_view_mapper=default_view_mapper,
exceptionresponse_view=exceptionresponse_view,
)
def _set_settings(self, mapping):
if not mapping:
mapping = {}
settings = Settings(mapping)
self.registry.settings = settings
return settings
@action_method
def _set_root_factory(self, factory):
""" Add a :term:`root factory` to the current configuration
state. If the ``factory`` argument is ``None`` a default root
factory will be registered."""
factory = self.maybe_dotted(factory)
if factory is None:
factory = DefaultRootFactory
def register():
self.registry.registerUtility(factory, IRootFactory)
self.registry.registerUtility(factory, IDefaultRootFactory) # b/c
self.action(IRootFactory, register)
@action_method
def _set_authentication_policy(self, policy):
""" Add a :app:`Pyramid` :term:`authentication policy` to
the current configuration."""
policy = self.maybe_dotted(policy)
self.registry.registerUtility(policy, IAuthenticationPolicy)
self.action(IAuthenticationPolicy)
@action_method
def _set_authorization_policy(self, policy):
""" Add a :app:`Pyramid` :term:`authorization policy` to
the current configuration state (also accepts a :term:`dotted
Python name`."""
policy = self.maybe_dotted(policy)
self.registry.registerUtility(policy, IAuthorizationPolicy)
self.action(IAuthorizationPolicy, None)
def _make_spec(self, path_or_spec):
package, filename = resolve_asset_spec(path_or_spec,
self.package_name)
if package is None:
return filename # absolute filename
return '%s:%s' % (package, filename)
def _split_spec(self, path_or_spec):
return resolve_asset_spec(path_or_spec, self.package_name)
# b/w compat
def _derive_view(self, view, permission=None, predicates=(),
attr=None, renderer=None, wrapper_viewname=None,
viewname=None, accept=None, order=MAX_ORDER,
phash=DEFAULT_PHASH, decorator=None,
mapper=None, http_cache=None):
view = self.maybe_dotted(view)
mapper = self.maybe_dotted(mapper)
if isinstance(renderer, basestring):
renderer = RendererHelper(name=renderer, package=self.package,
registry = self.registry)
if renderer is None:
# use default renderer if one exists
if self.registry.queryUtility(IRendererFactory) is not None:
renderer = RendererHelper(name=None,
package=self.package,
registry=self.registry)
deriver = ViewDeriver(registry=self.registry,
permission=permission,
predicates=predicates,
attr=attr,
renderer=renderer,
wrapper_viewname=wrapper_viewname,
viewname=viewname,
accept=accept,
order=order,
phash=phash,
package=self.package,
mapper=mapper,
decorator=decorator,
http_cache=http_cache)
return deriver(view)
@action_method
def _set_security_policies(self, authentication, authorization=None):
if (authorization is not None) and (not authentication):
raise ConfigurationError(
'If the "authorization" is passed a value, '
'the "authentication" argument must also be '
'passed a value; authorization requires authentication.')
if authorization is None:
authorization = ACLAuthorizationPolicy() # default
self._set_authentication_policy(authentication)
self._set_authorization_policy(authorization)
def _fix_registry(self):
""" Fix up a ZCA component registry that is not a
pyramid.registry.Registry by adding analogues of ``has_listeners``,
``notify``, ``queryAdapterOrSelf``, and ``registerSelfAdapter``
through monkey-patching."""
_registry = self.registry
if not hasattr(_registry, 'notify'):
def notify(*events):
[ _ for _ in _registry.subscribers(events, None) ]
_registry.notify = notify
if not hasattr(_registry, 'has_listeners'):
_registry.has_listeners = True
if not hasattr(_registry, 'queryAdapterOrSelf'):
def queryAdapterOrSelf(object, interface, default=None):
if not interface.providedBy(object):
return _registry.queryAdapter(object, interface,
default=default)
return object
_registry.queryAdapterOrSelf = queryAdapterOrSelf
if not hasattr(_registry, 'registerSelfAdapter'):
def registerSelfAdapter(required=None, provided=None,
name=u'', info=u'', event=True):
return _registry.registerAdapter(lambda x: x,
required=required,
provided=provided, name=name,
info=info, event=event)
_registry.registerSelfAdapter = registerSelfAdapter
def _make_context(self, autocommit=False):
context = PyramidConfigurationMachine()
registerCommonDirectives(context)
context.registry = self.registry
context.autocommit = autocommit
return context
# API
[docs] def action(self, discriminator, callable=None, args=(), kw=None, order=0):
""" Register an action which will be executed when
:meth:`pyramid.config.Configuration.commit` is called (or executed
immediately if ``autocommit`` is ``True``).
.. warning:: This method is typically only used by :app:`Pyramid`
framework extension authors, not by :app:`Pyramid` application
developers.
The ``discriminator`` uniquely identifies the action. It must be
given, but it can be ``None``, to indicate that the action never
conflicts. It must be a hashable value.
The ``callable`` is a callable object which performs the action. It
is optional. ``args`` and ``kw`` are tuple and dict objects
respectively, which are passed to ``callable`` when this action is
executed.
``order`` is a crude order control mechanism, only rarely used (has
no effect when autocommit is ``True``).
"""
if kw is None:
kw = {}
context = self._ctx
if context is None:
autocommit = self.autocommit
else:
autocommit = context.autocommit
if autocommit:
if callable is not None:
callable(*args, **kw)
else:
if context is None: # defer expensive creation of context
context = self._ctx = self._make_context(self.autocommit)
if not context.info:
# Try to provide more accurate info for conflict reports by
# wrapping the context in a decorator and attaching caller info
# to it, unless the context already has info (if it already has
# info, it's likely a context generated by a ZCML directive).
context = GroupingContextDecorator(context)
if self._ainfo:
info = self._ainfo[0]
else:
info = ''
context.info = info
context.action(discriminator, callable, args, kw, order)
[docs] def commit(self):
""" Commit any pending configuration actions. If a configuration
conflict is detected in the pending configuration actins, this method
will raise a :exc:`ConfigurationConflictError`; within the traceback
of this error will be information about the source of the conflict,
usually including file names and line numbers of the cause of the
configuration conflicts."""
if self._ctx is None:
return
self._ctx.execute_actions()
# unwrap and reset the context
self._ctx = None
[docs] def include(self, *callables):
"""Include one or more configuration callables, to support imperative
application extensibility.
A configuration callable should be a callable that accepts a single
argument named ``config``, which will be an instance of a
:term:`Configurator` (be warned that it will not be the same
configurator instance on which you call this method, however). The
code which runs as the result of calling the callable should invoke
methods on the configurator passed to it which add configuration
state. The return value of a callable will be ignored.
Values allowed to be presented via the ``*callables`` argument to
this method: any callable Python object or any :term:`dotted Python
name` which resolves to a callable Python object. It may also be a
Python :term:`module`, in which case, the module will be searched for
a callable named ``includeme``, which will be treated as the
configuration callable.
For example, if the ``includeme`` function below lives in a module
named ``myapp.myconfig``:
.. code-block:: python
:linenos:
# myapp.myconfig module
def my_view(request):
from pyramid.response import Response
return Response('OK')
def includeme(config):
config.add_view(my_view)
You might cause it be included within your Pyramid application like
so:
.. code-block:: python
:linenos:
from pyramid.config import Configurator
def main(global_config, **settings):
config = Configurator()
config.include('myapp.myconfig.includeme')
Because the function is named ``includeme``, the function name can
also be omitted from the dotted name reference:
.. code-block:: python
:linenos:
from pyramid.config import Configurator
def main(global_config, **settings):
config = Configurator()
config.include('myapp.myconfig')
Included configuration statements will be overridden by local
configuration statements if an included callable causes a
configuration conflict by registering something with the same
configuration parameters."""
_context = self._ctx
if _context is None:
_context = self._ctx = self._make_context(self.autocommit)
for c in callables:
c = self.maybe_dotted(c)
module = inspect.getmodule(c)
if module is c:
c = getattr(module, 'includeme')
spec = module.__name__ + ':' + c.__name__
sourcefile = inspect.getsourcefile(c)
if _context.processSpec(spec):
context = GroupingContextDecorator(_context)
context.basepath = os.path.dirname(sourcefile)
context.includepath = _context.includepath + (spec,)
context.package = package_of(module)
config = self.__class__.with_context(context)
c(config)
[docs] def add_directive(self, name, directive, action_wrap=True):
"""
Add a directive method to the configurator.
.. warning:: This method is typically only used by :app:`Pyramid`
framework extension authors, not by :app:`Pyramid` application
developers.
Framework extenders can add directive methods to a configurator by
instructing their users to call ``config.add_directive('somename',
'some.callable')``. This will make ``some.callable`` accessible as
``config.somename``. ``some.callable`` should be a function which
accepts ``config`` as a first argument, and arbitrary positional and
keyword arguments following. It should use config.action as
necessary to perform actions. Directive methods can then be invoked
like 'built-in' directives such as ``add_view``, ``add_route``, etc.
The ``action_wrap`` argument should be ``True`` for directives which
perform ``config.action`` with potentially conflicting
discriminators. ``action_wrap`` will cause the directive to be
wrapped in a decorator which provides more accurate conflict
cause information.
``add_directive`` does not participate in conflict detection, and
later calls to ``add_directive`` will override earlier calls.
"""
c = self.maybe_dotted(directive)
if not hasattr(self.registry, '_directives'):
self.registry._directives = {}
self.registry._directives[name] = (c, action_wrap)
def __getattr__(self, name):
# allow directive extension names to work
directives = getattr(self.registry, '_directives', {})
c = directives.get(name)
if c is None:
raise AttributeError(name)
c, action_wrap = c
if action_wrap:
c = action_method(c)
m = types.MethodType(c, self, self.__class__)
return m
@classmethod
def with_context(cls, context):
"""A classmethod used by ZCML directives,
:meth:`pyramid.config.Configurator.with_package`, and
:meth:`pyramid.config.Configurator.include` to obtain a configurator
with 'the right' context. Returns a new Configurator instance."""
configurator = cls(registry=context.registry, package=context.package,
autocommit=context.autocommit)
configurator._ctx = context
return configurator
[docs] def with_package(self, package):
""" Return a new Configurator instance with the same registry
as this configurator using the package supplied as the
``package`` argument to the new configurator. ``package`` may
be an actual Python package object or a Python dotted name
representing a package."""
context = self._ctx
if context is None:
context = self._ctx = self._make_context(self.autocommit)
context = GroupingContextDecorator(context)
context.package = package
return self.__class__.with_context(context)
[docs] def maybe_dotted(self, dotted):
""" Resolve the :term:`dotted Python name` ``dotted`` to a
global Python object. If ``dotted`` is not a string, return
it without attempting to do any name resolution. If
``dotted`` is a relative dotted name (e.g. ``.foo.bar``,
consider it relative to the ``package`` argument supplied to
this Configurator's constructor."""
return self.name_resolver.maybe_resolve(dotted)
[docs] def absolute_asset_spec(self, relative_spec):
""" Resolve the potentially relative :term:`asset
specification` string passed as ``relative_spec`` into an
absolute asset specification string and return the string.
Use the ``package`` of this configurator as the package to
which the asset specification will be considered relative
when generating an absolute asset specification. If the
provided ``relative_spec`` argument is already absolute, or if
the ``relative_spec`` is not a string, it is simply returned."""
if not isinstance(relative_spec, basestring):
return relative_spec
return self._make_spec(relative_spec)
absolute_resource_spec = absolute_asset_spec # b/w compat forever
[docs] def setup_registry(self, settings=None, root_factory=None,
authentication_policy=None, authorization_policy=None,
renderers=DEFAULT_RENDERERS, debug_logger=None,
locale_negotiator=None, request_factory=None,
renderer_globals_factory=None, default_permission=None,
session_factory=None, default_view_mapper=None,
exceptionresponse_view=default_exceptionresponse_view):
""" When you pass a non-``None`` ``registry`` argument to the
:term:`Configurator` constructor, no initial 'setup' is performed
against the registry. This is because the registry you pass in may
have already been initialized for use under :app:`Pyramid` via a
different configurator. However, in some circumstances (such as when
you want to use the Zope 'global` registry instead of a registry
created as a result of the Configurator constructor), or when you
want to reset the initial setup of a registry, you *do* want to
explicitly initialize the registry associated with a Configurator for
use under :app:`Pyramid`. Use ``setup_registry`` to do this
initialization.
``setup_registry`` configures settings, a root factory, security
policies, renderers, a debug logger, a locale negotiator, and various
other settings using the configurator's current registry, as per the
descriptions in the Configurator constructor."""
registry = self.registry
self._fix_registry()
self._set_settings(settings)
self._set_root_factory(root_factory)
# cope with WebOb response objects that aren't decorated with IResponse
from webob import Response as WebobResponse
# cope with WebOb exc objects not decoratored with IExceptionResponse
from webob.exc import WSGIHTTPException as WebobWSGIHTTPException
registry.registerSelfAdapter((WebobResponse,), IResponse)
debug_logger = self.maybe_dotted(debug_logger)
if debug_logger is None:
debug_logger = make_stream_logger('pyramid.debug', sys.stderr)
registry.registerUtility(debug_logger, IDebugLogger)
if authentication_policy or authorization_policy:
self._set_security_policies(authentication_policy,
authorization_policy)
for name, renderer in renderers:
self.add_renderer(name, renderer)
if exceptionresponse_view is not None:
exceptionresponse_view = self.maybe_dotted(exceptionresponse_view)
self.add_view(exceptionresponse_view, context=IExceptionResponse)
self.add_view(exceptionresponse_view,context=WebobWSGIHTTPException)
if locale_negotiator:
locale_negotiator = self.maybe_dotted(locale_negotiator)
registry.registerUtility(locale_negotiator, ILocaleNegotiator)
if request_factory:
request_factory = self.maybe_dotted(request_factory)
self.set_request_factory(request_factory)
if renderer_globals_factory:
warnings.warn(
'Passing ``renderer_globals_factory`` as a Configurator '
'constructor parameter is deprecated as of Pyramid 1.1. '
'Use a BeforeRender event subscriber as documented in the '
'"Hooks" chapter of the Pyramid narrative documentation '
'instead',
DeprecationWarning,
2)
renderer_globals_factory = self.maybe_dotted(
renderer_globals_factory)
self.set_renderer_globals_factory(renderer_globals_factory,
warn=False)
if default_permission:
self.set_default_permission(default_permission)
if session_factory is not None:
self.set_session_factory(session_factory)
# commit before adding default_view_mapper, as the
# exceptionresponse_view above requires the superdefault view
# mapper
self.commit()
if default_view_mapper is not None:
self.set_view_mapper(default_view_mapper)
self.commit()
[docs] def hook_zca(self):
""" Call :func:`zope.component.getSiteManager.sethook` with
the argument
:data:`pyramid.threadlocal.get_current_registry`, causing
the :term:`Zope Component Architecture` 'global' APIs such as
:func:`zope.component.getSiteManager`,
:func:`zope.component.getAdapter` and others to use the
:app:`Pyramid` :term:`application registry` rather than the
Zope 'global' registry. If :mod:`zope.component` cannot be
imported, this method will raise an :exc:`ImportError`."""
from zope.component import getSiteManager
getSiteManager.sethook(get_current_registry)
[docs] def unhook_zca(self):
""" Call :func:`zope.component.getSiteManager.reset` to undo
the action of
:meth:`pyramid.config.Configurator.hook_zca`. If
:mod:`zope.component` cannot be imported, this method will
raise an :exc:`ImportError`."""
from zope.component import getSiteManager
getSiteManager.reset()
[docs] def begin(self, request=None):
""" Indicate that application or test configuration has begun.
This pushes a dictionary containing the :term:`application
registry` implied by ``registry`` attribute of this
configurator and the :term:`request` implied by the
``request`` argument on to the :term:`thread local` stack
consulted by various :mod:`pyramid.threadlocal` API
functions."""
self.manager.push({'registry':self.registry, 'request':request})
[docs] def end(self):
""" Indicate that application or test configuration has ended.
This pops the last value pushed on to the :term:`thread local`
stack (usually by the ``begin`` method) and returns that
value.
"""
return self.manager.pop()
[docs] def derive_view(self, view, attr=None, renderer=None):
"""
Create a :term:`view callable` using the function, instance,
or class (or :term:`dotted Python name` referring to the same)
provided as ``view`` object.
.. warning:: This method is typically only used by :app:`Pyramid`
framework extension authors, not by :app:`Pyramid` application
developers.
This is API is useful to framework extenders who create
pluggable systems which need to register 'proxy' view
callables for functions, instances, or classes which meet the
requirements of being a :app:`Pyramid` view callable. For
example, a ``some_other_framework`` function in another
framework may want to allow a user to supply a view callable,
but he may want to wrap the view callable in his own before
registering the wrapper as a :app:`Pyramid` view callable.
Because a :app:`Pyramid` view callable can be any of a
number of valid objects, the framework extender will not know
how to call the user-supplied object. Running it through
``derive_view`` normalizes it to a callable which accepts two
arguments: ``context`` and ``request``.
For example:
.. code-block:: python
def some_other_framework(user_supplied_view):
config = Configurator(reg)
proxy_view = config.derive_view(user_supplied_view)
def my_wrapper(context, request):
do_something_that_mutates(request)
return proxy_view(context, request)
config.add_view(my_wrapper)
The ``view`` object provided should be one of the following:
- A function or another non-class callable object that accepts
a :term:`request` as a single positional argument and which
returns a :term:`response` object.
- A function or other non-class callable object that accepts
two positional arguments, ``context, request`` and which
returns a :term:`response` object.
- A class which accepts a single positional argument in its
constructor named ``request``, and which has a ``__call__``
method that accepts no arguments that returns a
:term:`response` object.
- A class which accepts two positional arguments named
``context, request``, and which has a ``__call__`` method
that accepts no arguments that returns a :term:`response`
object.
- A :term:`dotted Python name` which refers to any of the
kinds of objects above.
This API returns a callable which accepts the arguments
``context, request`` and which returns the result of calling
the provided ``view`` object.
The ``attr`` keyword argument is most useful when the view
object is a class. It names the method that should be used as
the callable. If ``attr`` is not provided, the attribute
effectively defaults to ``__call__``. See
:ref:`class_as_view` for more information.
The ``renderer`` keyword argument should be a renderer
name. If supplied, it will cause the returned callable to use
a :term:`renderer` to convert the user-supplied view result to
a :term:`response` object. If a ``renderer`` argument is not
supplied, the user-supplied view must itself return a
:term:`response` object. """
return self._derive_view(view, attr=attr, renderer=renderer)
@action_method
[docs] def add_subscriber(self, subscriber, iface=None):
"""Add an event :term:`subscriber` for the event stream
implied by the supplied ``iface`` interface. The
``subscriber`` argument represents a callable object (or a
:term:`dotted Python name` which identifies a callable); it
will be called with a single object ``event`` whenever
:app:`Pyramid` emits an :term:`event` associated with the
``iface``, which may be an :term:`interface` or a class or a
:term:`dotted Python name` to a global object representing an
interface or a class. Using the default ``iface`` value,
``None`` will cause the subscriber to be registered for all
event types. See :ref:`events_chapter` for more information
about events and subscribers."""
dotted = self.maybe_dotted
subscriber, iface = dotted(subscriber), dotted(iface)
if iface is None:
iface = (Interface,)
if not isinstance(iface, (tuple, list)):
iface = (iface,)
def register():
self.registry.registerHandler(subscriber, iface)
self.action(None, register)
return subscriber
@action_method
[docs] def add_response_adapter(self, adapter, type_or_iface):
""" When an object of type (or interface) ``type_or_iface`` is
returned from a view callable, Pyramid will use the adapter
``adapter`` to convert it into an object which implements the
:class:`pyramid.interfaces.IResponse` interface. If ``adapter`` is
None, an object returned of type (or interface) ``type_or_iface``
will itself be used as a response object.
``adapter`` and ``type_or_interface`` may be Python objects or
strings representing dotted names to importable Python global
objects.
See :ref:`using_iresponse` for more information."""
adapter = self.maybe_dotted(adapter)
type_or_iface = self.maybe_dotted(type_or_iface)
def register():
reg = self.registry
if adapter is None:
reg.registerSelfAdapter((type_or_iface,), IResponse)
else:
reg.registerAdapter(adapter, (type_or_iface,), IResponse)
self.action((IResponse, type_or_iface), register)
[docs] def add_settings(self, settings=None, **kw):
"""Augment the ``settings`` argument passed in to the Configurator
constructor with one or more 'setting' key/value pairs. A setting is
a single key/value pair in the dictionary-ish object returned from
the API :attr:`pyramid.registry.Registry.settings` and
:meth:`pyramid.config.Configurator.get_settings`.
You may pass a dictionary::
config.add_settings({'external_uri':'http://example.com'})
Or a set of key/value pairs::
config.add_settings(external_uri='http://example.com')
This function is useful when you need to test code that accesses the
:attr:`pyramid.registry.Registry.settings` API (or the
:meth:`pyramid.config.Configurator.get_settings` API) and
which uses values from that API.
"""
if settings is None:
settings = {}
utility = self.registry.settings
if utility is None:
utility = self._set_settings(settings)
utility.update(settings)
utility.update(kw)
[docs] def get_settings(self):
"""
Return a :term:`deployment settings` object for the current
application. A deployment settings object is a dictionary-like
object that contains key/value pairs based on the dictionary passed
as the ``settings`` argument to the
:class:`pyramid.config.Configurator` constructor or the
:func:`pyramid.router.make_app` API.
.. note:: For backwards compatibility, dictionary keys can also be
looked up as attributes of the settings object.
.. note:: the :attr:`pyramid.registry.Registry.settings` API
performs the same duty.
"""
return self.registry.settings
[docs] def make_wsgi_app(self):
""" Commits any pending configuration statements, sends a
:class:`pyramid.events.ApplicationCreated` event to all listeners,
adds this configuration's registry to
:attr:`pyramid.config.global_registries`, and returns a
:app:`Pyramid` WSGI application representing the committed
configuration state."""
self.commit()
from pyramid.router import Router # avoid circdep
app = Router(self.registry)
global_registries.add(self.registry)
# We push the registry on to the stack here in case any code
# that depends on the registry threadlocal APIs used in
# listeners subscribed to the IApplicationCreated event.
self.manager.push({'registry':self.registry, 'request':None})
try:
self.registry.notify(ApplicationCreated(app))
finally:
self.manager.pop()
return app
@action_method
[docs] def add_view(self, view=None, name="", for_=None, permission=None,
request_type=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, decorator=None, mapper=None, http_cache=None):
""" Add a :term:`view configuration` to the current
configuration state. Arguments to ``add_view`` are broken
down below into *predicate* arguments and *non-predicate*
arguments. Predicate arguments narrow the circumstances in
which the view callable will be invoked when a request is
presented to :app:`Pyramid`; non-predicate arguments are
informational.
Non-Predicate Arguments
view
A :term:`view callable` or a :term:`dotted Python name`
which refers to a view callable. This argument is required
unless a ``renderer`` argument also exists. If a
``renderer`` argument is passed, and a ``view`` argument is
not provided, the view callable defaults to a callable that
returns an empty dictionary (see
:ref:`views_which_use_a_renderer`).
permission
The name of a :term:`permission` that the user must possess
in order to invoke the :term:`view callable`. See
:ref:`view_security_section` for more information about view
security and permissions. If ``permission`` is omitted, a
*default* permission may be used for this view registration
if one was named as the
:class:`pyramid.config.Configurator` constructor's
``default_permission`` argument, or if
:meth:`pyramid.config.Configurator.set_default_permission`
was used prior to this view registration. Pass the string
``__no_permission_required__`` as the permission argument to
explicitly indicate that the view should always be
executable by entirely anonymous users, regardless of the
default permission, bypassing any :term:`authorization
policy` that may be in effect.
attr
The view machinery defaults to using the ``__call__`` method
of the :term:`view callable` (or the function itself, if the
view callable is a function) to obtain a response. The
``attr`` value allows you to vary the method attribute used
to obtain the response. For example, if your view was a
class, and the class has a method named ``index`` and you
wanted to use this method instead of the class' ``__call__``
method to return the response, you'd say ``attr="index"`` in the
view configuration for the view. This is
most useful when the view definition is a class.
renderer
This is either a single string term (e.g. ``json``) or a
string implying a path or :term:`asset specification`
(e.g. ``templates/views.pt``) naming a :term:`renderer`
implementation. If the ``renderer`` value does not contain
a dot ``.``, the specified string will be used to look up a
renderer implementation, and that renderer implementation
will be used to construct a response from the view return
value. If the ``renderer`` value contains a dot (``.``),
the specified term will be treated as a path, and the
filename extension of the last element in the path will be
used to look up the renderer implementation, which will be
passed the full path. The renderer implementation will be
used to construct a :term:`response` from the view return
value.
Note that if the view itself returns a :term:`response` (see
:ref:`the_response`), the specified renderer implementation
is never called.
When the renderer is a path, although a path is usually just
a simple relative pathname (e.g. ``templates/foo.pt``,
implying that a template named "foo.pt" is in the
"templates" directory relative to the directory of the
current :term:`package` of the Configurator), a path can be
absolute, starting with a slash on UNIX or a drive letter
prefix on Windows. The path can alternately be a
:term:`asset specification` in the form
``some.dotted.package_name:relative/path``, making it
possible to address template assets which live in a
separate package.
The ``renderer`` attribute is optional. If it is not
defined, the "null" renderer is assumed (no rendering is
performed and the value is passed back to the upstream
:app:`Pyramid` machinery unmodified).
http_cache
.. note:: This feature is new as of Pyramid 1.1.
When you supply an ``http_cache`` value to a view configuration,
the ``Expires`` and ``Cache-Control`` headers of a response
generated by the associated view callable are modified. The value
for ``http_cache`` may be one of the following:
- A nonzero integer. If it's a nonzero integer, it's treated as a
number of seconds. This number of seconds will be used to
compute the ``Expires`` header and the ``Cache-Control:
max-age`` parameter of responses to requests which call this view.
For example: ``http_cache=3600`` instructs the requesting browser
to 'cache this response for an hour, please'.
- A ``datetime.timedelta`` instance. If it's a
``datetime.timedelta`` instance, it will be converted into a
number of seconds, and that number of seconds will be used to
compute the ``Expires`` header and the ``Cache-Control:
max-age`` parameter of responses to requests which call this view.
For example: ``http_cache=datetime.timedelta(days=1)`` instructs
the requesting browser to 'cache this response for a day, please'.
- Zero (``0``). If the value is zero, the ``Cache-Control`` and
``Expires`` headers present in all responses from this view will
be composed such that client browser cache (and any intermediate
caches) are instructed to never cache the response.
- A two-tuple. If it's a two tuple (e.g. ``http_cache=(1,
{'public':True})``), the first value in the tuple may be a
nonzero integer or a ``datetime.timedelta`` instance; in either
case this value will be used as the number of seconds to cache
the response. The second value in the tuple must be a
dictionary. The values present in the dictionary will be used as
input to the ``Cache-Control`` response header. For example:
``http_cache=(3600, {'public':True})`` means 'cache for an hour,
and add ``public`` to the Cache-Control header of the response'.
All keys and values supported by the
``webob.cachecontrol.CacheControl`` interface may be added to the
dictionary. Supplying ``{'public':True}`` is equivalent to
calling ``response.cache_control.public = True``.
Providing a non-tuple value as ``http_cache`` is equivalent to
calling ``response.cache_expires(value)`` within your view's body.
Providing a two-tuple value as ``http_cache`` is equivalent to
calling ``response.cache_expires(value[0], **value[1])`` within your
view's body.
If you wish to avoid influencing, the ``Expires`` header, and
instead wish to only influence ``Cache-Control`` headers, pass a
tuple as ``http_cache`` with the first element of ``None``, e.g.:
``(None, {'public':True})``.
If you wish to prevent a view that uses ``http_cache`` in its
configuration from having its caching response headers changed by
this machinery, set ``response.cache_control.prevent_auto = True``
before returning the response from the view. This effectively
disables any HTTP caching done by ``http_cache`` for that response.
wrapper
The :term:`view name` of a different :term:`view
configuration` which will receive the response body of this
view as the ``request.wrapped_body`` attribute of its own
:term:`request`, and the :term:`response` returned by this
view as the ``request.wrapped_response`` attribute of its
own request. Using a wrapper makes it possible to "chain"
views together to form a composite response. The response
of the outermost wrapper view will be returned to the user.
The wrapper view will be found as any view is found: see
:ref:`view_lookup`. The "best" wrapper view will be found
based on the lookup ordering: "under the hood" this wrapper
view is looked up via
``pyramid.view.render_view_to_response(context, request,
'wrapper_viewname')``. The context and request of a wrapper
view is the same context and request of the inner view. If
this attribute is unspecified, no view wrapping is done.
decorator
A :term:`dotted Python name` to function (or the function itself)
which will be used to decorate the registered :term:`view
callable`. The decorator function will be called with the view
callable as a single argument. The view callable it is passed will
accept ``(context, request)``. The decorator must return a
replacement view callable which also accepts ``(context,
request)``.
mapper
A Python object or :term:`dotted Python name` which refers to a
:term:`view mapper`, or ``None``. By default it is ``None``, which
indicates that the view should use the default view mapper. This
plug-point is useful for Pyramid extension developers, but it's not
very useful for 'civilians' who are just developing stock Pyramid
applications. Pay no attention to the man behind the curtain.
Predicate Arguments
name
The :term:`view name`. Read :ref:`traversal_chapter` to
understand the concept of a view name.
context
An object or a :term:`dotted Python name` referring to an
interface or class object that the :term:`context` must be
an instance of, *or* the :term:`interface` that the
:term:`context` must provide in order for this view to be
found and called. This predicate is true when the
:term:`context` is an instance of the represented class or
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).
route_name
This value must match the ``name`` of a :term:`route
configuration` declaration (see :ref:`urldispatch_chapter`)
that must match before this view will be called.
request_type
This value should be an :term:`interface` that the
:term:`request` must provide in order for this view to be
found and called. This value exists only for backwards
compatibility purposes.
request_method
This value can be one of the strings ``GET``,
``POST``, ``PUT``, ``DELETE``, or ``HEAD`` representing an
HTTP ``REQUEST_METHOD``. A view declaration with this
argument ensures that the view will only be called when the
request's ``method`` attribute (aka the ``REQUEST_METHOD`` of
the WSGI environment) string matches the supplied value.
request_param
This value can be any string. A view declaration with this
argument ensures that the view will only be called when the
:term:`request` has a key in the ``request.params``
dictionary (an HTTP ``GET`` or ``POST`` variable) that has a
name which matches the supplied value. If the value
supplied has a ``=`` sign in it,
e.g. ``request_param="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
This value should be a Python class or :term:`interface` (or a
:term:`dotted Python name`) that an object in the
:term:`lineage` of the context must provide in order for this view
to be found and called. The nodes in your object graph must be
"location-aware" to use this feature. See
:ref:`location_aware` for more information about
location-awareness.
xhr
This value should be either ``True`` or ``False``. If this
value is specified and is ``True``, the :term:`request`
must possess an ``HTTP_X_REQUESTED_WITH`` (aka
``X-Requested-With``) header that has the value
``XMLHttpRequest`` for this view to be found and called.
This is useful for detecting AJAX requests issued from
jQuery, Prototype and other Javascript libraries.
accept
The value of this argument represents a match query for one
or more mimetypes in the ``Accept`` HTTP request header. If
this value is specified, it must be in one of the following
forms: a mimetype match token in the form ``text/plain``, a
wildcard mimetype match token in the form ``text/*`` or a
match-all wildcard mimetype match token in the form ``*/*``.
If any of the forms matches the ``Accept`` header of the
request, this predicate will be true.
header
This value represents an HTTP header name or a header
name/value pair. If the value contains a ``:`` (colon), it
will be considered a name/value pair
(e.g. ``User-Agent:Mozilla/.*`` or ``Host:localhost``). The
value portion should be a regular expression. If the value
does not contain a colon, the entire value will be
considered to be the header name
(e.g. ``If-Modified-Since``). If the value evaluates to a
header name only without a value, the header specified by
the name must be present in the request for this predicate
to be true. If the value evaluates to a header name/value
pair, the header specified by the name must be present in
the request *and* the regular expression specified as the
value must match the header value. Whether or not the value
represents a header name or a header name/value pair, the
case of the header name is not significant.
path_info
This value represents a regular expression pattern that will
be tested against the ``PATH_INFO`` WSGI environment
variable. If the regex matches, this predicate will be
``True``.
custom_predicates
This value should be a sequence of references to custom
predicate callables. Use custom predicates when no set of
predefined predicates do what you need. Custom predicates
can be combined with predefined predicates as necessary.
Each custom predicate callable should accept two arguments:
``context`` and ``request`` and should return either
``True`` or ``False`` after doing arbitrary evaluation of
the context and/or the request. If all callables return
``True``, the associated view callable will be considered
viable for a given request.
"""
view = self.maybe_dotted(view)
context = self.maybe_dotted(context)
for_ = self.maybe_dotted(for_)
containment = self.maybe_dotted(containment)
mapper = self.maybe_dotted(mapper)
decorator = self.maybe_dotted(decorator)
if not view:
if renderer:
def view(context, request):
return {}
else:
raise ConfigurationError('"view" was not specified and '
'no "renderer" specified')
if request_type is not None:
request_type = self.maybe_dotted(request_type)
if not IInterface.providedBy(request_type):
raise ConfigurationError(
'request_type must be an interface, not %s' % request_type)
request_iface = IRequest
if route_name is not None:
request_iface = self.registry.queryUtility(IRouteRequest,
name=route_name)
if request_iface is None:
deferred_views = getattr(self.registry,
'deferred_route_views', None)
if deferred_views is None:
deferred_views = self.registry.deferred_route_views = {}
info = dict(
view=view, name=name, for_=for_, permission=permission,
request_type=request_type, route_name=route_name,
request_method=request_method, request_param=request_param,
containment=containment, attr=attr,
renderer=renderer, wrapper=wrapper, xhr=xhr, accept=accept,
header=header, path_info=path_info,
custom_predicates=custom_predicates, context=context,
mapper = mapper, http_cache = http_cache,
)
view_info = deferred_views.setdefault(route_name, [])
view_info.append(info)
return
order, predicates, phash = _make_predicates(xhr=xhr,
request_method=request_method, path_info=path_info,
request_param=request_param, header=header, accept=accept,
containment=containment, request_type=request_type,
custom=custom_predicates)
if context is None:
context = for_
r_context = context
if r_context is None:
r_context = Interface
if not IInterface.providedBy(r_context):
r_context = implementedBy(r_context)
if isinstance(renderer, basestring):
renderer = RendererHelper(name=renderer, package=self.package,
registry = self.registry)
def register(permission=permission, renderer=renderer):
if renderer is None:
# use default renderer if one exists
if self.registry.queryUtility(IRendererFactory) is not None:
renderer = RendererHelper(name=None,
package=self.package,
registry=self.registry)
if permission is None:
# intent: will be None if no default permission is registered
permission = self.registry.queryUtility(IDefaultPermission)
# __no_permission_required__ handled by _secure_view
deriver = ViewDeriver(registry=self.registry,
permission=permission,
predicates=predicates,
attr=attr,
renderer=renderer,
wrapper_viewname=wrapper,
viewname=name,
accept=accept,
order=order,
phash=phash,
package=self.package,
mapper=mapper,
decorator=decorator,
http_cache=http_cache)
derived_view = deriver(view)
registered = self.registry.adapters.registered
# 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
# predicates which it possesses.
# To find a previously registered view for a context
# type/request type/name triad, we need to use the
# ``registered`` method of the adapter registry rather than
# ``lookup``. ``registered`` ignores interface inheritance
# for the required and provided arguments, returning only a
# view registered previously with the *exact* triad we pass
# in.
# We need to do this three times, because we use three
# different interfaces as the ``provided`` interface while
# doing registrations, and ``registered`` performs exact
# matches on all the arguments it receives.
old_view = None
for view_type in (IView, ISecuredView, IMultiView):
old_view = registered((IViewClassifier, 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 = ISecuredView
else:
view_iface = IView
self.registry.registerAdapter(
derived_view,
(IViewClassifier, 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)
if old_view is None:
# - No component was yet registered for any of our I*View
# interfaces exactly; this is the first view for this
# triad.
regclosure()
elif (not is_multiview) and (old_phash == phash):
# - A single view component was previously registered with
# the same predicate hash as this view; this registration
# is therefore an override.
regclosure()
else:
# - A view or multiview was already registered for this
# triad, and the new view is not an override.
# XXX we could try to be more efficient here and register
# a non-secured view for a multiview if none of the
# multiview's consituent views have a permission
# associated with them, but this code is getting pretty
# rough already
if is_multiview:
multiview = old_view
else:
multiview = MultiView(name)
old_accept = getattr(old_view, '__accept__', None)
old_order = getattr(old_view, '__order__', MAX_ORDER)
multiview.add(old_view, old_order, old_accept, old_phash)
multiview.add(derived_view, order, accept, phash)
for view_type in (IView, ISecuredView):
# unregister any existing views
self.registry.adapters.unregister(
(IViewClassifier, 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),
IMultiView, name=name)
if isexc:
self.registry.registerAdapter(
multiview,
(IExceptionViewClassifier, request_iface, context),
IMultiView, name=name)
discriminator = [
'view', context, name, request_type, IView, containment,
request_param, request_method, route_name, attr,
xhr, accept, header, path_info]
discriminator.extend(sorted(custom_predicates))
discriminator = tuple(discriminator)
self.action(discriminator, register)
def _add_view_from_route(self,
route_name,
view,
context,
permission,
renderer,
attr,
):
if view:
self.add_view(
permission=permission,
context=context,
view=view,
name='',
route_name=route_name,
renderer=renderer,
attr=attr,
)
else:
# prevent mistakes due to misunderstanding of how hybrid calls to
# add_route and add_view interact
if attr:
raise ConfigurationError(
'view_attr argument not permitted without view '
'argument')
if context:
raise ConfigurationError(
'view_context argument not permitted without view '
'argument')
if permission:
raise ConfigurationError(
'view_permission argument not permitted without view '
'argument')
if renderer:
raise ConfigurationError(
'view_renderer argument not permitted without '
'view argument')
warnings.warn(
'Passing view-related arguments to add_route() is deprecated as of '
'Pyramid 1.1. Use add_view() to associate a view with a route '
'instead. See "Deprecations" in "What\'s New in Pyramid 1.1" '
'within the general Pyramid documentation for further details.',
DeprecationWarning,
4)
@action_method
[docs] def add_route(self,
name,
pattern=None,
view=None,
view_for=None,
permission=None,
factory=None,
for_=None,
header=None,
xhr=False,
accept=None,
path_info=None,
request_method=None,
request_param=None,
traverse=None,
custom_predicates=(),
view_permission=None,
renderer=None,
view_renderer=None,
view_context=None,
view_attr=None,
use_global_views=False,
path=None,
pregenerator=None,
static=False,
):
""" Add a :term:`route configuration` to the current
configuration state, as well as possibly a :term:`view
configuration` to be used to specify a :term:`view callable`
that will be invoked when this route matches. The arguments
to this method are divided into *predicate*, *non-predicate*,
and *view-related* types. :term:`Route predicate` arguments
narrow the circumstances in which a route will be match a
request; non-predicate arguments are informational.
Non-Predicate Arguments
name
The name of the route, e.g. ``myroute``. This attribute is
required. It must be unique among all defined routes in a given
application.
factory
A Python object (often a function or a class) or a :term:`dotted
Python name` which refers to the same object that will generate a
:app:`Pyramid` root resource object when this route matches. For
example, ``mypackage.resources.MyFactory``. If this argument is
not specified, a default root factory will be used.
traverse
If you would like to cause the :term:`context` to be
something other than the :term:`root` object when this route
matches, you can spell a traversal pattern as the
``traverse`` argument. This traversal pattern will be used
as the traversal path: traversal will begin at the root
object implied by this route (either the global root, or the
object returned by the ``factory`` associated with this
route).
The syntax of the ``traverse`` argument is the same as it is
for ``pattern``. For example, if the ``pattern`` provided to
``add_route`` is ``articles/{article}/edit``, and the
``traverse`` argument provided to ``add_route`` is
``/{article}``, when a request comes in that causes the route
to match in such a way that the ``article`` match value is
'1' (when the request URI is ``/articles/1/edit``), the
traversal path will be generated as ``/1``. This means that
the root object's ``__getitem__`` will be called with the
name ``1`` during the traversal phase. If the ``1`` object
exists, it will become the :term:`context` of the request.
:ref:`traversal_chapter` has more information about
traversal.
If the traversal path contains segment marker names which
are not present in the ``pattern`` argument, a runtime error
will occur. The ``traverse`` pattern should not contain
segment markers that do not exist in the ``pattern``
argument.
A similar combining of routing and traversal is available
when a route is matched which contains a ``*traverse``
remainder marker in its pattern (see
:ref:`using_traverse_in_a_route_pattern`). The ``traverse``
argument to add_route allows you to associate route patterns
with an arbitrary traversal path without using a a
``*traverse`` remainder marker; instead you can use other
match information.
Note that the ``traverse`` argument to ``add_route`` is
ignored when attached to a route that has a ``*traverse``
remainder marker in its pattern.
pregenerator
This option should be a callable object that implements the
:class:`pyramid.interfaces.IRoutePregenerator`
interface. A :term:`pregenerator` is a callable called by
the :mod:`pyramid.url.route_url` function to augment or
replace the arguments it is passed when generating a URL
for the route. This is a feature not often used directly
by applications, it is meant to be hooked by frameworks
that use :app:`Pyramid` as a base.
use_global_views
When a request matches this route, and view lookup cannot
find a view which has a ``route_name`` predicate argument
that matches the route, try to fall back to using a view
that otherwise matches the context, request, and view name
(but which does not match the route_name predicate).
static
If ``static`` is ``True``, this route will never match an incoming
request; it will only be useful for URL generation. By default,
``static`` is ``False``. See :ref:`static_route_narr`.
.. note:: New in :app:`Pyramid` 1.1.
Predicate Arguments
pattern
The pattern of the route e.g. ``ideas/{idea}``. This
argument is required. See :ref:`route_pattern_syntax`
for information about the syntax of route patterns. If the
pattern doesn't match the current URL, route matching
continues.
.. note::
For backwards compatibility purposes (as of :app:`Pyramid` 1.0), a
``path`` keyword argument passed to this function will be used to
represent the pattern value if the ``pattern`` argument is
``None``. If both ``path`` and ``pattern`` are passed, ``pattern``
wins.
xhr
This value should be either ``True`` or ``False``. If this
value is specified and is ``True``, the :term:`request` must
possess an ``HTTP_X_REQUESTED_WITH`` (aka
``X-Requested-With``) header for this route to match. This
is useful for detecting AJAX requests issued from jQuery,
Prototype and other Javascript libraries. If this predicate
returns ``False``, route matching continues.
request_method
A string representing an HTTP method name, e.g. ``GET``,
``POST``, ``HEAD``, ``DELETE``, ``PUT``. If this argument
is not specified, this route will match if the request has
*any* request method. If this predicate returns ``False``,
route matching continues.
path_info
This value represents a regular expression pattern that will
be tested against the ``PATH_INFO`` WSGI environment
variable. If the regex matches, this predicate will return
``True``. If this predicate returns ``False``, route
matching continues.
request_param
This value can be any string. A view declaration with this
argument ensures that the associated route will only match
when the request has a key in the ``request.params``
dictionary (an HTTP ``GET`` or ``POST`` variable) that has a
name which matches the supplied value. If the value
supplied as the argument has a ``=`` sign in it,
e.g. ``request_param="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 route to "match" the current request. If this predicate
returns ``False``, route matching continues.
header
This argument represents an HTTP header name or a header
name/value pair. If the argument contains a ``:`` (colon),
it will be considered a name/value pair
(e.g. ``User-Agent:Mozilla/.*`` or ``Host:localhost``). If
the value contains a colon, the value portion should be a
regular expression. If the value does not contain a colon,
the entire value will be considered to be the header name
(e.g. ``If-Modified-Since``). If the value evaluates to a
header name only without a value, the header specified by
the name must be present in the request for this predicate
to be true. If the value evaluates to a header name/value
pair, the header specified by the name must be present in
the request *and* the regular expression specified as the
value must match the header value. Whether or not the value
represents a header name or a header name/value pair, the
case of the header name is not significant. If this
predicate returns ``False``, route matching continues.
accept
This value represents a match query for one or more
mimetypes in the ``Accept`` HTTP request header. If this
value is specified, it must be in one of the following
forms: a mimetype match token in the form ``text/plain``, a
wildcard mimetype match token in the form ``text/*`` or a
match-all wildcard mimetype match token in the form ``*/*``.
If any of the forms matches the ``Accept`` header of the
request, this predicate will be true. If this predicate
returns ``False``, route matching continues.
custom_predicates
This value should be a sequence of references to custom
predicate callables. Use custom predicates when no set of
predefined predicates does what you need. Custom predicates
can be combined with predefined predicates as necessary.
Each custom predicate callable should accept two arguments:
``info`` and ``request`` and should return either ``True``
or ``False`` after doing arbitrary evaluation of the info
and/or the request. If all custom and non-custom predicate
callables return ``True`` the associated route will be
considered viable for a given request. If any predicate
callable returns ``False``, route matching continues. Note
that the value ``info`` passed to a custom route predicate
is a dictionary containing matching information; see
:ref:`custom_route_predicates` for more information about
``info``.
View-Related Arguments
.. warning::
The arguments described below have been deprecated as of
:app:`Pyramid` 1.1. *Do not use these for new development; they
should only be used to support older code bases which depend upon
them.* Use a separate call to
:meth:`pyramid.config.Configurator.add_view` to associate a view with
a route using the ``route_name`` argument.
view
.. warning:: Deprecated as of :app:`Pyramid` 1.1.
A Python object or :term:`dotted Python name` to the same
object that will be used as a view callable when this route
matches. e.g. ``mypackage.views.my_view``.
view_context
.. warning:: Deprecated as of :app:`Pyramid` 1.1.
A class or an :term:`interface` or :term:`dotted Python
name` to the same object which the :term:`context` of the
view should match for the view named by the route to be
used. This argument is only useful if the ``view``
attribute is used. If this attribute is not specified, the
default (``None``) will be used.
If the ``view`` argument is not provided, this argument has
no effect.
This attribute can also be spelled as ``for_`` or ``view_for``.
view_permission
.. warning:: Deprecated as of :app:`Pyramid` 1.1.
The permission name required to invoke the view associated
with this route. e.g. ``edit``. (see
:ref:`using_security_with_urldispatch` for more information
about permissions).
If the ``view`` attribute is not provided, this argument has
no effect.
This argument can also be spelled as ``permission``.
view_renderer
.. warning:: Deprecated as of :app:`Pyramid` 1.1.
This is either a single string term (e.g. ``json``) or a
string implying a path or :term:`asset specification`
(e.g. ``templates/views.pt``). If the renderer value is a
single term (does not contain a dot ``.``), the specified
term will be used to look up a renderer implementation, and
that renderer implementation will be used to construct a
response from the view return value. If the renderer term
contains a dot (``.``), the specified term will be treated
as a path, and the filename extension of the last element in
the path will be used to look up the renderer
implementation, which will be passed the full path. The
renderer implementation will be used to construct a response
from the view return value. See
:ref:`views_which_use_a_renderer` for more information.
If the ``view`` argument is not provided, this argument has
no effect.
This argument can also be spelled as ``renderer``.
view_attr
.. warning:: Deprecated as of :app:`Pyramid` 1.1.
The view machinery defaults to using the ``__call__`` method
of the view callable (or the function itself, if the view
callable is a function) to obtain a response dictionary.
The ``attr`` value allows you to vary the method attribute
used to obtain the response. For example, if your view was
a class, and the class has a method named ``index`` and you
wanted to use this method instead of the class' ``__call__``
method to return the response, you'd say ``attr="index"`` in
the view configuration for the view. This is
most useful when the view definition is a class.
If the ``view`` argument is not provided, this argument has no
effect.
"""
# these are route predicates; if they do not match, the next route
# in the routelist will be tried
ignored, predicates, ignored = _make_predicates(
xhr=xhr,
request_method=request_method,
path_info=path_info,
request_param=request_param,
header=header,
accept=accept,
traverse=traverse,
custom=custom_predicates
)
request_iface = self.registry.queryUtility(IRouteRequest, name=name)
if request_iface is None:
if use_global_views:
bases = (IRequest,)
else:
bases = ()
request_iface = route_request_iface(name, bases)
self.registry.registerUtility(
request_iface, IRouteRequest, name=name)
deferred_views = getattr(self.registry, 'deferred_route_views', {})
view_info = deferred_views.pop(name, ())
for info in view_info:
self.add_view(**info)
# deprecated adding views from add_route
if any([view, view_context, view_permission, view_renderer,
view_for, for_, permission, renderer, view_attr]):
self._add_view_from_route(
route_name=name,
view=view,
permission=view_permission or permission,
context=view_context or view_for or for_,
renderer=view_renderer or renderer,
attr=view_attr,
)
mapper = self.get_routes_mapper()
factory = self.maybe_dotted(factory)
if pattern is None:
pattern = path
if pattern is None:
raise ConfigurationError('"pattern" argument may not be None')
discriminator = ('route', name)
self.action(discriminator, None)
return mapper.connect(name, pattern, factory, predicates=predicates,
pregenerator=pregenerator, static=static)
def get_routes_mapper(self):
""" Return the :term:`routes mapper` object associated with
this configurator's :term:`registry`."""
mapper = self.registry.queryUtility(IRoutesMapper)
if mapper is None:
mapper = RoutesMapper()
self.registry.registerUtility(mapper, IRoutesMapper)
return mapper
# this is *not* an action method (uses caller_package)
[docs] def scan(self, package=None, categories=None, **kw):
"""Scan a Python package and any of its subpackages for objects
marked with :term:`configuration decoration` such as
:class:`pyramid.view.view_config`. Any decorated object found will
influence the current configuration state.
The ``package`` argument should be a Python :term:`package` or module
object (or a :term:`dotted Python name` which refers to such a
package or module). If ``package`` is ``None``, the package of the
*caller* is used.
The ``categories`` argument, if provided, should be the
:term:`Venusian` 'scan categories' to use during scanning. Providing
this argument is not often necessary; specifying scan categories is
an extremely advanced usage. By default, ``categories`` is ``None``
which will execute *all* Venusian decorator callbacks including
:app:`Pyramid`-related decorators such as
:class:`pyramid.view.view_config`. See the :term:`Venusian`
documentation for more information about limiting a scan by using an
explicit set of categories.
To perform a ``scan``, Pyramid creates a Venusian ``Scanner`` object.
The ``kw`` argument represents a set of keyword arguments to pass to
the Venusian ``Scanner`` object's constructor. See the
:term:`venusian` documentation (its ``Scanner`` class) for more
information about the constructor. By default, the only keyword
arguments passed to the Scanner constructor are ``{'config':self}``
where ``self`` is this configurator object. This services the
requirement of all built-in Pyramid decorators, but extension systems
may require additional arguments. Providing this argument is not
often necessary; it's an advanced usage.
.. note:: the ``**kw`` argument is new in Pyramid 1.1
"""
package = self.maybe_dotted(package)
if package is None: # pragma: no cover
package = caller_package()
scankw = {'config':self}
scankw.update(kw)
scanner = self.venusian.Scanner(**scankw)
scanner.scan(package, categories=categories)
@action_method
[docs] def add_renderer(self, name, factory):
"""
Add a :app:`Pyramid` :term:`renderer` factory to the
current configuration state.
The ``name`` argument is the renderer name. Use ``None`` to
represent the default renderer (a renderer which will be used for all
views unless they name another renderer specifically).
The ``factory`` argument is Python reference to an
implementation of a :term:`renderer` factory or a
:term:`dotted Python name` to same.
Note that this function must be called *before* any
``add_view`` invocation that names the renderer name as an
argument. As a result, it's usually a better idea to pass
globally used renderers into the ``Configurator`` constructor
in the sequence of renderers passed as ``renderer`` than it is
to use this method.
"""
factory = self.maybe_dotted(factory)
# if name is None or the empty string, we're trying to register
# a default renderer, but registerUtility is too dumb to accept None
# as a name
if not name:
name = ''
# we need to register renderers eagerly because they are used during
# view configuration
self.registry.registerUtility(factory, IRendererFactory, name=name)
self.action((IRendererFactory, name), None)
def _override(self, package, path, override_package, override_prefix,
PackageOverrides=PackageOverrides):
pkg_name = package.__name__
override_pkg_name = override_package.__name__
override = self.registry.queryUtility(IPackageOverrides, name=pkg_name)
if override is None:
override = PackageOverrides(package)
self.registry.registerUtility(override, IPackageOverrides,
name=pkg_name)
override.insert(path, override_pkg_name, override_prefix)
@action_method
[docs] def override_asset(self, to_override, override_with, _override=None):
""" Add a :app:`Pyramid` asset override to the current
configuration state.
``to_override`` is a :term:`asset specification` to the
asset being overridden.
``override_with`` is a :term:`asset specification` to the
asset that is performing the override.
See :ref:`assets_chapter` for more
information about asset overrides."""
if to_override == override_with:
raise ConfigurationError('You cannot override an asset with itself')
package = to_override
path = ''
if ':' in to_override:
package, path = to_override.split(':', 1)
override_package = override_with
override_prefix = ''
if ':' in override_with:
override_package, override_prefix = override_with.split(':', 1)
# *_isdir = override is package or directory
overridden_isdir = path=='' or path.endswith('/')
override_isdir = override_prefix=='' or override_prefix.endswith('/')
if overridden_isdir and (not override_isdir):
raise ConfigurationError(
'A directory cannot be overridden with a file (put a '
'slash at the end of override_with if necessary)')
if (not overridden_isdir) and override_isdir:
raise ConfigurationError(
'A file cannot be overridden with a directory (put a '
'slash at the end of to_override if necessary)')
override = _override or self._override # test jig
def register():
__import__(package)
__import__(override_package)
from_package = sys.modules[package]
to_package = sys.modules[override_package]
override(from_package, path, to_package, override_prefix)
self.action(None, register)
override_resource = override_asset # bw compat
@action_method
[docs] def set_forbidden_view(self, view=None, attr=None, renderer=None,
wrapper=None):
""" Add a default forbidden view to the current configuration
state.
.. warning::
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_forbidden_view`
to see how a forbidden view should be registered in new projects.
The ``view`` argument should be a :term:`view callable` or a
:term:`dotted Python name` which refers to a view callable.
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).
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).
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, basestring):
renderer = 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=HTTPForbidden,
wrapper=wrapper, renderer=renderer)
@action_method
[docs] def set_notfound_view(self, view=None, attr=None, renderer=None,
wrapper=None):
""" Add a default not found view to the current configuration
state.
.. warning::
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.
The ``view`` argument should be a :term:`view callable` or a
:term:`dotted Python name` which refers to a view callable.
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).
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).
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, basestring):
renderer = 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)
@action_method
[docs] def set_request_factory(self, factory):
""" The object passed as ``factory`` should be an object (or a
:term:`dotted Python name` which refers to an object) which
will be used by the :app:`Pyramid` router to create all
request objects. This factory object must have the same
methods and attributes as the
:class:`pyramid.request.Request` class (particularly
``__call__``, and ``blank``).
.. note::
Using the ``request_factory`` argument to the
:class:`pyramid.config.Configurator` constructor can be used to
achieve the same purpose.
"""
factory = self.maybe_dotted(factory)
def register():
self.registry.registerUtility(factory, IRequestFactory)
self.action(IRequestFactory, register)
@action_method
[docs] def set_renderer_globals_factory(self, factory, warn=True):
""" The object passed as ``factory`` should be an callable (or
a :term:`dotted Python name` which refers to an callable) that
will be used by the :app:`Pyramid` rendering machinery as a
renderers global factory (see :ref:`adding_renderer_globals`).
The ``factory`` callable must accept a single argument named
``system`` (which will be a dictionary) and it must return a
dictionary. When an application uses a renderer, the
factory's return dictionary will be merged into the ``system``
dictionary, and therefore will be made available to the code
which uses the renderer.
.. warning:: This method is deprecated as of Pyramid 1.1.
.. note:: Using the ``renderer_globals_factory`` argument
to the :class:`pyramid.config.Configurator` constructor
can be used to achieve the same purpose.
"""
if warn:
warnings.warn(
'Calling the ``set_renderer_globals`` method of a Configurator '
'is deprecated as of Pyramid 1.1. Use a BeforeRender event '
'subscriber as documented in the "Hooks" chapter of the '
'Pyramid narrative documentation instead',
DeprecationWarning,
3)
factory = self.maybe_dotted(factory)
def register():
self.registry.registerUtility(factory, IRendererGlobalsFactory)
self.action(IRendererGlobalsFactory, register)
@action_method
[docs] def set_locale_negotiator(self, negotiator):
"""
Set the :term:`locale negotiator` for this application. The
:term:`locale negotiator` is a callable which accepts a
:term:`request` object and which returns a :term:`locale
name`. The ``negotiator`` argument should be the locale
negotiator implementation or a :term:`dotted Python name`
which refers to such an implementation.
Later calls to this method override earlier calls; there can
be only one locale negotiator active at a time within an
application. See :ref:`activating_translation` for more
information.
.. note::
Using the ``locale_negotiator`` argument to the
:class:`pyramid.config.Configurator` constructor can be used to
achieve the same purpose.
"""
negotiator = self.maybe_dotted(negotiator)
def register():
self.registry.registerUtility(negotiator, ILocaleNegotiator)
self.action(ILocaleNegotiator, register)
@action_method
[docs] def set_default_permission(self, permission):
"""
Set the default permission to be used by all subsequent
:term:`view configuration` registrations. ``permission``
should be a :term:`permission` string to be used as the
default permission. An example of a permission
string:``'view'``. Adding a default permission makes it
unnecessary to protect each view configuration with an
explicit permission, unless your application policy requires
some exception for a particular view.
If a default permission is *not* set, views represented by
view configuration registrations which do not explicitly
declare a permission will be executable by entirely anonymous
users (any authorization policy is ignored).
Later calls to this method override will conflict with earlier calls;
there can be only one default permission active at a time within an
application.
.. warning::
If a default permission is in effect, view configurations meant to
create a truly anonymously accessible view (even :term:`exception
view` views) *must* use the explicit permission string
``__no_permission_required__`` as the permission. When this string
is used as the ``permission`` for a view configuration, the default
permission is ignored, and the view is registered, making it
available to all callers regardless of their credentials.
See also :ref:`setting_a_default_permission`.
.. note::
Using the ``default_permission`` argument to the
:class:`pyramid.config.Configurator` constructor can be used to
achieve the same purpose.
"""
# default permission used during view registration
self.registry.registerUtility(permission, IDefaultPermission)
self.action(IDefaultPermission, None)
@action_method
[docs] def set_view_mapper(self, mapper):
"""
Setting a :term:`view mapper` makes it possible to make use of
:term:`view callable` objects which implement different call
signatures than the ones supported by :app:`Pyramid` as described in
its narrative documentation.
The ``mapper`` should argument be an object implementing
:class:`pyramid.interfaces.IViewMapperFactory` or a :term:`dotted
Python name` to such an object.
The provided ``mapper`` will become the default view mapper to be
used by all subsequent :term:`view configuration` registrations, as
if you had passed a ``default_view_mapper`` argument to the
:class:`pyramid.config.Configurator` constructor.
See also :ref:`using_a_view_mapper`.
"""
mapper = self.maybe_dotted(mapper)
self.registry.registerUtility(mapper, IViewMapperFactory)
self.action(IViewMapperFactory, None)
@action_method
[docs] def set_session_factory(self, session_factory):
"""
Configure the application with a :term:`session factory`. If
this method is called, the ``session_factory`` argument must
be a session factory callable.
.. note::
Using the ``session_factory`` argument to the
:class:`pyramid.config.Configurator` constructor can be used to
achieve the same purpose.
"""
def register():
self.registry.registerUtility(session_factory, ISessionFactory)
self.action(ISessionFactory, register)
[docs] def add_translation_dirs(self, *specs):
""" Add one or more :term:`translation directory` paths to the
current configuration state. The ``specs`` argument is a
sequence that may contain absolute directory paths
(e.g. ``/usr/share/locale``) or :term:`asset specification`
names naming a directory path (e.g. ``some.package:locale``)
or a combination of the two.
Example:
.. code-block:: python
config.add_translation_dirs('/usr/share/locale',
'some.package:locale')
Later calls to ``add_translation_dir`` insert directories into the
beginning of the list of translation directories created by earlier
calls. This means that the same translation found in a directory
added later in the configuration process will be found before one
added earlier in the configuration process. However, if multiple
specs are provided in a single call to ``add_translation_dirs``, the
directories will be inserted into the beginning of the directory list
in the order they're provided in the ``*specs`` list argument (items
earlier in the list trump ones later in the list).
"""
for spec in specs[::-1]: # reversed
package_name, filename = self._split_spec(spec)
if package_name is None: # absolute filename
directory = filename
else:
__import__(package_name)
package = sys.modules[package_name]
directory = os.path.join(package_path(package), filename)
if not os.path.isdir(os.path.realpath(directory)):
raise ConfigurationError('"%s" is not a directory' % directory)
tdirs = self.registry.queryUtility(ITranslationDirectories)
if tdirs is None:
tdirs = []
self.registry.registerUtility(tdirs, ITranslationDirectories)
tdirs.insert(0, directory)
# XXX no action?
if specs:
# We actually only need an IChameleonTranslate function
# utility to be registered zero or one times. We register the
# same function once for each added translation directory,
# which does too much work, but has the same effect.
ctranslate = ChameleonTranslate(translator)
self.registry.registerUtility(ctranslate, IChameleonTranslate)
@action_method
[docs] def add_static_view(self, name, path, **kw):
""" Add a view used to render static assets such as images
and CSS files.
The ``name`` argument is a string representing an
application-relative local URL prefix. It may alternately be a full
URL.
The ``path`` argument is the path on disk where the static files
reside. This can be an absolute path, a package-relative path, or a
:term:`asset specification`.
The ``cache_max_age`` keyword argument is input to set the
``Expires`` and ``Cache-Control`` headers for static assets served.
Note that this argument has no effect when the ``name`` is a *url
prefix*. By default, this argument is ``None``, meaning that no
particular Expires or Cache-Control headers are set in the response.
The ``permission`` keyword argument is used to specify the
:term:`permission` required by a user to execute the static view. By
default, it is the string ``__no_permission_required__``. The
``__no_permission_required__`` string is a special sentinel which
indicates that, even if a :term:`default permission` exists for the
current application, the static view should be renderered to
completely anonymous users. This default value is permissive
because, in most web apps, static assets seldom need protection from
viewing. If ``permission`` is specified, the security checking will
be performed against the default root factory ACL.
Any other keyword arguments sent to ``add_static_view`` are passed on
to :meth:`pyramid.config.Configuration.add_route` (e.g. ``factory``,
perhaps to define a custom factory with a custom ACL for this static
view).
*Usage*
The ``add_static_view`` function is typically used in conjunction
with the :func:`pyramid.url.static_url` function.
``add_static_view`` adds a view which renders a static asset when
some URL is visited; :func:`pyramid.url.static_url` generates a URL
to that asset.
The ``name`` argument to ``add_static_view`` is usually a :term:`view
name`. When this is the case, the :func:`pyramid.url.static_url` API
will generate a URL which points to a Pyramid view, which will serve
up a set of assets that live in the package itself. For example:
.. code-block:: python
add_static_view('images', 'mypackage:images/')
Code that registers such a view can generate URLs to the view via
:func:`pyramid.url.static_url`:
.. code-block:: python
static_url('mypackage:images/logo.png', request)
When ``add_static_view`` is called with a ``name`` argument that
represents a URL prefix, as it is above, subsequent calls to
:func:`pyramid.url.static_url` with paths that start with the
``path`` argument passed to ``add_static_view`` will generate a URL
something like ``http://<Pyramid app URL>/images/logo.png``, which
will cause the ``logo.png`` file in the ``images`` subdirectory of
the ``mypackage`` package to be served.
``add_static_view`` can alternately be used with a ``name`` argument
which is a *URL*, causing static assets to be served from an external
webserver. This happens when the ``name`` argument is a fully
qualified URL (e.g. starts with ``http://`` or similar). In this
mode, the ``name`` is used as the prefix of the full URL when
generating a URL using :func:`pyramid.url.static_url`. For example,
if ``add_static_view`` is called like so:
.. code-block:: python
add_static_view('http://example.com/images', 'mypackage:images/')
Subsequently, the URLs generated by :func:`pyramid.url.static_url`
for that static view will be prefixed with
``http://example.com/images``:
.. code-block:: python
static_url('mypackage:images/logo.png', request)
When ``add_static_view`` is called with a ``name`` argument that is
the URL ``http://example.com/images``, subsequent calls to
:func:`pyramid.url.static_url` with paths that start with the
``path`` argument passed to ``add_static_view`` will generate a URL
something like ``http://example.com/logo.png``. The external
webserver listening on ``example.com`` must be itself configured to
respond properly to such a request.
See :ref:`static_assets_section` for more information.
"""
spec = self._make_spec(path)
info = self.registry.queryUtility(IStaticURLInfo)
if info is None:
info = StaticURLInfo(self)
self.registry.registerUtility(info, IStaticURLInfo)
info.add(name, spec, **kw)
# testing API
[docs] def testing_securitypolicy(self, userid=None, groupids=(),
permissive=True):
"""Unit/integration testing helper: Registers a pair of faux
:app:`Pyramid` security policies: a :term:`authentication
policy` and a :term:`authorization policy`.
The behavior of the registered :term:`authorization policy`
depends on the ``permissive`` argument. If ``permissive`` is
true, a permissive :term:`authorization policy` is registered;
this policy allows all access. If ``permissive`` is false, a
nonpermissive :term:`authorization policy` is registered; this
policy denies all access.
The behavior of the registered :term:`authentication policy`
depends on the values provided for the ``userid`` and
``groupids`` argument. The authentication policy will return
the userid identifier implied by the ``userid`` argument and
the group ids implied by the ``groupids`` argument when the
:func:`pyramid.security.authenticated_userid` or
:func:`pyramid.security.effective_principals` APIs are
used.
This function is most useful when testing code that uses
the APIs named :func:`pyramid.security.has_permission`,
:func:`pyramid.security.authenticated_userid`,
:func:`pyramid.security.effective_principals`, and
:func:`pyramid.security.principals_allowed_by_permission`.
"""
from pyramid.testing import DummySecurityPolicy
policy = DummySecurityPolicy(userid, groupids, permissive)
self.registry.registerUtility(policy, IAuthorizationPolicy)
self.registry.registerUtility(policy, IAuthenticationPolicy)
[docs] def testing_resources(self, resources):
"""Unit/integration testing helper: registers a dictionary of
:term:`resource` objects that can be resolved via the
:func:`pyramid.traversal.find_resource` API.
The :func:`pyramid.traversal.find_resource` API is called with
a path as one of its arguments. If the dictionary you
register when calling this method contains that path as a
string key (e.g. ``/foo/bar`` or ``foo/bar``), the
corresponding value will be returned to ``find_resource`` (and
thus to your code) when
:func:`pyramid.traversal.find_resource` is called with an
equivalent path string or tuple.
"""
class DummyTraverserFactory:
def __init__(self, context):
self.context = context
def __call__(self, request):
path = request.environ['PATH_INFO']
ob = resources[path]
traversed = traversal_path(path)
return {'context':ob, 'view_name':'','subpath':(),
'traversed':traversed, 'virtual_root':ob,
'virtual_root_path':(), 'root':ob}
self.registry.registerAdapter(DummyTraverserFactory, (Interface,),
ITraverser)
return resources
testing_models = testing_resources # b/w compat
@action_method
[docs] def testing_add_subscriber(self, event_iface=None):
"""Unit/integration testing helper: Registers a
:term:`subscriber` which listens for events of the type
``event_iface``. This method returns a list object which is
appended to by the subscriber whenever an event is captured.
When an event is dispatched that matches the value implied by
the ``event_iface`` argument, that event will be appended to
the list. You can then compare the values in the list to
expected event notifications. This method is useful when
testing code that wants to call
:meth:`pyramid.registry.Registry.notify`,
or :func:`zope.component.event.dispatch`.
The default value of ``event_iface`` (``None``) implies a
subscriber registered for *any* kind of event.
"""
event_iface = self.maybe_dotted(event_iface)
L = []
def subscriber(*event):
L.extend(event)
self.add_subscriber(subscriber, event_iface)
return L
[docs] def testing_add_renderer(self, path, renderer=None):
"""Unit/integration testing helper: register a renderer at
``path`` (usually a relative filename ala ``templates/foo.pt``
or an asset specification) and return the renderer object.
If the ``renderer`` argument is None, a 'dummy' renderer will
be used. This function is useful when testing code that calls
the :func:`pyramid.renderers.render` function or
:func:`pyramid.renderers.render_to_response` function or
any other ``render_*`` or ``get_*`` API of the
:mod:`pyramid.renderers` module.
Note that calling this method for with a ``path`` argument
representing a renderer factory type (e.g. for ``foo.pt``
usually implies the ``chameleon_zpt`` renderer factory)
clobbers any existing renderer factory registered for that
type.
.. note:: This method is also available under the alias
``testing_add_template`` (an older name for it).
"""
from pyramid.testing import DummyRendererFactory
helper = RendererHelper(name=path, registry=self.registry)
factory = self.registry.queryUtility(IRendererFactory, name=helper.type)
if not isinstance(factory, DummyRendererFactory):
factory = DummyRendererFactory(helper.type, factory)
self.registry.registerUtility(factory, IRendererFactory,
name=helper.type)
from pyramid.testing import DummyTemplateRenderer
if renderer is None:
renderer = DummyTemplateRenderer()
factory.add(path, renderer)
return renderer
testing_add_template = testing_add_renderer
def _make_predicates(xhr=None, request_method=None, path_info=None,
request_param=None, header=None, accept=None,
containment=None, request_type=None,
traverse=None, custom=()):
# PREDICATES
# ----------
#
# Given an argument list, a predicate list is computed.
# Predicates are added to a predicate list in (presumed)
# computation expense order. All predicates associated with a
# view or route must evaluate true for the view or route to
# "match" during a request. Elsewhere in the code, we evaluate
# predicates using a generator expression. The fastest predicate
# should be evaluated first, then the next fastest, and so on, as
# if one returns false, the remainder of the predicates won't need
# to be evaluated.
#
# While we compute predicates, we also compute a predicate hash
# (aka phash) that can be used by a caller to identify identical
# predicate lists.
#
# ORDERING
# --------
#
# A "order" is computed for the predicate list. An order is
# a scoring.
#
# Each predicate is associated with a weight value, which is a
# multiple of 2. The weight of a predicate symbolizes the
# relative potential "importance" of the predicate to all other
# predicates. A larger weight indicates greater importance.
#
# All weights for a given predicate list are bitwise ORed together
# to create a "score"; this score is then subtracted from
# MAX_ORDER and divided by an integer representing the number of
# predicates+1 to determine the order.
#
# The order represents the ordering in which a "multiview" ( a
# collection of views that share the same context/request/name
# triad but differ in other ways via predicates) will attempt to
# call its set of views. Views with lower orders will be tried
# first. The intent is to a) ensure that views with more
# predicates are always evaluated before views with fewer
# predicates and b) to ensure a stable call ordering of views that
# share the same number of predicates. Views which do not have
# any predicates get an order of MAX_ORDER, meaning that they will
# be tried very last.
predicates = []
weights = []
h = md5()
if xhr:
def xhr_predicate(context, request):
return request.is_xhr
xhr_predicate.__text__ = "xhr = True"
weights.append(1 << 1)
predicates.append(xhr_predicate)
h.update('xhr:%r' % bool(xhr))
if request_method is not None:
def request_method_predicate(context, request):
return request.method == request_method
text = "request method = %s"
request_method_predicate.__text__ = text % request_method
weights.append(1 << 2)
predicates.append(request_method_predicate)
h.update('request_method:%r' % request_method)
if path_info is not None:
try:
path_info_val = re.compile(path_info)
except re.error, why:
raise ConfigurationError(why[0])
def path_info_predicate(context, request):
return path_info_val.match(request.path_info) is not None
text = "path_info = %s"
path_info_predicate.__text__ = text % path_info
weights.append(1 << 3)
predicates.append(path_info_predicate)
h.update('path_info:%r' % path_info)
if request_param is not None:
request_param_val = None
if '=' in request_param:
request_param, request_param_val = request_param.split('=', 1)
if request_param_val is None:
text = "request_param %s" % request_param
else:
text = "request_param %s = %s" % (request_param, request_param_val)
def request_param_predicate(context, request):
if request_param_val is None:
return request_param in request.params
return request.params.get(request_param) == request_param_val
request_param_predicate.__text__ = text
weights.append(1 << 4)
predicates.append(request_param_predicate)
h.update('request_param:%r=%r' % (request_param, request_param_val))
if header is not None:
header_name = header
header_val = None
if ':' in header:
header_name, header_val = header.split(':', 1)
try:
header_val = re.compile(header_val)
except re.error, why:
raise ConfigurationError(why[0])
if header_val is None:
text = "header %s" % header_name
else:
text = "header %s = %s" % (header_name, header_val)
def header_predicate(context, request):
if header_val is None:
return header_name in request.headers
val = request.headers.get(header_name)
if val is None:
return False
return header_val.match(val) is not None
header_predicate.__text__ = text
weights.append(1 << 5)
predicates.append(header_predicate)
h.update('header:%r=%r' % (header_name, header_val))
if accept is not None:
def accept_predicate(context, request):
return accept in request.accept
accept_predicate.__text__ = "accept = %s" % accept
weights.append(1 << 6)
predicates.append(accept_predicate)
h.update('accept:%r' % accept)
if containment is not None:
def containment_predicate(context, request):
return find_interface(context, containment) is not None
containment_predicate.__text__ = "containment = %s" % containment
weights.append(1 << 7)
predicates.append(containment_predicate)
h.update('containment:%r' % hash(containment))
if request_type is not None:
def request_type_predicate(context, request):
return request_type.providedBy(request)
text = "request_type = %s"
request_type_predicate.__text__ = text % request_type
weights.append(1 << 8)
predicates.append(request_type_predicate)
h.update('request_type:%r' % hash(request_type))
if traverse is not None:
# ``traverse`` can only be used as a *route* "predicate"; it
# adds 'traverse' to the matchdict if it's specified in the
# routing args. This causes the ResourceTreeTraverser to use
# the resolved traverse pattern as the traversal path.
from pyramid.urldispatch import _compile_route
_, tgenerate = _compile_route(traverse)
def traverse_predicate(context, request):
if 'traverse' in context:
return True
m = context['match']
tvalue = tgenerate(m)
m['traverse'] = traversal_path(tvalue)
return True
# This isn't actually a predicate, it's just a infodict
# modifier that injects ``traverse`` into the matchdict. As a
# result, the ``traverse_predicate`` function above always
# returns True, and we don't need to update the hash or attach
# a weight to it
predicates.append(traverse_predicate)
if custom:
for num, predicate in enumerate(custom):
if getattr(predicate, '__text__', None) is None:
text = '<unknown custom predicate>'
try:
predicate.__text__ = text
except AttributeError:
# if this happens the predicate is probably a classmethod
if hasattr(predicate, '__func__'):
predicate.__func__.__text__ = text
else: # # pragma: no cover ; 2.5 doesn't have __func__
predicate.im_func.__text__ = text
predicates.append(predicate)
# using hash() here rather than id() is intentional: we
# want to allow custom predicates that are part of
# frameworks to be able to define custom __hash__
# functions for custom predicates, so that the hash output
# of predicate instances which are "logically the same"
# may compare equal.
h.update('custom%s:%r' % (num, hash(predicate)))
weights.append(1 << 10)
score = 0
for bit in weights:
score = score | bit
order = (MAX_ORDER - score) / (len(predicates) + 1)
phash = h.hexdigest()
return order, predicates, phash
class MultiView(object):
implements(IMultiView)
def __init__(self, name):
self.name = name
self.media_views = {}
self.views = []
self.accepts = []
def add(self, view, order, accept=None, phash=None):
if phash is not None:
for i, (s, v, h) in enumerate(list(self.views)):
if phash == h:
self.views[i] = (order, view, phash)
return
if accept is None or '*' in accept:
self.views.append((order, view, phash))
self.views.sort()
else:
subset = self.media_views.setdefault(accept, [])
subset.append((order, view, phash))
subset.sort()
accepts = set(self.accepts)
accepts.add(accept)
self.accepts = list(accepts) # dedupe
def get_views(self, request):
if self.accepts and hasattr(request, 'accept'):
accepts = self.accepts[:]
views = []
while accepts:
match = request.accept.best_match(accepts)
if match is None:
break
subset = self.media_views[match]
views.extend(subset)
accepts.remove(match)
views.extend(self.views)
return views
return self.views
def match(self, context, request):
for order, view, phash in self.get_views(request):
if not hasattr(view, '__predicated__'):
return view
if view.__predicated__(context, request):
return view
raise PredicateMismatch(self.name)
def __permitted__(self, context, request):
view = self.match(context, request)
if hasattr(view, '__permitted__'):
return view.__permitted__(context, request)
return True
def __call_permissive__(self, context, request):
view = self.match(context, request)
view = getattr(view, '__call_permissive__', view)
return view(context, request)
def __call__(self, context, request):
for order, view, phash in self.get_views(request):
try:
return view(context, request)
except PredicateMismatch:
continue
raise PredicateMismatch(self.name)
def wraps_view(wrapper):
def inner(self, view):
wrapper_view = wrapper(self, view)
return preserve_view_attrs(view, wrapper_view)
return inner
def preserve_view_attrs(view, wrapper):
if wrapper is view:
return view
if view is None:
return wrapper
original_view = getattr(view, '__original_view__', None)
if original_view is None:
original_view = view
wrapper.__wraps__ = view
wrapper.__original_view__ = original_view
wrapper.__module__ = view.__module__
wrapper.__doc__ = view.__doc__
try:
wrapper.__name__ = view.__name__
except AttributeError:
wrapper.__name__ = repr(view)
# attrs that may not exist on "view", but, if so, must be attached to
# "wrapped view"
for attr in ('__permitted__', '__call_permissive__', '__permission__',
'__predicated__', '__predicates__', '__accept__',
'__order__'):
try:
setattr(wrapper, attr, getattr(view, attr))
except AttributeError:
pass
return wrapper
class ViewDeriver(object):
def __init__(self, **kw):
self.kw = kw
self.registry = kw['registry']
self.authn_policy = self.registry.queryUtility(IAuthenticationPolicy)
self.authz_policy = self.registry.queryUtility(IAuthorizationPolicy)
self.logger = self.registry.queryUtility(IDebugLogger)
def __call__(self, view):
return self.attr_wrapped_view(
self.predicated_view(
self.authdebug_view(
self.secured_view(
self.owrapped_view(
self.http_cached_view(
self.decorated_view(
self.rendered_view(
self.mapped_view(view)))))))))
@wraps_view
def mapped_view(self, view):
mapper = self.kw.get('mapper')
if mapper is None:
mapper = getattr(view, '__view_mapper__', None)
if mapper is None:
mapper = self.registry.queryUtility(IViewMapperFactory)
if mapper is None:
mapper = DefaultViewMapper
mapped_view = mapper(**self.kw)(view)
return mapped_view
@wraps_view
def owrapped_view(self, view):
wrapper_viewname = self.kw.get('wrapper_viewname')
viewname = self.kw.get('viewname')
if not wrapper_viewname:
return view
def _owrapped_view(context, request):
response = view(context, request)
request.wrapped_response = response
request.wrapped_body = response.body
request.wrapped_view = view
wrapped_response = render_view_to_response(context, request,
wrapper_viewname)
if wrapped_response is None:
raise ValueError(
'No wrapper view named %r found when executing view '
'named %r' % (wrapper_viewname, viewname))
return wrapped_response
return _owrapped_view
@wraps_view
def http_cached_view(self, view):
if self.registry.settings.get('prevent_http_cache', False):
return view
seconds = self.kw.get('http_cache')
if seconds is None:
return view
options = {}
if isinstance(seconds, (tuple, list)):
try:
seconds, options = seconds
except ValueError:
raise ConfigurationError(
'If http_cache parameter is a tuple or list, it must be '
'in the form (seconds, options); not %s' % (seconds,))
def wrapper(context, request):
response = view(context, request)
prevent_caching = getattr(response.cache_control, 'prevent_auto',
False)
if not prevent_caching:
response.cache_expires(seconds, **options)
return response
return wrapper
@wraps_view
def secured_view(self, view):
permission = self.kw.get('permission')
if permission == '__no_permission_required__':
# allow views registered within configurations that have a
# default permission to explicitly override the default
# permission, replacing it with no permission at all
permission = None
wrapped_view = view
if self.authn_policy and self.authz_policy and (permission is not None):
def _permitted(context, request):
principals = self.authn_policy.effective_principals(request)
return self.authz_policy.permits(context, principals,
permission)
def _secured_view(context, request):
result = _permitted(context, request)
if result:
return view(context, request)
msg = getattr(request, 'authdebug_message',
'Unauthorized: %s failed permission check' % view)
raise HTTPForbidden(msg, result=result)
_secured_view.__call_permissive__ = view
_secured_view.__permitted__ = _permitted
_secured_view.__permission__ = permission
wrapped_view = _secured_view
return wrapped_view
@wraps_view
def authdebug_view(self, view):
wrapped_view = view
settings = self.registry.settings
permission = self.kw.get('permission')
if settings and settings.get('debug_authorization', False):
def _authdebug_view(context, request):
view_name = getattr(request, 'view_name', None)
if self.authn_policy and self.authz_policy:
if permission is None:
msg = 'Allowed (no permission registered)'
else:
principals = self.authn_policy.effective_principals(
request)
msg = str(self.authz_policy.permits(context, principals,
permission))
else:
msg = 'Allowed (no authorization policy in use)'
view_name = getattr(request, 'view_name', None)
url = getattr(request, 'url', None)
msg = ('debug_authorization of url %s (view name %r against '
'context %r): %s' % (url, view_name, context, msg))
self.logger and self.logger.debug(msg)
if request is not None:
request.authdebug_message = msg
return view(context, request)
wrapped_view = _authdebug_view
return wrapped_view
@wraps_view
def predicated_view(self, view):
predicates = self.kw.get('predicates', ())
if not predicates:
return view
def predicate_wrapper(context, request):
if all((predicate(context, request) for predicate in predicates)):
return view(context, request)
raise PredicateMismatch(
'predicate mismatch for view %s' % view)
def checker(context, request):
return all((predicate(context, request) for predicate in
predicates))
predicate_wrapper.__predicated__ = checker
predicate_wrapper.__predicates__ = predicates
return predicate_wrapper
@wraps_view
def attr_wrapped_view(self, view):
kw = self.kw
accept, order, phash = (kw.get('accept', None),
kw.get('order', MAX_ORDER),
kw.get('phash', DEFAULT_PHASH))
# this is a little silly but we don't want to decorate the original
# function with attributes that indicate accept, order, and phash,
# so we use a wrapper
if (
(accept is None) and
(order == MAX_ORDER) and
(phash == DEFAULT_PHASH)
):
return view # defaults
def attr_view(context, request):
return view(context, request)
attr_view.__accept__ = accept
attr_view.__order__ = order
attr_view.__phash__ = phash
attr_view.__view_attr__ = self.kw.get('attr')
attr_view.__permission__ = self.kw.get('permission')
return attr_view
@wraps_view
def rendered_view(self, view):
# one way or another this wrapper must produce a Response (unless
# the renderer is a NullRendererHelper)
renderer = self.kw.get('renderer')
if renderer is None:
# register a default renderer if you want super-dynamic
# rendering. registering a default renderer will also allow
# override_renderer to work if a renderer is left unspecified for
# a view registration.
return self._response_resolved_view(view)
if renderer is renderers.null_renderer:
return view
return self._rendered_view(view, renderer)
def _rendered_view(self, view, view_renderer):
def rendered_view(context, request):
renderer = view_renderer
result = view(context, request)
registry = self.registry
# this must adapt, it can't do a simple interface check
# (avoid trying to render webob responses)
response = registry.queryAdapterOrSelf(result, IResponse)
if response is None:
attrs = getattr(request, '__dict__', {})
if 'override_renderer' in attrs:
# renderer overridden by newrequest event or other
renderer_name = attrs.pop('override_renderer')
renderer = RendererHelper(name=renderer_name,
package=self.kw.get('package'),
registry = registry)
if '__view__' in attrs:
view_inst = attrs.pop('__view__')
else:
view_inst = getattr(view, '__original_view__', view)
response = renderer.render_view(request, result, view_inst,
context)
return response
return rendered_view
def _response_resolved_view(self, view):
registry = self.registry
def viewresult_to_response(context, request):
result = view(context, request)
response = registry.queryAdapterOrSelf(result, IResponse)
if response is None:
raise ValueError(
'Could not convert view return value "%s" into a '
'response object' % (result,))
return response
return viewresult_to_response
@wraps_view
def decorated_view(self, view):
decorator = self.kw.get('decorator')
if decorator is None:
return view
return decorator(view)
class DefaultViewMapper(object):
classProvides(IViewMapperFactory)
implements(IViewMapper)
def __init__(self, **kw):
self.attr = kw.get('attr')
def __call__(self, view):
if inspect.isclass(view):
view = self.map_class(view)
else:
view = self.map_nonclass(view)
return view
def map_class(self, view):
ronly = requestonly(view, self.attr)
if ronly:
mapped_view = self.map_class_requestonly(view)
else:
mapped_view = self.map_class_native(view)
return mapped_view
def map_nonclass(self, view):
# We do more work here than appears necessary to avoid wrapping the
# view unless it actually requires wrapping (to avoid function call
# overhead).
mapped_view = view
ronly = requestonly(view, self.attr)
if ronly:
mapped_view = self.map_nonclass_requestonly(view)
elif self.attr:
mapped_view = self.map_nonclass_attr(view)
return mapped_view
def map_class_requestonly(self, view):
# its a class that has an __init__ which only accepts request
attr = self.attr
def _class_requestonly_view(context, request):
inst = view(request)
request.__view__ = inst
if attr is None:
response = inst()
else:
response = getattr(inst, attr)()
return response
return _class_requestonly_view
def map_class_native(self, view):
# its a class that has an __init__ which accepts both context and
# request
attr = self.attr
def _class_view(context, request):
inst = view(context, request)
request.__view__ = inst
if attr is None:
response = inst()
else:
response = getattr(inst, attr)()
return response
return _class_view
def map_nonclass_requestonly(self, view):
# its a function that has a __call__ which accepts only a single
# request argument
attr = self.attr
def _requestonly_view(context, request):
if attr is None:
response = view(request)
else:
response = getattr(view, attr)(request)
return response
return _requestonly_view
def map_nonclass_attr(self, view):
# its a function that has a __call__ which accepts both context and
# request, but still has an attr
def _attr_view(context, request):
response = getattr(view, self.attr)(context, request)
return response
return _attr_view
def requestonly(view, attr=None):
if attr is None:
attr = '__call__'
if inspect.isfunction(view):
fn = view
elif inspect.isclass(view):
try:
fn = view.__init__
except AttributeError:
return False
else:
try:
fn = getattr(view, attr)
except AttributeError:
return False
try:
argspec = inspect.getargspec(fn)
except TypeError:
return False
args = argspec[0]
if hasattr(fn, 'im_func'):
# it's an instance method
if not args:
return False
args = args[1:]
if not args:
return False
if len(args) == 1:
return True
defaults = argspec[3]
if defaults is None:
defaults = ()
if args[0] == 'request':
if len(args) - len(defaults) == 1:
return True
return False
class PyramidConfigurationMachine(ConfigurationMachine):
autocommit = False
def processSpec(self, spec):
"""Check whether a callable needs to be processed. The ``spec``
refers to a unique identifier for the callable.
Return True if processing is needed and False otherwise. If
the callable needs to be processed, it will be marked as
processed, assuming that the caller will procces the callable if
it needs to be processed.
"""
if spec in self._seen_files:
return False
self._seen_files.add(spec)
return True
def translator(msg):
request = get_current_request()
localizer = get_localizer(request)
return localizer.translate(msg)
def isexception(o):
if IInterface.providedBy(o):
if IException.isEqualOrExtendedBy(o):
return True
return (
isinstance(o, Exception) or
(inspect.isclass(o) and (issubclass(o, Exception)))
)
global_registries = WeakOrderedSet()