import inspect
|
import logging
|
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 ITweens
|
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.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.security import NO_PERMISSION_REQUIRED
|
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.tweens import excview_tween_factory
|
from pyramid.tweens import Tweens
|
from pyramid.tweens import tween_factory_name
|
from pyramid.tweens import MAIN, INGRESS, EXCVIEW
|
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
|
|
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 a
|
logger will be used (the logger name will be the package name of the
|
*caller* of this configurator). If it is passed, it should be an
|
instance of the :class:`logging.Logger` (PEP 282) standard library class
|
or a Python logger name. 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
|
|
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)
|
|
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
|
|
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)
|
|
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
|
|
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 :term:`dotted Python 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)
|
|
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)
|
|
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
|
|
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."""
|
tweens = []
|
includes = []
|
if settings:
|
includes = [x.strip() for x in
|
settings.get('pyramid.include', '').splitlines()]
|
tweens = [x.strip() for x in
|
settings.get('pyramid.tweens','').splitlines()]
|
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)
|
|
if debug_logger is None:
|
debug_logger = logging.getLogger(self.package_name)
|
elif isinstance(debug_logger, basestring):
|
debug_logger = logging.getLogger(debug_logger)
|
|
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()
|
for inc in includes:
|
self.include(inc)
|
for factory in tweens:
|
self._add_tween(factory, explicit=True)
|
|
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)
|
|
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()
|
|
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})
|
|
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()
|
|
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
|
def add_tween(self, tween_factory, alias=None, under=None, over=None):
|
"""
|
Add a 'tween factory'. A :term:`tween` (a contraction of 'between')
|
is a bit of code that sits between the Pyramid router's main request
|
handling function and the upstream WSGI component that uses
|
:app:`Pyramid` as its 'app'. This is a feature that may be used by
|
Pyramid framework extensions, to provide, for example,
|
Pyramid-specific view timing support, bookkeeping code that examines
|
exceptions before they are returned to the upstream WSGI application,
|
or a variety of other features. Tweens behave a bit like
|
:term:`WSGI` 'middleware' but they have the benefit of running in a
|
context in which they have access to the Pyramid :term:`application
|
registry` as well as the Pyramid rendering machinery.
|
|
.. note:: You can view the tween ordering configured into a given
|
Pyramid application by using the ``paster ptweens``
|
command. See :ref:`displaying_tweens`.
|
|
The ``alias`` argument, if it is not ``None``, should be a string.
|
The string will represent a value that other callers of ``add_tween``
|
may pass as an ``under`` and ``over`` argument instead of a dotted
|
name to a tween factory.
|
|
The ``under`` and ``over`` arguments allow the caller of
|
``add_tween`` to provide a hint about where in the tween chain this
|
tween factory should be placed when an implicit tween chain is used.
|
These hints are only used used when an explicit tween chain is not
|
used (when the ``pyramid.tweens`` configuration value is not set).
|
Allowable values for ``under`` or ``over`` (or both) are:
|
|
- ``None`` (the default).
|
|
- A :term:`dotted Python name` to a tween factory: a string
|
representing the predicted dotted name of a tween factory added in
|
a call to ``add_tween`` in the same configuration session.
|
|
- A tween alias: a string representing the predicted value of
|
``alias`` in a separate call to ``add_tween`` in the same
|
configuration session
|
|
- One of the constants :attr:`pyramid.tweens.MAIN`,
|
:attr:`pyramid.tweens.INGRESS`, or :attr:`pyramid.tweens.EXCVIEW`.
|
|
``under`` means 'closer to the main Pyramid application than',
|
``over`` means 'closer to the request ingress than'.
|
|
For example, calling ``add_tween(factory, over=pyramid.tweens.MAIN)``
|
will attempt to place the tween factory represented by ``factory``
|
directly 'above' (in ``paster ptweens`` order) the main Pyramid
|
request handler. Likewise, calling ``add_tween(factory,
|
over=pyramid.tweens.MAIN, under='someothertween')`` will attempt to
|
place this tween factory 'above' the main handler but 'below' (a
|
fictional) 'someothertween' tween factory (which was presumably added
|
via ``add_tween(factory, alias='someothertween')``).
|
|
If an ``under`` or ``over`` value is provided that does not match a
|
tween factory dotted name or alias in the current configuration, that
|
value will be ignored. It is not an error to provide an ``under`` or
|
``over`` value that matches an unused tween factory.
|
|
Specifying neither ``over`` nor ``under`` is equivalent to specifying
|
``under=INGRESS``.
|
|
Implicit tween ordering is obviously only best-effort. Pyramid will
|
attempt to present an implicit order of tweens as best it can, but
|
the only surefire way to get any particular ordering is to use an
|
explicit tween order. A user may always override the implicit tween
|
ordering by using an explicit ``pyramid.tweens`` configuration value
|
setting.
|
|
``alias``, ``under``, and ``over`` arguments are ignored when an
|
explicit tween chain is specified using the ``pyramid.tweens``
|
configuration value.
|
|
For more information, see :ref:`registering_tweens`.
|
|
.. note:: This feature is new as of Pyramid 1.1.1.
|
"""
|
return self._add_tween(tween_factory, alias=alias, under=under,
|
over=over, explicit=False)
|
|
def _add_tween(self, tween_factory, alias=None, under=None, over=None,
|
explicit=False):
|
tween_factory = self.maybe_dotted(tween_factory)
|
name = tween_factory_name(tween_factory)
|
if alias in (MAIN, INGRESS):
|
raise ConfigurationError('%s is a reserved tween name' % alias)
|
|
registry = self.registry
|
tweens = registry.queryUtility(ITweens)
|
if tweens is None:
|
tweens = Tweens()
|
registry.registerUtility(tweens, ITweens)
|
tweens.add_implicit(tween_factory_name(excview_tween_factory),
|
excview_tween_factory, alias=EXCVIEW,
|
over=MAIN)
|
if explicit:
|
tweens.add_explicit(name, tween_factory)
|
else:
|
tweens.add_implicit(name, tween_factory, alias=alias, under=under,
|
over=over)
|
self.action(('tween', name, explicit))
|
if not explicit and alias is not None:
|
self.action(('tween', alias, explicit))
|
|
@action_method
|
def add_request_handler(self, factory, name): # pragma: no cover
|
# XXX bw compat for debugtoolbar
|
return self._add_tween(factory, explicit=False)
|
|
@action_method
|
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
|
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)
|
|
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)
|
|
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
|
|
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
|
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
|
:data:`pyramid.security.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
|
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)
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
:data:`pyramid.security.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
|
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
|
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)
|
|
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
|
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
|
:data:`pyramid.security.NO_PERMISSION_REQUIRED`, 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
|
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)
|
|
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
|
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
|
|
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
|
|
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()
|