summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris McDonough <chrism@plope.com>2011-08-15 06:05:39 -0400
committerChris McDonough <chrism@plope.com>2011-08-15 06:05:39 -0400
commit5bf23fad3e0c9a949e8b01ab68991633af2d6df7 (patch)
tree7d21b98be3d30f47a432d76cde0f427a7eeedc05
parent7f8896e42e5ed9eb3c5c599cfb087cb8692b449e (diff)
downloadpyramid-5bf23fad3e0c9a949e8b01ab68991633af2d6df7.tar.gz
pyramid-5bf23fad3e0c9a949e8b01ab68991633af2d6df7.tar.bz2
pyramid-5bf23fad3e0c9a949e8b01ab68991633af2d6df7.zip
- Refactor ``pyramid.config`` into a package.
-rw-r--r--CHANGES.txt2
-rw-r--r--pyramid/config.py3570
-rw-r--r--pyramid/config/__init__.py779
-rw-r--r--pyramid/config/adapters.py61
-rw-r--r--pyramid/config/assets.py73
-rw-r--r--pyramid/config/factories.py57
-rw-r--r--pyramid/config/i18n.py105
-rw-r--r--pyramid/config/rendering.py97
-rw-r--r--pyramid/config/routes.py444
-rw-r--r--pyramid/config/security.py108
-rw-r--r--pyramid/config/settings.py55
-rw-r--r--pyramid/config/testing.py140
-rw-r--r--pyramid/config/tests/__init__.py1
-rw-r--r--pyramid/config/tests/test_config.py (renamed from pyramid/tests/test_config.py)2093
-rw-r--r--pyramid/config/tests/test_util.py262
-rw-r--r--pyramid/config/tests/test_views.py1654
-rw-r--r--pyramid/config/tweens.py160
-rw-r--r--pyramid/config/util.py236
-rw-r--r--pyramid/config/views.py1374
-rw-r--r--pyramid/config/zca.py24
-rw-r--r--pyramid/configuration.py2
-rw-r--r--pyramid/tests/test_router.py2
-rw-r--r--setup.py2
23 files changed, 5778 insertions, 5523 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 54a39b614..a66637c28 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -85,6 +85,8 @@ Internal
pyramid.url.URLMethodsMixin to make this possible, and all url/path
generation logic is embedded in this mixin.
+- Refactor ``pyramid.config`` into a package.
+
Deprecations
------------
diff --git a/pyramid/config.py b/pyramid/config.py
deleted file mode 100644
index 950c90f00..000000000
--- a/pyramid/config.py
+++ /dev/null
@@ -1,3570 +0,0 @@
-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.settings import aslist
-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 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.
-
- If ``route_prefix`` is passed, all routes added with
- :meth:`pyramid.config.Configurator.add_route` will have the specified path
- prepended to their pattern. This parameter is new in Pyramid 1.2."""
-
- 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,
- route_prefix=None,
- ):
- 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
- self.route_prefix = route_prefix
- 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):
- """ Override the :app:`Pyramid` :term:`authentication policy` in the
- current configuration. The ``policy`` argument must be an instance
- of an authentication policy or a :term:`dotted Python name`
- that points at an instance of an authentication policy.
- """
- self._set_authentication_policy(policy)
- def ensure():
- if self.autocommit:
- return
- if self.registry.queryUtility(IAuthorizationPolicy) is None:
- raise ConfigurationError(
- 'Cannot configure an authentication policy without '
- 'also configuring an authorization policy '
- '(see the set_authorization_policy method)')
- self.action(IAuthenticationPolicy, callable=ensure)
-
- @action_method
- def _set_authentication_policy(self, policy):
- policy = self.maybe_dotted(policy)
- self.registry.registerUtility(policy, IAuthenticationPolicy)
-
- @action_method
- def set_authorization_policy(self, policy):
- """ Override the :app:`Pyramid` :term:`authorization policy` in the
- current configuration. The ``policy`` argument must be an instance
- of an authorization policy or a :term:`dotted Python name` that points
- at an instance of an authorization policy.
- """
- self._set_authorization_policy(policy)
- def ensure():
- if self.registry.queryUtility(IAuthenticationPolicy) is None:
- raise ConfigurationError(
- 'Cannot configure an authorization policy without also '
- 'configuring an authentication policy '
- '(see the set_authentication_policy method)')
- self.action(IAuthorizationPolicy, callable=ensure)
-
- @action_method
- def _set_authorization_policy(self, policy):
- policy = self.maybe_dotted(policy)
- self.registry.registerUtility(policy, IAuthorizationPolicy)
-
- @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 _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)
-
- 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
- context.route_prefix = self.route_prefix
- 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, callable, route_prefix=None):
- """Include a configuration callables, to support imperative
- application extensibility.
-
- .. warning:: In versions of :app:`Pyramid` prior to 1.2, this
- function accepted ``*callables``, but this has been changed
- to support only a single callable.
-
- 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 ``callable`` 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.
-
- If the ``route_prefix`` is supplied, it must be a string. Any calls
- to :meth:`pyramid.config.Configurator.add_route` within the included
- callable will have their pattern prefixed with the value of
- ``route_prefix``. This can be used to help mount a set of routes at a
- different location than the included callable's author intended while
- still maintaining the same route names. For example:
-
- .. code-block:: python
- :linenos:
-
- from pyramid.config import Configurator
-
- def included(config):
- config.add_route('show_users', '/show')
-
- def main(global_config, **settings):
- config = Configurator()
- config.include(included, route_prefix='/users')
-
- In the above configuration, the ``show_users`` route will have an
- effective route pattern of ``/users/show``, instead of ``/show``
- because the ``route_prefix`` argument will be prepended to the
- pattern.
-
- The ``route_prefix`` parameter is new as of Pyramid 1.2.
- """
-
- _context = self._ctx
- if _context is None:
- _context = self._ctx = self._make_context(self.autocommit)
-
- if self.route_prefix:
- old_prefix = self.route_prefix.rstrip('/') + '/'
- else:
- old_prefix = ''
-
- if route_prefix:
- route_prefix = old_prefix + route_prefix.lstrip('/')
-
- c = self.maybe_dotted(callable)
- 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)
- context.route_prefix = route_prefix
- 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,
- route_prefix=context.route_prefix)
- 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 = aslist(settings.get('pyramid.includes', ''))
- tweens = aslist(settings.get('pyramid.tweens', ''))
- 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'. Tweens are 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 ``tween_factory`` argument must be a :term:`dotted Python name`
- to a global object representing the tween factory.
-
- 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 this
- tween's factory name.
-
- 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 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 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`.
-
- - An iterable of any combination of the above. This allows the user
- to specify fallbacks if the desired tween is not included, as well
- as compatibility with multiple other tweens.
-
- ``under`` means 'closer to the main Pyramid application than',
- ``over`` means 'closer to the request ingress than'.
-
- For example, calling ``add_tween('myapp.tfactory',
- over=pyramid.tweens.MAIN)`` will attempt to place the tween factory
- represented by the dotted name ``myapp.tfactory`` directly 'above' (in
- ``paster ptweens`` order) the main Pyramid request handler.
- Likewise, calling ``add_tween('myapp.tfactory',
- 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('myapp.tfactory', alias='someothertween')``).
-
- If all options for ``under`` (or ``over``) cannot be found in the
- current configuration, it is an error. If some options are specified
- purely for compatibilty with other tweens, just add a fallback of
- MAIN or INGRESS. For example,
- ``under=('someothertween', 'someothertween2', INGRESS)``.
- This constraint will require the tween to be located under both the
- 'someothertween' tween, the 'someothertween2' tween, and INGRESS. If
- any of these is not in the current configuration, this constraint will
- only organize itself based on the tweens that are present.
-
- 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.2.
- """
- 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):
-
- if not isinstance(tween_factory, basestring):
- raise ConfigurationError(
- 'The "tween_factory" argument to add_tween must be a '
- 'dotted name to a globally importable object, not %r' %
- tween_factory)
-
- name = tween_factory
- tween_factory = self.maybe_dotted(tween_factory)
-
- def is_string_or_iterable(v):
- if isinstance(v, basestring):
- return True
- if hasattr(v, '__iter__'):
- return True
-
- for t, p in [('over', over), ('under', under)]:
- if p is not None:
- if not is_string_or_iterable(p):
- raise ConfigurationError(
- '"%s" must be a string or iterable, not %s' % (t, p))
-
- if alias in (MAIN, INGRESS):
- raise ConfigurationError('%s is a reserved tween name' % alias)
-
- if over is INGRESS or hasattr(over, '__iter__') and INGRESS in over:
- raise ConfigurationError('%s cannot be over INGRESS' % name)
-
- if under is MAIN or hasattr(under, '__iter__') and MAIN in under:
- raise ConfigurationError('%s cannot be under MAIN' % name)
-
- registry = self.registry
- tweens = registry.queryUtility(ITweens)
- if tweens is None:
- tweens = Tweens()
- registry.registerUtility(tweens, ITweens)
- tweens.add_implicit('pyramid.tweens.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
- :meth:`pyramid.request.Request.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')
-
- if self.route_prefix:
- pattern = self.route_prefix.rstrip('/') + '/' + pattern.lstrip('/')
-
- 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 :meth:`pyramid.request.Request.static_url` method.
- ``add_static_view`` adds a view which renders a static asset when
- some URL is visited; :meth:`pyramid.request.Request.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
- :meth:`pyramid.request.Request.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
- :meth:`pyramid.request.Request.static_url`:
-
- .. code-block:: python
-
- request.static_url('mypackage:images/logo.png')
-
- When ``add_static_view`` is called with a ``name`` argument that
- represents a URL prefix, as it is above, subsequent calls to
- :meth:`pyramid.request.Request.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 :meth:`pyramid.request.Request.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
- :meth:`pyramid.request.Request.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
- :meth:`pyramid.request.Request.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()
-
diff --git a/pyramid/config/__init__.py b/pyramid/config/__init__.py
new file mode 100644
index 000000000..7802c2b97
--- /dev/null
+++ b/pyramid/config/__init__.py
@@ -0,0 +1,779 @@
+import inspect
+import logging
+import os
+import types
+import warnings
+
+import venusian
+
+from zope.configuration.config import GroupingContextDecorator
+from zope.configuration.config import ConfigurationMachine
+from zope.configuration.xmlconfig import registerCommonDirectives
+
+from pyramid.interfaces import IExceptionResponse
+from pyramid.interfaces import IDebugLogger
+
+from pyramid.events import ApplicationCreated
+from pyramid.exceptions import ConfigurationError # bw compat
+from pyramid.httpexceptions import default_exceptionresponse_view
+from pyramid.path import caller_package
+from pyramid.path import package_of
+from pyramid.registry import Registry
+from pyramid.asset import resolve_asset_spec
+from pyramid.settings import aslist
+from pyramid.threadlocal import manager
+from pyramid.util import DottedNameResolver
+from pyramid.util import WeakOrderedSet
+
+from pyramid.config.util import action_method
+
+from pyramid.config.testing import TestingConfiguratorMixin
+from pyramid.config.tweens import TweensConfiguratorMixin
+from pyramid.config.security import SecurityConfiguratorMixin
+from pyramid.config.views import ViewsConfiguratorMixin
+from pyramid.config.routes import RoutesConfiguratorMixin
+from pyramid.config.zca import ZCAConfiguratorMixin
+from pyramid.config.i18n import I18NConfiguratorMixin
+from pyramid.config.rendering import RenderingConfiguratorMixin
+from pyramid.config.rendering import DEFAULT_RENDERERS
+from pyramid.config.assets import AssetsConfiguratorMixin
+from pyramid.config.settings import SettingsConfiguratorMixin
+from pyramid.config.factories import FactoriesConfiguratorMixin
+from pyramid.config.adapters import AdaptersConfiguratorMixin
+
+ConfigurationError = ConfigurationError # pyflakes
+
+class Configurator(
+ TestingConfiguratorMixin,
+ TweensConfiguratorMixin,
+ SecurityConfiguratorMixin,
+ ViewsConfiguratorMixin,
+ RoutesConfiguratorMixin,
+ ZCAConfiguratorMixin,
+ I18NConfiguratorMixin,
+ RenderingConfiguratorMixin,
+ AssetsConfiguratorMixin,
+ SettingsConfiguratorMixin,
+ FactoriesConfiguratorMixin,
+ AdaptersConfiguratorMixin,
+ ):
+ """
+ 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.
+
+ If ``route_prefix`` is passed, all routes added with
+ :meth:`pyramid.config.Configurator.add_route` will have the specified path
+ prepended to their pattern. This parameter is new in Pyramid 1.2."""
+
+ 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,
+ route_prefix=None,
+ ):
+ 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
+ self.route_prefix = route_prefix
+ 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 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."""
+ from webob.exc import WSGIHTTPException as WebobWSGIHTTPException
+
+ tweens = []
+ includes = []
+ if settings:
+ includes = aslist(settings.get('pyramid.includes', ''))
+ tweens = aslist(settings.get('pyramid.tweens', ''))
+ registry = self.registry
+ self._fix_registry()
+ self._set_settings(settings)
+ self._set_root_factory(root_factory)
+ self._register_response_adapters()
+
+ 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:
+ self._set_locale_negotiator(locale_negotiator)
+
+ if 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)
+ 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 _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)
+
+ 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
+ context.route_prefix = self.route_prefix
+ 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, callable, route_prefix=None):
+ """Include a configuration callables, to support imperative
+ application extensibility.
+
+ .. warning:: In versions of :app:`Pyramid` prior to 1.2, this
+ function accepted ``*callables``, but this has been changed
+ to support only a single callable.
+
+ 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 ``callable`` 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.
+
+ If the ``route_prefix`` is supplied, it must be a string. Any calls
+ to :meth:`pyramid.config.Configurator.add_route` within the included
+ callable will have their pattern prefixed with the value of
+ ``route_prefix``. This can be used to help mount a set of routes at a
+ different location than the included callable's author intended while
+ still maintaining the same route names. For example:
+
+ .. code-block:: python
+ :linenos:
+
+ from pyramid.config import Configurator
+
+ def included(config):
+ config.add_route('show_users', '/show')
+
+ def main(global_config, **settings):
+ config = Configurator()
+ config.include(included, route_prefix='/users')
+
+ In the above configuration, the ``show_users`` route will have an
+ effective route pattern of ``/users/show``, instead of ``/show``
+ because the ``route_prefix`` argument will be prepended to the
+ pattern.
+
+ The ``route_prefix`` parameter is new as of Pyramid 1.2.
+ """
+
+ _context = self._ctx
+ if _context is None:
+ _context = self._ctx = self._make_context(self.autocommit)
+
+ if self.route_prefix:
+ old_prefix = self.route_prefix.rstrip('/') + '/'
+ else:
+ old_prefix = ''
+
+ if route_prefix:
+ route_prefix = old_prefix + route_prefix.lstrip('/')
+
+ c = self.maybe_dotted(callable)
+ 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)
+ context.route_prefix = route_prefix
+ 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,
+ route_prefix=context.route_prefix)
+ 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 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()
+
+ # 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)
+
+ 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
+
+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
+
+global_registries = WeakOrderedSet()
+
diff --git a/pyramid/config/adapters.py b/pyramid/config/adapters.py
new file mode 100644
index 000000000..3f4acfa59
--- /dev/null
+++ b/pyramid/config/adapters.py
@@ -0,0 +1,61 @@
+from zope.interface import Interface
+
+from pyramid.interfaces import IResponse
+
+from pyramid.config.util import action_method
+
+class AdaptersConfiguratorMixin(object):
+ @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 _register_response_adapters(self):
+ # 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
+ self.registry.registerSelfAdapter((WebobResponse,), IResponse)
diff --git a/pyramid/config/assets.py b/pyramid/config/assets.py
new file mode 100644
index 000000000..cba171426
--- /dev/null
+++ b/pyramid/config/assets.py
@@ -0,0 +1,73 @@
+import sys
+
+from pyramid.interfaces import IPackageOverrides
+
+from pyramid.asset import PackageOverrides
+from pyramid.exceptions import ConfigurationError
+
+from pyramid.config.util import action_method
+
+class AssetsConfiguratorMixin(object):
+ 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
+
diff --git a/pyramid/config/factories.py b/pyramid/config/factories.py
new file mode 100644
index 000000000..cae070fc9
--- /dev/null
+++ b/pyramid/config/factories.py
@@ -0,0 +1,57 @@
+from pyramid.config.util import action_method
+
+from pyramid.interfaces import IDefaultRootFactory
+from pyramid.interfaces import IRequestFactory
+from pyramid.interfaces import IRootFactory
+from pyramid.interfaces import ISessionFactory
+
+from pyramid.traversal import DefaultRootFactory
+
+class FactoriesConfiguratorMixin(object):
+ @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_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)
+
+ @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)
+
diff --git a/pyramid/config/i18n.py b/pyramid/config/i18n.py
new file mode 100644
index 000000000..ce007e7f4
--- /dev/null
+++ b/pyramid/config/i18n.py
@@ -0,0 +1,105 @@
+import os
+import sys
+
+from translationstring import ChameleonTranslate
+
+from pyramid.interfaces import ITranslationDirectories
+from pyramid.interfaces import IChameleonTranslate
+from pyramid.interfaces import ILocaleNegotiator
+
+from pyramid.exceptions import ConfigurationError
+from pyramid.i18n import get_localizer
+from pyramid.path import package_path
+from pyramid.threadlocal import get_current_request
+
+from pyramid.config.util import action_method
+
+class I18NConfiguratorMixin(object):
+ @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.
+ """
+ def register():
+ self._set_locale_negotiator(negotiator)
+ self.action(ILocaleNegotiator, register)
+
+ def _set_locale_negotiator(self, negotiator):
+ locale_negotiator = self.maybe_dotted(negotiator)
+ self.registry.registerUtility(locale_negotiator, ILocaleNegotiator)
+
+ 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)
+
+def translator(msg):
+ request = get_current_request()
+ localizer = get_localizer(request)
+ return localizer.translate(msg)
+
diff --git a/pyramid/config/rendering.py b/pyramid/config/rendering.py
new file mode 100644
index 000000000..ad75acb9f
--- /dev/null
+++ b/pyramid/config/rendering.py
@@ -0,0 +1,97 @@
+import warnings
+
+from pyramid.interfaces import IRendererFactory
+from pyramid.interfaces import IRendererGlobalsFactory
+
+from pyramid.config.util import action_method
+
+from pyramid import renderers
+from pyramid.mako_templating import renderer_factory as mako_renderer_factory
+
+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
+
+
+class RenderingConfiguratorMixin(object):
+ @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)
+
+ @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)
+
diff --git a/pyramid/config/routes.py b/pyramid/config/routes.py
new file mode 100644
index 000000000..625ef436d
--- /dev/null
+++ b/pyramid/config/routes.py
@@ -0,0 +1,444 @@
+import warnings
+
+from pyramid.interfaces import IRequest
+from pyramid.interfaces import IRouteRequest
+from pyramid.interfaces import IRoutesMapper
+
+from pyramid.exceptions import ConfigurationError
+from pyramid.request import route_request_iface
+from pyramid.urldispatch import RoutesMapper
+
+from pyramid.config.util import action_method
+from pyramid.config.util import make_predicates
+
+class RoutesConfiguratorMixin(object):
+ @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
+ :meth:`pyramid.request.Request.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')
+
+ if self.route_prefix:
+ pattern = self.route_prefix.rstrip('/') + '/' + pattern.lstrip('/')
+
+ 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
+
+ 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)
+
diff --git a/pyramid/config/security.py b/pyramid/config/security.py
new file mode 100644
index 000000000..58c735968
--- /dev/null
+++ b/pyramid/config/security.py
@@ -0,0 +1,108 @@
+from pyramid.interfaces import IAuthorizationPolicy
+from pyramid.interfaces import IAuthenticationPolicy
+from pyramid.interfaces import IDefaultPermission
+
+from pyramid.authorization import ACLAuthorizationPolicy
+from pyramid.exceptions import ConfigurationError
+from pyramid.config.util import action_method
+
+class SecurityConfiguratorMixin(object):
+ @action_method
+ def set_authentication_policy(self, policy):
+ """ Override the :app:`Pyramid` :term:`authentication policy` in the
+ current configuration. The ``policy`` argument must be an instance
+ of an authentication policy or a :term:`dotted Python name`
+ that points at an instance of an authentication policy.
+ """
+ self._set_authentication_policy(policy)
+ def ensure():
+ if self.autocommit:
+ return
+ if self.registry.queryUtility(IAuthorizationPolicy) is None:
+ raise ConfigurationError(
+ 'Cannot configure an authentication policy without '
+ 'also configuring an authorization policy '
+ '(see the set_authorization_policy method)')
+ self.action(IAuthenticationPolicy, callable=ensure)
+
+ @action_method
+ def _set_authentication_policy(self, policy):
+ policy = self.maybe_dotted(policy)
+ self.registry.registerUtility(policy, IAuthenticationPolicy)
+
+ @action_method
+ def set_authorization_policy(self, policy):
+ """ Override the :app:`Pyramid` :term:`authorization policy` in the
+ current configuration. The ``policy`` argument must be an instance
+ of an authorization policy or a :term:`dotted Python name` that points
+ at an instance of an authorization policy.
+ """
+ self._set_authorization_policy(policy)
+ def ensure():
+ if self.registry.queryUtility(IAuthenticationPolicy) is None:
+ raise ConfigurationError(
+ 'Cannot configure an authorization policy without also '
+ 'configuring an authentication policy '
+ '(see the set_authentication_policy method)')
+ self.action(IAuthorizationPolicy, callable=ensure)
+
+ @action_method
+ def _set_authorization_policy(self, policy):
+ policy = self.maybe_dotted(policy)
+ self.registry.registerUtility(policy, IAuthorizationPolicy)
+
+ @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)
+
+ @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)
+
+
diff --git a/pyramid/config/settings.py b/pyramid/config/settings.py
new file mode 100644
index 000000000..513a57871
--- /dev/null
+++ b/pyramid/config/settings.py
@@ -0,0 +1,55 @@
+from pyramid.settings import Settings
+
+class SettingsConfiguratorMixin(object):
+ def _set_settings(self, mapping):
+ if not mapping:
+ mapping = {}
+ settings = Settings(mapping)
+ self.registry.settings = settings
+ return settings
+
+ 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
+
diff --git a/pyramid/config/testing.py b/pyramid/config/testing.py
new file mode 100644
index 000000000..9daa54ff1
--- /dev/null
+++ b/pyramid/config/testing.py
@@ -0,0 +1,140 @@
+from zope.interface import Interface
+
+from pyramid.interfaces import ITraverser
+from pyramid.interfaces import IAuthorizationPolicy
+from pyramid.interfaces import IAuthenticationPolicy
+from pyramid.interfaces import IRendererFactory
+from pyramid.renderers import RendererHelper
+from pyramid.traversal import traversal_path
+
+from pyramid.config.util import action_method
+
+class TestingConfiguratorMixin(object):
+ # 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
+
+
diff --git a/pyramid/config/tests/__init__.py b/pyramid/config/tests/__init__.py
new file mode 100644
index 000000000..5bb534f79
--- /dev/null
+++ b/pyramid/config/tests/__init__.py
@@ -0,0 +1 @@
+# package
diff --git a/pyramid/tests/test_config.py b/pyramid/config/tests/test_config.py
index b222a398a..ad7b51700 100644
--- a/pyramid/tests/test_config.py
+++ b/pyramid/config/tests/test_config.py
@@ -2,6 +2,16 @@ import unittest
from pyramid import testing
+import os
+
+here = os.path.dirname(__file__)
+locale = os.path.abspath(
+ os.path.join(here, '..', '..', 'tests', 'localeapp', 'locale'))
+locale2 = os.path.abspath(
+ os.path.join(here, '..', '..', 'tests', 'localeapp', 'locale2'))
+locale3 = os.path.abspath(
+ os.path.join(here, '..', '..', 'tests', 'localeapp', 'locale3'))
+
try:
import __pypy__
except:
@@ -81,7 +91,7 @@ class ConfiguratorTests(unittest.TestCase):
from pyramid.config import Configurator
from pyramid.interfaces import IRendererFactory
config = Configurator()
- this_pkg = sys.modules['pyramid.tests']
+ this_pkg = sys.modules['pyramid.config.tests']
self.assertTrue(config.registry.getUtility(ISettings))
self.assertEqual(config.package, this_pkg)
self.assertTrue(config.registry.getUtility(IRendererFactory, 'json'))
@@ -153,7 +163,7 @@ class ConfiguratorTests(unittest.TestCase):
from pyramid.interfaces import IDebugLogger
config = self._makeOne()
logger = config.registry.getUtility(IDebugLogger)
- self.assertEqual(logger.name, 'pyramid.tests')
+ self.assertEqual(logger.name, 'pyramid.config.tests')
def test_ctor_noreg_debug_logger_non_None(self):
from pyramid.interfaces import IDebugLogger
@@ -404,7 +414,7 @@ class ConfiguratorTests(unittest.TestCase):
config = self._makeOne(reg)
config.setup_registry()
logger = reg.getUtility(IDebugLogger)
- self.assertEqual(logger.name, 'pyramid.tests')
+ self.assertEqual(logger.name, 'pyramid.config.tests')
def test_setup_registry_debug_logger_non_None(self):
from pyramid.registry import Registry
@@ -577,8 +587,9 @@ class ConfiguratorTests(unittest.TestCase):
reg = Registry()
config = self._makeOne(reg)
settings = {
- 'pyramid.includes': """pyramid.tests.test_config.dummy_include
-pyramid.tests.test_config.dummy_include2""",
+ 'pyramid.includes':
+"""pyramid.config.tests.test_config.dummy_include
+pyramid.config.tests.test_config.dummy_include2""",
}
config.setup_registry(settings=settings)
self.assert_(reg.included)
@@ -589,7 +600,8 @@ pyramid.tests.test_config.dummy_include2""",
reg = Registry()
config = self._makeOne(reg)
settings = {
- 'pyramid.includes': """pyramid.tests.test_config.dummy_include pyramid.tests.test_config.dummy_include2""",
+ 'pyramid.includes':
+"""pyramid.config.tests.test_config.dummy_include pyramid.config.tests.test_config.dummy_include2""",
}
config.setup_registry(settings=settings)
self.assert_(reg.included)
@@ -601,14 +613,16 @@ pyramid.tests.test_config.dummy_include2""",
reg = Registry()
config = self._makeOne(reg)
settings = {
- 'pyramid.tweens': 'pyramid.tests.test_config.dummy_tween_factory'
+ 'pyramid.tweens':
+ 'pyramid.config.tests.test_config.dummy_tween_factory'
}
config.setup_registry(settings=settings)
config.commit()
tweens = config.registry.getUtility(ITweens)
- self.assertEqual(tweens.explicit,
- [('pyramid.tests.test_config.dummy_tween_factory',
- dummy_tween_factory)])
+ self.assertEqual(
+ tweens.explicit,
+ [('pyramid.config.tests.test_config.dummy_tween_factory',
+ dummy_tween_factory)])
def test_get_settings_nosettings(self):
from pyramid.registry import Registry
@@ -647,17 +661,19 @@ pyramid.tests.test_config.dummy_include2""",
def factory1(handler, registry): return handler
def factory2(handler, registry): return handler
config = self._makeOne()
- config.add_tween('pyramid.tests.test_config.dummy_tween_factory')
- config.add_tween('pyramid.tests.test_config.dummy_tween_factory2')
+ config.add_tween(
+ 'pyramid.config.tests.test_config.dummy_tween_factory')
+ config.add_tween(
+ 'pyramid.config.tests.test_config.dummy_tween_factory2')
config.commit()
tweens = config.registry.queryUtility(ITweens)
implicit = tweens.implicit()
self.assertEqual(
implicit,
[
- ('pyramid.tests.test_config.dummy_tween_factory2',
+ ('pyramid.config.tests.test_config.dummy_tween_factory2',
dummy_tween_factory2),
- ('pyramid.tests.test_config.dummy_tween_factory',
+ ('pyramid.config.tests.test_config.dummy_tween_factory',
dummy_tween_factory),
('pyramid.tweens.excview_tween_factory',
excview_tween_factory),
@@ -669,11 +685,13 @@ pyramid.tests.test_config.dummy_include2""",
from pyramid.tweens import excview_tween_factory
from pyramid.tweens import MAIN
config = self._makeOne()
- config.add_tween('pyramid.tests.test_config.dummy_tween_factory',
- over=MAIN)
- config.add_tween('pyramid.tests.test_config.dummy_tween_factory2',
- over=MAIN,
- under='pyramid.tests.test_config.dummy_tween_factory')
+ config.add_tween(
+ 'pyramid.config.tests.test_config.dummy_tween_factory',
+ over=MAIN)
+ config.add_tween(
+ 'pyramid.config.tests.test_config.dummy_tween_factory2',
+ over=MAIN,
+ under='pyramid.config.tests.test_config.dummy_tween_factory')
config.commit()
tweens = config.registry.queryUtility(ITweens)
implicit = tweens.implicit()
@@ -681,39 +699,42 @@ pyramid.tests.test_config.dummy_include2""",
implicit,
[
('pyramid.tweens.excview_tween_factory', excview_tween_factory),
- ('pyramid.tests.test_config.dummy_tween_factory',
+ ('pyramid.config.tests.test_config.dummy_tween_factory',
dummy_tween_factory),
- ('pyramid.tests.test_config.dummy_tween_factory2',
+ ('pyramid.config.tests.test_config.dummy_tween_factory2',
dummy_tween_factory2),
])
def test_add_tweens_names_with_under_nonstringoriter(self):
from pyramid.exceptions import ConfigurationError
config = self._makeOne()
- self.assertRaises(ConfigurationError, config.add_tween,
- 'pyramid.tests.test_config.dummy_tween_factory',
- under=False)
+ self.assertRaises(
+ ConfigurationError, config.add_tween,
+ 'pyramid.config.tests.test_config.dummy_tween_factory',
+ under=False)
def test_add_tweens_names_with_over_nonstringoriter(self):
from pyramid.exceptions import ConfigurationError
config = self._makeOne()
- self.assertRaises(ConfigurationError, config.add_tween,
- 'pyramid.tests.test_config.dummy_tween_factory',
- over=False)
+ self.assertRaises(
+ ConfigurationError, config.add_tween,
+ 'pyramid.config.tests.test_config.dummy_tween_factory',
+ over=False)
def test_add_tween_dottedname(self):
from pyramid.interfaces import ITweens
from pyramid.tweens import excview_tween_factory
config = self._makeOne()
- config.add_tween('pyramid.tests.test_config.dummy_tween_factory')
+ config.add_tween('pyramid.config.tests.test_config.dummy_tween_factory')
config.commit()
tweens = config.registry.queryUtility(ITweens)
self.assertEqual(
tweens.implicit(),
[
- ('pyramid.tests.test_config.dummy_tween_factory',
+ ('pyramid.config.tests.test_config.dummy_tween_factory',
dummy_tween_factory),
- ('pyramid.tweens.excview_tween_factory', excview_tween_factory),
+ ('pyramid.tweens.excview_tween_factory',
+ excview_tween_factory),
])
def test_add_tween_instance(self):
@@ -733,64 +754,78 @@ pyramid.tests.test_config.dummy_include2""",
from pyramid.exceptions import ConfigurationError
from pyramid.tweens import INGRESS
config = self._makeOne()
- self.assertRaises(ConfigurationError,
- config.add_tween, 'pyramid.tests.test_config.dummy_tween_factory',
+ self.assertRaises(
+ ConfigurationError,
+ config.add_tween,
+ 'pyramid.config.tests.test_config.dummy_tween_factory',
alias=INGRESS)
def test_add_tween_alias_main(self):
from pyramid.exceptions import ConfigurationError
from pyramid.tweens import MAIN
config = self._makeOne()
- self.assertRaises(ConfigurationError,
- config.add_tween, 'pyramid.tests.test_config.dummy_tween_factory',
+ self.assertRaises(
+ ConfigurationError,
+ config.add_tween,
+ 'pyramid.config.tests.test_config.dummy_tween_factory',
alias=MAIN)
def test_add_tweens_conflict(self):
from zope.configuration.config import ConfigurationConflictError
config = self._makeOne()
- config.add_tween('pyramid.tests.test_config.dummy_tween_factory')
- config.add_tween('pyramid.tests.test_config.dummy_tween_factory')
+ config.add_tween('pyramid.config.tests.test_config.dummy_tween_factory')
+ config.add_tween('pyramid.config.tests.test_config.dummy_tween_factory')
self.assertRaises(ConfigurationConflictError, config.commit)
def test_add_tweens_conflict_same_alias(self):
from zope.configuration.config import ConfigurationConflictError
config = self._makeOne()
- config.add_tween('pyramid.tests.test_config.dummy_tween_factory',
- alias='a')
- config.add_tween('pyramid.tests.test_config.dummy_tween_factory2',
- alias='a')
+ config.add_tween(
+ 'pyramid.config.tests.test_config.dummy_tween_factory',
+ alias='a')
+ config.add_tween(
+ 'pyramid.config.tests.test_config.dummy_tween_factory2',
+ alias='a')
self.assertRaises(ConfigurationConflictError, config.commit)
def test_add_tween_over_ingress(self):
from pyramid.exceptions import ConfigurationError
from pyramid.tweens import INGRESS
config = self._makeOne()
- self.assertRaises(ConfigurationError,
- config.add_tween, 'pyramid.tests.test_config.dummy_tween_factory',
+ self.assertRaises(
+ ConfigurationError,
+ config.add_tween,
+ 'pyramid.config.tests.test_config.dummy_tween_factory',
over=INGRESS)
def test_add_tween_over_ingress_iterable(self):
from pyramid.exceptions import ConfigurationError
from pyramid.tweens import INGRESS
config = self._makeOne()
- self.assertRaises(ConfigurationError,
- config.add_tween, 'pyramid.tests.test_config.dummy_tween_factory',
+ self.assertRaises(
+ ConfigurationError,
+ config.add_tween,
+ 'pyramid.config.tests.test_config.dummy_tween_factory',
over=('a', INGRESS))
def test_add_tween_under_main(self):
from pyramid.exceptions import ConfigurationError
from pyramid.tweens import MAIN
config = self._makeOne()
- self.assertRaises(ConfigurationError,
- config.add_tween, 'pyramid.tests.test_config.dummy_tween_factory',
+ self.assertRaises(
+ ConfigurationError,
+ config.add_tween,
+ 'pyramid.config.tests.test_config.dummy_tween_factory',
under=MAIN)
def test_add_tween_under_main_iterable(self):
from pyramid.exceptions import ConfigurationError
from pyramid.tweens import MAIN
config = self._makeOne()
- self.assertRaises(ConfigurationError,
- config.add_tween, 'pyramid.tests.test_config.dummy_tween_factory',
+ self.assertRaises(
+ ConfigurationError,
+ config.add_tween,
+ 'pyramid.config.tests.test_config.dummy_tween_factory',
under=('a', MAIN))
def test_add_subscriber_defaults(self):
@@ -901,11 +936,11 @@ pyramid.tests.test_config.dummy_include2""",
global_registries.empty()
def test_include_with_dotted_name(self):
- from pyramid import tests
+ from pyramid.config import tests
config = self._makeOne()
context_before = config._make_context()
config._ctx = context_before
- config.include('pyramid.tests.test_config.dummy_include')
+ config.include('pyramid.config.tests.test_config.dummy_include')
context_after = config._ctx
actions = context_after.actions
self.assertEqual(len(actions), 1)
@@ -918,7 +953,7 @@ pyramid.tests.test_config.dummy_include2""",
self.assertTrue(context_after is context_before)
def test_include_with_python_callable(self):
- from pyramid import tests
+ from pyramid.config import tests
config = self._makeOne()
context_before = config._make_context()
config._ctx = context_before
@@ -935,11 +970,11 @@ pyramid.tests.test_config.dummy_include2""",
self.assertTrue(context_after is context_before)
def test_include_with_module_defaults_to_includeme(self):
- from pyramid import tests
+ from pyramid.config import tests
config = self._makeOne()
context_before = config._make_context()
config._ctx = context_before
- config.include('pyramid.tests.test_config')
+ config.include('pyramid.config.tests.test_config')
context_after = config._ctx
actions = context_after.actions
self.assertEqual(len(actions), 1)
@@ -1017,7 +1052,7 @@ pyramid.tests.test_config.dummy_include2""",
def test_add_view_view_callable_dottedname(self):
from pyramid.renderers import null_renderer
config = self._makeOne(autocommit=True)
- config.add_view(view='pyramid.tests.test_config.dummy_view',
+ config.add_view(view='pyramid.config.tests.test_config.dummy_view',
renderer=null_renderer)
wrapper = self._getViewCallable(config)
self.assertEqual(wrapper(None, None), 'OK')
@@ -1164,7 +1199,7 @@ pyramid.tests.test_config.dummy_include2""",
from pyramid.renderers import null_renderer
view = lambda *arg: 'OK'
config = self._makeOne(autocommit=True)
- config.add_view(context='pyramid.tests.test_config.IDummy',
+ config.add_view(context='pyramid.config.tests.test_config.IDummy',
view=view, renderer=null_renderer)
wrapper = self._getViewCallable(config, IDummy)
self.assertEqual(wrapper, view)
@@ -1173,7 +1208,7 @@ pyramid.tests.test_config.dummy_include2""",
from pyramid.renderers import null_renderer
view = lambda *arg: 'OK'
config = self._makeOne(autocommit=True)
- config.add_view(for_='pyramid.tests.test_config.IDummy',
+ config.add_view(for_='pyramid.config.tests.test_config.IDummy',
view=view, renderer=null_renderer)
wrapper = self._getViewCallable(config, IDummy)
self.assertEqual(wrapper, view)
@@ -1340,7 +1375,7 @@ pyramid.tests.test_config.dummy_include2""",
def test_add_view_default_phash_overrides_default_phash(self):
from pyramid.renderers import null_renderer
- from pyramid.config import DEFAULT_PHASH
+ from pyramid.config.util import DEFAULT_PHASH
from zope.interface import Interface
from pyramid.interfaces import IRequest
from pyramid.interfaces import IView
@@ -1362,7 +1397,7 @@ pyramid.tests.test_config.dummy_include2""",
def test_add_view_exc_default_phash_overrides_default_phash(self):
from pyramid.renderers import null_renderer
- from pyramid.config import DEFAULT_PHASH
+ from pyramid.config.util import DEFAULT_PHASH
from zope.interface import implementedBy
from pyramid.interfaces import IRequest
from pyramid.interfaces import IView
@@ -1774,7 +1809,7 @@ pyramid.tests.test_config.dummy_include2""",
self.assertEqual(wrapper(ctx, request), 'view8')
def test_add_view_with_template_renderer(self):
- import pyramid.tests
+ import pyramid.config.tests
from pyramid.interfaces import ISettings
class view(object):
def __init__(self, context, request):
@@ -1795,7 +1830,7 @@ pyramid.tests.test_config.dummy_include2""",
result = renderer.info
self.assertEqual(result.registry, config.registry)
self.assertEqual(result.type, '.txt')
- self.assertEqual(result.package, pyramid.tests)
+ self.assertEqual(result.package, pyramid.config.tests)
self.assertEqual(result.name, fixture)
self.assertEqual(result.settings, settings)
@@ -1821,7 +1856,7 @@ pyramid.tests.test_config.dummy_include2""",
self.assertEqual(result.body, 'moo')
def test_add_view_with_template_renderer_no_callable(self):
- import pyramid.tests
+ import pyramid.config.tests
from pyramid.interfaces import ISettings
config = self._makeOne(autocommit=True)
renderer = self._registerRenderer(config)
@@ -1835,7 +1870,7 @@ pyramid.tests.test_config.dummy_include2""",
result = renderer.info
self.assertEqual(result.registry, config.registry)
self.assertEqual(result.type, '.txt')
- self.assertEqual(result.package, pyramid.tests)
+ self.assertEqual(result.package, pyramid.config.tests)
self.assertEqual(result.name, fixture)
self.assertEqual(result.settings, settings)
@@ -2114,7 +2149,7 @@ pyramid.tests.test_config.dummy_include2""",
config = self._makeOne(autocommit=True)
config.add_view(
view=view,
- containment='pyramid.tests.test_config.IDummy',
+ containment='pyramid.config.tests.test_config.IDummy',
renderer=null_renderer)
wrapper = self._getViewCallable(config)
context = DummyContext()
@@ -2337,7 +2372,7 @@ pyramid.tests.test_config.dummy_include2""",
config = self._makeOne(autocommit=True)
route = config.add_route(
'name', 'path',
- factory='pyramid.tests.test_config.dummyfactory')
+ factory='pyramid.config.tests.test_config.dummyfactory')
self.assertEqual(route.factory, dummyfactory)
def test_add_route_with_xhr(self):
@@ -2489,7 +2524,8 @@ pyramid.tests.test_config.dummy_include2""",
from pyramid.renderers import null_renderer
config = self._makeOne()
result = config.derive_view(
- 'pyramid.tests.test_config.dummy_view', renderer=null_renderer)
+ 'pyramid.config.tests.test_config.dummy_view',
+ renderer=null_renderer)
self.assertFalse(result is dummy_view)
self.assertEqual(result(None, None), 'OK')
@@ -2604,8 +2640,9 @@ pyramid.tests.test_config.dummy_include2""",
config = self._makeOne(autocommit=True)
config.registry.registerUtility(info, IStaticURLInfo)
config.add_static_view('static', 'pyramid.tests:fixtures/static')
- self.assertEqual(info.added,
- [('static', 'pyramid.tests:fixtures/static', {})])
+ self.assertEqual(
+ info.added,
+ [('static', 'pyramid.tests:fixtures/static', {})])
def test_add_static_view_package_here_relative(self):
from pyramid.interfaces import IStaticURLInfo
@@ -2613,8 +2650,9 @@ pyramid.tests.test_config.dummy_include2""",
config = self._makeOne(autocommit=True)
config.registry.registerUtility(info, IStaticURLInfo)
config.add_static_view('static', 'fixtures/static')
- self.assertEqual(info.added,
- [('static', 'pyramid.tests:fixtures/static', {})])
+ self.assertEqual(
+ info.added,
+ [('static', 'pyramid.config.tests:fixtures/static', {})])
def test_add_static_view_absolute(self):
import os
@@ -2666,8 +2704,9 @@ pyramid.tests.test_config.dummy_include2""",
from pyramid.httpexceptions import HTTPNotFound
config = self._makeOne(autocommit=True)
view = lambda *arg: {}
- config.set_notfound_view(view,
- renderer='pyramid.tests:fixtures/minimal.pt')
+ config.set_notfound_view(
+ view,
+ renderer='pyramid.tests:fixtures/minimal.pt')
config.begin()
try: # chameleon depends on being able to find a threadlocal registry
request = self._makeRequest(config)
@@ -2717,8 +2756,9 @@ pyramid.tests.test_config.dummy_include2""",
from pyramid.httpexceptions import HTTPForbidden
config = self._makeOne(autocommit=True)
view = lambda *arg: {}
- config.set_forbidden_view(view,
- renderer='pyramid.tests:fixtures/minimal.pt')
+ config.set_forbidden_view(
+ view,
+ renderer='pyramid.tests:fixtures/minimal.pt')
config.begin()
try: # chameleon requires a threadlocal registry
request = self._makeRequest(config)
@@ -2817,7 +2857,7 @@ pyramid.tests.test_config.dummy_include2""",
from pyramid.interfaces import ILocaleNegotiator
config = self._makeOne(autocommit=True)
config.set_locale_negotiator(
- 'pyramid.tests.test_config.dummyfactory')
+ 'pyramid.config.tests.test_config.dummyfactory')
self.assertEqual(config.registry.getUtility(ILocaleNegotiator),
dummyfactory)
@@ -2832,7 +2872,7 @@ pyramid.tests.test_config.dummy_include2""",
from pyramid.interfaces import IRequestFactory
config = self._makeOne(autocommit=True)
config.set_request_factory(
- 'pyramid.tests.test_config.dummyfactory')
+ 'pyramid.config.tests.test_config.dummyfactory')
self.assertEqual(config.registry.getUtility(IRequestFactory),
dummyfactory)
@@ -2857,7 +2897,7 @@ pyramid.tests.test_config.dummy_include2""",
from pyramid.interfaces import IRendererGlobalsFactory
config = self._makeOne(autocommit=True)
config.set_renderer_globals_factory(
- 'pyramid.tests.test_config.dummyfactory')
+ 'pyramid.config.tests.test_config.dummyfactory')
self.assertEqual(
config.registry.getUtility(IRendererGlobalsFactory),
dummyfactory)
@@ -2882,9 +2922,9 @@ pyramid.tests.test_config.dummy_include2""",
def test_add_view_mapper_dottedname(self):
from pyramid.interfaces import IViewMapperFactory
config = self._makeOne(autocommit=True)
- config.set_view_mapper('pyramid.tests.test_config')
+ config.set_view_mapper('pyramid.config.tests.test_config')
result = config.registry.getUtility(IViewMapperFactory)
- from pyramid.tests import test_config
+ from pyramid.config.tests import test_config
self.assertEqual(result, test_config)
def test_set_session_factory(self):
@@ -2912,50 +2952,35 @@ pyramid.tests.test_config.dummy_include2""",
None)
def test_add_translation_dirs_asset_spec(self):
- import os
from pyramid.interfaces import ITranslationDirectories
config = self._makeOne(autocommit=True)
config.add_translation_dirs('pyramid.tests.localeapp:locale')
- here = os.path.dirname(__file__)
- locale = os.path.join(here, 'localeapp', 'locale')
self.assertEqual(config.registry.getUtility(ITranslationDirectories),
[locale])
def test_add_translation_dirs_asset_spec_existing_translation_dirs(self):
- import os
from pyramid.interfaces import ITranslationDirectories
config = self._makeOne(autocommit=True)
directories = ['abc']
config.registry.registerUtility(directories, ITranslationDirectories)
config.add_translation_dirs('pyramid.tests.localeapp:locale')
- here = os.path.dirname(__file__)
- locale = os.path.join(here, 'localeapp', 'locale')
result = config.registry.getUtility(ITranslationDirectories)
self.assertEqual(result, [locale, 'abc'])
def test_add_translation_dirs_multiple_specs(self):
- import os
from pyramid.interfaces import ITranslationDirectories
config = self._makeOne(autocommit=True)
config.add_translation_dirs('pyramid.tests.localeapp:locale',
'pyramid.tests.localeapp:locale2')
- here = os.path.dirname(__file__)
- locale = os.path.join(here, 'localeapp', 'locale')
- locale2 = os.path.join(here, 'localeapp', 'locale2')
self.assertEqual(config.registry.getUtility(ITranslationDirectories),
[locale, locale2])
def test_add_translation_dirs_multiple_specs_multiple_calls(self):
- import os
from pyramid.interfaces import ITranslationDirectories
config = self._makeOne(autocommit=True)
config.add_translation_dirs('pyramid.tests.localeapp:locale',
'pyramid.tests.localeapp:locale2')
config.add_translation_dirs('pyramid.tests.localeapp:locale3')
- here = os.path.dirname(__file__)
- locale = os.path.join(here, 'localeapp', 'locale')
- locale2 = os.path.join(here, 'localeapp', 'locale2')
- locale3 = os.path.join(here, 'localeapp', 'locale3')
self.assertEqual(config.registry.getUtility(ITranslationDirectories),
[locale3, locale, locale2])
@@ -2973,11 +2998,8 @@ pyramid.tests.test_config.dummy_include2""",
manager.pop()
def test_add_translation_dirs_abspath(self):
- import os
from pyramid.interfaces import ITranslationDirectories
config = self._makeOne(autocommit=True)
- here = os.path.dirname(__file__)
- locale = os.path.join(here, 'localeapp', 'locale')
config.add_translation_dirs(locale)
self.assertEqual(config.registry.getUtility(ITranslationDirectories),
[locale])
@@ -3301,7 +3323,7 @@ pyramid.tests.test_config.dummy_include2""",
def test_testing_add_subscriber_dottedname(self):
config = self._makeOne(autocommit=True)
L = config.testing_add_subscriber(
- 'pyramid.tests.test_config.IDummy')
+ 'pyramid.config.tests.test_config.IDummy')
event = DummyEvent()
config.registry.notify(event)
self.assertEqual(len(L), 1)
@@ -3807,10 +3829,10 @@ class TestConfigurator_add_directive(unittest.TestCase):
self.config = Configurator()
def test_extend_with_dotted_name(self):
- from pyramid import tests
+ from pyramid.config import tests
config = self.config
config.add_directive(
- 'dummy_extend', 'pyramid.tests.test_config.dummy_extend')
+ 'dummy_extend', 'pyramid.config.tests.test_config.dummy_extend')
self.assert_(hasattr(config, 'dummy_extend'))
config.dummy_extend('discrim')
context_after = config._ctx
@@ -3822,7 +3844,7 @@ class TestConfigurator_add_directive(unittest.TestCase):
)
def test_extend_with_python_callable(self):
- from pyramid import tests
+ from pyramid.config import tests
config = self.config
config.add_directive(
'dummy_extend', dummy_extend)
@@ -3877,1824 +3899,6 @@ class TestConfigurator_add_directive(unittest.TestCase):
('discrim', None, config2.package),
)
-class TestViewDeriver(unittest.TestCase):
- def setUp(self):
- self.config = testing.setUp()
-
- def tearDown(self):
- self.config = None
-
- def _makeOne(self, **kw):
- kw['registry'] = self.config.registry
- from pyramid.config import ViewDeriver
- return ViewDeriver(**kw)
-
- def _makeRequest(self):
- request = DummyRequest()
- request.registry = self.config.registry
- return request
-
- def _registerLogger(self):
- from pyramid.interfaces import IDebugLogger
- logger = DummyLogger()
- self.config.registry.registerUtility(logger, IDebugLogger)
- return logger
-
- def _registerSecurityPolicy(self, permissive):
- from pyramid.interfaces import IAuthenticationPolicy
- from pyramid.interfaces import IAuthorizationPolicy
- policy = DummySecurityPolicy(permissive)
- self.config.registry.registerUtility(policy, IAuthenticationPolicy)
- self.config.registry.registerUtility(policy, IAuthorizationPolicy)
-
- def test_requestonly_function(self):
- response = DummyResponse()
- def view(request):
- return response
- deriver = self._makeOne()
- result = deriver(view)
- self.assertFalse(result is view)
- self.assertEqual(result(None, None), response)
-
- def test_requestonly_function_with_renderer(self):
- response = DummyResponse()
- class moo(object):
- def render_view(inself, req, resp, view_inst, ctx):
- self.assertEqual(req, request)
- self.assertEqual(resp, 'OK')
- self.assertEqual(view_inst, view)
- self.assertEqual(ctx, context)
- return response
- def view(request):
- return 'OK'
- deriver = self._makeOne(renderer=moo())
- result = deriver(view)
- self.assertFalse(result.__wraps__ is view)
- request = self._makeRequest()
- context = testing.DummyResource()
- self.assertEqual(result(context, request), response)
-
- def test_requestonly_function_with_renderer_request_override(self):
- def moo(info):
- def inner(value, system):
- self.assertEqual(value, 'OK')
- self.assertEqual(system['request'], request)
- self.assertEqual(system['context'], context)
- return 'moo'
- return inner
- def view(request):
- return 'OK'
- self.config.add_renderer('moo', moo)
- deriver = self._makeOne(renderer='string')
- result = deriver(view)
- self.assertFalse(result is view)
- request = self._makeRequest()
- request.override_renderer = 'moo'
- context = testing.DummyResource()
- self.assertEqual(result(context, request).body, 'moo')
-
- def test_requestonly_function_with_renderer_request_has_view(self):
- response = DummyResponse()
- class moo(object):
- def render_view(inself, req, resp, view_inst, ctx):
- self.assertEqual(req, request)
- self.assertEqual(resp, 'OK')
- self.assertEqual(view_inst, 'view')
- self.assertEqual(ctx, context)
- return response
- def view(request):
- return 'OK'
- deriver = self._makeOne(renderer=moo())
- result = deriver(view)
- self.assertFalse(result.__wraps__ is view)
- request = self._makeRequest()
- request.__view__ = 'view'
- context = testing.DummyResource()
- r = result(context, request)
- self.assertEqual(r, response)
- self.assertFalse(hasattr(request, '__view__'))
-
- def test_class_without_attr(self):
- response = DummyResponse()
- class View(object):
- def __init__(self, request):
- pass
- def __call__(self):
- return response
- deriver = self._makeOne()
- result = deriver(View)
- request = self._makeRequest()
- self.assertEqual(result(None, request), response)
- self.assertEqual(request.__view__.__class__, View)
-
- def test_class_with_attr(self):
- response = DummyResponse()
- class View(object):
- def __init__(self, request):
- pass
- def another(self):
- return response
- deriver = self._makeOne(attr='another')
- result = deriver(View)
- request = self._makeRequest()
- self.assertEqual(result(None, request), response)
- self.assertEqual(request.__view__.__class__, View)
-
- def test_as_function_context_and_request(self):
- def view(context, request):
- return 'OK'
- deriver = self._makeOne()
- result = deriver(view)
- self.assertTrue(result.__wraps__ is view)
- self.assertFalse(hasattr(result, '__call_permissive__'))
- self.assertEqual(view(None, None), 'OK')
-
- def test_as_function_requestonly(self):
- response = DummyResponse()
- def view(request):
- return response
- deriver = self._makeOne()
- result = deriver(view)
- self.assertFalse(result is view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
- self.assertFalse(hasattr(result, '__call_permissive__'))
- self.assertEqual(result(None, None), response)
-
- def test_as_newstyle_class_context_and_request(self):
- response = DummyResponse()
- class view(object):
- def __init__(self, context, request):
- pass
- def __call__(self):
- return response
- deriver = self._makeOne()
- result = deriver(view)
- self.assertFalse(result is view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
- self.assertFalse(hasattr(result, '__call_permissive__'))
- request = self._makeRequest()
- self.assertEqual(result(None, request), response)
- self.assertEqual(request.__view__.__class__, view)
-
- def test_as_newstyle_class_requestonly(self):
- response = DummyResponse()
- class view(object):
- def __init__(self, context, request):
- pass
- def __call__(self):
- return response
- deriver = self._makeOne()
- result = deriver(view)
- self.assertFalse(result is view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
- self.assertFalse(hasattr(result, '__call_permissive__'))
- request = self._makeRequest()
- self.assertEqual(result(None, request), response)
- self.assertEqual(request.__view__.__class__, view)
-
- def test_as_oldstyle_class_context_and_request(self):
- response = DummyResponse()
- class view:
- def __init__(self, context, request):
- pass
- def __call__(self):
- return response
- deriver = self._makeOne()
- result = deriver(view)
- self.assertFalse(result is view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
- self.assertFalse(hasattr(result, '__call_permissive__'))
- request = self._makeRequest()
- self.assertEqual(result(None, request), response)
- self.assertEqual(request.__view__.__class__, view)
-
- def test_as_oldstyle_class_requestonly(self):
- response = DummyResponse()
- class view:
- def __init__(self, context, request):
- pass
- def __call__(self):
- return response
- deriver = self._makeOne()
- result = deriver(view)
- self.assertFalse(result is view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
- self.assertFalse(hasattr(result, '__call_permissive__'))
- request = self._makeRequest()
- self.assertEqual(result(None, request), response)
- self.assertEqual(request.__view__.__class__, view)
-
- def test_as_instance_context_and_request(self):
- response = DummyResponse()
- class View:
- def __call__(self, context, request):
- return response
- view = View()
- deriver = self._makeOne()
- result = deriver(view)
- self.assertTrue(result.__wraps__ is view)
- self.assertFalse(hasattr(result, '__call_permissive__'))
- self.assertEqual(result(None, None), response)
-
- def test_as_instance_requestonly(self):
- response = DummyResponse()
- class View:
- def __call__(self, request):
- return response
- view = View()
- deriver = self._makeOne()
- result = deriver(view)
- self.assertFalse(result is view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertTrue('instance' in result.__name__)
- self.assertFalse(hasattr(result, '__call_permissive__'))
- self.assertEqual(result(None, None), response)
-
- def test_with_debug_authorization_no_authpol(self):
- response = DummyResponse()
- view = lambda *arg: response
- self.config.registry.settings = dict(
- debug_authorization=True, reload_templates=True)
- logger = self._registerLogger()
- deriver = self._makeOne(permission='view')
- result = deriver(view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
- self.assertFalse(hasattr(result, '__call_permissive__'))
- request = self._makeRequest()
- request.view_name = 'view_name'
- request.url = 'url'
- self.assertEqual(result(None, request), response)
- self.assertEqual(len(logger.messages), 1)
- self.assertEqual(logger.messages[0],
- "debug_authorization of url url (view name "
- "'view_name' against context None): Allowed "
- "(no authorization policy in use)")
-
- def test_with_debug_authorization_authn_policy_no_authz_policy(self):
- response = DummyResponse()
- view = lambda *arg: response
- self.config.registry.settings = dict(debug_authorization=True)
- from pyramid.interfaces import IAuthenticationPolicy
- policy = DummySecurityPolicy(False)
- self.config.registry.registerUtility(policy, IAuthenticationPolicy)
- logger = self._registerLogger()
- deriver = self._makeOne(permission='view')
- result = deriver(view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
- self.assertFalse(hasattr(result, '__call_permissive__'))
- request = self._makeRequest()
- request.view_name = 'view_name'
- request.url = 'url'
- self.assertEqual(result(None, request), response)
- self.assertEqual(len(logger.messages), 1)
- self.assertEqual(logger.messages[0],
- "debug_authorization of url url (view name "
- "'view_name' against context None): Allowed "
- "(no authorization policy in use)")
-
- def test_with_debug_authorization_authz_policy_no_authn_policy(self):
- response = DummyResponse()
- view = lambda *arg: response
- self.config.registry.settings = dict(debug_authorization=True)
- from pyramid.interfaces import IAuthorizationPolicy
- policy = DummySecurityPolicy(False)
- self.config.registry.registerUtility(policy, IAuthorizationPolicy)
- logger = self._registerLogger()
- deriver = self._makeOne(permission='view')
- result = deriver(view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
- self.assertFalse(hasattr(result, '__call_permissive__'))
- request = self._makeRequest()
- request.view_name = 'view_name'
- request.url = 'url'
- self.assertEqual(result(None, request), response)
- self.assertEqual(len(logger.messages), 1)
- self.assertEqual(logger.messages[0],
- "debug_authorization of url url (view name "
- "'view_name' against context None): Allowed "
- "(no authorization policy in use)")
-
- def test_with_debug_authorization_no_permission(self):
- response = DummyResponse()
- view = lambda *arg: response
- self.config.registry.settings = dict(
- debug_authorization=True, reload_templates=True)
- self._registerSecurityPolicy(True)
- logger = self._registerLogger()
- deriver = self._makeOne()
- result = deriver(view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
- self.assertFalse(hasattr(result, '__call_permissive__'))
- request = self._makeRequest()
- request.view_name = 'view_name'
- request.url = 'url'
- self.assertEqual(result(None, request), response)
- self.assertEqual(len(logger.messages), 1)
- self.assertEqual(logger.messages[0],
- "debug_authorization of url url (view name "
- "'view_name' against context None): Allowed ("
- "no permission registered)")
-
- def test_debug_auth_permission_authpol_permitted(self):
- response = DummyResponse()
- view = lambda *arg: response
- self.config.registry.settings = dict(
- debug_authorization=True, reload_templates=True)
- logger = self._registerLogger()
- self._registerSecurityPolicy(True)
- deriver = self._makeOne(permission='view')
- result = deriver(view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
- self.assertEqual(result.__call_permissive__.__wraps__, view)
- request = self._makeRequest()
- request.view_name = 'view_name'
- request.url = 'url'
- self.assertEqual(result(None, request), response)
- self.assertEqual(len(logger.messages), 1)
- self.assertEqual(logger.messages[0],
- "debug_authorization of url url (view name "
- "'view_name' against context None): True")
-
- def test_debug_auth_permission_authpol_permitted_no_request(self):
- response = DummyResponse()
- view = lambda *arg: response
- self.config.registry.settings = dict(
- debug_authorization=True, reload_templates=True)
- logger = self._registerLogger()
- self._registerSecurityPolicy(True)
- deriver = self._makeOne(permission='view')
- result = deriver(view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
- self.assertEqual(result.__call_permissive__.__wraps__, view)
- self.assertEqual(result(None, None), response)
- self.assertEqual(len(logger.messages), 1)
- self.assertEqual(logger.messages[0],
- "debug_authorization of url None (view name "
- "None against context None): True")
-
- def test_debug_auth_permission_authpol_denied(self):
- from pyramid.httpexceptions import HTTPForbidden
- response = DummyResponse()
- view = lambda *arg: response
- self.config.registry.settings = dict(
- debug_authorization=True, reload_templates=True)
- logger = self._registerLogger()
- self._registerSecurityPolicy(False)
- deriver = self._makeOne(permission='view')
- result = deriver(view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
- self.assertEqual(result.__call_permissive__.__wraps__, view)
- request = self._makeRequest()
- request.view_name = 'view_name'
- request.url = 'url'
- self.assertRaises(HTTPForbidden, result, None, request)
- self.assertEqual(len(logger.messages), 1)
- self.assertEqual(logger.messages[0],
- "debug_authorization of url url (view name "
- "'view_name' against context None): False")
-
- def test_debug_auth_permission_authpol_denied2(self):
- view = lambda *arg: 'OK'
- self.config.registry.settings = dict(
- debug_authorization=True, reload_templates=True)
- self._registerLogger()
- self._registerSecurityPolicy(False)
- deriver = self._makeOne(permission='view')
- result = deriver(view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
- request = self._makeRequest()
- request.view_name = 'view_name'
- request.url = 'url'
- permitted = result.__permitted__(None, None)
- self.assertEqual(permitted, False)
-
- def test_debug_auth_permission_authpol_overridden(self):
- from pyramid.security import NO_PERMISSION_REQUIRED
- response = DummyResponse()
- view = lambda *arg: response
- self.config.registry.settings = dict(
- debug_authorization=True, reload_templates=True)
- logger = self._registerLogger()
- self._registerSecurityPolicy(False)
- deriver = self._makeOne(permission=NO_PERMISSION_REQUIRED)
- result = deriver(view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
- self.assertFalse(hasattr(result, '__call_permissive__'))
- request = self._makeRequest()
- request.view_name = 'view_name'
- request.url = 'url'
- self.assertEqual(result(None, request), response)
- self.assertEqual(len(logger.messages), 1)
- self.assertEqual(logger.messages[0],
- "debug_authorization of url url (view name "
- "'view_name' against context None): False")
-
- def test_secured_view_authn_policy_no_authz_policy(self):
- response = DummyResponse()
- view = lambda *arg: response
- self.config.registry.settings = {}
- from pyramid.interfaces import IAuthenticationPolicy
- policy = DummySecurityPolicy(False)
- self.config.registry.registerUtility(policy, IAuthenticationPolicy)
- deriver = self._makeOne(permission='view')
- result = deriver(view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
- self.assertFalse(hasattr(result, '__call_permissive__'))
- request = self._makeRequest()
- request.view_name = 'view_name'
- request.url = 'url'
- self.assertEqual(result(None, request), response)
-
- def test_secured_view_authz_policy_no_authn_policy(self):
- response = DummyResponse()
- view = lambda *arg: response
- self.config.registry.settings = {}
- from pyramid.interfaces import IAuthorizationPolicy
- policy = DummySecurityPolicy(False)
- self.config.registry.registerUtility(policy, IAuthorizationPolicy)
- deriver = self._makeOne(permission='view')
- result = deriver(view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
- self.assertFalse(hasattr(result, '__call_permissive__'))
- request = self._makeRequest()
- request.view_name = 'view_name'
- request.url = 'url'
- self.assertEqual(result(None, request), response)
-
- def test_with_predicates_all(self):
- response = DummyResponse()
- view = lambda *arg: response
- predicates = []
- def predicate1(context, request):
- predicates.append(True)
- return True
- def predicate2(context, request):
- predicates.append(True)
- return True
- deriver = self._makeOne(predicates=[predicate1, predicate2])
- result = deriver(view)
- request = self._makeRequest()
- request.method = 'POST'
- next = result(None, None)
- self.assertEqual(next, response)
- self.assertEqual(predicates, [True, True])
-
- def test_with_predicates_checker(self):
- view = lambda *arg: 'OK'
- predicates = []
- def predicate1(context, request):
- predicates.append(True)
- return True
- def predicate2(context, request):
- predicates.append(True)
- return True
- deriver = self._makeOne(predicates=[predicate1, predicate2])
- result = deriver(view)
- request = self._makeRequest()
- request.method = 'POST'
- next = result.__predicated__(None, None)
- self.assertEqual(next, True)
- self.assertEqual(predicates, [True, True])
-
- def test_with_predicates_notall(self):
- from pyramid.httpexceptions import HTTPNotFound
- view = lambda *arg: 'OK'
- predicates = []
- def predicate1(context, request):
- predicates.append(True)
- return True
- def predicate2(context, request):
- predicates.append(True)
- return False
- deriver = self._makeOne(predicates=[predicate1, predicate2])
- result = deriver(view)
- request = self._makeRequest()
- request.method = 'POST'
- self.assertRaises(HTTPNotFound, result, None, None)
- self.assertEqual(predicates, [True, True])
-
- def test_with_wrapper_viewname(self):
- from pyramid.response import Response
- from pyramid.interfaces import IView
- from pyramid.interfaces import IViewClassifier
- inner_response = Response('OK')
- def inner_view(context, request):
- return inner_response
- def outer_view(context, request):
- self.assertEqual(request.wrapped_response, inner_response)
- self.assertEqual(request.wrapped_body, inner_response.body)
- self.assertEqual(request.wrapped_view.__original_view__,
- inner_view)
- return Response('outer ' + request.wrapped_body)
- self.config.registry.registerAdapter(
- outer_view, (IViewClassifier, None, None), IView, 'owrap')
- deriver = self._makeOne(viewname='inner',
- wrapper_viewname='owrap')
- result = deriver(inner_view)
- self.assertFalse(result is inner_view)
- self.assertEqual(inner_view.__module__, result.__module__)
- self.assertEqual(inner_view.__doc__, result.__doc__)
- request = self._makeRequest()
- response = result(None, request)
- self.assertEqual(response.body, 'outer OK')
-
- def test_with_wrapper_viewname_notfound(self):
- from pyramid.response import Response
- inner_response = Response('OK')
- def inner_view(context, request):
- return inner_response
- deriver = self._makeOne(viewname='inner', wrapper_viewname='owrap')
- wrapped = deriver(inner_view)
- request = self._makeRequest()
- self.assertRaises(ValueError, wrapped, None, request)
-
- def test_as_newstyle_class_context_and_request_attr_and_renderer(self):
- response = DummyResponse()
- class renderer(object):
- def render_view(inself, req, resp, view_inst, ctx):
- self.assertEqual(req, request)
- self.assertEqual(resp, {'a':'1'})
- self.assertEqual(view_inst.__class__, View)
- self.assertEqual(ctx, context)
- return response
- class View(object):
- def __init__(self, context, request):
- pass
- def index(self):
- return {'a':'1'}
- deriver = self._makeOne(renderer=renderer(), attr='index')
- result = deriver(View)
- self.assertFalse(result is View)
- self.assertEqual(result.__module__, View.__module__)
- self.assertEqual(result.__doc__, View.__doc__)
- self.assertEqual(result.__name__, View.__name__)
- request = self._makeRequest()
- context = testing.DummyResource()
- self.assertEqual(result(context, request), response)
-
- def test_as_newstyle_class_requestonly_attr_and_renderer(self):
- response = DummyResponse()
- class renderer(object):
- def render_view(inself, req, resp, view_inst, ctx):
- self.assertEqual(req, request)
- self.assertEqual(resp, {'a':'1'})
- self.assertEqual(view_inst.__class__, View)
- self.assertEqual(ctx, context)
- return response
- class View(object):
- def __init__(self, request):
- pass
- def index(self):
- return {'a':'1'}
- deriver = self._makeOne(renderer=renderer(), attr='index')
- result = deriver(View)
- self.assertFalse(result is View)
- self.assertEqual(result.__module__, View.__module__)
- self.assertEqual(result.__doc__, View.__doc__)
- self.assertEqual(result.__name__, View.__name__)
- request = self._makeRequest()
- context = testing.DummyResource()
- self.assertEqual(result(context, request), response)
-
- def test_as_oldstyle_cls_context_request_attr_and_renderer(self):
- response = DummyResponse()
- class renderer(object):
- def render_view(inself, req, resp, view_inst, ctx):
- self.assertEqual(req, request)
- self.assertEqual(resp, {'a':'1'})
- self.assertEqual(view_inst.__class__, View)
- self.assertEqual(ctx, context)
- return response
- class View:
- def __init__(self, context, request):
- pass
- def index(self):
- return {'a':'1'}
- deriver = self._makeOne(renderer=renderer(), attr='index')
- result = deriver(View)
- self.assertFalse(result is View)
- self.assertEqual(result.__module__, View.__module__)
- self.assertEqual(result.__doc__, View.__doc__)
- self.assertEqual(result.__name__, View.__name__)
- request = self._makeRequest()
- context = testing.DummyResource()
- self.assertEqual(result(context, request), response)
-
- def test_as_oldstyle_cls_requestonly_attr_and_renderer(self):
- response = DummyResponse()
- class renderer(object):
- def render_view(inself, req, resp, view_inst, ctx):
- self.assertEqual(req, request)
- self.assertEqual(resp, {'a':'1'})
- self.assertEqual(view_inst.__class__, View)
- self.assertEqual(ctx, context)
- return response
- class View:
- def __init__(self, request):
- pass
- def index(self):
- return {'a':'1'}
- deriver = self._makeOne(renderer=renderer(), attr='index')
- result = deriver(View)
- self.assertFalse(result is View)
- self.assertEqual(result.__module__, View.__module__)
- self.assertEqual(result.__doc__, View.__doc__)
- self.assertEqual(result.__name__, View.__name__)
- request = self._makeRequest()
- context = testing.DummyResource()
- self.assertEqual(result(context, request), response)
-
- def test_as_instance_context_and_request_attr_and_renderer(self):
- response = DummyResponse()
- class renderer(object):
- def render_view(inself, req, resp, view_inst, ctx):
- self.assertEqual(req, request)
- self.assertEqual(resp, {'a':'1'})
- self.assertEqual(view_inst, view)
- self.assertEqual(ctx, context)
- return response
- class View:
- def index(self, context, request):
- return {'a':'1'}
- deriver = self._makeOne(renderer=renderer(), attr='index')
- view = View()
- result = deriver(view)
- self.assertFalse(result is view)
- self.assertEqual(result.__module__, view.__module__)
- self.assertEqual(result.__doc__, view.__doc__)
- request = self._makeRequest()
- context = testing.DummyResource()
- self.assertEqual(result(context, request), response)
-
- def test_as_instance_requestonly_attr_and_renderer(self):
- response = DummyResponse()
- class renderer(object):
- def render_view(inself, req, resp, view_inst, ctx):
- self.assertEqual(req, request)
- self.assertEqual(resp, {'a':'1'})
- self.assertEqual(view_inst, view)
- self.assertEqual(ctx, context)
- return response
- class View:
- def index(self, request):
- return {'a':'1'}
- deriver = self._makeOne(renderer=renderer(), attr='index')
- view = View()
- result = deriver(view)
- self.assertFalse(result is view)
- self.assertEqual(result.__module__, view.__module__)
- self.assertEqual(result.__doc__, view.__doc__)
- request = self._makeRequest()
- context = testing.DummyResource()
- self.assertEqual(result(context, request), response)
-
- def test_with_view_mapper_config_specified(self):
- response = DummyResponse()
- class mapper(object):
- def __init__(self, **kw):
- self.kw = kw
- def __call__(self, view):
- def wrapped(context, request):
- return response
- return wrapped
- def view(context, request): return 'NOTOK'
- deriver = self._makeOne(mapper=mapper)
- result = deriver(view)
- self.assertFalse(result.__wraps__ is view)
- self.assertEqual(result(None, None), response)
-
- def test_with_view_mapper_view_specified(self):
- from pyramid.response import Response
- response = Response()
- def mapper(**kw):
- def inner(view):
- def superinner(context, request):
- self.assertEqual(request, None)
- return response
- return superinner
- return inner
- def view(context, request): return 'NOTOK'
- view.__view_mapper__ = mapper
- deriver = self._makeOne()
- result = deriver(view)
- self.assertFalse(result.__wraps__ is view)
- self.assertEqual(result(None, None), response)
-
- def test_with_view_mapper_default_mapper_specified(self):
- from pyramid.response import Response
- response = Response()
- def mapper(**kw):
- def inner(view):
- def superinner(context, request):
- self.assertEqual(request, None)
- return response
- return superinner
- return inner
- self.config.set_view_mapper(mapper)
- def view(context, request): return 'NOTOK'
- deriver = self._makeOne()
- result = deriver(view)
- self.assertFalse(result.__wraps__ is view)
- self.assertEqual(result(None, None), response)
-
- def test_attr_wrapped_view_branching_default_phash(self):
- from pyramid.config import DEFAULT_PHASH
- def view(context, request): pass
- deriver = self._makeOne(phash=DEFAULT_PHASH)
- result = deriver(view)
- self.assertEqual(result.__wraps__, view)
-
- def test_attr_wrapped_view_branching_nondefault_phash(self):
- def view(context, request): pass
- deriver = self._makeOne(phash='nondefault')
- result = deriver(view)
- self.assertNotEqual(result, view)
-
- def test_http_cached_view_integer(self):
- import datetime
- from pyramid.response import Response
- response = Response('OK')
- def inner_view(context, request):
- return response
- deriver = self._makeOne(http_cache=3600)
- result = deriver(inner_view)
- self.assertFalse(result is inner_view)
- self.assertEqual(inner_view.__module__, result.__module__)
- self.assertEqual(inner_view.__doc__, result.__doc__)
- request = self._makeRequest()
- when = datetime.datetime.utcnow() + datetime.timedelta(hours=1)
- result = result(None, request)
- self.assertEqual(result, response)
- headers = dict(result.headerlist)
- expires = parse_httpdate(headers['Expires'])
- assert_similar_datetime(expires, when)
- self.assertEqual(headers['Cache-Control'], 'max-age=3600')
-
- def test_http_cached_view_timedelta(self):
- import datetime
- from pyramid.response import Response
- response = Response('OK')
- def inner_view(context, request):
- return response
- deriver = self._makeOne(http_cache=datetime.timedelta(hours=1))
- result = deriver(inner_view)
- self.assertFalse(result is inner_view)
- self.assertEqual(inner_view.__module__, result.__module__)
- self.assertEqual(inner_view.__doc__, result.__doc__)
- request = self._makeRequest()
- when = datetime.datetime.utcnow() + datetime.timedelta(hours=1)
- result = result(None, request)
- self.assertEqual(result, response)
- headers = dict(result.headerlist)
- expires = parse_httpdate(headers['Expires'])
- assert_similar_datetime(expires, when)
- self.assertEqual(headers['Cache-Control'], 'max-age=3600')
-
- def test_http_cached_view_tuple(self):
- import datetime
- from pyramid.response import Response
- response = Response('OK')
- def inner_view(context, request):
- return response
- deriver = self._makeOne(http_cache=(3600, {'public':True}))
- result = deriver(inner_view)
- self.assertFalse(result is inner_view)
- self.assertEqual(inner_view.__module__, result.__module__)
- self.assertEqual(inner_view.__doc__, result.__doc__)
- request = self._makeRequest()
- when = datetime.datetime.utcnow() + datetime.timedelta(hours=1)
- result = result(None, request)
- self.assertEqual(result, response)
- headers = dict(result.headerlist)
- expires = parse_httpdate(headers['Expires'])
- assert_similar_datetime(expires, when)
- self.assertEqual(headers['Cache-Control'], 'max-age=3600, public')
-
- def test_http_cached_view_tuple_seconds_None(self):
- from pyramid.response import Response
- response = Response('OK')
- def inner_view(context, request):
- return response
- deriver = self._makeOne(http_cache=(None, {'public':True}))
- result = deriver(inner_view)
- self.assertFalse(result is inner_view)
- self.assertEqual(inner_view.__module__, result.__module__)
- self.assertEqual(inner_view.__doc__, result.__doc__)
- request = self._makeRequest()
- result = result(None, request)
- self.assertEqual(result, response)
- headers = dict(result.headerlist)
- self.assertFalse('Expires' in headers)
- self.assertEqual(headers['Cache-Control'], 'public')
-
- def test_http_cached_view_prevent_auto_set(self):
- from pyramid.response import Response
- response = Response()
- response.cache_control.prevent_auto = True
- def inner_view(context, request):
- return response
- deriver = self._makeOne(http_cache=3600)
- result = deriver(inner_view)
- request = self._makeRequest()
- result = result(None, request)
- self.assertEqual(result, response) # doesn't blow up
- headers = dict(result.headerlist)
- self.assertFalse('Expires' in headers)
- self.assertFalse('Cache-Control' in headers)
-
- def test_http_cached_prevent_http_cache_in_settings(self):
- self.config.registry.settings['prevent_http_cache'] = True
- from pyramid.response import Response
- response = Response()
- def inner_view(context, request):
- return response
- deriver = self._makeOne(http_cache=3600)
- result = deriver(inner_view)
- request = self._makeRequest()
- result = result(None, request)
- self.assertEqual(result, response)
- headers = dict(result.headerlist)
- self.assertFalse('Expires' in headers)
- self.assertFalse('Cache-Control' in headers)
-
- def test_http_cached_view_bad_tuple(self):
- from pyramid.exceptions import ConfigurationError
- deriver = self._makeOne(http_cache=(None,))
- def view(request): pass
- self.assertRaises(ConfigurationError, deriver, view)
-
-class TestDefaultViewMapper(unittest.TestCase):
- def setUp(self):
- self.config = testing.setUp()
- self.registry = self.config.registry
-
- def tearDown(self):
- del self.registry
- testing.tearDown()
-
- def _makeOne(self, **kw):
- from pyramid.config import DefaultViewMapper
- kw['registry'] = self.registry
- return DefaultViewMapper(**kw)
-
- def _makeRequest(self):
- request = DummyRequest()
- request.registry = self.registry
- return request
-
- def test_view_as_function_context_and_request(self):
- def view(context, request):
- return 'OK'
- mapper = self._makeOne()
- result = mapper(view)
- self.assertTrue(result is view)
- request = self._makeRequest()
- self.assertEqual(result(None, request), 'OK')
-
- def test__view_as_function_with_attr(self):
- def view(context, request):
- """ """
- mapper = self._makeOne(attr='__name__')
- result = mapper(view)
- self.assertFalse(result is view)
- request = self._makeRequest()
- self.assertRaises(TypeError, result, None, request)
-
- def test_view_as_function_requestonly(self):
- def view(request):
- return 'OK'
- mapper = self._makeOne()
- result = mapper(view)
- self.assertFalse(result is view)
- request = self._makeRequest()
- self.assertEqual(result(None, request), 'OK')
-
- def test_view_as_function_requestonly_with_attr(self):
- def view(request):
- """ """
- mapper = self._makeOne(attr='__name__')
- result = mapper(view)
- self.assertFalse(result is view)
- request = self._makeRequest()
- self.assertRaises(TypeError, result, None, request)
-
- def test_view_as_newstyle_class_context_and_request(self):
- class view(object):
- def __init__(self, context, request):
- pass
- def __call__(self):
- return 'OK'
- mapper = self._makeOne()
- result = mapper(view)
- self.assertFalse(result is view)
- request = self._makeRequest()
- self.assertEqual(result(None, request), 'OK')
-
- def test_view_as_newstyle_class_context_and_request_with_attr(self):
- class view(object):
- def __init__(self, context, request):
- pass
- def index(self):
- return 'OK'
- mapper = self._makeOne(attr='index')
- result = mapper(view)
- self.assertFalse(result is view)
- request = self._makeRequest()
- self.assertEqual(result(None, request), 'OK')
-
- def test_view_as_newstyle_class_requestonly(self):
- class view(object):
- def __init__(self, request):
- pass
- def __call__(self):
- return 'OK'
- mapper = self._makeOne()
- result = mapper(view)
- self.assertFalse(result is view)
- request = self._makeRequest()
- self.assertEqual(result(None, request), 'OK')
-
- def test_view_as_newstyle_class_requestonly_with_attr(self):
- class view(object):
- def __init__(self, request):
- pass
- def index(self):
- return 'OK'
- mapper = self._makeOne(attr='index')
- result = mapper(view)
- self.assertFalse(result is view)
- request = self._makeRequest()
- self.assertEqual(result(None, request), 'OK')
-
- def test_view_as_oldstyle_class_context_and_request(self):
- class view:
- def __init__(self, context, request):
- pass
- def __call__(self):
- return 'OK'
- mapper = self._makeOne()
- result = mapper(view)
- self.assertFalse(result is view)
- request = self._makeRequest()
- self.assertEqual(result(None, request), 'OK')
-
- def test_view_as_oldstyle_class_context_and_request_with_attr(self):
- class view:
- def __init__(self, context, request):
- pass
- def index(self):
- return 'OK'
- mapper = self._makeOne(attr='index')
- result = mapper(view)
- self.assertFalse(result is view)
- request = self._makeRequest()
- self.assertEqual(result(None, request), 'OK')
-
- def test_view_as_oldstyle_class_requestonly(self):
- class view:
- def __init__(self, request):
- pass
- def __call__(self):
- return 'OK'
- mapper = self._makeOne()
- result = mapper(view)
- self.assertFalse(result is view)
- request = self._makeRequest()
- self.assertEqual(result(None, request), 'OK')
-
- def test_view_as_oldstyle_class_requestonly_with_attr(self):
- class view:
- def __init__(self, request):
- pass
- def index(self):
- return 'OK'
- mapper = self._makeOne(attr='index')
- result = mapper(view)
- self.assertFalse(result is view)
- request = self._makeRequest()
- self.assertEqual(result(None, request), 'OK')
-
- def test_view_as_instance_context_and_request(self):
- class View:
- def __call__(self, context, request):
- return 'OK'
- view = View()
- mapper = self._makeOne()
- result = mapper(view)
- self.assertTrue(result is view)
- request = self._makeRequest()
- self.assertEqual(result(None, request), 'OK')
-
- def test_view_as_instance_context_and_request_and_attr(self):
- class View:
- def index(self, context, request):
- return 'OK'
- view = View()
- mapper = self._makeOne(attr='index')
- result = mapper(view)
- self.assertFalse(result is view)
- request = self._makeRequest()
- self.assertEqual(result(None, request), 'OK')
-
- def test_view_as_instance_requestonly(self):
- class View:
- def __call__(self, request):
- return 'OK'
- view = View()
- mapper = self._makeOne()
- result = mapper(view)
- self.assertFalse(result is view)
- request = self._makeRequest()
- self.assertEqual(result(None, request), 'OK')
-
- def test_view_as_instance_requestonly_with_attr(self):
- class View:
- def index(self, request):
- return 'OK'
- view = View()
- mapper = self._makeOne(attr='index')
- result = mapper(view)
- self.assertFalse(result is view)
- request = self._makeRequest()
- self.assertEqual(result(None, request), 'OK')
-
-class Test_preserve_view_attrs(unittest.TestCase):
- def _callFUT(self, view, wrapped_view):
- from pyramid.config import preserve_view_attrs
- return preserve_view_attrs(view, wrapped_view)
-
- def test_it_same(self):
- def view(context, request):
- """ """
- result = self._callFUT(view, view)
- self.assertTrue(result is view)
-
- def test_it_different_with_existing_original_view(self):
- def view1(context, request): pass
- view1.__original_view__ = 'abc'
- def view2(context, request): pass
- result = self._callFUT(view1, view2)
- self.assertEqual(result.__original_view__, 'abc')
- self.assertFalse(result is view1)
-
- def test_it_different(self):
- class DummyView1:
- """ 1 """
- __name__ = '1'
- __module__ = '1'
- def __call__(self, context, request):
- """ """
- def __call_permissive__(self, context, request):
- """ """
- def __predicated__(self, context, request):
- """ """
- def __permitted__(self, context, request):
- """ """
- class DummyView2:
- """ 2 """
- __name__ = '2'
- __module__ = '2'
- def __call__(self, context, request):
- """ """
- def __call_permissive__(self, context, request):
- """ """
- def __predicated__(self, context, request):
- """ """
- def __permitted__(self, context, request):
- """ """
- view1 = DummyView1()
- view2 = DummyView2()
- result = self._callFUT(view2, view1)
- self.assertEqual(result, view1)
- self.assertTrue(view1.__original_view__ is view2)
- self.assertTrue(view1.__doc__ is view2.__doc__)
- self.assertTrue(view1.__module__ is view2.__module__)
- self.assertTrue(view1.__name__ is view2.__name__)
- self.assertTrue(view1.__call_permissive__.im_func is
- view2.__call_permissive__.im_func)
- self.assertTrue(view1.__permitted__.im_func is
- view2.__permitted__.im_func)
- self.assertTrue(view1.__predicated__.im_func is
- view2.__predicated__.im_func)
-
-class Test__make_predicates(unittest.TestCase):
- def _callFUT(self, **kw):
- from pyramid.config import _make_predicates
- return _make_predicates(**kw)
-
- def test_ordering_xhr_and_request_method_trump_only_containment(self):
- order1, _, _ = self._callFUT(xhr=True, request_method='GET')
- order2, _, _ = self._callFUT(containment=True)
- self.assertTrue(order1 < order2)
-
- def test_ordering_number_of_predicates(self):
- order1, _, _ = self._callFUT(
- xhr='xhr',
- request_method='request_method',
- path_info='path_info',
- request_param='param',
- header='header',
- accept='accept',
- containment='containment',
- request_type='request_type',
- custom=(DummyCustomPredicate(),),
- )
- order2, _, _ = self._callFUT(
- xhr='xhr',
- request_method='request_method',
- path_info='path_info',
- request_param='param',
- header='header',
- accept='accept',
- containment='containment',
- request_type='request_type',
- custom=(DummyCustomPredicate(),),
- )
- order3, _, _ = self._callFUT(
- xhr='xhr',
- request_method='request_method',
- path_info='path_info',
- request_param='param',
- header='header',
- accept='accept',
- containment='containment',
- request_type='request_type',
- )
- order4, _, _ = self._callFUT(
- xhr='xhr',
- request_method='request_method',
- path_info='path_info',
- request_param='param',
- header='header',
- accept='accept',
- containment='containment',
- )
- order5, _, _ = self._callFUT(
- xhr='xhr',
- request_method='request_method',
- path_info='path_info',
- request_param='param',
- header='header',
- accept='accept',
- )
- order6, _, _ = self._callFUT(
- xhr='xhr',
- request_method='request_method',
- path_info='path_info',
- request_param='param',
- header='header',
- )
- order7, _, _ = self._callFUT(
- xhr='xhr',
- request_method='request_method',
- path_info='path_info',
- request_param='param',
- )
- order8, _, _ = self._callFUT(
- xhr='xhr',
- request_method='request_method',
- path_info='path_info',
- )
- order9, _, _ = self._callFUT(
- xhr='xhr',
- request_method='request_method',
- )
- order10, _, _ = self._callFUT(
- xhr='xhr',
- )
- order11, _, _ = self._callFUT(
- )
- self.assertEqual(order1, order2)
- self.assertTrue(order3 > order2)
- self.assertTrue(order4 > order3)
- self.assertTrue(order5 > order4)
- self.assertTrue(order6 > order5)
- self.assertTrue(order7 > order6)
- self.assertTrue(order8 > order7)
- self.assertTrue(order9 > order8)
- self.assertTrue(order10 > order9)
- self.assertTrue(order11 > order10)
-
- def test_ordering_importance_of_predicates(self):
- order1, _, _ = self._callFUT(
- xhr='xhr',
- )
- order2, _, _ = self._callFUT(
- request_method='request_method',
- )
- order3, _, _ = self._callFUT(
- path_info='path_info',
- )
- order4, _, _ = self._callFUT(
- request_param='param',
- )
- order5, _, _ = self._callFUT(
- header='header',
- )
- order6, _, _ = self._callFUT(
- accept='accept',
- )
- order7, _, _ = self._callFUT(
- containment='containment',
- )
- order8, _, _ = self._callFUT(
- request_type='request_type',
- )
- order9, _, _ = self._callFUT(
- custom=(DummyCustomPredicate(),),
- )
- self.assertTrue(order1 > order2)
- self.assertTrue(order2 > order3)
- self.assertTrue(order3 > order4)
- self.assertTrue(order4 > order5)
- self.assertTrue(order5 > order6)
- self.assertTrue(order6 > order7)
- self.assertTrue(order7 > order8)
- self.assertTrue(order8 > order9)
-
- def test_ordering_importance_and_number(self):
- order1, _, _ = self._callFUT(
- xhr='xhr',
- request_method='request_method',
- )
- order2, _, _ = self._callFUT(
- custom=(DummyCustomPredicate(),),
- )
- self.assertTrue(order1 < order2)
-
- order1, _, _ = self._callFUT(
- xhr='xhr',
- request_method='request_method',
- )
- order2, _, _ = self._callFUT(
- request_method='request_method',
- custom=(DummyCustomPredicate(),),
- )
- self.assertTrue(order1 > order2)
-
- order1, _, _ = self._callFUT(
- xhr='xhr',
- request_method='request_method',
- path_info='path_info',
- )
- order2, _, _ = self._callFUT(
- request_method='request_method',
- custom=(DummyCustomPredicate(),),
- )
- self.assertTrue(order1 < order2)
-
- order1, _, _ = self._callFUT(
- xhr='xhr',
- request_method='request_method',
- path_info='path_info',
- )
- order2, _, _ = self._callFUT(
- xhr='xhr',
- request_method='request_method',
- custom=(DummyCustomPredicate(),),
- )
- self.assertTrue(order1 > order2)
-
- def test_different_custom_predicates_with_same_hash(self):
- class PredicateWithHash(object):
- def __hash__(self):
- return 1
- a = PredicateWithHash()
- b = PredicateWithHash()
- _, _, a_phash = self._callFUT(custom=(a,))
- _, _, b_phash = self._callFUT(custom=(b,))
- self.assertEqual(a_phash, b_phash)
-
- def test_traverse_has_remainder_already(self):
- order, predicates, phash = self._callFUT(traverse='/1/:a/:b')
- self.assertEqual(len(predicates), 1)
- pred = predicates[0]
- info = {'traverse':'abc'}
- request = DummyRequest()
- result = pred(info, request)
- self.assertEqual(result, True)
- self.assertEqual(info, {'traverse':'abc'})
-
- def test_traverse_matches(self):
- order, predicates, phash = self._callFUT(traverse='/1/:a/:b')
- self.assertEqual(len(predicates), 1)
- pred = predicates[0]
- info = {'match':{'a':'a', 'b':'b'}}
- request = DummyRequest()
- result = pred(info, request)
- self.assertEqual(result, True)
- self.assertEqual(info, {'match':
- {'a':'a', 'b':'b', 'traverse':('1', 'a', 'b')}})
-
- def test_predicate_text_is_correct(self):
- _, predicates, _ = self._callFUT(
- xhr='xhr',
- request_method='request_method',
- path_info='path_info',
- request_param='param',
- header='header',
- accept='accept',
- containment='containment',
- request_type='request_type',
- custom=(DummyCustomPredicate(),
- DummyCustomPredicate.classmethod_predicate,
- DummyCustomPredicate.classmethod_predicate_no_text))
- self.assertEqual(predicates[0].__text__, 'xhr = True')
- self.assertEqual(predicates[1].__text__,
- 'request method = request_method')
- self.assertEqual(predicates[2].__text__, 'path_info = path_info')
- self.assertEqual(predicates[3].__text__, 'request_param param')
- self.assertEqual(predicates[4].__text__, 'header header')
- self.assertEqual(predicates[5].__text__, 'accept = accept')
- self.assertEqual(predicates[6].__text__, 'containment = containment')
- self.assertEqual(predicates[7].__text__, 'request_type = request_type')
- self.assertEqual(predicates[8].__text__, 'custom predicate')
- self.assertEqual(predicates[9].__text__, 'classmethod predicate')
- self.assertEqual(predicates[10].__text__, '<unknown custom predicate>')
-
-class TestMultiView(unittest.TestCase):
- def _getTargetClass(self):
- from pyramid.config import MultiView
- return MultiView
-
- def _makeOne(self, name='name'):
- return self._getTargetClass()(name)
-
- def test_class_implements_ISecuredView(self):
- from zope.interface.verify import verifyClass
- from pyramid.interfaces import ISecuredView
- verifyClass(ISecuredView, self._getTargetClass())
-
- def test_instance_implements_ISecuredView(self):
- from zope.interface.verify import verifyObject
- from pyramid.interfaces import ISecuredView
- verifyObject(ISecuredView, self._makeOne())
-
- def test_add(self):
- mv = self._makeOne()
- mv.add('view', 100)
- self.assertEqual(mv.views, [(100, 'view', None)])
- mv.add('view2', 99)
- self.assertEqual(mv.views, [(99, 'view2', None), (100, 'view', None)])
- mv.add('view3', 100, 'text/html')
- self.assertEqual(mv.media_views['text/html'], [(100, 'view3', None)])
- mv.add('view4', 99, 'text/html')
- self.assertEqual(mv.media_views['text/html'],
- [(99, 'view4', None), (100, 'view3', None)])
- mv.add('view5', 100, 'text/xml')
- self.assertEqual(mv.media_views['text/xml'], [(100, 'view5', None)])
- self.assertEqual(set(mv.accepts), set(['text/xml', 'text/html']))
- self.assertEqual(mv.views, [(99, 'view2', None), (100, 'view', None)])
- mv.add('view6', 98, 'text/*')
- self.assertEqual(mv.views, [(98, 'view6', None),
- (99, 'view2', None),
- (100, 'view', None)])
-
- def test_add_with_phash(self):
- mv = self._makeOne()
- mv.add('view', 100, phash='abc')
- self.assertEqual(mv.views, [(100, 'view', 'abc')])
- mv.add('view', 100, phash='abc')
- self.assertEqual(mv.views, [(100, 'view', 'abc')])
- mv.add('view', 100, phash='def')
- self.assertEqual(mv.views, [(100, 'view', 'abc'), (100, 'view', 'def')])
- mv.add('view', 100, phash='abc')
- self.assertEqual(mv.views, [(100, 'view', 'abc'), (100, 'view', 'def')])
-
- def test_get_views_request_has_no_accept(self):
- request = DummyRequest()
- mv = self._makeOne()
- mv.views = [(99, lambda *arg: None)]
- self.assertEqual(mv.get_views(request), mv.views)
-
- def test_get_views_no_self_accepts(self):
- request = DummyRequest()
- request.accept = True
- mv = self._makeOne()
- mv.accepts = []
- mv.views = [(99, lambda *arg: None)]
- self.assertEqual(mv.get_views(request), mv.views)
-
- def test_get_views(self):
- request = DummyRequest()
- request.accept = DummyAccept('text/html')
- mv = self._makeOne()
- mv.accepts = ['text/html']
- mv.views = [(99, lambda *arg: None)]
- html_views = [(98, lambda *arg: None)]
- mv.media_views['text/html'] = html_views
- self.assertEqual(mv.get_views(request), html_views + mv.views)
-
- def test_get_views_best_match_returns_None(self):
- request = DummyRequest()
- request.accept = DummyAccept(None)
- mv = self._makeOne()
- mv.accepts = ['text/html']
- mv.views = [(99, lambda *arg: None)]
- self.assertEqual(mv.get_views(request), mv.views)
-
- def test_match_not_found(self):
- from pyramid.httpexceptions import HTTPNotFound
- mv = self._makeOne()
- context = DummyContext()
- request = DummyRequest()
- self.assertRaises(HTTPNotFound, mv.match, context, request)
-
- def test_match_predicate_fails(self):
- from pyramid.httpexceptions import HTTPNotFound
- mv = self._makeOne()
- def view(context, request):
- """ """
- view.__predicated__ = lambda *arg: False
- mv.views = [(100, view, None)]
- context = DummyContext()
- request = DummyRequest()
- self.assertRaises(HTTPNotFound, mv.match, context, request)
-
- def test_match_predicate_succeeds(self):
- mv = self._makeOne()
- def view(context, request):
- """ """
- view.__predicated__ = lambda *arg: True
- mv.views = [(100, view, None)]
- context = DummyContext()
- request = DummyRequest()
- result = mv.match(context, request)
- self.assertEqual(result, view)
-
- def test_permitted_no_views(self):
- from pyramid.httpexceptions import HTTPNotFound
- mv = self._makeOne()
- context = DummyContext()
- request = DummyRequest()
- self.assertRaises(HTTPNotFound, mv.__permitted__, context, request)
-
- def test_permitted_no_match_with__permitted__(self):
- mv = self._makeOne()
- def view(context, request):
- """ """
- mv.views = [(100, view, None)]
- self.assertEqual(mv.__permitted__(None, None), True)
-
- def test_permitted(self):
- mv = self._makeOne()
- def view(context, request):
- """ """
- def permitted(context, request):
- return False
- view.__permitted__ = permitted
- mv.views = [(100, view, None)]
- context = DummyContext()
- request = DummyRequest()
- result = mv.__permitted__(context, request)
- self.assertEqual(result, False)
-
- def test__call__not_found(self):
- from pyramid.httpexceptions import HTTPNotFound
- mv = self._makeOne()
- context = DummyContext()
- request = DummyRequest()
- self.assertRaises(HTTPNotFound, mv, context, request)
-
- def test___call__intermediate_not_found(self):
- from pyramid.exceptions import PredicateMismatch
- mv = self._makeOne()
- context = DummyContext()
- request = DummyRequest()
- request.view_name = ''
- expected_response = DummyResponse()
- def view1(context, request):
- raise PredicateMismatch
- def view2(context, request):
- return expected_response
- mv.views = [(100, view1, None), (99, view2, None)]
- response = mv(context, request)
- self.assertEqual(response, expected_response)
-
- def test___call__raise_not_found_isnt_interpreted_as_pred_mismatch(self):
- from pyramid.httpexceptions import HTTPNotFound
- mv = self._makeOne()
- context = DummyContext()
- request = DummyRequest()
- request.view_name = ''
- def view1(context, request):
- raise HTTPNotFound
- def view2(context, request):
- """ """
- mv.views = [(100, view1, None), (99, view2, None)]
- self.assertRaises(HTTPNotFound, mv, context, request)
-
- def test___call__(self):
- mv = self._makeOne()
- context = DummyContext()
- request = DummyRequest()
- request.view_name = ''
- expected_response = DummyResponse()
- def view(context, request):
- return expected_response
- mv.views = [(100, view, None)]
- response = mv(context, request)
- self.assertEqual(response, expected_response)
-
- def test__call_permissive__not_found(self):
- from pyramid.httpexceptions import HTTPNotFound
- mv = self._makeOne()
- context = DummyContext()
- request = DummyRequest()
- self.assertRaises(HTTPNotFound, mv, context, request)
-
- def test___call_permissive_has_call_permissive(self):
- mv = self._makeOne()
- context = DummyContext()
- request = DummyRequest()
- request.view_name = ''
- expected_response = DummyResponse()
- def view(context, request):
- """ """
- def permissive(context, request):
- return expected_response
- view.__call_permissive__ = permissive
- mv.views = [(100, view, None)]
- response = mv.__call_permissive__(context, request)
- self.assertEqual(response, expected_response)
-
- def test___call_permissive_has_no_call_permissive(self):
- mv = self._makeOne()
- context = DummyContext()
- request = DummyRequest()
- request.view_name = ''
- expected_response = DummyResponse()
- def view(context, request):
- return expected_response
- mv.views = [(100, view, None)]
- response = mv.__call_permissive__(context, request)
- self.assertEqual(response, expected_response)
-
- def test__call__with_accept_match(self):
- mv = self._makeOne()
- context = DummyContext()
- request = DummyRequest()
- request.accept = DummyAccept('text/html', 'text/xml')
- expected_response = DummyResponse()
- def view(context, request):
- return expected_response
- mv.views = [(100, None)]
- mv.media_views['text/xml'] = [(100, view, None)]
- mv.accepts = ['text/xml']
- response = mv(context, request)
- self.assertEqual(response, expected_response)
-
- def test__call__with_accept_miss(self):
- mv = self._makeOne()
- context = DummyContext()
- request = DummyRequest()
- request.accept = DummyAccept('text/plain', 'text/html')
- expected_response = DummyResponse()
- def view(context, request):
- return expected_response
- mv.views = [(100, view, None)]
- mv.media_views['text/xml'] = [(100, None, None)]
- mv.accepts = ['text/xml']
- response = mv(context, request)
- self.assertEqual(response, expected_response)
-
-class Test_requestonly(unittest.TestCase):
- def _callFUT(self, view, attr=None):
- from pyramid.config import requestonly
- return requestonly(view, attr)
-
- def test_requestonly_newstyle_class_no_init(self):
- class foo(object):
- """ """
- self.assertFalse(self._callFUT(foo))
-
- def test_requestonly_newstyle_class_init_toomanyargs(self):
- class foo(object):
- def __init__(self, context, request):
- """ """
- self.assertFalse(self._callFUT(foo))
-
- def test_requestonly_newstyle_class_init_onearg_named_request(self):
- class foo(object):
- def __init__(self, request):
- """ """
- self.assertTrue(self._callFUT(foo))
-
- def test_newstyle_class_init_onearg_named_somethingelse(self):
- class foo(object):
- def __init__(self, req):
- """ """
- self.assertTrue(self._callFUT(foo))
-
- def test_newstyle_class_init_defaultargs_firstname_not_request(self):
- class foo(object):
- def __init__(self, context, request=None):
- """ """
- self.assertFalse(self._callFUT(foo))
-
- def test_newstyle_class_init_defaultargs_firstname_request(self):
- class foo(object):
- def __init__(self, request, foo=1, bar=2):
- """ """
- self.assertTrue(self._callFUT(foo))
-
- def test_newstyle_class_init_firstname_request_with_secondname(self):
- class foo(object):
- def __init__(self, request, two):
- """ """
- self.assertFalse(self._callFUT(foo))
-
- def test_newstyle_class_init_noargs(self):
- class foo(object):
- def __init__():
- """ """
- self.assertFalse(self._callFUT(foo))
-
- def test_oldstyle_class_no_init(self):
- class foo:
- """ """
- self.assertFalse(self._callFUT(foo))
-
- def test_oldstyle_class_init_toomanyargs(self):
- class foo:
- def __init__(self, context, request):
- """ """
- self.assertFalse(self._callFUT(foo))
-
- def test_oldstyle_class_init_onearg_named_request(self):
- class foo:
- def __init__(self, request):
- """ """
- self.assertTrue(self._callFUT(foo))
-
- def test_oldstyle_class_init_onearg_named_somethingelse(self):
- class foo:
- def __init__(self, req):
- """ """
- self.assertTrue(self._callFUT(foo))
-
- def test_oldstyle_class_init_defaultargs_firstname_not_request(self):
- class foo:
- def __init__(self, context, request=None):
- """ """
- self.assertFalse(self._callFUT(foo))
-
- def test_oldstyle_class_init_defaultargs_firstname_request(self):
- class foo:
- def __init__(self, request, foo=1, bar=2):
- """ """
- self.assertTrue(self._callFUT(foo), True)
-
- def test_oldstyle_class_init_noargs(self):
- class foo:
- def __init__():
- """ """
- self.assertFalse(self._callFUT(foo))
-
- def test_function_toomanyargs(self):
- def foo(context, request):
- """ """
- self.assertFalse(self._callFUT(foo))
-
- def test_function_with_attr_false(self):
- def bar(context, request):
- """ """
- def foo(context, request):
- """ """
- foo.bar = bar
- self.assertFalse(self._callFUT(foo, 'bar'))
-
- def test_function_with_attr_true(self):
- def bar(context, request):
- """ """
- def foo(request):
- """ """
- foo.bar = bar
- self.assertTrue(self._callFUT(foo, 'bar'))
-
- def test_function_onearg_named_request(self):
- def foo(request):
- """ """
- self.assertTrue(self._callFUT(foo))
-
- def test_function_onearg_named_somethingelse(self):
- def foo(req):
- """ """
- self.assertTrue(self._callFUT(foo))
-
- def test_function_defaultargs_firstname_not_request(self):
- def foo(context, request=None):
- """ """
- self.assertFalse(self._callFUT(foo))
-
- def test_function_defaultargs_firstname_request(self):
- def foo(request, foo=1, bar=2):
- """ """
- self.assertTrue(self._callFUT(foo))
-
- def test_function_noargs(self):
- def foo():
- """ """
- self.assertFalse(self._callFUT(foo))
-
- def test_instance_toomanyargs(self):
- class Foo:
- def __call__(self, context, request):
- """ """
- foo = Foo()
- self.assertFalse(self._callFUT(foo))
-
- def test_instance_defaultargs_onearg_named_request(self):
- class Foo:
- def __call__(self, request):
- """ """
- foo = Foo()
- self.assertTrue(self._callFUT(foo))
-
- def test_instance_defaultargs_onearg_named_somethingelse(self):
- class Foo:
- def __call__(self, req):
- """ """
- foo = Foo()
- self.assertTrue(self._callFUT(foo))
-
- def test_instance_defaultargs_firstname_not_request(self):
- class Foo:
- def __call__(self, context, request=None):
- """ """
- foo = Foo()
- self.assertFalse(self._callFUT(foo))
-
- def test_instance_defaultargs_firstname_request(self):
- class Foo:
- def __call__(self, request, foo=1, bar=2):
- """ """
- foo = Foo()
- self.assertTrue(self._callFUT(foo), True)
-
- def test_instance_nocall(self):
- class Foo: pass
- foo = Foo()
- self.assertFalse(self._callFUT(foo))
-
-class Test_isexception(unittest.TestCase):
- def _callFUT(self, ob):
- from pyramid.config import isexception
- return isexception(ob)
-
- def test_is_exception_instance(self):
- class E(Exception):
- pass
- e = E()
- self.assertEqual(self._callFUT(e), True)
-
- def test_is_exception_class(self):
- class E(Exception):
- pass
- self.assertEqual(self._callFUT(E), True)
-
- def test_is_IException(self):
- from pyramid.interfaces import IException
- self.assertEqual(self._callFUT(IException), True)
-
- def test_is_IException_subinterface(self):
- from pyramid.interfaces import IException
- class ISubException(IException):
- pass
- self.assertEqual(self._callFUT(ISubException), True)
class DummyRequest:
subpath = ()
@@ -5764,19 +3968,6 @@ class DummySecurityPolicy:
def permits(self, context, principals, permission):
return self.permitted
-class DummyAccept(object):
- def __init__(self, *matches):
- self.matches = list(matches)
-
- def best_match(self, offered):
- if self.matches:
- for match in self.matches:
- if match in offered:
- self.matches.remove(match)
- return match
- def __contains__(self, val):
- return val in self.matches
-
from zope.interface import implements
from pyramid.interfaces import IMultiView
class DummyMultiView:
@@ -5817,17 +4008,6 @@ class DummyStaticURLInfo:
def add(self, name, spec, **kw):
self.added.append((name, spec, kw))
-class DummyCustomPredicate(object):
- def __init__(self):
- self.__text__ = 'custom predicate'
-
- def classmethod_predicate(*args): pass
- classmethod_predicate.__text__ = 'classmethod predicate'
- classmethod_predicate = classmethod(classmethod_predicate)
-
- @classmethod
- def classmethod_predicate_no_text(*args): pass # pragma: no cover
-
def dummy_view(request):
return 'OK'
@@ -5865,6 +4045,27 @@ class DummyRegistry(object):
def queryAdapter(self, *arg, **kw):
return self.adaptation
+from pyramid.interfaces import IResponse
+class DummyResponse(object):
+ implements(IResponse)
+
+def dummy_tween_factory(handler, registry): pass
+
+def dummy_tween_factory2(handler, registry): pass
+
+class DummyAccept(object):
+ def __init__(self, *matches):
+ self.matches = list(matches)
+
+ def best_match(self, offered):
+ if self.matches:
+ for match in self.matches:
+ if match in offered:
+ self.matches.remove(match)
+ return match
+ def __contains__(self, val):
+ return val in self.matches
+
def parse_httpdate(s):
import datetime
# cannot use %Z, must use literal GMT; Jython honors timezone
@@ -5877,11 +4078,3 @@ def assert_similar_datetime(one, two):
two_attr = getattr(two, attr)
if not one_attr == two_attr: # pragma: no cover
raise AssertionError('%r != %r in %s' % (one_attr, two_attr, attr))
-
-from pyramid.interfaces import IResponse
-class DummyResponse(object):
- implements(IResponse)
-
-def dummy_tween_factory(handler, registry): pass
-
-def dummy_tween_factory2(handler, registry): pass
diff --git a/pyramid/config/tests/test_util.py b/pyramid/config/tests/test_util.py
new file mode 100644
index 000000000..b16ddecc3
--- /dev/null
+++ b/pyramid/config/tests/test_util.py
@@ -0,0 +1,262 @@
+import unittest
+
+class Test__make_predicates(unittest.TestCase):
+ def _callFUT(self, **kw):
+ from pyramid.config.util import make_predicates
+ return make_predicates(**kw)
+
+ def test_ordering_xhr_and_request_method_trump_only_containment(self):
+ order1, _, _ = self._callFUT(xhr=True, request_method='GET')
+ order2, _, _ = self._callFUT(containment=True)
+ self.assertTrue(order1 < order2)
+
+ def test_ordering_number_of_predicates(self):
+ order1, _, _ = self._callFUT(
+ xhr='xhr',
+ request_method='request_method',
+ path_info='path_info',
+ request_param='param',
+ header='header',
+ accept='accept',
+ containment='containment',
+ request_type='request_type',
+ custom=(DummyCustomPredicate(),),
+ )
+ order2, _, _ = self._callFUT(
+ xhr='xhr',
+ request_method='request_method',
+ path_info='path_info',
+ request_param='param',
+ header='header',
+ accept='accept',
+ containment='containment',
+ request_type='request_type',
+ custom=(DummyCustomPredicate(),),
+ )
+ order3, _, _ = self._callFUT(
+ xhr='xhr',
+ request_method='request_method',
+ path_info='path_info',
+ request_param='param',
+ header='header',
+ accept='accept',
+ containment='containment',
+ request_type='request_type',
+ )
+ order4, _, _ = self._callFUT(
+ xhr='xhr',
+ request_method='request_method',
+ path_info='path_info',
+ request_param='param',
+ header='header',
+ accept='accept',
+ containment='containment',
+ )
+ order5, _, _ = self._callFUT(
+ xhr='xhr',
+ request_method='request_method',
+ path_info='path_info',
+ request_param='param',
+ header='header',
+ accept='accept',
+ )
+ order6, _, _ = self._callFUT(
+ xhr='xhr',
+ request_method='request_method',
+ path_info='path_info',
+ request_param='param',
+ header='header',
+ )
+ order7, _, _ = self._callFUT(
+ xhr='xhr',
+ request_method='request_method',
+ path_info='path_info',
+ request_param='param',
+ )
+ order8, _, _ = self._callFUT(
+ xhr='xhr',
+ request_method='request_method',
+ path_info='path_info',
+ )
+ order9, _, _ = self._callFUT(
+ xhr='xhr',
+ request_method='request_method',
+ )
+ order10, _, _ = self._callFUT(
+ xhr='xhr',
+ )
+ order11, _, _ = self._callFUT(
+ )
+ self.assertEqual(order1, order2)
+ self.assertTrue(order3 > order2)
+ self.assertTrue(order4 > order3)
+ self.assertTrue(order5 > order4)
+ self.assertTrue(order6 > order5)
+ self.assertTrue(order7 > order6)
+ self.assertTrue(order8 > order7)
+ self.assertTrue(order9 > order8)
+ self.assertTrue(order10 > order9)
+ self.assertTrue(order11 > order10)
+
+ def test_ordering_importance_of_predicates(self):
+ order1, _, _ = self._callFUT(
+ xhr='xhr',
+ )
+ order2, _, _ = self._callFUT(
+ request_method='request_method',
+ )
+ order3, _, _ = self._callFUT(
+ path_info='path_info',
+ )
+ order4, _, _ = self._callFUT(
+ request_param='param',
+ )
+ order5, _, _ = self._callFUT(
+ header='header',
+ )
+ order6, _, _ = self._callFUT(
+ accept='accept',
+ )
+ order7, _, _ = self._callFUT(
+ containment='containment',
+ )
+ order8, _, _ = self._callFUT(
+ request_type='request_type',
+ )
+ order9, _, _ = self._callFUT(
+ custom=(DummyCustomPredicate(),),
+ )
+ self.assertTrue(order1 > order2)
+ self.assertTrue(order2 > order3)
+ self.assertTrue(order3 > order4)
+ self.assertTrue(order4 > order5)
+ self.assertTrue(order5 > order6)
+ self.assertTrue(order6 > order7)
+ self.assertTrue(order7 > order8)
+ self.assertTrue(order8 > order9)
+
+ def test_ordering_importance_and_number(self):
+ order1, _, _ = self._callFUT(
+ xhr='xhr',
+ request_method='request_method',
+ )
+ order2, _, _ = self._callFUT(
+ custom=(DummyCustomPredicate(),),
+ )
+ self.assertTrue(order1 < order2)
+
+ order1, _, _ = self._callFUT(
+ xhr='xhr',
+ request_method='request_method',
+ )
+ order2, _, _ = self._callFUT(
+ request_method='request_method',
+ custom=(DummyCustomPredicate(),),
+ )
+ self.assertTrue(order1 > order2)
+
+ order1, _, _ = self._callFUT(
+ xhr='xhr',
+ request_method='request_method',
+ path_info='path_info',
+ )
+ order2, _, _ = self._callFUT(
+ request_method='request_method',
+ custom=(DummyCustomPredicate(),),
+ )
+ self.assertTrue(order1 < order2)
+
+ order1, _, _ = self._callFUT(
+ xhr='xhr',
+ request_method='request_method',
+ path_info='path_info',
+ )
+ order2, _, _ = self._callFUT(
+ xhr='xhr',
+ request_method='request_method',
+ custom=(DummyCustomPredicate(),),
+ )
+ self.assertTrue(order1 > order2)
+
+ def test_different_custom_predicates_with_same_hash(self):
+ class PredicateWithHash(object):
+ def __hash__(self):
+ return 1
+ a = PredicateWithHash()
+ b = PredicateWithHash()
+ _, _, a_phash = self._callFUT(custom=(a,))
+ _, _, b_phash = self._callFUT(custom=(b,))
+ self.assertEqual(a_phash, b_phash)
+
+ def test_traverse_has_remainder_already(self):
+ order, predicates, phash = self._callFUT(traverse='/1/:a/:b')
+ self.assertEqual(len(predicates), 1)
+ pred = predicates[0]
+ info = {'traverse':'abc'}
+ request = DummyRequest()
+ result = pred(info, request)
+ self.assertEqual(result, True)
+ self.assertEqual(info, {'traverse':'abc'})
+
+ def test_traverse_matches(self):
+ order, predicates, phash = self._callFUT(traverse='/1/:a/:b')
+ self.assertEqual(len(predicates), 1)
+ pred = predicates[0]
+ info = {'match':{'a':'a', 'b':'b'}}
+ request = DummyRequest()
+ result = pred(info, request)
+ self.assertEqual(result, True)
+ self.assertEqual(info, {'match':
+ {'a':'a', 'b':'b', 'traverse':('1', 'a', 'b')}})
+
+ def test_predicate_text_is_correct(self):
+ _, predicates, _ = self._callFUT(
+ xhr='xhr',
+ request_method='request_method',
+ path_info='path_info',
+ request_param='param',
+ header='header',
+ accept='accept',
+ containment='containment',
+ request_type='request_type',
+ custom=(DummyCustomPredicate(),
+ DummyCustomPredicate.classmethod_predicate,
+ DummyCustomPredicate.classmethod_predicate_no_text))
+ self.assertEqual(predicates[0].__text__, 'xhr = True')
+ self.assertEqual(predicates[1].__text__,
+ 'request method = request_method')
+ self.assertEqual(predicates[2].__text__, 'path_info = path_info')
+ self.assertEqual(predicates[3].__text__, 'request_param param')
+ self.assertEqual(predicates[4].__text__, 'header header')
+ self.assertEqual(predicates[5].__text__, 'accept = accept')
+ self.assertEqual(predicates[6].__text__, 'containment = containment')
+ self.assertEqual(predicates[7].__text__, 'request_type = request_type')
+ self.assertEqual(predicates[8].__text__, 'custom predicate')
+ self.assertEqual(predicates[9].__text__, 'classmethod predicate')
+ self.assertEqual(predicates[10].__text__, '<unknown custom predicate>')
+
+class DummyCustomPredicate(object):
+ def __init__(self):
+ self.__text__ = 'custom predicate'
+
+ def classmethod_predicate(*args): pass
+ classmethod_predicate.__text__ = 'classmethod predicate'
+ classmethod_predicate = classmethod(classmethod_predicate)
+
+ @classmethod
+ def classmethod_predicate_no_text(*args): pass # pragma: no cover
+
+class DummyRequest:
+ subpath = ()
+ matchdict = None
+ def __init__(self, environ=None):
+ if environ is None:
+ environ = {}
+ self.environ = environ
+ self.params = {}
+ self.cookies = {}
+ def copy(self):
+ return self
+ def get_response(self, app):
+ return app
+
diff --git a/pyramid/config/tests/test_views.py b/pyramid/config/tests/test_views.py
new file mode 100644
index 000000000..da31428cd
--- /dev/null
+++ b/pyramid/config/tests/test_views.py
@@ -0,0 +1,1654 @@
+import unittest
+from pyramid import testing
+
+class Test_requestonly(unittest.TestCase):
+ def _callFUT(self, view, attr=None):
+ from pyramid.config.views import requestonly
+ return requestonly(view, attr)
+
+ def test_requestonly_newstyle_class_no_init(self):
+ class foo(object):
+ """ """
+ self.assertFalse(self._callFUT(foo))
+
+ def test_requestonly_newstyle_class_init_toomanyargs(self):
+ class foo(object):
+ def __init__(self, context, request):
+ """ """
+ self.assertFalse(self._callFUT(foo))
+
+ def test_requestonly_newstyle_class_init_onearg_named_request(self):
+ class foo(object):
+ def __init__(self, request):
+ """ """
+ self.assertTrue(self._callFUT(foo))
+
+ def test_newstyle_class_init_onearg_named_somethingelse(self):
+ class foo(object):
+ def __init__(self, req):
+ """ """
+ self.assertTrue(self._callFUT(foo))
+
+ def test_newstyle_class_init_defaultargs_firstname_not_request(self):
+ class foo(object):
+ def __init__(self, context, request=None):
+ """ """
+ self.assertFalse(self._callFUT(foo))
+
+ def test_newstyle_class_init_defaultargs_firstname_request(self):
+ class foo(object):
+ def __init__(self, request, foo=1, bar=2):
+ """ """
+ self.assertTrue(self._callFUT(foo))
+
+ def test_newstyle_class_init_firstname_request_with_secondname(self):
+ class foo(object):
+ def __init__(self, request, two):
+ """ """
+ self.assertFalse(self._callFUT(foo))
+
+ def test_newstyle_class_init_noargs(self):
+ class foo(object):
+ def __init__():
+ """ """
+ self.assertFalse(self._callFUT(foo))
+
+ def test_oldstyle_class_no_init(self):
+ class foo:
+ """ """
+ self.assertFalse(self._callFUT(foo))
+
+ def test_oldstyle_class_init_toomanyargs(self):
+ class foo:
+ def __init__(self, context, request):
+ """ """
+ self.assertFalse(self._callFUT(foo))
+
+ def test_oldstyle_class_init_onearg_named_request(self):
+ class foo:
+ def __init__(self, request):
+ """ """
+ self.assertTrue(self._callFUT(foo))
+
+ def test_oldstyle_class_init_onearg_named_somethingelse(self):
+ class foo:
+ def __init__(self, req):
+ """ """
+ self.assertTrue(self._callFUT(foo))
+
+ def test_oldstyle_class_init_defaultargs_firstname_not_request(self):
+ class foo:
+ def __init__(self, context, request=None):
+ """ """
+ self.assertFalse(self._callFUT(foo))
+
+ def test_oldstyle_class_init_defaultargs_firstname_request(self):
+ class foo:
+ def __init__(self, request, foo=1, bar=2):
+ """ """
+ self.assertTrue(self._callFUT(foo), True)
+
+ def test_oldstyle_class_init_noargs(self):
+ class foo:
+ def __init__():
+ """ """
+ self.assertFalse(self._callFUT(foo))
+
+ def test_function_toomanyargs(self):
+ def foo(context, request):
+ """ """
+ self.assertFalse(self._callFUT(foo))
+
+ def test_function_with_attr_false(self):
+ def bar(context, request):
+ """ """
+ def foo(context, request):
+ """ """
+ foo.bar = bar
+ self.assertFalse(self._callFUT(foo, 'bar'))
+
+ def test_function_with_attr_true(self):
+ def bar(context, request):
+ """ """
+ def foo(request):
+ """ """
+ foo.bar = bar
+ self.assertTrue(self._callFUT(foo, 'bar'))
+
+ def test_function_onearg_named_request(self):
+ def foo(request):
+ """ """
+ self.assertTrue(self._callFUT(foo))
+
+ def test_function_onearg_named_somethingelse(self):
+ def foo(req):
+ """ """
+ self.assertTrue(self._callFUT(foo))
+
+ def test_function_defaultargs_firstname_not_request(self):
+ def foo(context, request=None):
+ """ """
+ self.assertFalse(self._callFUT(foo))
+
+ def test_function_defaultargs_firstname_request(self):
+ def foo(request, foo=1, bar=2):
+ """ """
+ self.assertTrue(self._callFUT(foo))
+
+ def test_function_noargs(self):
+ def foo():
+ """ """
+ self.assertFalse(self._callFUT(foo))
+
+ def test_instance_toomanyargs(self):
+ class Foo:
+ def __call__(self, context, request):
+ """ """
+ foo = Foo()
+ self.assertFalse(self._callFUT(foo))
+
+ def test_instance_defaultargs_onearg_named_request(self):
+ class Foo:
+ def __call__(self, request):
+ """ """
+ foo = Foo()
+ self.assertTrue(self._callFUT(foo))
+
+ def test_instance_defaultargs_onearg_named_somethingelse(self):
+ class Foo:
+ def __call__(self, req):
+ """ """
+ foo = Foo()
+ self.assertTrue(self._callFUT(foo))
+
+ def test_instance_defaultargs_firstname_not_request(self):
+ class Foo:
+ def __call__(self, context, request=None):
+ """ """
+ foo = Foo()
+ self.assertFalse(self._callFUT(foo))
+
+ def test_instance_defaultargs_firstname_request(self):
+ class Foo:
+ def __call__(self, request, foo=1, bar=2):
+ """ """
+ foo = Foo()
+ self.assertTrue(self._callFUT(foo), True)
+
+ def test_instance_nocall(self):
+ class Foo: pass
+ foo = Foo()
+ self.assertFalse(self._callFUT(foo))
+
+class Test_isexception(unittest.TestCase):
+ def _callFUT(self, ob):
+ from pyramid.config.views import isexception
+ return isexception(ob)
+
+ def test_is_exception_instance(self):
+ class E(Exception):
+ pass
+ e = E()
+ self.assertEqual(self._callFUT(e), True)
+
+ def test_is_exception_class(self):
+ class E(Exception):
+ pass
+ self.assertEqual(self._callFUT(E), True)
+
+ def test_is_IException(self):
+ from pyramid.interfaces import IException
+ self.assertEqual(self._callFUT(IException), True)
+
+ def test_is_IException_subinterface(self):
+ from pyramid.interfaces import IException
+ class ISubException(IException):
+ pass
+ self.assertEqual(self._callFUT(ISubException), True)
+
+class TestMultiView(unittest.TestCase):
+ def _getTargetClass(self):
+ from pyramid.config.views import MultiView
+ return MultiView
+
+ def _makeOne(self, name='name'):
+ return self._getTargetClass()(name)
+
+ def test_class_implements_ISecuredView(self):
+ from zope.interface.verify import verifyClass
+ from pyramid.interfaces import ISecuredView
+ verifyClass(ISecuredView, self._getTargetClass())
+
+ def test_instance_implements_ISecuredView(self):
+ from zope.interface.verify import verifyObject
+ from pyramid.interfaces import ISecuredView
+ verifyObject(ISecuredView, self._makeOne())
+
+ def test_add(self):
+ mv = self._makeOne()
+ mv.add('view', 100)
+ self.assertEqual(mv.views, [(100, 'view', None)])
+ mv.add('view2', 99)
+ self.assertEqual(mv.views, [(99, 'view2', None), (100, 'view', None)])
+ mv.add('view3', 100, 'text/html')
+ self.assertEqual(mv.media_views['text/html'], [(100, 'view3', None)])
+ mv.add('view4', 99, 'text/html')
+ self.assertEqual(mv.media_views['text/html'],
+ [(99, 'view4', None), (100, 'view3', None)])
+ mv.add('view5', 100, 'text/xml')
+ self.assertEqual(mv.media_views['text/xml'], [(100, 'view5', None)])
+ self.assertEqual(set(mv.accepts), set(['text/xml', 'text/html']))
+ self.assertEqual(mv.views, [(99, 'view2', None), (100, 'view', None)])
+ mv.add('view6', 98, 'text/*')
+ self.assertEqual(mv.views, [(98, 'view6', None),
+ (99, 'view2', None),
+ (100, 'view', None)])
+
+ def test_add_with_phash(self):
+ mv = self._makeOne()
+ mv.add('view', 100, phash='abc')
+ self.assertEqual(mv.views, [(100, 'view', 'abc')])
+ mv.add('view', 100, phash='abc')
+ self.assertEqual(mv.views, [(100, 'view', 'abc')])
+ mv.add('view', 100, phash='def')
+ self.assertEqual(mv.views, [(100, 'view', 'abc'), (100, 'view', 'def')])
+ mv.add('view', 100, phash='abc')
+ self.assertEqual(mv.views, [(100, 'view', 'abc'), (100, 'view', 'def')])
+
+ def test_get_views_request_has_no_accept(self):
+ request = DummyRequest()
+ mv = self._makeOne()
+ mv.views = [(99, lambda *arg: None)]
+ self.assertEqual(mv.get_views(request), mv.views)
+
+ def test_get_views_no_self_accepts(self):
+ request = DummyRequest()
+ request.accept = True
+ mv = self._makeOne()
+ mv.accepts = []
+ mv.views = [(99, lambda *arg: None)]
+ self.assertEqual(mv.get_views(request), mv.views)
+
+ def test_get_views(self):
+ request = DummyRequest()
+ request.accept = DummyAccept('text/html')
+ mv = self._makeOne()
+ mv.accepts = ['text/html']
+ mv.views = [(99, lambda *arg: None)]
+ html_views = [(98, lambda *arg: None)]
+ mv.media_views['text/html'] = html_views
+ self.assertEqual(mv.get_views(request), html_views + mv.views)
+
+ def test_get_views_best_match_returns_None(self):
+ request = DummyRequest()
+ request.accept = DummyAccept(None)
+ mv = self._makeOne()
+ mv.accepts = ['text/html']
+ mv.views = [(99, lambda *arg: None)]
+ self.assertEqual(mv.get_views(request), mv.views)
+
+ def test_match_not_found(self):
+ from pyramid.httpexceptions import HTTPNotFound
+ mv = self._makeOne()
+ context = DummyContext()
+ request = DummyRequest()
+ self.assertRaises(HTTPNotFound, mv.match, context, request)
+
+ def test_match_predicate_fails(self):
+ from pyramid.httpexceptions import HTTPNotFound
+ mv = self._makeOne()
+ def view(context, request):
+ """ """
+ view.__predicated__ = lambda *arg: False
+ mv.views = [(100, view, None)]
+ context = DummyContext()
+ request = DummyRequest()
+ self.assertRaises(HTTPNotFound, mv.match, context, request)
+
+ def test_match_predicate_succeeds(self):
+ mv = self._makeOne()
+ def view(context, request):
+ """ """
+ view.__predicated__ = lambda *arg: True
+ mv.views = [(100, view, None)]
+ context = DummyContext()
+ request = DummyRequest()
+ result = mv.match(context, request)
+ self.assertEqual(result, view)
+
+ def test_permitted_no_views(self):
+ from pyramid.httpexceptions import HTTPNotFound
+ mv = self._makeOne()
+ context = DummyContext()
+ request = DummyRequest()
+ self.assertRaises(HTTPNotFound, mv.__permitted__, context, request)
+
+ def test_permitted_no_match_with__permitted__(self):
+ mv = self._makeOne()
+ def view(context, request):
+ """ """
+ mv.views = [(100, view, None)]
+ self.assertEqual(mv.__permitted__(None, None), True)
+
+ def test_permitted(self):
+ mv = self._makeOne()
+ def view(context, request):
+ """ """
+ def permitted(context, request):
+ return False
+ view.__permitted__ = permitted
+ mv.views = [(100, view, None)]
+ context = DummyContext()
+ request = DummyRequest()
+ result = mv.__permitted__(context, request)
+ self.assertEqual(result, False)
+
+ def test__call__not_found(self):
+ from pyramid.httpexceptions import HTTPNotFound
+ mv = self._makeOne()
+ context = DummyContext()
+ request = DummyRequest()
+ self.assertRaises(HTTPNotFound, mv, context, request)
+
+ def test___call__intermediate_not_found(self):
+ from pyramid.exceptions import PredicateMismatch
+ mv = self._makeOne()
+ context = DummyContext()
+ request = DummyRequest()
+ request.view_name = ''
+ expected_response = DummyResponse()
+ def view1(context, request):
+ raise PredicateMismatch
+ def view2(context, request):
+ return expected_response
+ mv.views = [(100, view1, None), (99, view2, None)]
+ response = mv(context, request)
+ self.assertEqual(response, expected_response)
+
+ def test___call__raise_not_found_isnt_interpreted_as_pred_mismatch(self):
+ from pyramid.httpexceptions import HTTPNotFound
+ mv = self._makeOne()
+ context = DummyContext()
+ request = DummyRequest()
+ request.view_name = ''
+ def view1(context, request):
+ raise HTTPNotFound
+ def view2(context, request):
+ """ """
+ mv.views = [(100, view1, None), (99, view2, None)]
+ self.assertRaises(HTTPNotFound, mv, context, request)
+
+ def test___call__(self):
+ mv = self._makeOne()
+ context = DummyContext()
+ request = DummyRequest()
+ request.view_name = ''
+ expected_response = DummyResponse()
+ def view(context, request):
+ return expected_response
+ mv.views = [(100, view, None)]
+ response = mv(context, request)
+ self.assertEqual(response, expected_response)
+
+ def test__call_permissive__not_found(self):
+ from pyramid.httpexceptions import HTTPNotFound
+ mv = self._makeOne()
+ context = DummyContext()
+ request = DummyRequest()
+ self.assertRaises(HTTPNotFound, mv, context, request)
+
+ def test___call_permissive_has_call_permissive(self):
+ mv = self._makeOne()
+ context = DummyContext()
+ request = DummyRequest()
+ request.view_name = ''
+ expected_response = DummyResponse()
+ def view(context, request):
+ """ """
+ def permissive(context, request):
+ return expected_response
+ view.__call_permissive__ = permissive
+ mv.views = [(100, view, None)]
+ response = mv.__call_permissive__(context, request)
+ self.assertEqual(response, expected_response)
+
+ def test___call_permissive_has_no_call_permissive(self):
+ mv = self._makeOne()
+ context = DummyContext()
+ request = DummyRequest()
+ request.view_name = ''
+ expected_response = DummyResponse()
+ def view(context, request):
+ return expected_response
+ mv.views = [(100, view, None)]
+ response = mv.__call_permissive__(context, request)
+ self.assertEqual(response, expected_response)
+
+ def test__call__with_accept_match(self):
+ mv = self._makeOne()
+ context = DummyContext()
+ request = DummyRequest()
+ request.accept = DummyAccept('text/html', 'text/xml')
+ expected_response = DummyResponse()
+ def view(context, request):
+ return expected_response
+ mv.views = [(100, None)]
+ mv.media_views['text/xml'] = [(100, view, None)]
+ mv.accepts = ['text/xml']
+ response = mv(context, request)
+ self.assertEqual(response, expected_response)
+
+ def test__call__with_accept_miss(self):
+ mv = self._makeOne()
+ context = DummyContext()
+ request = DummyRequest()
+ request.accept = DummyAccept('text/plain', 'text/html')
+ expected_response = DummyResponse()
+ def view(context, request):
+ return expected_response
+ mv.views = [(100, view, None)]
+ mv.media_views['text/xml'] = [(100, None, None)]
+ mv.accepts = ['text/xml']
+ response = mv(context, request)
+ self.assertEqual(response, expected_response)
+
+class TestViewDeriver(unittest.TestCase):
+ def setUp(self):
+ self.config = testing.setUp()
+
+ def tearDown(self):
+ self.config = None
+
+ def _makeOne(self, **kw):
+ kw['registry'] = self.config.registry
+ from pyramid.config.views import ViewDeriver
+ return ViewDeriver(**kw)
+
+ def _makeRequest(self):
+ request = DummyRequest()
+ request.registry = self.config.registry
+ return request
+
+ def _registerLogger(self):
+ from pyramid.interfaces import IDebugLogger
+ logger = DummyLogger()
+ self.config.registry.registerUtility(logger, IDebugLogger)
+ return logger
+
+ def _registerSecurityPolicy(self, permissive):
+ from pyramid.interfaces import IAuthenticationPolicy
+ from pyramid.interfaces import IAuthorizationPolicy
+ policy = DummySecurityPolicy(permissive)
+ self.config.registry.registerUtility(policy, IAuthenticationPolicy)
+ self.config.registry.registerUtility(policy, IAuthorizationPolicy)
+
+ def test_requestonly_function(self):
+ response = DummyResponse()
+ def view(request):
+ return response
+ deriver = self._makeOne()
+ result = deriver(view)
+ self.assertFalse(result is view)
+ self.assertEqual(result(None, None), response)
+
+ def test_requestonly_function_with_renderer(self):
+ response = DummyResponse()
+ class moo(object):
+ def render_view(inself, req, resp, view_inst, ctx):
+ self.assertEqual(req, request)
+ self.assertEqual(resp, 'OK')
+ self.assertEqual(view_inst, view)
+ self.assertEqual(ctx, context)
+ return response
+ def view(request):
+ return 'OK'
+ deriver = self._makeOne(renderer=moo())
+ result = deriver(view)
+ self.assertFalse(result.__wraps__ is view)
+ request = self._makeRequest()
+ context = testing.DummyResource()
+ self.assertEqual(result(context, request), response)
+
+ def test_requestonly_function_with_renderer_request_override(self):
+ def moo(info):
+ def inner(value, system):
+ self.assertEqual(value, 'OK')
+ self.assertEqual(system['request'], request)
+ self.assertEqual(system['context'], context)
+ return 'moo'
+ return inner
+ def view(request):
+ return 'OK'
+ self.config.add_renderer('moo', moo)
+ deriver = self._makeOne(renderer='string')
+ result = deriver(view)
+ self.assertFalse(result is view)
+ request = self._makeRequest()
+ request.override_renderer = 'moo'
+ context = testing.DummyResource()
+ self.assertEqual(result(context, request).body, 'moo')
+
+ def test_requestonly_function_with_renderer_request_has_view(self):
+ response = DummyResponse()
+ class moo(object):
+ def render_view(inself, req, resp, view_inst, ctx):
+ self.assertEqual(req, request)
+ self.assertEqual(resp, 'OK')
+ self.assertEqual(view_inst, 'view')
+ self.assertEqual(ctx, context)
+ return response
+ def view(request):
+ return 'OK'
+ deriver = self._makeOne(renderer=moo())
+ result = deriver(view)
+ self.assertFalse(result.__wraps__ is view)
+ request = self._makeRequest()
+ request.__view__ = 'view'
+ context = testing.DummyResource()
+ r = result(context, request)
+ self.assertEqual(r, response)
+ self.assertFalse(hasattr(request, '__view__'))
+
+ def test_class_without_attr(self):
+ response = DummyResponse()
+ class View(object):
+ def __init__(self, request):
+ pass
+ def __call__(self):
+ return response
+ deriver = self._makeOne()
+ result = deriver(View)
+ request = self._makeRequest()
+ self.assertEqual(result(None, request), response)
+ self.assertEqual(request.__view__.__class__, View)
+
+ def test_class_with_attr(self):
+ response = DummyResponse()
+ class View(object):
+ def __init__(self, request):
+ pass
+ def another(self):
+ return response
+ deriver = self._makeOne(attr='another')
+ result = deriver(View)
+ request = self._makeRequest()
+ self.assertEqual(result(None, request), response)
+ self.assertEqual(request.__view__.__class__, View)
+
+ def test_as_function_context_and_request(self):
+ def view(context, request):
+ return 'OK'
+ deriver = self._makeOne()
+ result = deriver(view)
+ self.assertTrue(result.__wraps__ is view)
+ self.assertFalse(hasattr(result, '__call_permissive__'))
+ self.assertEqual(view(None, None), 'OK')
+
+ def test_as_function_requestonly(self):
+ response = DummyResponse()
+ def view(request):
+ return response
+ deriver = self._makeOne()
+ result = deriver(view)
+ self.assertFalse(result is view)
+ self.assertEqual(view.__module__, result.__module__)
+ self.assertEqual(view.__doc__, result.__doc__)
+ self.assertEqual(view.__name__, result.__name__)
+ self.assertFalse(hasattr(result, '__call_permissive__'))
+ self.assertEqual(result(None, None), response)
+
+ def test_as_newstyle_class_context_and_request(self):
+ response = DummyResponse()
+ class view(object):
+ def __init__(self, context, request):
+ pass
+ def __call__(self):
+ return response
+ deriver = self._makeOne()
+ result = deriver(view)
+ self.assertFalse(result is view)
+ self.assertEqual(view.__module__, result.__module__)
+ self.assertEqual(view.__doc__, result.__doc__)
+ self.assertEqual(view.__name__, result.__name__)
+ self.assertFalse(hasattr(result, '__call_permissive__'))
+ request = self._makeRequest()
+ self.assertEqual(result(None, request), response)
+ self.assertEqual(request.__view__.__class__, view)
+
+ def test_as_newstyle_class_requestonly(self):
+ response = DummyResponse()
+ class view(object):
+ def __init__(self, context, request):
+ pass
+ def __call__(self):
+ return response
+ deriver = self._makeOne()
+ result = deriver(view)
+ self.assertFalse(result is view)
+ self.assertEqual(view.__module__, result.__module__)
+ self.assertEqual(view.__doc__, result.__doc__)
+ self.assertEqual(view.__name__, result.__name__)
+ self.assertFalse(hasattr(result, '__call_permissive__'))
+ request = self._makeRequest()
+ self.assertEqual(result(None, request), response)
+ self.assertEqual(request.__view__.__class__, view)
+
+ def test_as_oldstyle_class_context_and_request(self):
+ response = DummyResponse()
+ class view:
+ def __init__(self, context, request):
+ pass
+ def __call__(self):
+ return response
+ deriver = self._makeOne()
+ result = deriver(view)
+ self.assertFalse(result is view)
+ self.assertEqual(view.__module__, result.__module__)
+ self.assertEqual(view.__doc__, result.__doc__)
+ self.assertEqual(view.__name__, result.__name__)
+ self.assertFalse(hasattr(result, '__call_permissive__'))
+ request = self._makeRequest()
+ self.assertEqual(result(None, request), response)
+ self.assertEqual(request.__view__.__class__, view)
+
+ def test_as_oldstyle_class_requestonly(self):
+ response = DummyResponse()
+ class view:
+ def __init__(self, context, request):
+ pass
+ def __call__(self):
+ return response
+ deriver = self._makeOne()
+ result = deriver(view)
+ self.assertFalse(result is view)
+ self.assertEqual(view.__module__, result.__module__)
+ self.assertEqual(view.__doc__, result.__doc__)
+ self.assertEqual(view.__name__, result.__name__)
+ self.assertFalse(hasattr(result, '__call_permissive__'))
+ request = self._makeRequest()
+ self.assertEqual(result(None, request), response)
+ self.assertEqual(request.__view__.__class__, view)
+
+ def test_as_instance_context_and_request(self):
+ response = DummyResponse()
+ class View:
+ def __call__(self, context, request):
+ return response
+ view = View()
+ deriver = self._makeOne()
+ result = deriver(view)
+ self.assertTrue(result.__wraps__ is view)
+ self.assertFalse(hasattr(result, '__call_permissive__'))
+ self.assertEqual(result(None, None), response)
+
+ def test_as_instance_requestonly(self):
+ response = DummyResponse()
+ class View:
+ def __call__(self, request):
+ return response
+ view = View()
+ deriver = self._makeOne()
+ result = deriver(view)
+ self.assertFalse(result is view)
+ self.assertEqual(view.__module__, result.__module__)
+ self.assertEqual(view.__doc__, result.__doc__)
+ self.assertTrue('instance' in result.__name__)
+ self.assertFalse(hasattr(result, '__call_permissive__'))
+ self.assertEqual(result(None, None), response)
+
+ def test_with_debug_authorization_no_authpol(self):
+ response = DummyResponse()
+ view = lambda *arg: response
+ self.config.registry.settings = dict(
+ debug_authorization=True, reload_templates=True)
+ logger = self._registerLogger()
+ deriver = self._makeOne(permission='view')
+ result = deriver(view)
+ self.assertEqual(view.__module__, result.__module__)
+ self.assertEqual(view.__doc__, result.__doc__)
+ self.assertEqual(view.__name__, result.__name__)
+ self.assertFalse(hasattr(result, '__call_permissive__'))
+ request = self._makeRequest()
+ request.view_name = 'view_name'
+ request.url = 'url'
+ self.assertEqual(result(None, request), response)
+ self.assertEqual(len(logger.messages), 1)
+ self.assertEqual(logger.messages[0],
+ "debug_authorization of url url (view name "
+ "'view_name' against context None): Allowed "
+ "(no authorization policy in use)")
+
+ def test_with_debug_authorization_authn_policy_no_authz_policy(self):
+ response = DummyResponse()
+ view = lambda *arg: response
+ self.config.registry.settings = dict(debug_authorization=True)
+ from pyramid.interfaces import IAuthenticationPolicy
+ policy = DummySecurityPolicy(False)
+ self.config.registry.registerUtility(policy, IAuthenticationPolicy)
+ logger = self._registerLogger()
+ deriver = self._makeOne(permission='view')
+ result = deriver(view)
+ self.assertEqual(view.__module__, result.__module__)
+ self.assertEqual(view.__doc__, result.__doc__)
+ self.assertEqual(view.__name__, result.__name__)
+ self.assertFalse(hasattr(result, '__call_permissive__'))
+ request = self._makeRequest()
+ request.view_name = 'view_name'
+ request.url = 'url'
+ self.assertEqual(result(None, request), response)
+ self.assertEqual(len(logger.messages), 1)
+ self.assertEqual(logger.messages[0],
+ "debug_authorization of url url (view name "
+ "'view_name' against context None): Allowed "
+ "(no authorization policy in use)")
+
+ def test_with_debug_authorization_authz_policy_no_authn_policy(self):
+ response = DummyResponse()
+ view = lambda *arg: response
+ self.config.registry.settings = dict(debug_authorization=True)
+ from pyramid.interfaces import IAuthorizationPolicy
+ policy = DummySecurityPolicy(False)
+ self.config.registry.registerUtility(policy, IAuthorizationPolicy)
+ logger = self._registerLogger()
+ deriver = self._makeOne(permission='view')
+ result = deriver(view)
+ self.assertEqual(view.__module__, result.__module__)
+ self.assertEqual(view.__doc__, result.__doc__)
+ self.assertEqual(view.__name__, result.__name__)
+ self.assertFalse(hasattr(result, '__call_permissive__'))
+ request = self._makeRequest()
+ request.view_name = 'view_name'
+ request.url = 'url'
+ self.assertEqual(result(None, request), response)
+ self.assertEqual(len(logger.messages), 1)
+ self.assertEqual(logger.messages[0],
+ "debug_authorization of url url (view name "
+ "'view_name' against context None): Allowed "
+ "(no authorization policy in use)")
+
+ def test_with_debug_authorization_no_permission(self):
+ response = DummyResponse()
+ view = lambda *arg: response
+ self.config.registry.settings = dict(
+ debug_authorization=True, reload_templates=True)
+ self._registerSecurityPolicy(True)
+ logger = self._registerLogger()
+ deriver = self._makeOne()
+ result = deriver(view)
+ self.assertEqual(view.__module__, result.__module__)
+ self.assertEqual(view.__doc__, result.__doc__)
+ self.assertEqual(view.__name__, result.__name__)
+ self.assertFalse(hasattr(result, '__call_permissive__'))
+ request = self._makeRequest()
+ request.view_name = 'view_name'
+ request.url = 'url'
+ self.assertEqual(result(None, request), response)
+ self.assertEqual(len(logger.messages), 1)
+ self.assertEqual(logger.messages[0],
+ "debug_authorization of url url (view name "
+ "'view_name' against context None): Allowed ("
+ "no permission registered)")
+
+ def test_debug_auth_permission_authpol_permitted(self):
+ response = DummyResponse()
+ view = lambda *arg: response
+ self.config.registry.settings = dict(
+ debug_authorization=True, reload_templates=True)
+ logger = self._registerLogger()
+ self._registerSecurityPolicy(True)
+ deriver = self._makeOne(permission='view')
+ result = deriver(view)
+ self.assertEqual(view.__module__, result.__module__)
+ self.assertEqual(view.__doc__, result.__doc__)
+ self.assertEqual(view.__name__, result.__name__)
+ self.assertEqual(result.__call_permissive__.__wraps__, view)
+ request = self._makeRequest()
+ request.view_name = 'view_name'
+ request.url = 'url'
+ self.assertEqual(result(None, request), response)
+ self.assertEqual(len(logger.messages), 1)
+ self.assertEqual(logger.messages[0],
+ "debug_authorization of url url (view name "
+ "'view_name' against context None): True")
+
+ def test_debug_auth_permission_authpol_permitted_no_request(self):
+ response = DummyResponse()
+ view = lambda *arg: response
+ self.config.registry.settings = dict(
+ debug_authorization=True, reload_templates=True)
+ logger = self._registerLogger()
+ self._registerSecurityPolicy(True)
+ deriver = self._makeOne(permission='view')
+ result = deriver(view)
+ self.assertEqual(view.__module__, result.__module__)
+ self.assertEqual(view.__doc__, result.__doc__)
+ self.assertEqual(view.__name__, result.__name__)
+ self.assertEqual(result.__call_permissive__.__wraps__, view)
+ self.assertEqual(result(None, None), response)
+ self.assertEqual(len(logger.messages), 1)
+ self.assertEqual(logger.messages[0],
+ "debug_authorization of url None (view name "
+ "None against context None): True")
+
+ def test_debug_auth_permission_authpol_denied(self):
+ from pyramid.httpexceptions import HTTPForbidden
+ response = DummyResponse()
+ view = lambda *arg: response
+ self.config.registry.settings = dict(
+ debug_authorization=True, reload_templates=True)
+ logger = self._registerLogger()
+ self._registerSecurityPolicy(False)
+ deriver = self._makeOne(permission='view')
+ result = deriver(view)
+ self.assertEqual(view.__module__, result.__module__)
+ self.assertEqual(view.__doc__, result.__doc__)
+ self.assertEqual(view.__name__, result.__name__)
+ self.assertEqual(result.__call_permissive__.__wraps__, view)
+ request = self._makeRequest()
+ request.view_name = 'view_name'
+ request.url = 'url'
+ self.assertRaises(HTTPForbidden, result, None, request)
+ self.assertEqual(len(logger.messages), 1)
+ self.assertEqual(logger.messages[0],
+ "debug_authorization of url url (view name "
+ "'view_name' against context None): False")
+
+ def test_debug_auth_permission_authpol_denied2(self):
+ view = lambda *arg: 'OK'
+ self.config.registry.settings = dict(
+ debug_authorization=True, reload_templates=True)
+ self._registerLogger()
+ self._registerSecurityPolicy(False)
+ deriver = self._makeOne(permission='view')
+ result = deriver(view)
+ self.assertEqual(view.__module__, result.__module__)
+ self.assertEqual(view.__doc__, result.__doc__)
+ self.assertEqual(view.__name__, result.__name__)
+ request = self._makeRequest()
+ request.view_name = 'view_name'
+ request.url = 'url'
+ permitted = result.__permitted__(None, None)
+ self.assertEqual(permitted, False)
+
+ def test_debug_auth_permission_authpol_overridden(self):
+ from pyramid.security import NO_PERMISSION_REQUIRED
+ response = DummyResponse()
+ view = lambda *arg: response
+ self.config.registry.settings = dict(
+ debug_authorization=True, reload_templates=True)
+ logger = self._registerLogger()
+ self._registerSecurityPolicy(False)
+ deriver = self._makeOne(permission=NO_PERMISSION_REQUIRED)
+ result = deriver(view)
+ self.assertEqual(view.__module__, result.__module__)
+ self.assertEqual(view.__doc__, result.__doc__)
+ self.assertEqual(view.__name__, result.__name__)
+ self.assertFalse(hasattr(result, '__call_permissive__'))
+ request = self._makeRequest()
+ request.view_name = 'view_name'
+ request.url = 'url'
+ self.assertEqual(result(None, request), response)
+ self.assertEqual(len(logger.messages), 1)
+ self.assertEqual(logger.messages[0],
+ "debug_authorization of url url (view name "
+ "'view_name' against context None): False")
+
+ def test_secured_view_authn_policy_no_authz_policy(self):
+ response = DummyResponse()
+ view = lambda *arg: response
+ self.config.registry.settings = {}
+ from pyramid.interfaces import IAuthenticationPolicy
+ policy = DummySecurityPolicy(False)
+ self.config.registry.registerUtility(policy, IAuthenticationPolicy)
+ deriver = self._makeOne(permission='view')
+ result = deriver(view)
+ self.assertEqual(view.__module__, result.__module__)
+ self.assertEqual(view.__doc__, result.__doc__)
+ self.assertEqual(view.__name__, result.__name__)
+ self.assertFalse(hasattr(result, '__call_permissive__'))
+ request = self._makeRequest()
+ request.view_name = 'view_name'
+ request.url = 'url'
+ self.assertEqual(result(None, request), response)
+
+ def test_secured_view_authz_policy_no_authn_policy(self):
+ response = DummyResponse()
+ view = lambda *arg: response
+ self.config.registry.settings = {}
+ from pyramid.interfaces import IAuthorizationPolicy
+ policy = DummySecurityPolicy(False)
+ self.config.registry.registerUtility(policy, IAuthorizationPolicy)
+ deriver = self._makeOne(permission='view')
+ result = deriver(view)
+ self.assertEqual(view.__module__, result.__module__)
+ self.assertEqual(view.__doc__, result.__doc__)
+ self.assertEqual(view.__name__, result.__name__)
+ self.assertFalse(hasattr(result, '__call_permissive__'))
+ request = self._makeRequest()
+ request.view_name = 'view_name'
+ request.url = 'url'
+ self.assertEqual(result(None, request), response)
+
+ def test_with_predicates_all(self):
+ response = DummyResponse()
+ view = lambda *arg: response
+ predicates = []
+ def predicate1(context, request):
+ predicates.append(True)
+ return True
+ def predicate2(context, request):
+ predicates.append(True)
+ return True
+ deriver = self._makeOne(predicates=[predicate1, predicate2])
+ result = deriver(view)
+ request = self._makeRequest()
+ request.method = 'POST'
+ next = result(None, None)
+ self.assertEqual(next, response)
+ self.assertEqual(predicates, [True, True])
+
+ def test_with_predicates_checker(self):
+ view = lambda *arg: 'OK'
+ predicates = []
+ def predicate1(context, request):
+ predicates.append(True)
+ return True
+ def predicate2(context, request):
+ predicates.append(True)
+ return True
+ deriver = self._makeOne(predicates=[predicate1, predicate2])
+ result = deriver(view)
+ request = self._makeRequest()
+ request.method = 'POST'
+ next = result.__predicated__(None, None)
+ self.assertEqual(next, True)
+ self.assertEqual(predicates, [True, True])
+
+ def test_with_predicates_notall(self):
+ from pyramid.httpexceptions import HTTPNotFound
+ view = lambda *arg: 'OK'
+ predicates = []
+ def predicate1(context, request):
+ predicates.append(True)
+ return True
+ def predicate2(context, request):
+ predicates.append(True)
+ return False
+ deriver = self._makeOne(predicates=[predicate1, predicate2])
+ result = deriver(view)
+ request = self._makeRequest()
+ request.method = 'POST'
+ self.assertRaises(HTTPNotFound, result, None, None)
+ self.assertEqual(predicates, [True, True])
+
+ def test_with_wrapper_viewname(self):
+ from pyramid.response import Response
+ from pyramid.interfaces import IView
+ from pyramid.interfaces import IViewClassifier
+ inner_response = Response('OK')
+ def inner_view(context, request):
+ return inner_response
+ def outer_view(context, request):
+ self.assertEqual(request.wrapped_response, inner_response)
+ self.assertEqual(request.wrapped_body, inner_response.body)
+ self.assertEqual(request.wrapped_view.__original_view__,
+ inner_view)
+ return Response('outer ' + request.wrapped_body)
+ self.config.registry.registerAdapter(
+ outer_view, (IViewClassifier, None, None), IView, 'owrap')
+ deriver = self._makeOne(viewname='inner',
+ wrapper_viewname='owrap')
+ result = deriver(inner_view)
+ self.assertFalse(result is inner_view)
+ self.assertEqual(inner_view.__module__, result.__module__)
+ self.assertEqual(inner_view.__doc__, result.__doc__)
+ request = self._makeRequest()
+ response = result(None, request)
+ self.assertEqual(response.body, 'outer OK')
+
+ def test_with_wrapper_viewname_notfound(self):
+ from pyramid.response import Response
+ inner_response = Response('OK')
+ def inner_view(context, request):
+ return inner_response
+ deriver = self._makeOne(viewname='inner', wrapper_viewname='owrap')
+ wrapped = deriver(inner_view)
+ request = self._makeRequest()
+ self.assertRaises(ValueError, wrapped, None, request)
+
+ def test_as_newstyle_class_context_and_request_attr_and_renderer(self):
+ response = DummyResponse()
+ class renderer(object):
+ def render_view(inself, req, resp, view_inst, ctx):
+ self.assertEqual(req, request)
+ self.assertEqual(resp, {'a':'1'})
+ self.assertEqual(view_inst.__class__, View)
+ self.assertEqual(ctx, context)
+ return response
+ class View(object):
+ def __init__(self, context, request):
+ pass
+ def index(self):
+ return {'a':'1'}
+ deriver = self._makeOne(renderer=renderer(), attr='index')
+ result = deriver(View)
+ self.assertFalse(result is View)
+ self.assertEqual(result.__module__, View.__module__)
+ self.assertEqual(result.__doc__, View.__doc__)
+ self.assertEqual(result.__name__, View.__name__)
+ request = self._makeRequest()
+ context = testing.DummyResource()
+ self.assertEqual(result(context, request), response)
+
+ def test_as_newstyle_class_requestonly_attr_and_renderer(self):
+ response = DummyResponse()
+ class renderer(object):
+ def render_view(inself, req, resp, view_inst, ctx):
+ self.assertEqual(req, request)
+ self.assertEqual(resp, {'a':'1'})
+ self.assertEqual(view_inst.__class__, View)
+ self.assertEqual(ctx, context)
+ return response
+ class View(object):
+ def __init__(self, request):
+ pass
+ def index(self):
+ return {'a':'1'}
+ deriver = self._makeOne(renderer=renderer(), attr='index')
+ result = deriver(View)
+ self.assertFalse(result is View)
+ self.assertEqual(result.__module__, View.__module__)
+ self.assertEqual(result.__doc__, View.__doc__)
+ self.assertEqual(result.__name__, View.__name__)
+ request = self._makeRequest()
+ context = testing.DummyResource()
+ self.assertEqual(result(context, request), response)
+
+ def test_as_oldstyle_cls_context_request_attr_and_renderer(self):
+ response = DummyResponse()
+ class renderer(object):
+ def render_view(inself, req, resp, view_inst, ctx):
+ self.assertEqual(req, request)
+ self.assertEqual(resp, {'a':'1'})
+ self.assertEqual(view_inst.__class__, View)
+ self.assertEqual(ctx, context)
+ return response
+ class View:
+ def __init__(self, context, request):
+ pass
+ def index(self):
+ return {'a':'1'}
+ deriver = self._makeOne(renderer=renderer(), attr='index')
+ result = deriver(View)
+ self.assertFalse(result is View)
+ self.assertEqual(result.__module__, View.__module__)
+ self.assertEqual(result.__doc__, View.__doc__)
+ self.assertEqual(result.__name__, View.__name__)
+ request = self._makeRequest()
+ context = testing.DummyResource()
+ self.assertEqual(result(context, request), response)
+
+ def test_as_oldstyle_cls_requestonly_attr_and_renderer(self):
+ response = DummyResponse()
+ class renderer(object):
+ def render_view(inself, req, resp, view_inst, ctx):
+ self.assertEqual(req, request)
+ self.assertEqual(resp, {'a':'1'})
+ self.assertEqual(view_inst.__class__, View)
+ self.assertEqual(ctx, context)
+ return response
+ class View:
+ def __init__(self, request):
+ pass
+ def index(self):
+ return {'a':'1'}
+ deriver = self._makeOne(renderer=renderer(), attr='index')
+ result = deriver(View)
+ self.assertFalse(result is View)
+ self.assertEqual(result.__module__, View.__module__)
+ self.assertEqual(result.__doc__, View.__doc__)
+ self.assertEqual(result.__name__, View.__name__)
+ request = self._makeRequest()
+ context = testing.DummyResource()
+ self.assertEqual(result(context, request), response)
+
+ def test_as_instance_context_and_request_attr_and_renderer(self):
+ response = DummyResponse()
+ class renderer(object):
+ def render_view(inself, req, resp, view_inst, ctx):
+ self.assertEqual(req, request)
+ self.assertEqual(resp, {'a':'1'})
+ self.assertEqual(view_inst, view)
+ self.assertEqual(ctx, context)
+ return response
+ class View:
+ def index(self, context, request):
+ return {'a':'1'}
+ deriver = self._makeOne(renderer=renderer(), attr='index')
+ view = View()
+ result = deriver(view)
+ self.assertFalse(result is view)
+ self.assertEqual(result.__module__, view.__module__)
+ self.assertEqual(result.__doc__, view.__doc__)
+ request = self._makeRequest()
+ context = testing.DummyResource()
+ self.assertEqual(result(context, request), response)
+
+ def test_as_instance_requestonly_attr_and_renderer(self):
+ response = DummyResponse()
+ class renderer(object):
+ def render_view(inself, req, resp, view_inst, ctx):
+ self.assertEqual(req, request)
+ self.assertEqual(resp, {'a':'1'})
+ self.assertEqual(view_inst, view)
+ self.assertEqual(ctx, context)
+ return response
+ class View:
+ def index(self, request):
+ return {'a':'1'}
+ deriver = self._makeOne(renderer=renderer(), attr='index')
+ view = View()
+ result = deriver(view)
+ self.assertFalse(result is view)
+ self.assertEqual(result.__module__, view.__module__)
+ self.assertEqual(result.__doc__, view.__doc__)
+ request = self._makeRequest()
+ context = testing.DummyResource()
+ self.assertEqual(result(context, request), response)
+
+ def test_with_view_mapper_config_specified(self):
+ response = DummyResponse()
+ class mapper(object):
+ def __init__(self, **kw):
+ self.kw = kw
+ def __call__(self, view):
+ def wrapped(context, request):
+ return response
+ return wrapped
+ def view(context, request): return 'NOTOK'
+ deriver = self._makeOne(mapper=mapper)
+ result = deriver(view)
+ self.assertFalse(result.__wraps__ is view)
+ self.assertEqual(result(None, None), response)
+
+ def test_with_view_mapper_view_specified(self):
+ from pyramid.response import Response
+ response = Response()
+ def mapper(**kw):
+ def inner(view):
+ def superinner(context, request):
+ self.assertEqual(request, None)
+ return response
+ return superinner
+ return inner
+ def view(context, request): return 'NOTOK'
+ view.__view_mapper__ = mapper
+ deriver = self._makeOne()
+ result = deriver(view)
+ self.assertFalse(result.__wraps__ is view)
+ self.assertEqual(result(None, None), response)
+
+ def test_with_view_mapper_default_mapper_specified(self):
+ from pyramid.response import Response
+ response = Response()
+ def mapper(**kw):
+ def inner(view):
+ def superinner(context, request):
+ self.assertEqual(request, None)
+ return response
+ return superinner
+ return inner
+ self.config.set_view_mapper(mapper)
+ def view(context, request): return 'NOTOK'
+ deriver = self._makeOne()
+ result = deriver(view)
+ self.assertFalse(result.__wraps__ is view)
+ self.assertEqual(result(None, None), response)
+
+ def test_attr_wrapped_view_branching_default_phash(self):
+ from pyramid.config.util import DEFAULT_PHASH
+ def view(context, request): pass
+ deriver = self._makeOne(phash=DEFAULT_PHASH)
+ result = deriver(view)
+ self.assertEqual(result.__wraps__, view)
+
+ def test_attr_wrapped_view_branching_nondefault_phash(self):
+ def view(context, request): pass
+ deriver = self._makeOne(phash='nondefault')
+ result = deriver(view)
+ self.assertNotEqual(result, view)
+
+ def test_http_cached_view_integer(self):
+ import datetime
+ from pyramid.response import Response
+ response = Response('OK')
+ def inner_view(context, request):
+ return response
+ deriver = self._makeOne(http_cache=3600)
+ result = deriver(inner_view)
+ self.assertFalse(result is inner_view)
+ self.assertEqual(inner_view.__module__, result.__module__)
+ self.assertEqual(inner_view.__doc__, result.__doc__)
+ request = self._makeRequest()
+ when = datetime.datetime.utcnow() + datetime.timedelta(hours=1)
+ result = result(None, request)
+ self.assertEqual(result, response)
+ headers = dict(result.headerlist)
+ expires = parse_httpdate(headers['Expires'])
+ assert_similar_datetime(expires, when)
+ self.assertEqual(headers['Cache-Control'], 'max-age=3600')
+
+ def test_http_cached_view_timedelta(self):
+ import datetime
+ from pyramid.response import Response
+ response = Response('OK')
+ def inner_view(context, request):
+ return response
+ deriver = self._makeOne(http_cache=datetime.timedelta(hours=1))
+ result = deriver(inner_view)
+ self.assertFalse(result is inner_view)
+ self.assertEqual(inner_view.__module__, result.__module__)
+ self.assertEqual(inner_view.__doc__, result.__doc__)
+ request = self._makeRequest()
+ when = datetime.datetime.utcnow() + datetime.timedelta(hours=1)
+ result = result(None, request)
+ self.assertEqual(result, response)
+ headers = dict(result.headerlist)
+ expires = parse_httpdate(headers['Expires'])
+ assert_similar_datetime(expires, when)
+ self.assertEqual(headers['Cache-Control'], 'max-age=3600')
+
+ def test_http_cached_view_tuple(self):
+ import datetime
+ from pyramid.response import Response
+ response = Response('OK')
+ def inner_view(context, request):
+ return response
+ deriver = self._makeOne(http_cache=(3600, {'public':True}))
+ result = deriver(inner_view)
+ self.assertFalse(result is inner_view)
+ self.assertEqual(inner_view.__module__, result.__module__)
+ self.assertEqual(inner_view.__doc__, result.__doc__)
+ request = self._makeRequest()
+ when = datetime.datetime.utcnow() + datetime.timedelta(hours=1)
+ result = result(None, request)
+ self.assertEqual(result, response)
+ headers = dict(result.headerlist)
+ expires = parse_httpdate(headers['Expires'])
+ assert_similar_datetime(expires, when)
+ self.assertEqual(headers['Cache-Control'], 'max-age=3600, public')
+
+ def test_http_cached_view_tuple_seconds_None(self):
+ from pyramid.response import Response
+ response = Response('OK')
+ def inner_view(context, request):
+ return response
+ deriver = self._makeOne(http_cache=(None, {'public':True}))
+ result = deriver(inner_view)
+ self.assertFalse(result is inner_view)
+ self.assertEqual(inner_view.__module__, result.__module__)
+ self.assertEqual(inner_view.__doc__, result.__doc__)
+ request = self._makeRequest()
+ result = result(None, request)
+ self.assertEqual(result, response)
+ headers = dict(result.headerlist)
+ self.assertFalse('Expires' in headers)
+ self.assertEqual(headers['Cache-Control'], 'public')
+
+ def test_http_cached_view_prevent_auto_set(self):
+ from pyramid.response import Response
+ response = Response()
+ response.cache_control.prevent_auto = True
+ def inner_view(context, request):
+ return response
+ deriver = self._makeOne(http_cache=3600)
+ result = deriver(inner_view)
+ request = self._makeRequest()
+ result = result(None, request)
+ self.assertEqual(result, response) # doesn't blow up
+ headers = dict(result.headerlist)
+ self.assertFalse('Expires' in headers)
+ self.assertFalse('Cache-Control' in headers)
+
+ def test_http_cached_prevent_http_cache_in_settings(self):
+ self.config.registry.settings['prevent_http_cache'] = True
+ from pyramid.response import Response
+ response = Response()
+ def inner_view(context, request):
+ return response
+ deriver = self._makeOne(http_cache=3600)
+ result = deriver(inner_view)
+ request = self._makeRequest()
+ result = result(None, request)
+ self.assertEqual(result, response)
+ headers = dict(result.headerlist)
+ self.assertFalse('Expires' in headers)
+ self.assertFalse('Cache-Control' in headers)
+
+ def test_http_cached_view_bad_tuple(self):
+ from pyramid.exceptions import ConfigurationError
+ deriver = self._makeOne(http_cache=(None,))
+ def view(request): pass
+ self.assertRaises(ConfigurationError, deriver, view)
+
+class TestDefaultViewMapper(unittest.TestCase):
+ def setUp(self):
+ self.config = testing.setUp()
+ self.registry = self.config.registry
+
+ def tearDown(self):
+ del self.registry
+ testing.tearDown()
+
+ def _makeOne(self, **kw):
+ from pyramid.config.views import DefaultViewMapper
+ kw['registry'] = self.registry
+ return DefaultViewMapper(**kw)
+
+ def _makeRequest(self):
+ request = DummyRequest()
+ request.registry = self.registry
+ return request
+
+ def test_view_as_function_context_and_request(self):
+ def view(context, request):
+ return 'OK'
+ mapper = self._makeOne()
+ result = mapper(view)
+ self.assertTrue(result is view)
+ request = self._makeRequest()
+ self.assertEqual(result(None, request), 'OK')
+
+ def test__view_as_function_with_attr(self):
+ def view(context, request):
+ """ """
+ mapper = self._makeOne(attr='__name__')
+ result = mapper(view)
+ self.assertFalse(result is view)
+ request = self._makeRequest()
+ self.assertRaises(TypeError, result, None, request)
+
+ def test_view_as_function_requestonly(self):
+ def view(request):
+ return 'OK'
+ mapper = self._makeOne()
+ result = mapper(view)
+ self.assertFalse(result is view)
+ request = self._makeRequest()
+ self.assertEqual(result(None, request), 'OK')
+
+ def test_view_as_function_requestonly_with_attr(self):
+ def view(request):
+ """ """
+ mapper = self._makeOne(attr='__name__')
+ result = mapper(view)
+ self.assertFalse(result is view)
+ request = self._makeRequest()
+ self.assertRaises(TypeError, result, None, request)
+
+ def test_view_as_newstyle_class_context_and_request(self):
+ class view(object):
+ def __init__(self, context, request):
+ pass
+ def __call__(self):
+ return 'OK'
+ mapper = self._makeOne()
+ result = mapper(view)
+ self.assertFalse(result is view)
+ request = self._makeRequest()
+ self.assertEqual(result(None, request), 'OK')
+
+ def test_view_as_newstyle_class_context_and_request_with_attr(self):
+ class view(object):
+ def __init__(self, context, request):
+ pass
+ def index(self):
+ return 'OK'
+ mapper = self._makeOne(attr='index')
+ result = mapper(view)
+ self.assertFalse(result is view)
+ request = self._makeRequest()
+ self.assertEqual(result(None, request), 'OK')
+
+ def test_view_as_newstyle_class_requestonly(self):
+ class view(object):
+ def __init__(self, request):
+ pass
+ def __call__(self):
+ return 'OK'
+ mapper = self._makeOne()
+ result = mapper(view)
+ self.assertFalse(result is view)
+ request = self._makeRequest()
+ self.assertEqual(result(None, request), 'OK')
+
+ def test_view_as_newstyle_class_requestonly_with_attr(self):
+ class view(object):
+ def __init__(self, request):
+ pass
+ def index(self):
+ return 'OK'
+ mapper = self._makeOne(attr='index')
+ result = mapper(view)
+ self.assertFalse(result is view)
+ request = self._makeRequest()
+ self.assertEqual(result(None, request), 'OK')
+
+ def test_view_as_oldstyle_class_context_and_request(self):
+ class view:
+ def __init__(self, context, request):
+ pass
+ def __call__(self):
+ return 'OK'
+ mapper = self._makeOne()
+ result = mapper(view)
+ self.assertFalse(result is view)
+ request = self._makeRequest()
+ self.assertEqual(result(None, request), 'OK')
+
+ def test_view_as_oldstyle_class_context_and_request_with_attr(self):
+ class view:
+ def __init__(self, context, request):
+ pass
+ def index(self):
+ return 'OK'
+ mapper = self._makeOne(attr='index')
+ result = mapper(view)
+ self.assertFalse(result is view)
+ request = self._makeRequest()
+ self.assertEqual(result(None, request), 'OK')
+
+ def test_view_as_oldstyle_class_requestonly(self):
+ class view:
+ def __init__(self, request):
+ pass
+ def __call__(self):
+ return 'OK'
+ mapper = self._makeOne()
+ result = mapper(view)
+ self.assertFalse(result is view)
+ request = self._makeRequest()
+ self.assertEqual(result(None, request), 'OK')
+
+ def test_view_as_oldstyle_class_requestonly_with_attr(self):
+ class view:
+ def __init__(self, request):
+ pass
+ def index(self):
+ return 'OK'
+ mapper = self._makeOne(attr='index')
+ result = mapper(view)
+ self.assertFalse(result is view)
+ request = self._makeRequest()
+ self.assertEqual(result(None, request), 'OK')
+
+ def test_view_as_instance_context_and_request(self):
+ class View:
+ def __call__(self, context, request):
+ return 'OK'
+ view = View()
+ mapper = self._makeOne()
+ result = mapper(view)
+ self.assertTrue(result is view)
+ request = self._makeRequest()
+ self.assertEqual(result(None, request), 'OK')
+
+ def test_view_as_instance_context_and_request_and_attr(self):
+ class View:
+ def index(self, context, request):
+ return 'OK'
+ view = View()
+ mapper = self._makeOne(attr='index')
+ result = mapper(view)
+ self.assertFalse(result is view)
+ request = self._makeRequest()
+ self.assertEqual(result(None, request), 'OK')
+
+ def test_view_as_instance_requestonly(self):
+ class View:
+ def __call__(self, request):
+ return 'OK'
+ view = View()
+ mapper = self._makeOne()
+ result = mapper(view)
+ self.assertFalse(result is view)
+ request = self._makeRequest()
+ self.assertEqual(result(None, request), 'OK')
+
+ def test_view_as_instance_requestonly_with_attr(self):
+ class View:
+ def index(self, request):
+ return 'OK'
+ view = View()
+ mapper = self._makeOne(attr='index')
+ result = mapper(view)
+ self.assertFalse(result is view)
+ request = self._makeRequest()
+ self.assertEqual(result(None, request), 'OK')
+
+class Test_preserve_view_attrs(unittest.TestCase):
+ def _callFUT(self, view, wrapped_view):
+ from pyramid.config.views import preserve_view_attrs
+ return preserve_view_attrs(view, wrapped_view)
+
+ def test_it_same(self):
+ def view(context, request):
+ """ """
+ result = self._callFUT(view, view)
+ self.assertTrue(result is view)
+
+ def test_it_different_with_existing_original_view(self):
+ def view1(context, request): pass
+ view1.__original_view__ = 'abc'
+ def view2(context, request): pass
+ result = self._callFUT(view1, view2)
+ self.assertEqual(result.__original_view__, 'abc')
+ self.assertFalse(result is view1)
+
+ def test_it_different(self):
+ class DummyView1:
+ """ 1 """
+ __name__ = '1'
+ __module__ = '1'
+ def __call__(self, context, request):
+ """ """
+ def __call_permissive__(self, context, request):
+ """ """
+ def __predicated__(self, context, request):
+ """ """
+ def __permitted__(self, context, request):
+ """ """
+ class DummyView2:
+ """ 2 """
+ __name__ = '2'
+ __module__ = '2'
+ def __call__(self, context, request):
+ """ """
+ def __call_permissive__(self, context, request):
+ """ """
+ def __predicated__(self, context, request):
+ """ """
+ def __permitted__(self, context, request):
+ """ """
+ view1 = DummyView1()
+ view2 = DummyView2()
+ result = self._callFUT(view2, view1)
+ self.assertEqual(result, view1)
+ self.assertTrue(view1.__original_view__ is view2)
+ self.assertTrue(view1.__doc__ is view2.__doc__)
+ self.assertTrue(view1.__module__ is view2.__module__)
+ self.assertTrue(view1.__name__ is view2.__name__)
+ self.assertTrue(view1.__call_permissive__.im_func is
+ view2.__call_permissive__.im_func)
+ self.assertTrue(view1.__permitted__.im_func is
+ view2.__permitted__.im_func)
+ self.assertTrue(view1.__predicated__.im_func is
+ view2.__predicated__.im_func)
+
+
+class DummyRequest:
+ subpath = ()
+ matchdict = None
+ def __init__(self, environ=None):
+ if environ is None:
+ environ = {}
+ self.environ = environ
+ self.params = {}
+ self.cookies = {}
+ def copy(self):
+ return self
+ def get_response(self, app):
+ return app
+
+class DummyContext:
+ pass
+
+from zope.interface import implements
+from pyramid.interfaces import IResponse
+class DummyResponse(object):
+ implements(IResponse)
+
+class DummyAccept(object):
+ def __init__(self, *matches):
+ self.matches = list(matches)
+
+ def best_match(self, offered):
+ if self.matches:
+ for match in self.matches:
+ if match in offered:
+ self.matches.remove(match)
+ return match
+ def __contains__(self, val):
+ return val in self.matches
+
+class DummyLogger:
+ def __init__(self):
+ self.messages = []
+ def info(self, msg):
+ self.messages.append(msg)
+ warn = info
+ debug = info
+
+class DummySecurityPolicy:
+ def __init__(self, permitted=True):
+ self.permitted = permitted
+
+ def effective_principals(self, request):
+ return []
+
+ def permits(self, context, principals, permission):
+ return self.permitted
+
+def parse_httpdate(s):
+ import datetime
+ # cannot use %Z, must use literal GMT; Jython honors timezone
+ # but CPython does not
+ return datetime.datetime.strptime(s, "%a, %d %b %Y %H:%M:%S GMT")
+
+def assert_similar_datetime(one, two):
+ for attr in ('year', 'month', 'day', 'hour', 'minute'):
+ one_attr = getattr(one, attr)
+ two_attr = getattr(two, attr)
+ if not one_attr == two_attr: # pragma: no cover
+ raise AssertionError('%r != %r in %s' % (one_attr, two_attr, attr))
+
diff --git a/pyramid/config/tweens.py b/pyramid/config/tweens.py
new file mode 100644
index 000000000..c381a1101
--- /dev/null
+++ b/pyramid/config/tweens.py
@@ -0,0 +1,160 @@
+from pyramid.interfaces import ITweens
+
+from pyramid.exceptions import ConfigurationError
+from pyramid.tweens import excview_tween_factory
+from pyramid.tweens import Tweens
+from pyramid.tweens import MAIN, INGRESS, EXCVIEW
+
+from pyramid.config.util import action_method
+
+class TweensConfiguratorMixin(object):
+ @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'. Tweens are 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 ``tween_factory`` argument must be a :term:`dotted Python name`
+ to a global object representing the tween factory.
+
+ 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 this
+ tween's factory name.
+
+ 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 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 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`.
+
+ - An iterable of any combination of the above. This allows the user
+ to specify fallbacks if the desired tween is not included, as well
+ as compatibility with multiple other tweens.
+
+ ``under`` means 'closer to the main Pyramid application than',
+ ``over`` means 'closer to the request ingress than'.
+
+ For example, calling ``add_tween('myapp.tfactory',
+ over=pyramid.tweens.MAIN)`` will attempt to place the tween factory
+ represented by the dotted name ``myapp.tfactory`` directly 'above' (in
+ ``paster ptweens`` order) the main Pyramid request handler.
+ Likewise, calling ``add_tween('myapp.tfactory',
+ 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('myapp.tfactory', alias='someothertween')``).
+
+ If all options for ``under`` (or ``over``) cannot be found in the
+ current configuration, it is an error. If some options are specified
+ purely for compatibilty with other tweens, just add a fallback of
+ MAIN or INGRESS. For example,
+ ``under=('someothertween', 'someothertween2', INGRESS)``.
+ This constraint will require the tween to be located under both the
+ 'someothertween' tween, the 'someothertween2' tween, and INGRESS. If
+ any of these is not in the current configuration, this constraint will
+ only organize itself based on the tweens that are present.
+
+ 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.2.
+ """
+ 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):
+
+ if not isinstance(tween_factory, basestring):
+ raise ConfigurationError(
+ 'The "tween_factory" argument to add_tween must be a '
+ 'dotted name to a globally importable object, not %r' %
+ tween_factory)
+
+ name = tween_factory
+ tween_factory = self.maybe_dotted(tween_factory)
+
+ def is_string_or_iterable(v):
+ if isinstance(v, basestring):
+ return True
+ if hasattr(v, '__iter__'):
+ return True
+
+ for t, p in [('over', over), ('under', under)]:
+ if p is not None:
+ if not is_string_or_iterable(p):
+ raise ConfigurationError(
+ '"%s" must be a string or iterable, not %s' % (t, p))
+
+ if alias in (MAIN, INGRESS):
+ raise ConfigurationError('%s is a reserved tween name' % alias)
+
+ if over is INGRESS or hasattr(over, '__iter__') and INGRESS in over:
+ raise ConfigurationError('%s cannot be over INGRESS' % name)
+
+ if under is MAIN or hasattr(under, '__iter__') and MAIN in under:
+ raise ConfigurationError('%s cannot be under MAIN' % name)
+
+ registry = self.registry
+ tweens = registry.queryUtility(ITweens)
+ if tweens is None:
+ tweens = Tweens()
+ registry.registerUtility(tweens, ITweens)
+ tweens.add_implicit('pyramid.tweens.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)
+
diff --git a/pyramid/config/util.py b/pyramid/config/util.py
new file mode 100644
index 000000000..e6be528bf
--- /dev/null
+++ b/pyramid/config/util.py
@@ -0,0 +1,236 @@
+import re
+import traceback
+
+from pyramid.exceptions import ConfigurationError
+from pyramid.traversal import find_interface
+from pyramid.traversal import traversal_path
+
+from hashlib import md5
+
+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
+
+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
+
diff --git a/pyramid/config/views.py b/pyramid/config/views.py
new file mode 100644
index 000000000..b7ae48525
--- /dev/null
+++ b/pyramid/config/views.py
@@ -0,0 +1,1374 @@
+import inspect
+
+from zope.interface import classProvides
+from zope.interface import implements
+from zope.interface import implementedBy
+from zope.interface.interfaces import IInterface
+from zope.interface import Interface
+
+from pyramid.interfaces import IStaticURLInfo
+from pyramid.interfaces import IException
+from pyramid.interfaces import IViewMapper
+from pyramid.interfaces import IAuthenticationPolicy
+from pyramid.interfaces import IAuthorizationPolicy
+from pyramid.interfaces import IDebugLogger
+from pyramid.interfaces import IViewMapperFactory
+from pyramid.interfaces import IResponse
+from pyramid.interfaces import IMultiView
+from pyramid.interfaces import IDefaultPermission
+from pyramid.interfaces import IExceptionViewClassifier
+from pyramid.interfaces import ISecuredView
+from pyramid.interfaces import IView
+from pyramid.interfaces import IViewClassifier
+from pyramid.interfaces import IRequest
+from pyramid.interfaces import IRouteRequest
+from pyramid.interfaces import IRendererFactory
+
+from pyramid.exceptions import ConfigurationError
+from pyramid.exceptions import PredicateMismatch
+from pyramid.httpexceptions import HTTPForbidden
+from pyramid.httpexceptions import HTTPNotFound
+from pyramid.security import NO_PERMISSION_REQUIRED
+from pyramid.static import StaticURLInfo
+from pyramid.view import render_view_to_response
+from pyramid import renderers
+
+from pyramid.config.util import MAX_ORDER
+from pyramid.config.util import DEFAULT_PHASH
+from pyramid.config.util import action_method
+from pyramid.config.util import make_predicates
+
+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 = renderers.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 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)
+
+class ViewsConfiguratorMixin(object):
+ @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 = renderers.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 = renderers.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 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)
+
+ # 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 = renderers.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 = renderers.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_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 = renderers.RendererHelper(
+ name=renderer, package=self.package,
+ registry = self.registry)
+ view = self._derive_view(view, attr=attr, renderer=renderer)
+ def bwcompat_view(context, request):
+ context = getattr(request, 'context', None)
+ return view(context, request)
+ return self.add_view(bwcompat_view, context=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 = renderers.RendererHelper(
+ name=renderer, package=self.package,
+ registry=self.registry)
+ view = self._derive_view(view, attr=attr, renderer=renderer)
+ def bwcompat_view(context, request):
+ context = getattr(request, 'context', None)
+ return view(context, request)
+ return self.add_view(bwcompat_view, context=HTTPNotFound,
+ wrapper=wrapper, renderer=renderer)
+
+ @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 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 :meth:`pyramid.request.Request.static_url` method.
+ ``add_static_view`` adds a view which renders a static asset when
+ some URL is visited; :meth:`pyramid.request.Request.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
+ :meth:`pyramid.request.Request.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
+ :meth:`pyramid.request.Request.static_url`:
+
+ .. code-block:: python
+
+ request.static_url('mypackage:images/logo.png')
+
+ When ``add_static_view`` is called with a ``name`` argument that
+ represents a URL prefix, as it is above, subsequent calls to
+ :meth:`pyramid.request.Request.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 :meth:`pyramid.request.Request.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
+ :meth:`pyramid.request.Request.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
+ :meth:`pyramid.request.Request.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)
+
+
+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)))
+ )
+
diff --git a/pyramid/config/zca.py b/pyramid/config/zca.py
new file mode 100644
index 000000000..c50093259
--- /dev/null
+++ b/pyramid/config/zca.py
@@ -0,0 +1,24 @@
+from pyramid.threadlocal import get_current_registry
+from zope.component import getSiteManager
+
+class ZCAConfiguratorMixin(object):
+ 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`."""
+ 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`."""
+ getSiteManager.reset()
+
diff --git a/pyramid/configuration.py b/pyramid/configuration.py
index cdb9bc983..a2ca0e66f 100644
--- a/pyramid/configuration.py
+++ b/pyramid/configuration.py
@@ -1,5 +1,5 @@
from pyramid.config import Configurator as BaseConfigurator
-from pyramid.config import ConfigurationError # API
+from pyramid.exceptions import ConfigurationError # API
from pyramid.config import DEFAULT_RENDERERS
from pyramid.path import caller_package
diff --git a/pyramid/tests/test_router.py b/pyramid/tests/test_router.py
index 2b00cba22..35f2e4352 100644
--- a/pyramid/tests/test_router.py
+++ b/pyramid/tests/test_router.py
@@ -136,7 +136,7 @@ class TestRouter(unittest.TestCase):
def test_tween_factories(self):
from pyramid.interfaces import ITweens
- from pyramid.config import Tweens
+ from pyramid.config.tweens import Tweens
from pyramid.response import Response
from pyramid.interfaces import IViewClassifier
from pyramid.interfaces import IResponse
diff --git a/setup.py b/setup.py
index 4c556dbc3..df73e788b 100644
--- a/setup.py
+++ b/setup.py
@@ -75,7 +75,7 @@ setup(name='pyramid',
zip_safe=False,
install_requires = install_requires,
tests_require = tests_require,
- test_suite="pyramid.tests",
+ test_suite="pyramid",
entry_points = """\
[paste.paster_create_template]
pyramid_starter=pyramid.scaffolds:StarterProjectTemplate