From 427a02393da7c9ad5198be3c7be870662d229f83 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Wed, 8 Dec 2010 18:29:50 -0500 Subject: configuration.Config->config.Configurator --- pyramid/config.py | 2871 ++++++++++++++++++++++ pyramid/configuration.py | 2949 +--------------------- pyramid/router.py | 69 +- pyramid/testing.py | 20 +- pyramid/tests/test_config.py | 4573 ++++++++++++++++++++++++++++++++++ pyramid/tests/test_configuration.py | 4646 +---------------------------------- pyramid/tests/test_integration.py | 12 +- pyramid/tests/test_router.py | 73 + pyramid/tests/test_util.py | 2 +- pyramid/tests/test_zcml.py | 8 +- pyramid/zcml.py | 36 +- 11 files changed, 7646 insertions(+), 7613 deletions(-) create mode 100644 pyramid/config.py create mode 100644 pyramid/tests/test_config.py diff --git a/pyramid/config.py b/pyramid/config.py new file mode 100644 index 000000000..d7820cdd1 --- /dev/null +++ b/pyramid/config.py @@ -0,0 +1,2871 @@ +import inspect +import os +import re +import sys +import threading +import traceback + +import venusian + +from translationstring import ChameleonTranslate + +from zope.configuration import xmlconfig +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 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 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 + +try: + from pyramid import chameleon_text +except TypeError: # pragma: no cover + chameleon_text = None # pypy +try: + from pyramid import chameleon_zpt +except TypeError: # pragma: no cover + chameleon_zpt = None # pypy + +from pyramid import renderers +from pyramid.authorization import ACLAuthorizationPolicy +from pyramid.compat import all +from pyramid.compat import md5 +from pyramid.events import ApplicationCreated +from pyramid.exceptions import ConfigurationError +from pyramid.exceptions import Forbidden +from pyramid.exceptions import NotFound +from pyramid.exceptions import PredicateMismatch +from pyramid.i18n import get_localizer +from pyramid.log import make_stream_logger +from pyramid.mako_templating import renderer_factory as mako_renderer_factory +from pyramid.path import caller_package +from pyramid.path import package_path +from pyramid.path import package_of +from pyramid.registry import Registry +from pyramid.renderers import RendererHelper +from pyramid.request import route_request_iface +from pyramid.resource import PackageOverrides +from pyramid.resource import resolve_resource_spec +from pyramid.settings import Settings +from pyramid.static import StaticURLInfo +from pyramid.threadlocal import get_current_registry +from pyramid.threadlocal import get_current_request +from pyramid.threadlocal import manager +from pyramid.traversal import DefaultRootFactory +from pyramid.traversal import find_interface +from pyramid.traversal import traversal_path +from pyramid.urldispatch import RoutesMapper +from pyramid.util import DottedNameResolver +from pyramid.view import default_exceptionresponse_view +from pyramid.view import render_view_to_response + +MAX_ORDER = 1 << 30 +DEFAULT_PHASH = md5().hexdigest() + +DEFAULT_RENDERERS = ( + ('.mak', mako_renderer_factory), + ('.mako', mako_renderer_factory), + ('json', renderers.json_renderer_factory), + ('string', renderers.string_renderer_factory), + ) + +if chameleon_text: + DEFAULT_RENDERERS += (('.pt', chameleon_zpt.renderer_factory),) +if chameleon_zpt: + DEFAULT_RENDERERS += (('.txt', chameleon_text.renderer_factory),) + +def config_method(wrapped): + def wrapper(self, *arg, **kw): + context = self._ctx + if context is not None: + if (not context.autocommit) and (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). + # This is nasty. + newctx = GroupingContextDecorator(context) + try: + f = traceback.extract_stack(limit=3) + info = f[-2] + except: + info = '' + newctx.info = info + self._ctx = newctx + result = wrapped(self, *arg, **kw) + if context is not None: + self._ctx = context + return result + wrapper.__doc__ = wrapped.__doc__ + wrapper.__name__ = wrapped.__name__ + 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``, and + ``renderer_globals_factory``. + + 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.configuration.Configurator.add_renderer`). If + it is not passed, a default set of renderer factories is used. + + If ``debug_logger`` is not passed, a default debug logger that + logs to stderr will be used. If it is passed, it should be an + instance of the :class:`logging.Logger` (PEP 282) standard library + class or a :term:`dotted Python name` to same. The debug logger + is used by :app:`Pyramid` itself to log warnings and + authorization debugging information. + + If ``locale_negotiator`` is passed, it should be a :term:`locale + negotiator` implementation or a :term:`dotted Python name` to + same. See :ref:`custom_locale_negotiator`. + + If ``request_factory`` is passed, it should be a :term:`request + factory` implementation or a :term:`dotted Python name` to same. + See :ref:`custom_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:`custom_renderer_globals_factory`. + By default, it is ``None``, which means use no renderer globals + factory. + + 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 + :ref:`pyramid.configration.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. """ + + manager = manager # for testing injection + venusian = venusian # for testing injection + _ctx = 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, + autocommit=False, + ): + if package is None: + package = caller_package() + name_resolver = DottedNameResolver(package) + self.name_resolver = name_resolver + self.package_name = name_resolver.package_name + self.package = name_resolver.package + self.registry = registry + self.autocommit = autocommit + if registry is None: + registry = Registry(self.package_name) + self.registry = registry + self.setup_registry( + settings=settings, + root_factory=root_factory, + authentication_policy=authentication_policy, + authorization_policy=authorization_policy, + renderers=renderers, + debug_logger=debug_logger, + locale_negotiator=locale_negotiator, + request_factory=request_factory, + renderer_globals_factory=renderer_globals_factory, + default_permission=default_permission, + session_factory=session_factory, + ) + + def _action(self, discriminator, callable=None, args=(), kw=None, order=0): + """ Register an action which will be executed during a commit. """ + if kw is None: + kw = {} + if self.autocommit: + if callable is not None: + callable(*args, **kw) + else: + if self._ctx is None: + self._ctx = self._make_context() + self._ctx.action(discriminator, callable, args, kw, order) + + def _set_settings(self, mapping): + settings = Settings(mapping or {}) + self.registry.settings = settings + return settings + + @config_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) + + @config_method + def _set_authentication_policy(self, policy): + """ Add a :app:`Pyramid` :term:`authentication policy` to + the current configuration.""" + policy = self.maybe_dotted(policy) + self.registry.registerUtility(policy, IAuthenticationPolicy) + self._action(IAuthenticationPolicy) + + @config_method + def _set_authorization_policy(self, policy): + """ Add a :app:`Pyramid` :term:`authorization policy` to + the current configuration state (also accepts a :term:`dotted + Python name`.""" + policy = self.maybe_dotted(policy) + self.registry.registerUtility(policy, IAuthorizationPolicy) + self._action(IAuthorizationPolicy, None) + + def _make_spec(self, path_or_spec): + package, filename = resolve_resource_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_resource_spec(path_or_spec, self.package_name) + + 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): + if renderer is None: # use default renderer if one exists + default_renderer_factory = self.registry.queryUtility( + IRendererFactory) + if default_renderer_factory is not None: + renderer = {'name':None, 'package':self.package} + view = self.maybe_dotted(view) + authn_policy = self.registry.queryUtility(IAuthenticationPolicy) + authz_policy = self.registry.queryUtility(IAuthorizationPolicy) + settings = self.registry.settings + logger = self.registry.queryUtility(IDebugLogger) + mapped_view = _map_view(view, self.registry, attr, renderer) + owrapped_view = _owrap_view(mapped_view, viewname, wrapper_viewname) + secured_view = _secure_view(owrapped_view, permission, + authn_policy, authz_policy) + debug_view = _authdebug_view(secured_view, permission, + authn_policy, authz_policy, settings, + logger) + predicated_view = _predicate_wrap(debug_view, predicates) + derived_view = _attr_wrap(predicated_view, accept, order, phash) + return derived_view + + 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) + + def _set_security_policies(self, authentication, authorization=None): + if authorization is None: + authorization = ACLAuthorizationPolicy() # default + if authorization and not authentication: + raise ConfigurationError( + 'If the "authorization" is passed a value, ' + 'the "authentication" argument must also be ' + 'passed a value; authorization requires authentication.') + self._set_authentication_policy(authentication) + self._set_authorization_policy(authorization) + + def _fix_registry(self): + """ Fix up a ZCA component registry that is not a + pyramid.registry.Registry by adding analogues of ``has_listeners``, + and ``notify`` 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 + + def _make_context(self, autocommit=False): + context = PyramidConfigurationMachine() + registerCommonDirectives(context) + context.registry = self.registry + context.autocommit = autocommit + return context + + # API + + def commit(self): + """ Commit pending configuration actions. """ + if self._ctx is None: + return + self._ctx.execute_actions() + # unwrap and reset the context + self._ctx = None + + @classmethod + def with_context(cls, context): + """ Used by ZCML directives to obtain a configurator with 'the right' + context """ + configurator = cls(registry=context.registry, package=context.package, + autocommit=context.autocommit) + configurator._ctx = context + return configurator + + def with_package(self, package): + """ Return a new Configurator instance with the same registry + as this configurator using the package supplied as the + ``package`` argument to the new configurator. ``package`` may + be an actual Python package object or a Python dotted name + representing a package.""" + context = self._ctx + if context is None: + context = self._ctx = self._make_context(self.autocommit) + context = GroupingContextDecorator(context) + context.package = package + return self.__class__.with_context(context) + + 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_resource_spec(self, relative_spec): + """ Resolve the potentially relative :term:`resource + specification` string passed as ``relative_spec`` into an + absolute resource specification string and return the string. + Use the ``package`` of this configurator as the package to + which the resource specification will be considered relative + when generating an absolute resource 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) + + 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): + """ When you pass a non-``None`` ``registry`` argument to the + :term:`Configurator` constructor, no initial 'setup' is + performed against the registry. This is because the registry + you pass in may have already been initialized for use under + :app:`Pyramid` via a different configurator. However, in + some circumstances, such as when you want to use the Zope + 'global` registry instead of a registry created as a result of + the Configurator constructor, or when you want to reset the + initial setup of a registry, you *do* want to explicitly + initialize the registry associated with a Configurator for use + under :app:`Pyramid`. Use ``setup_registry`` to do this + initialization. + + ``setup_registry`` configures settings, a root factory, + security policies, renderers, a debug logger, a locale + negotiator, and various other settings using the + configurator's current registry, as per the descriptions in + the Configurator constructor.""" + registry = self.registry + self._fix_registry() + self._set_settings(settings) + self._set_root_factory(root_factory) + debug_logger = self.maybe_dotted(debug_logger) + if debug_logger is None: + debug_logger = make_stream_logger('pyramid.debug', sys.stderr) + registry.registerUtility(debug_logger, IDebugLogger) + if authentication_policy or authorization_policy: + self._set_security_policies(authentication_policy, + authorization_policy) + for name, renderer in renderers: + self.add_renderer(name, renderer) + self.add_view(default_exceptionresponse_view, + context=IExceptionResponse) + 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: + renderer_globals_factory = self.maybe_dotted( + renderer_globals_factory) + self.set_renderer_globals_factory(renderer_globals_factory) + if default_permission: + self.set_default_permission(default_permission) + if session_factory is not None: + self.set_session_factory(session_factory) + self.commit() + + # getSiteManager is a unit testing dep injection + def hook_zca(self, getSiteManager=None): + """ 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`.""" + if getSiteManager is None: + from zope.component import getSiteManager + getSiteManager.sethook(get_current_registry) + + # getSiteManager is a unit testing dep injection + def unhook_zca(self, getSiteManager=None): + """ Call :func:`zope.component.getSiteManager.reset` to undo + the action of + :meth:`pyramid.configuration.Configurator.hook_zca`. If + :mod:`zope.component` cannot be imported, this method will + raise an :exc:`ImportError`.""" + if getSiteManager is None: # pragma: no cover + 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. + + 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. """ + + if renderer is not None and not isinstance(renderer, dict): + renderer = {'name':renderer, 'package':self.package} + return self._derive_view(view, attr=attr, renderer=renderer) + + @config_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 + + 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.configuration.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.configuration.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 'settings' object for the current application. A + '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.configuration.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): + """ Returns a :app:`Pyramid` WSGI application representing + the current configuration state and sends a + :class:`pyramid.events.ApplicationCreated` + event to all listeners.""" + self.commit() + from pyramid.router import Router # avoid circdep + app = Router(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 + + @config_method + def load_zcml(self, spec='configure.zcml', lock=threading.Lock()): + """ Load configuration from a :term:`ZCML` file into the + current configuration state. The ``spec`` argument is an + absolute filename, a relative filename, or a :term:`resource + specification`, defaulting to ``configure.zcml`` (relative to + the package of the configurator's caller).""" + package_name, filename = self._split_spec(spec) + if package_name is None: # absolute filename + package = self.package + else: + __import__(package_name) + package = sys.modules[package_name] + + registry = self.registry + self.manager.push({'registry':registry, 'request':None}) + context = self._ctx + if context is None: + context = self._ctx = self._make_context(self.autocommit) + + lock.acquire() + try: + context.package = package + xmlconfig.file(filename, package, context=context, + execute=self.autocommit) + finally: + lock.release() + self.manager.pop() + return registry + + def include(self, *funcs): + """ Include one or more configuration callables. 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 which + add configuration state. The return value of a callable will be + ignored. + + Values allowed to be presented via the ``*funcs`` argument to this + method: any callable Python object or any :term:`dotted Python name` + which resolves to a callable Python object. + """ + sourcefiles = [] + + for func in funcs: + func = self.maybe_dotted(func) + sourcefile = inspect.getsourcefile(func) + module = inspect.getmodule(func) + sourcefiles.append((sourcefile, func, module)) + + _context = self._ctx + if _context is None: + _context = self._ctx = self._make_context(self.autocommit) + + for filename, func, module in sourcefiles: + spec = module.__name__ + ':' + func.__name__ + if _context.processSpec(spec): + context = GroupingContextDecorator(_context) + context.basepath = os.path.dirname(filename) + context.includepath = _context.includepath + (spec,) + context.package = package_of(module) + config = self.__class__.with_context(context) + func(config) + + def add_handler(self, route_name, pattern, handler, action=None, **kw): + + """ Add a Pylons-style view handler. This function adds a + route and some number of views based on a handler object + (usually a class). + + ``route_name`` is the name of the route (to be used later in + URL generation). + + ``pattern`` is the matching pattern, + e.g. ``'/blog/{action}'``. ``pattern`` may be ``None``, in + which case the pattern of an existing route named the same as + ``route_name`` is used. If ``pattern`` is ``None`` and no + route named ``route_name`` exists, a ``ConfigurationError`` is + raised. + + ``handler`` is a dotted name of (or direct reference to) a + Python handler class, + e.g. ``'my.package.handlers.MyHandler'``. + + If ``{action}`` or ``:action`` is in + the pattern, the exposed methods of the handler will be used + as views. + + If ``action`` is passed, it will be considered the method name + of the handler to use as a view. + + Passing both ``action`` and having an ``{action}`` in the + route pattern is disallowed. + + Any extra keyword arguments are passed along to ``add_route``. + + See :ref:`handlers_chapter` for more explanatory documentation. + + This method returns the result of add_route.""" + handler = self.maybe_dotted(handler) + + if pattern is not None: + route = self.add_route(route_name, pattern, **kw) + else: + mapper = self.get_routes_mapper() + route = mapper.get_route(route_name) + if route is None: + raise ConfigurationError( + 'The "pattern" parameter may only be "None" when a route ' + 'with the route_name argument was previously registered. ' + 'No such route named %r exists' % route_name) + + pattern = route.pattern + + path_has_action = ':action' in pattern or '{action}' in pattern + + if action and path_has_action: + raise ConfigurationError( + 'action= (%r) disallowed when an action is in the route ' + 'path %r' % (action, pattern)) + + if path_has_action: + autoexpose = getattr(handler, '__autoexpose__', r'[A-Za-z]+') + if autoexpose: + try: + autoexpose = re.compile(autoexpose).match + except (re.error, TypeError), why: + raise ConfigurationError(why[0]) + for method_name, method in inspect.getmembers( + handler, inspect.ismethod): + configs = getattr(method, '__exposed__', []) + if autoexpose and not configs: + if autoexpose(method_name): + configs = [{}] + for expose_config in configs: + # we don't want to mutate any dict in __exposed__, + # so we copy each + view_args = expose_config.copy() + action = view_args.pop('name', method_name) + preds = list(view_args.pop('custom_predicates', [])) + preds.append(ActionPredicate(action)) + view_args['custom_predicates'] = preds + self.add_view(view=handler, attr=method_name, + route_name=route_name, **view_args) + else: + method_name = action + if method_name is None: + method_name = '__call__' + + # Scan the controller for any other methods with this action name + for meth_name, method in inspect.getmembers( + handler, inspect.ismethod): + configs = getattr(method, '__exposed__', [{}]) + for expose_config in configs: + # Don't re-register the same view if this method name is + # the action name + if meth_name == action: + continue + # We only reg a view if the name matches the action + if expose_config.get('name') != method_name: + continue + # we don't want to mutate any dict in __exposed__, + # so we copy each + view_args = expose_config.copy() + del view_args['name'] + self.add_view(view=handler, attr=meth_name, + route_name=route_name, **view_args) + + # Now register the method itself + method = getattr(handler, method_name, None) + configs = getattr(method, '__exposed__', [{}]) + for expose_config in configs: + self.add_view(view=handler, attr=action, route_name=route_name, + **expose_config) + + return route + + @config_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): + """ 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.configuration.Configurator` constructor's + ``default_permission`` argument, or if + :meth:`pyramid.configuration.Configurator.set_default_permission` + was used prior to this view registration. Pass the string + ``__no_permission_required__`` as the permission argument to + explicitly indicate that the view should always be + executable by entirely anonymous users, regardless of the + default permission, bypassing any :term:`authorization + policy` that may be in effect. + + attr + + The view machinery defaults to using the ``__call__`` method + of the :term:`view callable` (or the function itself, if the + view callable is a function) to obtain a response. The + ``attr`` value allows you to vary the method attribute used + to obtain the response. For example, if your view was a + class, and the class has a method named ``index`` and you + wanted to use this method instead of the class' ``__call__`` + method to return the response, you'd say ``attr="index"`` in the + view configuration for the view. This is + most useful when the view definition is a class. + + renderer + + This is either a single string term (e.g. ``json``) or a + string implying a path or :term:`resource 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:`resource specification` in the form + ``some.dotted.package_name:relative/path``, making it + possible to address template resources 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 unmolested). + + 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. + + 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. Note that + the ``route`` configuration referred to by ``route_name`` + usually has a ``*traverse`` token in the value of its + ``path``, representing a part of the path that will be used + by :term:`traversal` against the result of the route's + :term:`root factory`. + + .. warning:: Using this argument services an advanced + feature that isn't often used unless you want to perform + traversal *after* a route has matched. See + :ref:`hybrid_chapter` for more information on using this + advanced feature. + + 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 either 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_params="foo=123"``, then the key (``foo``) + must both exist in the ``request.params`` dictionary, *and* + the value must match the right hand side of the expression + (``123``) for the view to "match" the current request. + + containment + + This value should be a Python class or :term:`interface` or + a :term:`dotted Python name` to such an object that a parent + object in the :term:`lineage` 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) + + 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, + ) + 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 renderer is not None and not isinstance(renderer, dict): + renderer = {'name':renderer, 'package':self.package} + + 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) + + def register(permission=permission): + + 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 + derived_view = self._derive_view(view, permission, predicates, attr, + renderer, wrapper, name, accept, + order, phash) + + 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) + + @config_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, + ): + """ 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` :term:`context` + object when this route matches. For example, + ``mypackage.models.MyFactoryClass``. If this argument is + not specified, a default root factory will be used. + + traverse + + If you would like to cause the :term:`context` to be + something other than the :term:`root` object when this route + matches, you can spell a traversal pattern as the + ``traverse`` argument. This traversal pattern will be used + as the traversal path: traversal will begin at the root + object implied by this route (either the global root, or the + object returned by the ``factory`` associated with this + route). + + The syntax of the ``traverse`` argument is the same as it is + for ``pattern``. For example, if the ``pattern`` provided to + ``add_route`` is ``articles/{article}/edit``, and the + ``traverse`` argument provided to ``add_route`` is + ``/{article}``, when a request comes in that causes the route + to match in such a way that the ``article`` match value is + '1' (when the request URI is ``/articles/1/edit``), the + traversal path will be generated as ``/1``. This means that + the root object's ``__getitem__`` will be called with the + name ``1`` during the traversal phase. If the ``1`` object + exists, it will become the :term:`context` of the request. + :ref:`traversal_chapter` has more information about + traversal. + + If the traversal path contains segment marker names which + are not present in the ``pattern`` argument, a runtime error + will occur. The ``traverse`` pattern should not contain + segment markers that do not exist in the ``pattern`` + argument. + + A similar combining of routing and traversal is available + when a route is matched which contains a ``*traverse`` + remainder marker in its pattern (see + :ref:`using_traverse_in_a_route_pattern`). The ``traverse`` + argument to add_route allows you to associate route patterns + with an arbitrary traversal path without using a a + ``*traverse`` remainder marker; instead you can use other + match information. + + Note that the ``traverse`` argument to ``add_route`` is + ignored when attached to a route that has a ``*traverse`` + remainder marker in its pattern. + + pregenerator + + This option should be a callable object that implements the + :class:`pyramid.interfaces.IRoutePregenerator` + interface. A :term:`pregenerator` is a callable called by + the :mod:`pyramid.url.route_url` function to augment or + replace the arguments it is passed when generating a URL + for the route. This is a feature not often used directly + by applications, it is meant to be hooked by frameworks + that use :app:`Pyramid` as a base. + + Predicate Arguments + + pattern + + The pattern of the route e.g. ``ideas/{idea}``. This + argument is required. See :ref:`route_path_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_params="foo=123"``, then the key + (``foo``) must both exist in the ``request.params`` dictionary, and + the value must match the right hand side of the expression (``123``) + for the 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 + + view + + 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 + + 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 + + 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 + + This is either a single string term (e.g. ``json``) or a + string implying a path or :term:`resource 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 + + 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. + + 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). + + """ + # 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: + bases = use_global_views and (IRequest,) or () + 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) + + if view: + if view_context is None: + view_context = view_for + if view_context is None: + view_context = for_ + view_permission = view_permission or permission + view_renderer = view_renderer or renderer + self.add_view( + permission=view_permission, + context=view_context, + view=view, + name='', + route_name=name, + renderer=view_renderer, + attr=view_attr, + ) + + mapper = self.get_routes_mapper() + + factory = self.maybe_dotted(factory) + if pattern is None: + pattern = path + if pattern is None: + raise ConfigurationError('"pattern" argument may not be None') + + discriminator = ['route', name, xhr, request_method, path_info, + request_param, header, accept] + discriminator.extend(sorted(custom_predicates)) + discriminator = tuple(discriminator) + + self._action(discriminator, None) + + return mapper.connect(name, pattern, factory, predicates=predicates, + pregenerator=pregenerator) + + 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 + + @config_method + def scan(self, package=None, categories=None): + """ 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`. If this is not desirable + because the codebase has other Venusian-using decorators that + aren't meant to be invoked during a particular scan, use + ``('pyramid',)`` as a ``categories`` value to limit the execution + of decorator callbacks to only those registered by + :app:`Pyramid` itself. Or pass a sequence of Venusian scan + categories as necessary (e.g. ``('pyramid', 'myframework')``) to + limit the decorators called to the set of categories required. + """ + package = self.maybe_dotted(package) + if package is None: # pragma: no cover + package = caller_package() + + scanner = self.venusian.Scanner(config=self) + scanner.scan(package, categories=categories) + + @config_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) + + @config_method + def override_resource(self, to_override, override_with, _override=None): + """ Add a :app:`Pyramid` resource override to the current + configuration state. + + ``to_override`` is a :term:`resource specification` to the + resource being overridden. + + ``override_with`` is a :term:`resource specification` to the + resource that is performing the override. + + See :ref:`resources_chapter` for more + information about resource overrides.""" + if to_override == override_with: + raise ConfigurationError('You cannot override a resource 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) + + if path and path.endswith('/'): + if override_prefix and (not override_prefix.endswith('/')): + raise ConfigurationError( + 'A directory cannot be overridden with a file (put a ' + 'slash at the end of override_with if necessary)') + + if override_prefix and override_prefix.endswith('/'): + if path and (not path.endswith('/')): + 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) + + 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.configuration.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 renderer is not None and not isinstance(renderer, dict): + renderer = {'name':renderer, 'package':self.package} + 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=Forbidden, wrapper=wrapper) + + 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.configuration.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 renderer is not None and not isinstance(renderer, dict): + renderer = {'name':renderer, 'package':self.package} + 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=NotFound, wrapper=wrapper) + + @config_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 :meth:``request_factory`` argument to the + :class:`pyramid.configuration.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) + + @config_method + def set_renderer_globals_factory(self, factory): + """ 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. + + .. note:: Using the :meth:`renderer_globals_factory` + argument to the + :class:`pyramid.configuration.Configurator` constructor + can be used to achieve the same purpose. + """ + factory = self.maybe_dotted(factory) + def register(): + self.registry.registerUtility(factory, IRendererGlobalsFactory) + self._action(IRendererGlobalsFactory, register) + + @config_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.configuration.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) + + @config_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 earlier calls; there can + be only one default permission active at a time within an + application. + + See also :ref:`setting_a_default_permission`. + + .. note:: Using the ``default_permission`` argument to the + :class:`pyramid.configuration.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) + + @config_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. + """ + def register(): + self.registry.registerUtility(session_factory, ISessionFactory) + self._action(ISessionFactory, register) + + @config_method + 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:`resource specification` + names naming a directory path (e.g. ``some.package:locale``) + or a combination of the two. + + Example: + + .. code-block:: python + + add_translations_dirs('/usr/share/locale', 'some.package:locale') + + """ + for spec in specs: + + 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. + + def translator(msg): + request = get_current_request() + localizer = get_localizer(request) + return localizer.translate(msg) + + ctranslate = ChameleonTranslate(translator) + self.registry.registerUtility(ctranslate, IChameleonTranslate) + + def add_static_view(self, name, path, **kw): + """ Add a view used to render static resources such as images + and CSS files. + + The ``name`` argument is a string representing :term:`view + name` of the view which is registered. It may alternately be + a *url prefix*. + + 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:`resource specification`. + + The ``cache_max_age`` keyword argument is input to set the + ``Expires`` and ``Cache-Control`` headers for static resources + served. Note that this argument has no effect when the + ``name`` is a *url prefix*. By default, this argument is + ``None``, meaning that no particular Expires or Cache-Control + headers are set in the response. + + The ``permission`` keyword argument is used to specify the + :term:`permission` required by a user to execute the static + view. By default, it is the string + ``__no_permission_required__``. The + ``__no_permission_required__`` string is a special sentinel + which indicates that, even if a :term:`default permission` + exists for the current application, the static view should be + renderered to completely anonymous users. This default value + is permissive because, in most web apps, static resources + seldom need protection from viewing. + + *Usage* + + The ``add_static_view`` function is typically used in + conjunction with the :func:`pyramid.url.static_url` + function. ``add_static_view`` adds a view which renders a + static resource when some URL is visited; + :func:`pyramid.url.static_url` generates a URL to that + resource. + + The ``name`` argument to ``add_static_view`` is usually a + :term:`view name`. When this is the case, the + :func:`pyramid.url.static_url` API will generate a URL + which points to a Pyramid view, which will serve up a set of + resources that live in the package itself. For example: + + .. code-block:: python + + add_static_view('images', 'mypackage:images/') + + Code that registers such a view can generate URLs to the view + via :func:`pyramid.url.static_url`: + + .. code-block:: python + + static_url('mypackage:images/logo.png', request) + + When ``add_static_view`` is called with a ``name`` argument + that represents a simple view name, as it is above, subsequent + calls to :func:`pyramid.url.static_url` with paths that + start with the ``path`` argument passed to ``add_static_view`` + will generate a URL something like ``http:///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 resources to be + served from an external webserver. This happens when the + ``name`` argument is a URL (detected as any string with a + slash in it). In this mode, the ``name`` is used as the URL + prefix when generating a URL using + :func:`pyramid.url.static_url`. For example, if + ``add_static_view`` is called like so: + + .. code-block:: python + + add_static_view('http://example.com/images', 'mypackage:images/') + + Subsequently, the URLs generated by + :func:`pyramid.url.static_url` for that static view will be + prefixed with ``http://example.com/images``: + + .. code-block:: python + + static_url('mypackage:images/logo.png', request) + + When ``add_static_view`` is called with a ``name`` argument + that is the URL prefix ``http://example.com/images``, + subsequent calls to :func:`pyramid.url.static_url` with + paths that start with the ``path`` argument passed to + ``add_static_view`` will generate a URL something like + ``http://example.com/logo.png``. The external webserver + listening on ``example.com`` must be itself configured to + respond properly to such a request. + + See :ref:`static_resources_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_models(self, models): + """Unit/integration testing helper: registers a dictionary of + :term:`model` objects that can be resolved via the + :func:`pyramid.traversal.find_model` API. + + The :func:`pyramid.traversal.find_model` 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_model`` (and + thus to your code) when + :func:`pyramid.traversal.find_model` is called with an + equivalent path string or tuple. + """ + class DummyTraverserFactory: + def __init__(self, context): + self.context = context + + def __call__(self, request): + path = request['PATH_INFO'] + ob = models[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 models + + 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`, + :func:`zope.component.event.dispatch` or + :func:`zope.component.event.objectEventNotify`. + + 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 a resource 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 + 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 + 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 + 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) + 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 + 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]) + 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 + 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 + 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 + 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) + 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 ModelGraphTraverser 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): + 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 decorate_view(wrapped_view, original_view): + if wrapped_view is original_view: + return False + wrapped_view.__module__ = original_view.__module__ + wrapped_view.__doc__ = original_view.__doc__ + try: + wrapped_view.__name__ = original_view.__name__ + except AttributeError: + wrapped_view.__name__ = repr(original_view) + try: + wrapped_view.__permitted__ = original_view.__permitted__ + except AttributeError: + pass + try: + wrapped_view.__call_permissive__ = original_view.__call_permissive__ + except AttributeError: + pass + try: + wrapped_view.__predicated__ = original_view.__predicated__ + except AttributeError: + pass + try: + wrapped_view.__accept__ = original_view.__accept__ + except AttributeError: + pass + try: + wrapped_view.__order__ = original_view.__order__ + except AttributeError: + pass + return True + +def requestonly(class_or_callable, attr=None): + """ Return true of the class or callable accepts only a request argument, + as opposed to something that accepts context, request """ + if attr is None: + attr = '__call__' + if inspect.isfunction(class_or_callable): + fn = class_or_callable + elif inspect.isclass(class_or_callable): + try: + fn = class_or_callable.__init__ + except AttributeError: + return False + else: + try: + fn = getattr(class_or_callable, attr) + except AttributeError: + return False + + try: + argspec = inspect.getargspec(fn) + except TypeError: + return False + + args = argspec[0] + defaults = argspec[3] + + 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 + + elif args[0] == 'request': + if len(args) - len(defaults) == 1: + return True + + return False + +def is_response(ob): + if ( hasattr(ob, 'app_iter') and hasattr(ob, 'headerlist') and + hasattr(ob, 'status') ): + return True + return False + +def _map_view(view, registry, attr=None, renderer=None): + wrapped_view = view + + helper = None + + if renderer is not None: + helper = RendererHelper(renderer['name'], + package=renderer['package'], + registry=registry) + + if inspect.isclass(view): + # If the object we've located is a class, turn it into a + # function that operates like a Zope view (when it's invoked, + # construct an instance using 'context' and 'request' as + # position arguments, then immediately invoke the __call__ + # method of the instance with no arguments; __call__ should + # return an IResponse). + if requestonly(view, attr): + # its __init__ accepts only a single request argument, + # instead of both context and request + def _class_requestonly_view(context, request): + inst = view(request) + if attr is None: + response = inst() + else: + response = getattr(inst, attr)() + if helper is not None: + if not is_response(response): + system = { + 'view':inst, + 'renderer_name':renderer['name'], # b/c + 'renderer_info':renderer, + 'context':context, + 'request':request + } + response = helper.render_to_response(response, system, + request=request) + return response + wrapped_view = _class_requestonly_view + else: + # its __init__ accepts both context and request + def _class_view(context, request): + inst = view(context, request) + if attr is None: + response = inst() + else: + response = getattr(inst, attr)() + if helper is not None: + if not is_response(response): + system = {'view':inst, + 'renderer_name':renderer['name'], # b/c + 'renderer_info':renderer, + 'context':context, + 'request':request + } + response = helper.render_to_response(response, system, + request=request) + return response + wrapped_view = _class_view + + elif requestonly(view, attr): + # its __call__ accepts only a single request argument, + # instead of both context and request + def _requestonly_view(context, request): + if attr is None: + response = view(request) + else: + response = getattr(view, attr)(request) + + if helper is not None: + if not is_response(response): + system = { + 'view':view, + 'renderer_name':renderer['name'], + 'renderer_info':renderer, + 'context':context, + 'request':request + } + response = helper.render_to_response(response, system, + request=request) + return response + wrapped_view = _requestonly_view + + elif attr: + def _attr_view(context, request): + response = getattr(view, attr)(context, request) + if helper is not None: + if not is_response(response): + system = { + 'view':view, + 'renderer_name':renderer['name'], + 'renderer_info':renderer, + 'context':context, + 'request':request + } + response = helper.render_to_response(response, system, + request=request) + return response + wrapped_view = _attr_view + + elif helper is not None: + def _rendered_view(context, request): + response = view(context, request) + if not is_response(response): + system = { + 'view':view, + 'renderer_name':renderer['name'], # b/c + 'renderer_info':renderer, + 'context':context, + 'request':request + } + response = helper.render_to_response(response, system, + request=request) + return response + wrapped_view = _rendered_view + + decorate_view(wrapped_view, view) + return wrapped_view + +def _owrap_view(view, viewname, wrapper_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 + decorate_view(_owrapped_view, view) + return _owrapped_view + +def _predicate_wrap(view, 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 + decorate_view(predicate_wrapper, view) + return predicate_wrapper + +def _secure_view(view, permission, authn_policy, authz_policy): + 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 authn_policy and authz_policy and (permission is not None): + def _secured_view(context, request): + principals = authn_policy.effective_principals(request) + if authz_policy.permits(context, principals, permission): + return view(context, request) + msg = getattr(request, 'authdebug_message', + 'Unauthorized: %s failed permission check' % view) + raise Forbidden(msg) + _secured_view.__call_permissive__ = view + def _permitted(context, request): + principals = authn_policy.effective_principals(request) + return authz_policy.permits(context, principals, permission) + _secured_view.__permitted__ = _permitted + wrapped_view = _secured_view + decorate_view(wrapped_view, view) + + return wrapped_view + +def _authdebug_view(view, permission, authn_policy, authz_policy, settings, + logger): + wrapped_view = view + if settings and settings.get('debug_authorization', False): + def _authdebug_view(context, request): + view_name = getattr(request, 'view_name', None) + + if authn_policy and authz_policy: + if permission is None: + msg = 'Allowed (no permission registered)' + else: + principals = authn_policy.effective_principals(request) + msg = str(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)) + logger and logger.debug(msg) + if request is not None: + request.authdebug_message = msg + return view(context, request) + + wrapped_view = _authdebug_view + decorate_view(wrapped_view, view) + + return wrapped_view + +def _attr_wrap(view, accept, order, 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 + decorate_view(attr_view, view) + return attr_view + +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))) + ) + +class ActionPredicate(object): + action_name = 'action' + def __init__(self, action): + self.action = action + try: + self.action_re = re.compile(action + '$') + except (re.error, TypeError), why: + raise ConfigurationError(why[0]) + + def __call__(self, context, request): + matchdict = request.matchdict + if matchdict is None: + return False + action = matchdict.get(self.action_name) + if action is None: + return False + return bool(self.action_re.match(action)) + + def __hash__(self): + # allow this predicate's phash to be compared as equal to + # others that share the same action name + return hash(self.action) + +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 + diff --git a/pyramid/configuration.py b/pyramid/configuration.py index 33a02e1b7..8cf764840 100644 --- a/pyramid/configuration.py +++ b/pyramid/configuration.py @@ -1,2285 +1,16 @@ -import inspect -import os -import re -import sys -import threading -import traceback - -import venusian - -from translationstring import ChameleonTranslate - -from zope.configuration import xmlconfig -from zope.configuration.config import GroupingContextDecorator -from zope.configuration.config import ConfigurationMachine -from zope.configuration.xmlconfig import registerCommonDirectives -from zope.deprecation import deprecated - -from zope.interface import Interface -from zope.interface import implementedBy -from zope.interface.interfaces import IInterface -from zope.interface import implements - -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 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 - -try: - from pyramid import chameleon_text -except TypeError: # pragma: no cover - chameleon_text = None # pypy -try: - from pyramid import chameleon_zpt -except TypeError: # pragma: no cover - chameleon_zpt = None # pypy - -from pyramid import renderers -from pyramid.authorization import ACLAuthorizationPolicy -from pyramid.compat import all -from pyramid.compat import md5 -from pyramid.events import ApplicationCreated -from pyramid.exceptions import ConfigurationError -from pyramid.exceptions import Forbidden -from pyramid.exceptions import NotFound -from pyramid.exceptions import PredicateMismatch -from pyramid.i18n import get_localizer -from pyramid.log import make_stream_logger -from pyramid.mako_templating import renderer_factory as mako_renderer_factory +from pyramid.config import Configurator as BaseConfigurator +from pyramid.config import ConfigurationError # API +from pyramid.config import DEFAULT_RENDERERS 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.resource import PackageOverrides -from pyramid.resource import resolve_resource_spec -from pyramid.settings import Settings -from pyramid.static import StaticURLInfo -from pyramid.threadlocal import get_current_registry -from pyramid.threadlocal import get_current_request -from pyramid.threadlocal import manager -from pyramid.traversal import DefaultRootFactory -from pyramid.traversal import find_interface -from pyramid.traversal import traversal_path -from pyramid.urldispatch import RoutesMapper -from pyramid.util import DottedNameResolver -from pyramid.view import default_exceptionresponse_view -from pyramid.view import render_view_to_response - -MAX_ORDER = 1 << 30 -DEFAULT_PHASH = md5().hexdigest() - -DEFAULT_RENDERERS = ( - ('.mak', mako_renderer_factory), - ('.mako', mako_renderer_factory), - ('json', renderers.json_renderer_factory), - ('string', renderers.string_renderer_factory), - ) - -if chameleon_text: - DEFAULT_RENDERERS += (('.pt', chameleon_zpt.renderer_factory),) -if chameleon_zpt: - DEFAULT_RENDERERS += (('.txt', chameleon_text.renderer_factory),) - -def config_method(wrapped): - def wrapper(self, *arg, **kw): - context = self._ctx - if context is not None: - if (not context.autocommit) and (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). - # This is nasty. - newctx = GroupingContextDecorator(context) - try: - f = traceback.extract_stack(limit=3) - info = f[-2] - except: - info = '' - newctx.info = info - self._ctx = newctx - result = wrapped(self, *arg, **kw) - if context is not None: - self._ctx = context - return result - wrapper.__doc__ = wrapped.__doc__ - wrapper.__name__ = wrapped.__name__ - return wrapper - -class Config(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``, and - ``renderer_globals_factory``. - - 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.configuration.Configurator.add_renderer`). If - it is not passed, a default set of renderer factories is used. - - If ``debug_logger`` is not passed, a default debug logger that - logs to stderr will be used. If it is passed, it should be an - instance of the :class:`logging.Logger` (PEP 282) standard library - class or a :term:`dotted Python name` to same. The debug logger - is used by :app:`Pyramid` itself to log warnings and - authorization debugging information. - - If ``locale_negotiator`` is passed, it should be a :term:`locale - negotiator` implementation or a :term:`dotted Python name` to - same. See :ref:`custom_locale_negotiator`. - - If ``request_factory`` is passed, it should be a :term:`request - factory` implementation or a :term:`dotted Python name` to same. - See :ref:`custom_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:`custom_renderer_globals_factory`. - By default, it is ``None``, which means use no renderer globals - factory. - - 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 - :ref:`pyramid.configration.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. """ - - manager = manager # for testing injection - venusian = venusian # for testing injection - _ctx = 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, - autocommit=False, - ): - if package is None: - package = caller_package() - name_resolver = DottedNameResolver(package) - self.name_resolver = name_resolver - self.package_name = name_resolver.package_name - self.package = name_resolver.package - self.registry = registry - self.autocommit = autocommit - if registry is None: - registry = Registry(self.package_name) - self.registry = registry - self.setup_registry( - settings=settings, - root_factory=root_factory, - authentication_policy=authentication_policy, - authorization_policy=authorization_policy, - renderers=renderers, - debug_logger=debug_logger, - locale_negotiator=locale_negotiator, - request_factory=request_factory, - renderer_globals_factory=renderer_globals_factory, - default_permission=default_permission, - session_factory=session_factory, - ) - - def _action(self, discriminator, callable=None, args=(), kw=None, order=0): - """ Register an action which will be executed during a commit. """ - if kw is None: - kw = {} - if self.autocommit: - if callable is not None: - callable(*args, **kw) - else: - if self._ctx is None: - self._ctx = self._make_context(self.autocommit) - self._ctx.action(discriminator, callable, args, kw, order) - - def _set_settings(self, mapping): - settings = Settings(mapping or {}) - self.registry.settings = settings - return settings - - @config_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) - - @config_method - def _set_authentication_policy(self, policy): - """ Add a :app:`Pyramid` :term:`authentication policy` to - the current configuration.""" - policy = self.maybe_dotted(policy) - self.registry.registerUtility(policy, IAuthenticationPolicy) - self._action(IAuthenticationPolicy) - - @config_method - def _set_authorization_policy(self, policy): - """ Add a :app:`Pyramid` :term:`authorization policy` to - the current configuration state (also accepts a :term:`dotted - Python name`.""" - policy = self.maybe_dotted(policy) - self.registry.registerUtility(policy, IAuthorizationPolicy) - self._action(IAuthorizationPolicy, None) - - def _make_spec(self, path_or_spec): - package, filename = resolve_resource_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_resource_spec(path_or_spec, self.package_name) - - 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): - if renderer is None: # use default renderer if one exists - default_renderer_factory = self.registry.queryUtility( - IRendererFactory) - if default_renderer_factory is not None: - renderer = {'name':None, 'package':self.package} - view = self.maybe_dotted(view) - authn_policy = self.registry.queryUtility(IAuthenticationPolicy) - authz_policy = self.registry.queryUtility(IAuthorizationPolicy) - settings = self.registry.settings - logger = self.registry.queryUtility(IDebugLogger) - mapped_view = _map_view(view, self.registry, attr, renderer) - owrapped_view = _owrap_view(mapped_view, viewname, wrapper_viewname) - secured_view = _secure_view(owrapped_view, permission, - authn_policy, authz_policy) - debug_view = _authdebug_view(secured_view, permission, - authn_policy, authz_policy, settings, - logger) - predicated_view = _predicate_wrap(debug_view, predicates) - derived_view = _attr_wrap(predicated_view, accept, order, phash) - return derived_view - - 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) - - def _set_security_policies(self, authentication, authorization=None): - if authorization is None: - authorization = ACLAuthorizationPolicy() # default - if authorization and not authentication: - raise ConfigurationError( - 'If the "authorization" is passed a value, ' - 'the "authentication" argument must also be ' - 'passed a value; authorization requires authentication.') - self._set_authentication_policy(authentication) - self._set_authorization_policy(authorization) - - def _fix_registry(self): - """ Fix up a ZCA component registry that is not a - pyramid.registry.Registry by adding analogues of ``has_listeners``, - and ``notify`` 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 - - def _make_context(self, autocommit=False): - context = PyramidConfigurationMachine() - registerCommonDirectives(context) - context.registry = self.registry - context.autocommit = autocommit - return context - - # API - - def commit(self): - """ Commit pending configuration actions. """ - if self._ctx is None: - return - self._ctx.execute_actions() - # unwrap and reset the context - self._ctx = None - - @classmethod - def with_context(cls, context): - """ Used by ZCML directives to obtain a configurator with 'the right' - context """ - configurator = cls(registry=context.registry, package=context.package, - autocommit=context.autocommit) - configurator._ctx = context - return configurator - - def with_package(self, package): - """ Return a new Configurator instance with the same registry - as this configurator using the package supplied as the - ``package`` argument to the new configurator. ``package`` may - be an actual Python package object or a Python dotted name - representing a package.""" - context = self._ctx - if context is None: - context = self._ctx = self._make_context(self.autocommit) - context = GroupingContextDecorator(context) - context.package = package - return self.__class__.with_context(context) - - 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_resource_spec(self, relative_spec): - """ Resolve the potentially relative :term:`resource - specification` string passed as ``relative_spec`` into an - absolute resource specification string and return the string. - Use the ``package`` of this configurator as the package to - which the resource specification will be considered relative - when generating an absolute resource 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) - - 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): - """ When you pass a non-``None`` ``registry`` argument to the - :term:`Configurator` constructor, no initial 'setup' is - performed against the registry. This is because the registry - you pass in may have already been initialized for use under - :app:`Pyramid` via a different configurator. However, in - some circumstances, such as when you want to use the Zope - 'global` registry instead of a registry created as a result of - the Configurator constructor, or when you want to reset the - initial setup of a registry, you *do* want to explicitly - initialize the registry associated with a Configurator for use - under :app:`Pyramid`. Use ``setup_registry`` to do this - initialization. - - ``setup_registry`` configures settings, a root factory, - security policies, renderers, a debug logger, a locale - negotiator, and various other settings using the - configurator's current registry, as per the descriptions in - the Configurator constructor.""" - registry = self.registry - self._fix_registry() - self._set_settings(settings) - self._set_root_factory(root_factory) - debug_logger = self.maybe_dotted(debug_logger) - if debug_logger is None: - debug_logger = make_stream_logger('pyramid.debug', sys.stderr) - registry.registerUtility(debug_logger, IDebugLogger) - if authentication_policy or authorization_policy: - self._set_security_policies(authentication_policy, - authorization_policy) - for name, renderer in renderers: - self.add_renderer(name, renderer) - self.add_view(default_exceptionresponse_view, - context=IExceptionResponse) - 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: - renderer_globals_factory = self.maybe_dotted( - renderer_globals_factory) - self.set_renderer_globals_factory(renderer_globals_factory) - if default_permission: - self.set_default_permission(default_permission) - if session_factory is not None: - self.set_session_factory(session_factory) - self.commit() - - # getSiteManager is a unit testing dep injection - def hook_zca(self, getSiteManager=None): - """ 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`.""" - if getSiteManager is None: - from zope.component import getSiteManager - getSiteManager.sethook(get_current_registry) - - # getSiteManager is a unit testing dep injection - def unhook_zca(self, getSiteManager=None): - """ Call :func:`zope.component.getSiteManager.reset` to undo - the action of - :meth:`pyramid.configuration.Configurator.hook_zca`. If - :mod:`zope.component` cannot be imported, this method will - raise an :exc:`ImportError`.""" - if getSiteManager is None: # pragma: no cover - 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. - - 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. """ - - if renderer is not None and not isinstance(renderer, dict): - renderer = {'name':renderer, 'package':self.package} - return self._derive_view(view, attr=attr, renderer=renderer) - - @config_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 - - 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.configuration.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.configuration.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 'settings' object for the current application. A - '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.configuration.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): - """ Returns a :app:`Pyramid` WSGI application representing - the current configuration state and sends a - :class:`pyramid.events.ApplicationCreated` - event to all listeners.""" - self.commit() - from pyramid.router import Router # avoid circdep - app = Router(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 - - @config_method - def load_zcml(self, spec='configure.zcml', lock=threading.Lock()): - """ Load configuration from a :term:`ZCML` file into the - current configuration state. The ``spec`` argument is an - absolute filename, a relative filename, or a :term:`resource - specification`, defaulting to ``configure.zcml`` (relative to - the package of the configurator's caller).""" - package_name, filename = self._split_spec(spec) - if package_name is None: # absolute filename - package = self.package - else: - __import__(package_name) - package = sys.modules[package_name] - - registry = self.registry - self.manager.push({'registry':registry, 'request':None}) - context = self._ctx - if context is None: - context = self._ctx = self._make_context(self.autocommit) - - lock.acquire() - try: - context.package = package - xmlconfig.file(filename, package, context=context, - execute=self.autocommit) - finally: - lock.release() - self.manager.pop() - return registry - - def include(self, *funcs): - """ Include one or more configuration callables. 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 which - add configuration state. The return value of a callable will be - ignored. - - Values allowed to be presented via the ``*funcs`` argument to this - method: any callable Python object or any :term:`dotted Python name` - which resolves to a callable Python object. - """ - sourcefiles = [] - - for func in funcs: - func = self.maybe_dotted(func) - sourcefile = inspect.getsourcefile(func) - module = inspect.getmodule(func) - sourcefiles.append((sourcefile, func, module)) - - _context = self._ctx - if _context is None: - _context = self._ctx = self._make_context(self.autocommit) - - for filename, func, module in sourcefiles: - spec = module.__name__ + ':' + func.__name__ - if _context.processSpec(spec): - context = GroupingContextDecorator(_context) - context.basepath = os.path.dirname(filename) - context.includepath = _context.includepath + (spec,) - context.package = package_of(module) - config = self.__class__.with_context(context) - func(config) - - def add_handler(self, route_name, pattern, handler, action=None, **kw): - - """ Add a Pylons-style view handler. This function adds a - route and some number of views based on a handler object - (usually a class). - - ``route_name`` is the name of the route (to be used later in - URL generation). - - ``pattern`` is the matching pattern, - e.g. ``'/blog/{action}'``. ``pattern`` may be ``None``, in - which case the pattern of an existing route named the same as - ``route_name`` is used. If ``pattern`` is ``None`` and no - route named ``route_name`` exists, a ``ConfigurationError`` is - raised. - - ``handler`` is a dotted name of (or direct reference to) a - Python handler class, - e.g. ``'my.package.handlers.MyHandler'``. - - If ``{action}`` or ``:action`` is in - the pattern, the exposed methods of the handler will be used - as views. - - If ``action`` is passed, it will be considered the method name - of the handler to use as a view. - - Passing both ``action`` and having an ``{action}`` in the - route pattern is disallowed. - - Any extra keyword arguments are passed along to ``add_route``. - - See :ref:`handlers_chapter` for more explanatory documentation. - - This method returns the result of add_route.""" - handler = self.maybe_dotted(handler) - - if pattern is not None: - route = self.add_route(route_name, pattern, **kw) - else: - mapper = self.get_routes_mapper() - route = mapper.get_route(route_name) - if route is None: - raise ConfigurationError( - 'The "pattern" parameter may only be "None" when a route ' - 'with the route_name argument was previously registered. ' - 'No such route named %r exists' % route_name) - - pattern = route.pattern - - path_has_action = ':action' in pattern or '{action}' in pattern - - if action and path_has_action: - raise ConfigurationError( - 'action= (%r) disallowed when an action is in the route ' - 'path %r' % (action, pattern)) - - if path_has_action: - autoexpose = getattr(handler, '__autoexpose__', r'[A-Za-z]+') - if autoexpose: - try: - autoexpose = re.compile(autoexpose).match - except (re.error, TypeError), why: - raise ConfigurationError(why[0]) - for method_name, method in inspect.getmembers( - handler, inspect.ismethod): - configs = getattr(method, '__exposed__', []) - if autoexpose and not configs: - if autoexpose(method_name): - configs = [{}] - for expose_config in configs: - # we don't want to mutate any dict in __exposed__, - # so we copy each - view_args = expose_config.copy() - action = view_args.pop('name', method_name) - preds = list(view_args.pop('custom_predicates', [])) - preds.append(ActionPredicate(action)) - view_args['custom_predicates'] = preds - self.add_view(view=handler, attr=method_name, - route_name=route_name, **view_args) - else: - method_name = action - if method_name is None: - method_name = '__call__' - - # Scan the controller for any other methods with this action name - for meth_name, method in inspect.getmembers( - handler, inspect.ismethod): - configs = getattr(method, '__exposed__', [{}]) - for expose_config in configs: - # Don't re-register the same view if this method name is - # the action name - if meth_name == action: - continue - # We only reg a view if the name matches the action - if expose_config.get('name') != method_name: - continue - # we don't want to mutate any dict in __exposed__, - # so we copy each - view_args = expose_config.copy() - del view_args['name'] - self.add_view(view=handler, attr=meth_name, - route_name=route_name, **view_args) - - # Now register the method itself - method = getattr(handler, method_name, None) - configs = getattr(method, '__exposed__', [{}]) - for expose_config in configs: - self.add_view(view=handler, attr=action, route_name=route_name, - **expose_config) - - return route - - @config_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): - """ 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.configuration.Configurator` constructor's - ``default_permission`` argument, or if - :meth:`pyramid.configuration.Configurator.set_default_permission` - was used prior to this view registration. Pass the string - ``__no_permission_required__`` as the permission argument to - explicitly indicate that the view should always be - executable by entirely anonymous users, regardless of the - default permission, bypassing any :term:`authorization - policy` that may be in effect. - - attr - - The view machinery defaults to using the ``__call__`` method - of the :term:`view callable` (or the function itself, if the - view callable is a function) to obtain a response. The - ``attr`` value allows you to vary the method attribute used - to obtain the response. For example, if your view was a - class, and the class has a method named ``index`` and you - wanted to use this method instead of the class' ``__call__`` - method to return the response, you'd say ``attr="index"`` in the - view configuration for the view. This is - most useful when the view definition is a class. - - renderer - - This is either a single string term (e.g. ``json``) or a - string implying a path or :term:`resource 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:`resource specification` in the form - ``some.dotted.package_name:relative/path``, making it - possible to address template resources 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 unmolested). - - 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. - - 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. Note that - the ``route`` configuration referred to by ``route_name`` - usually has a ``*traverse`` token in the value of its - ``path``, representing a part of the path that will be used - by :term:`traversal` against the result of the route's - :term:`root factory`. - - .. warning:: Using this argument services an advanced - feature that isn't often used unless you want to perform - traversal *after* a route has matched. See - :ref:`hybrid_chapter` for more information on using this - advanced feature. - - 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 either 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_params="foo=123"``, then the key (``foo``) - must both exist in the ``request.params`` dictionary, *and* - the value must match the right hand side of the expression - (``123``) for the view to "match" the current request. - - containment - - This value should be a Python class or :term:`interface` or - a :term:`dotted Python name` to such an object that a parent - object in the :term:`lineage` 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) - - 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, - ) - 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 renderer is not None and not isinstance(renderer, dict): - renderer = {'name':renderer, 'package':self.package} - - 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) - - def register(permission=permission): - - 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 - derived_view = self._derive_view(view, permission, predicates, attr, - renderer, wrapper, name, accept, - order, phash) - - 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) - - @config_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, - ): - """ 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` :term:`context` - object when this route matches. For example, - ``mypackage.models.MyFactoryClass``. If this argument is - not specified, a default root factory will be used. - - traverse - - If you would like to cause the :term:`context` to be - something other than the :term:`root` object when this route - matches, you can spell a traversal pattern as the - ``traverse`` argument. This traversal pattern will be used - as the traversal path: traversal will begin at the root - object implied by this route (either the global root, or the - object returned by the ``factory`` associated with this - route). - - The syntax of the ``traverse`` argument is the same as it is - for ``pattern``. For example, if the ``pattern`` provided to - ``add_route`` is ``articles/{article}/edit``, and the - ``traverse`` argument provided to ``add_route`` is - ``/{article}``, when a request comes in that causes the route - to match in such a way that the ``article`` match value is - '1' (when the request URI is ``/articles/1/edit``), the - traversal path will be generated as ``/1``. This means that - the root object's ``__getitem__`` will be called with the - name ``1`` during the traversal phase. If the ``1`` object - exists, it will become the :term:`context` of the request. - :ref:`traversal_chapter` has more information about - traversal. - - If the traversal path contains segment marker names which - are not present in the ``pattern`` argument, a runtime error - will occur. The ``traverse`` pattern should not contain - segment markers that do not exist in the ``pattern`` - argument. - - A similar combining of routing and traversal is available - when a route is matched which contains a ``*traverse`` - remainder marker in its pattern (see - :ref:`using_traverse_in_a_route_pattern`). The ``traverse`` - argument to add_route allows you to associate route patterns - with an arbitrary traversal path without using a a - ``*traverse`` remainder marker; instead you can use other - match information. - - Note that the ``traverse`` argument to ``add_route`` is - ignored when attached to a route that has a ``*traverse`` - remainder marker in its pattern. - - pregenerator - This option should be a callable object that implements the - :class:`pyramid.interfaces.IRoutePregenerator` - interface. A :term:`pregenerator` is a callable called by - the :mod:`pyramid.url.route_url` function to augment or - replace the arguments it is passed when generating a URL - for the route. This is a feature not often used directly - by applications, it is meant to be hooked by frameworks - that use :app:`Pyramid` as a base. - - Predicate Arguments - - pattern - - The pattern of the route e.g. ``ideas/{idea}``. This - argument is required. See :ref:`route_path_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_params="foo=123"``, then the key - (``foo``) must both exist in the ``request.params`` dictionary, and - the value must match the right hand side of the expression (``123``) - for the 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 - - view - - 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 - - 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 - - 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 - - This is either a single string term (e.g. ``json``) or a - string implying a path or :term:`resource 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 - - 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. - - 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). - - """ - # 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: - bases = use_global_views and (IRequest,) or () - 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) - - if view: - if view_context is None: - view_context = view_for - if view_context is None: - view_context = for_ - view_permission = view_permission or permission - view_renderer = view_renderer or renderer - self.add_view( - permission=view_permission, - context=view_context, - view=view, - name='', - route_name=name, - renderer=view_renderer, - attr=view_attr, - ) - - mapper = self.get_routes_mapper() - - factory = self.maybe_dotted(factory) - if pattern is None: - pattern = path - if pattern is None: - raise ConfigurationError('"pattern" argument may not be None') - - discriminator = ['route', name, xhr, request_method, path_info, - request_param, header, accept] - discriminator.extend(sorted(custom_predicates)) - discriminator = tuple(discriminator) - - self._action(discriminator, None) - - return mapper.connect(name, pattern, factory, predicates=predicates, - pregenerator=pregenerator) - - 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 - - @config_method - def scan(self, package=None, categories=None): - """ 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`. If this is not desirable - because the codebase has other Venusian-using decorators that - aren't meant to be invoked during a particular scan, use - ``('pyramid',)`` as a ``categories`` value to limit the execution - of decorator callbacks to only those registered by - :app:`Pyramid` itself. Or pass a sequence of Venusian scan - categories as necessary (e.g. ``('pyramid', 'myframework')``) to - limit the decorators called to the set of categories required. - """ - package = self.maybe_dotted(package) - if package is None: # pragma: no cover - package = caller_package() - - scanner = self.venusian.Scanner(config=self) - scanner.scan(package, categories=categories) - - @config_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) - - @config_method - def override_resource(self, to_override, override_with, _override=None): - """ Add a :app:`Pyramid` resource override to the current - configuration state. - - ``to_override`` is a :term:`resource specification` to the - resource being overridden. - - ``override_with`` is a :term:`resource specification` to the - resource that is performing the override. - - See :ref:`resources_chapter` for more - information about resource overrides.""" - if to_override == override_with: - raise ConfigurationError('You cannot override a resource 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) - - if path and path.endswith('/'): - if override_prefix and (not override_prefix.endswith('/')): - raise ConfigurationError( - 'A directory cannot be overridden with a file (put a ' - 'slash at the end of override_with if necessary)') - - if override_prefix and override_prefix.endswith('/'): - if path and (not path.endswith('/')): - 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) - - 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.configuration.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 renderer is not None and not isinstance(renderer, dict): - renderer = {'name':renderer, 'package':self.package} - 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=Forbidden, wrapper=wrapper) - - 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.configuration.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 renderer is not None and not isinstance(renderer, dict): - renderer = {'name':renderer, 'package':self.package} - 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=NotFound, wrapper=wrapper) - - @config_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 :meth:``request_factory`` argument to the - :class:`pyramid.configuration.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) - - @config_method - def set_renderer_globals_factory(self, factory): - """ 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. - - .. note:: Using the :meth:`renderer_globals_factory` - argument to the - :class:`pyramid.configuration.Configurator` constructor - can be used to achieve the same purpose. - """ - factory = self.maybe_dotted(factory) - def register(): - self.registry.registerUtility(factory, IRendererGlobalsFactory) - self._action(IRendererGlobalsFactory, register) - - @config_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.configuration.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) - - @config_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 earlier calls; there can - be only one default permission active at a time within an - application. - - See also :ref:`setting_a_default_permission`. - - .. note:: Using the ``default_permission`` argument to the - :class:`pyramid.configuration.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) - - @config_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. - """ - def register(): - self.registry.registerUtility(session_factory, ISessionFactory) - self._action(ISessionFactory, register) - - @config_method - 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:`resource specification` - names naming a directory path (e.g. ``some.package:locale``) - or a combination of the two. - - Example: - - .. code-block:: python - - add_translations_dirs('/usr/share/locale', 'some.package:locale') - - """ - for spec in specs: - - 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. - - def translator(msg): - request = get_current_request() - localizer = get_localizer(request) - return localizer.translate(msg) - - ctranslate = ChameleonTranslate(translator) - self.registry.registerUtility(ctranslate, IChameleonTranslate) - - def add_static_view(self, name, path, **kw): - """ Add a view used to render static resources such as images - and CSS files. - - The ``name`` argument is a string representing :term:`view - name` of the view which is registered. It may alternately be - a *url prefix*. - - 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:`resource specification`. - - The ``cache_max_age`` keyword argument is input to set the - ``Expires`` and ``Cache-Control`` headers for static resources - served. Note that this argument has no effect when the - ``name`` is a *url prefix*. By default, this argument is - ``None``, meaning that no particular Expires or Cache-Control - headers are set in the response. - - The ``permission`` keyword argument is used to specify the - :term:`permission` required by a user to execute the static - view. By default, it is the string - ``__no_permission_required__``. The - ``__no_permission_required__`` string is a special sentinel - which indicates that, even if a :term:`default permission` - exists for the current application, the static view should be - renderered to completely anonymous users. This default value - is permissive because, in most web apps, static resources - seldom need protection from viewing. - - *Usage* - - The ``add_static_view`` function is typically used in - conjunction with the :func:`pyramid.url.static_url` - function. ``add_static_view`` adds a view which renders a - static resource when some URL is visited; - :func:`pyramid.url.static_url` generates a URL to that - resource. - - The ``name`` argument to ``add_static_view`` is usually a - :term:`view name`. When this is the case, the - :func:`pyramid.url.static_url` API will generate a URL - which points to a Pyramid view, which will serve up a set of - resources that live in the package itself. For example: - - .. code-block:: python - - add_static_view('images', 'mypackage:images/') - - Code that registers such a view can generate URLs to the view - via :func:`pyramid.url.static_url`: - - .. code-block:: python - - static_url('mypackage:images/logo.png', request) - - When ``add_static_view`` is called with a ``name`` argument - that represents a simple view name, as it is above, subsequent - calls to :func:`pyramid.url.static_url` with paths that - start with the ``path`` argument passed to ``add_static_view`` - will generate a URL something like ``http:///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 resources to be - served from an external webserver. This happens when the - ``name`` argument is a URL (detected as any string with a - slash in it). In this mode, the ``name`` is used as the URL - prefix when generating a URL using - :func:`pyramid.url.static_url`. For example, if - ``add_static_view`` is called like so: - - .. code-block:: python - - add_static_view('http://example.com/images', 'mypackage:images/') - - Subsequently, the URLs generated by - :func:`pyramid.url.static_url` for that static view will be - prefixed with ``http://example.com/images``: - - .. code-block:: python - - static_url('mypackage:images/logo.png', request) - - When ``add_static_view`` is called with a ``name`` argument - that is the URL prefix ``http://example.com/images``, - subsequent calls to :func:`pyramid.url.static_url` with - paths that start with the ``path`` argument passed to - ``add_static_view`` will generate a URL something like - ``http://example.com/logo.png``. The external webserver - listening on ``example.com`` must be itself configured to - respond properly to such a request. - - See :ref:`static_resources_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_models(self, models): - """Unit/integration testing helper: registers a dictionary of - :term:`model` objects that can be resolved via the - :func:`pyramid.traversal.find_model` API. - - The :func:`pyramid.traversal.find_model` 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_model`` (and - thus to your code) when - :func:`pyramid.traversal.find_model` is called with an - equivalent path string or tuple. - """ - class DummyTraverserFactory: - def __init__(self, context): - self.context = context - - def __call__(self, request): - path = request['PATH_INFO'] - ob = models[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 models - - 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`, - :func:`zope.component.event.dispatch` or - :func:`zope.component.event.objectEventNotify`. - - 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 a resource 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 +from zope.deprecation import deprecated - testing_add_template = testing_add_renderer +deprecated( + 'ConfigurationError', + 'pyramid.configuration.ConfigurationError is deprecated as of ' + 'Pyramid 1.0. Use ``pyramid.config.ConfigurationError`` instead.') -class Configurator(Config): +class Configurator(BaseConfigurator): def __init__(self, registry=None, package=None, @@ -2295,11 +26,10 @@ class Configurator(Config): default_permission=None, session_factory=None, autocommit=True, - context=None, ): if package is None: package = caller_package() - Config.__init__( + BaseConfigurator.__init__( self, registry=registry, package=package, @@ -2315,665 +45,10 @@ class Configurator(Config): default_permission=default_permission, session_factory=session_factory, autocommit=autocommit, - context=context, ) deprecated( 'Configurator', - '(pyramid.configuration.Configurator is deprecated as of Pyramid 1.0. Use' - '``pyramid.configuration.Config`` with ``autocommit=True`` instead.) ') - -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 - 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 - 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 - 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) - 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 - 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]) - 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 - 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 - 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 - 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) - 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 ModelGraphTraverser 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): - 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 decorate_view(wrapped_view, original_view): - if wrapped_view is original_view: - return False - wrapped_view.__module__ = original_view.__module__ - wrapped_view.__doc__ = original_view.__doc__ - try: - wrapped_view.__name__ = original_view.__name__ - except AttributeError: - wrapped_view.__name__ = repr(original_view) - try: - wrapped_view.__permitted__ = original_view.__permitted__ - except AttributeError: - pass - try: - wrapped_view.__call_permissive__ = original_view.__call_permissive__ - except AttributeError: - pass - try: - wrapped_view.__predicated__ = original_view.__predicated__ - except AttributeError: - pass - try: - wrapped_view.__accept__ = original_view.__accept__ - except AttributeError: - pass - try: - wrapped_view.__order__ = original_view.__order__ - except AttributeError: - pass - return True - -def requestonly(class_or_callable, attr=None): - """ Return true of the class or callable accepts only a request argument, - as opposed to something that accepts context, request """ - if attr is None: - attr = '__call__' - if inspect.isfunction(class_or_callable): - fn = class_or_callable - elif inspect.isclass(class_or_callable): - try: - fn = class_or_callable.__init__ - except AttributeError: - return False - else: - try: - fn = getattr(class_or_callable, attr) - except AttributeError: - return False - - try: - argspec = inspect.getargspec(fn) - except TypeError: - return False - - args = argspec[0] - defaults = argspec[3] - - 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 - - elif args[0] == 'request': - if len(args) - len(defaults) == 1: - return True - - return False - -def is_response(ob): - if ( hasattr(ob, 'app_iter') and hasattr(ob, 'headerlist') and - hasattr(ob, 'status') ): - return True - return False - -def _map_view(view, registry, attr=None, renderer=None): - wrapped_view = view - - helper = None - - if renderer is not None: - helper = RendererHelper(renderer['name'], - package=renderer['package'], - registry=registry) - - if inspect.isclass(view): - # If the object we've located is a class, turn it into a - # function that operates like a Zope view (when it's invoked, - # construct an instance using 'context' and 'request' as - # position arguments, then immediately invoke the __call__ - # method of the instance with no arguments; __call__ should - # return an IResponse). - if requestonly(view, attr): - # its __init__ accepts only a single request argument, - # instead of both context and request - def _class_requestonly_view(context, request): - inst = view(request) - if attr is None: - response = inst() - else: - response = getattr(inst, attr)() - if helper is not None: - if not is_response(response): - system = { - 'view':inst, - 'renderer_name':renderer['name'], # b/c - 'renderer_info':renderer, - 'context':context, - 'request':request - } - response = helper.render_to_response(response, system, - request=request) - return response - wrapped_view = _class_requestonly_view - else: - # its __init__ accepts both context and request - def _class_view(context, request): - inst = view(context, request) - if attr is None: - response = inst() - else: - response = getattr(inst, attr)() - if helper is not None: - if not is_response(response): - system = {'view':inst, - 'renderer_name':renderer['name'], # b/c - 'renderer_info':renderer, - 'context':context, - 'request':request - } - response = helper.render_to_response(response, system, - request=request) - return response - wrapped_view = _class_view - - elif requestonly(view, attr): - # its __call__ accepts only a single request argument, - # instead of both context and request - def _requestonly_view(context, request): - if attr is None: - response = view(request) - else: - response = getattr(view, attr)(request) - - if helper is not None: - if not is_response(response): - system = { - 'view':view, - 'renderer_name':renderer['name'], - 'renderer_info':renderer, - 'context':context, - 'request':request - } - response = helper.render_to_response(response, system, - request=request) - return response - wrapped_view = _requestonly_view - - elif attr: - def _attr_view(context, request): - response = getattr(view, attr)(context, request) - if helper is not None: - if not is_response(response): - system = { - 'view':view, - 'renderer_name':renderer['name'], - 'renderer_info':renderer, - 'context':context, - 'request':request - } - response = helper.render_to_response(response, system, - request=request) - return response - wrapped_view = _attr_view - - elif helper is not None: - def _rendered_view(context, request): - response = view(context, request) - if not is_response(response): - system = { - 'view':view, - 'renderer_name':renderer['name'], # b/c - 'renderer_info':renderer, - 'context':context, - 'request':request - } - response = helper.render_to_response(response, system, - request=request) - return response - wrapped_view = _rendered_view - - decorate_view(wrapped_view, view) - return wrapped_view - -def _owrap_view(view, viewname, wrapper_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 - decorate_view(_owrapped_view, view) - return _owrapped_view - -def _predicate_wrap(view, 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 - decorate_view(predicate_wrapper, view) - return predicate_wrapper - -def _secure_view(view, permission, authn_policy, authz_policy): - 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 authn_policy and authz_policy and (permission is not None): - def _secured_view(context, request): - principals = authn_policy.effective_principals(request) - if authz_policy.permits(context, principals, permission): - return view(context, request) - msg = getattr(request, 'authdebug_message', - 'Unauthorized: %s failed permission check' % view) - raise Forbidden(msg) - _secured_view.__call_permissive__ = view - def _permitted(context, request): - principals = authn_policy.effective_principals(request) - return authz_policy.permits(context, principals, permission) - _secured_view.__permitted__ = _permitted - wrapped_view = _secured_view - decorate_view(wrapped_view, view) - - return wrapped_view - -def _authdebug_view(view, permission, authn_policy, authz_policy, settings, - logger): - wrapped_view = view - if settings and settings.get('debug_authorization', False): - def _authdebug_view(context, request): - view_name = getattr(request, 'view_name', None) - - if authn_policy and authz_policy: - if permission is None: - msg = 'Allowed (no permission registered)' - else: - principals = authn_policy.effective_principals(request) - msg = str(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)) - logger and logger.debug(msg) - if request is not None: - request.authdebug_message = msg - return view(context, request) - - wrapped_view = _authdebug_view - decorate_view(wrapped_view, view) - - return wrapped_view - -def _attr_wrap(view, accept, order, 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 - decorate_view(attr_view, view) - return attr_view - -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))) - ) - -# note that ``options`` is a b/w compat alias for ``settings`` and -# ``Configurator`` is a testing dep inj -def make_app(root_factory, package=None, filename='configure.zcml', - settings=None, options=None, Configurator=Config): - """ Return a Router object, representing a fully configured - :app:`Pyramid` WSGI application. - - .. warning:: Use of this function is deprecated as of - :app:`Pyramid` 1.0. You should instead use a - :class:`pyramid.configuration.Configurator` instance to - perform startup configuration as shown in - :ref:`configuration_narr`. - - ``root_factory`` must be a callable that accepts a :term:`request` - object and which returns a traversal root object. The traversal - root returned by the root factory is the *default* traversal root; - it can be overridden on a per-view basis. ``root_factory`` may be - ``None``, in which case a 'default default' traversal root is - used. - - ``package`` is a Python :term:`package` or module representing the - application's package. It is optional, defaulting to ``None``. - ``package`` may be ``None``. If ``package`` is ``None``, the - ``filename`` passed or the value in the ``options`` dictionary - named ``configure_zcml`` must be a) absolute pathname to a - :term:`ZCML` file that represents the application's configuration - *or* b) a :term:`resource specification` to a :term:`ZCML` file in - the form ``dotted.package.name:relative/file/path.zcml``. - - ``filename`` is the filesystem path to a ZCML file (optionally - relative to the package path) that should be parsed to create the - application registry. It defaults to ``configure.zcml``. It can - also be a ;term:`resource specification` in the form - ``dotted_package_name:relative/file/path.zcml``. Note that if any - value for ``configure_zcml`` is passed within the ``settings`` - dictionary, the value passed as ``filename`` will be ignored, - replaced with the ``configure_zcml`` value. - - ``settings``, if used, should be a dictionary containing runtime - settings (e.g. the key/value pairs in an app section of a - PasteDeploy file), with each key representing the option and the - key's value representing the specific option value, - e.g. ``{'reload_templates':True}``. Note that the keyword - parameter ``options`` is a backwards compatibility alias for the - ``settings`` keyword parameter. - """ - settings = settings or options or {} - zcml_file = settings.get('configure_zcml', filename) - config = Configurator(package=package, settings=settings, - root_factory=root_factory) - config.hook_zca() - config.begin() - config.load_zcml(zcml_file) - config.end() - return config.make_wsgi_app() - -class ActionPredicate(object): - action_name = 'action' - def __init__(self, action): - self.action = action - try: - self.action_re = re.compile(action + '$') - except (re.error, TypeError), why: - raise ConfigurationError(why[0]) - - def __call__(self, context, request): - matchdict = request.matchdict - if matchdict is None: - return False - action = matchdict.get(self.action_name) - if action is None: - return False - return bool(self.action_re.match(action)) - - def __hash__(self): - # allow this predicate's phash to be compared as equal to - # others that share the same action name - return hash(self.action) - -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 + 'pyramid.configuration.Configurator is deprecated as of Pyramid 1.0. Use' + '``pyramid.config.Configurator`` with ``autocommit=True`` instead.') -def context_base(context): - while 1: - newcontext = getattr(context, 'context', None) - if newcontext is None: - return context - context = newcontext - diff --git a/pyramid/router.py b/pyramid/router.py index 972c05b62..ca2f0adc5 100644 --- a/pyramid/router.py +++ b/pyramid/router.py @@ -1,6 +1,8 @@ from zope.interface import implements from zope.interface import providedBy +from zope.deprecation import deprecated + from pyramid.interfaces import IDebugLogger from pyramid.interfaces import IExceptionViewClassifier from pyramid.interfaces import IRequest @@ -22,10 +24,7 @@ from pyramid.threadlocal import manager from pyramid.traversal import DefaultRootFactory from pyramid.traversal import ModelGraphTraverser -from pyramid.configuration import make_app # b/c - -make_app # prevent PyFlakes from complaining - +from pyramid.config import Configurator # b/c class Router(object): implements(IRouter) @@ -188,3 +187,65 @@ class Router(object): +# note that ``options`` is a b/w compat alias for ``settings`` and +# ``Configurator`` is a testing dep inj +def make_app(root_factory, package=None, filename='configure.zcml', + settings=None, options=None, Configurator=Configurator): + """ Return a Router object, representing a fully configured + :app:`Pyramid` WSGI application. + + .. warning:: Use of this function is deprecated as of + :app:`Pyramid` 1.0. You should instead use a + :class:`pyramid.config.Configurator` instance to + perform startup configuration as shown in + :ref:`configuration_narr`. + + ``root_factory`` must be a callable that accepts a :term:`request` + object and which returns a traversal root object. The traversal + root returned by the root factory is the *default* traversal root; + it can be overridden on a per-view basis. ``root_factory`` may be + ``None``, in which case a 'default default' traversal root is + used. + + ``package`` is a Python :term:`package` or module representing the + application's package. It is optional, defaulting to ``None``. + ``package`` may be ``None``. If ``package`` is ``None``, the + ``filename`` passed or the value in the ``options`` dictionary + named ``configure_zcml`` must be a) absolute pathname to a + :term:`ZCML` file that represents the application's configuration + *or* b) a :term:`resource specification` to a :term:`ZCML` file in + the form ``dotted.package.name:relative/file/path.zcml``. + + ``filename`` is the filesystem path to a ZCML file (optionally + relative to the package path) that should be parsed to create the + application registry. It defaults to ``configure.zcml``. It can + also be a ;term:`resource specification` in the form + ``dotted_package_name:relative/file/path.zcml``. Note that if any + value for ``configure_zcml`` is passed within the ``settings`` + dictionary, the value passed as ``filename`` will be ignored, + replaced with the ``configure_zcml`` value. + + ``settings``, if used, should be a dictionary containing runtime + settings (e.g. the key/value pairs in an app section of a + PasteDeploy file), with each key representing the option and the + key's value representing the specific option value, + e.g. ``{'reload_templates':True}``. Note that the keyword + parameter ``options`` is a backwards compatibility alias for the + ``settings`` keyword parameter. + """ + settings = settings or options or {} + zcml_file = settings.get('configure_zcml', filename) + config = Configurator(package=package, settings=settings, + root_factory=root_factory, autocommit=True) + config.hook_zca() + config.begin() + config.load_zcml(zcml_file) + config.end() + return config.make_wsgi_app() + + +deprecated( + 'make_app', + 'pyramid.router.make_app is deprecated as of Pyramid 1.0. Use ' + 'an instance of ``pyramid.config.Configurator`` to configure your ' + 'application instead.') diff --git a/pyramid/testing.py b/pyramid/testing.py index e6f6c5cab..b584c3b73 100644 --- a/pyramid/testing.py +++ b/pyramid/testing.py @@ -12,7 +12,7 @@ from pyramid.interfaces import ISecuredView from pyramid.interfaces import IView from pyramid.interfaces import IViewClassifier -from pyramid.configuration import Config +from pyramid.config import Configurator from pyramid.exceptions import Forbidden from pyramid.response import Response from pyramid.registry import Registry @@ -56,7 +56,7 @@ def registerDummySecurityPolicy(userid=None, groupids=(), permissive=True): method in your unit and integration tests. """ registry = get_current_registry() - config = Config(registry=registry) + config = Configurator(registry=registry) result = config.testing_securitypolicy(userid=userid, groupids=groupids, permissive=permissive) config.commit() @@ -80,7 +80,7 @@ def registerModels(models): method in your unit and integration tests. """ registry = get_current_registry() - config = Config(registry=registry) + config = Configurator(registry=registry) result = config.testing_models(models) config.commit() return result @@ -108,7 +108,7 @@ def registerEventListener(event_iface=None): method in your unit and integration tests. """ registry = get_current_registry() - config = Config(registry=registry) + config = Configurator(registry=registry) result = config.testing_add_subscriber(event_iface) config.commit() return result @@ -130,7 +130,7 @@ def registerTemplateRenderer(path, renderer=None): """ registry = get_current_registry() - config = Config(registry=registry) + config = Configurator(registry=registry) result = config.testing_add_template(path, renderer) config.commit() return result @@ -256,7 +256,7 @@ def registerSubscriber(subscriber, iface=Interface): method in your unit and integration tests. """ registry = get_current_registry() - config = Config(registry) + config = Configurator(registry) result = config.add_subscriber(subscriber, iface=iface) config.commit() return result @@ -279,7 +279,7 @@ def registerRoute(pattern, name, factory=None): method in your unit and integration tests. """ reg = get_current_registry() - config = Config(registry=reg) + config = Configurator(registry=reg) result = config.add_route(name, pattern, factory=factory) config.commit() return result @@ -307,7 +307,7 @@ def registerSettings(dictarg=None, **kw): method in your unit and integration tests. """ registry = get_current_registry() - config = Config(registry=registry) + config = Configurator(registry=registry) config.add_settings(dictarg, **kw) class DummyRootFactory(object): @@ -630,14 +630,14 @@ def setUp(registry=None, request=None, hook_zca=True, autocommit=True): manager.clear() if registry is None: registry = Registry('testing') - config = Config(registry=registry, autocommit=autocommit) + config = Configurator(registry=registry, autocommit=autocommit) if hasattr(registry, 'registerUtility'): # Sometimes nose calls us with a non-registry object because # it thinks this function is module test setup. Likewise, # someone may be passing us an esoteric "dummy" registry, and # the below won't succeed if it doesn't have a registerUtility # method. - from pyramid.configuration import DEFAULT_RENDERERS + from pyramid.config import DEFAULT_RENDERERS for name, renderer in DEFAULT_RENDERERS: # Cause the default renderers to be registered because # in-the-wild test code relies on being able to call diff --git a/pyramid/tests/test_config.py b/pyramid/tests/test_config.py new file mode 100644 index 000000000..2a41f1504 --- /dev/null +++ b/pyramid/tests/test_config.py @@ -0,0 +1,4573 @@ +import unittest + +from pyramid import testing + +try: + import __pypy__ +except: + __pypy__ = None + +class ConfiguratorTests(unittest.TestCase): + def _makeOne(self, *arg, **kw): + from pyramid.config import Configurator + return Configurator(*arg, **kw) + + def _registerRenderer(self, config, name='.txt'): + from pyramid.interfaces import IRendererFactory + from pyramid.interfaces import ITemplateRenderer + from zope.interface import implements + class Renderer: + implements(ITemplateRenderer) + def __init__(self, info): + self.__class__.info = info + def __call__(self, *arg): + return 'Hello!' + config.registry.registerUtility(Renderer, IRendererFactory, name=name) + return Renderer + + def _getViewCallable(self, config, ctx_iface=None, request_iface=None, + name='', exception_view=False): + from zope.interface import Interface + from pyramid.interfaces import IRequest + from pyramid.interfaces import IView + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IExceptionViewClassifier + if exception_view: + classifier = IExceptionViewClassifier + else: + classifier = IViewClassifier + if ctx_iface is None: + ctx_iface = Interface + if request_iface is None: + request_iface = IRequest + return config.registry.adapters.lookup( + (classifier, request_iface, ctx_iface), IView, name=name, + default=None) + + def _getRouteRequestIface(self, config, name): + from pyramid.interfaces import IRouteRequest + iface = config.registry.getUtility(IRouteRequest, name) + return iface + + def _assertNotFound(self, wrapper, *arg): + from pyramid.exceptions import NotFound + self.assertRaises(NotFound, wrapper, *arg) + + def _registerEventListener(self, config, event_iface=None): + if event_iface is None: # pragma: no cover + from zope.interface import Interface + event_iface = Interface + L = [] + def subscriber(*event): + L.extend(event) + config.registry.registerHandler(subscriber, (event_iface,)) + return L + + def _registerLogger(self, config): + from pyramid.interfaces import IDebugLogger + logger = DummyLogger() + config.registry.registerUtility(logger, IDebugLogger) + return logger + + def _makeRequest(self, config): + request = DummyRequest() + request.registry = config.registry + return request + + def _registerSecurityPolicy(self, config, permissive): + from pyramid.interfaces import IAuthenticationPolicy + from pyramid.interfaces import IAuthorizationPolicy + policy = DummySecurityPolicy(permissive) + config.registry.registerUtility(policy, IAuthenticationPolicy) + config.registry.registerUtility(policy, IAuthorizationPolicy) + + def _registerSettings(self, config, **settings): + config.registry.settings = settings + + def test_ctor_no_registry(self): + import sys + from pyramid.interfaces import ISettings + from pyramid.config import Configurator + from pyramid.interfaces import IRendererFactory + config = Configurator() + this_pkg = sys.modules['pyramid.tests'] + self.failUnless(config.registry.getUtility(ISettings)) + self.assertEqual(config.package, this_pkg) + self.failUnless(config.registry.getUtility(IRendererFactory, 'json')) + self.failUnless(config.registry.getUtility(IRendererFactory, 'string')) + if not __pypy__: + self.failUnless(config.registry.getUtility(IRendererFactory, '.pt')) + self.failUnless(config.registry.getUtility(IRendererFactory,'.txt')) + self.failUnless(config.registry.getUtility(IRendererFactory, '.mak')) + self.failUnless(config.registry.getUtility(IRendererFactory, '.mako')) + + def test_begin(self): + from pyramid.config import Configurator + config = Configurator() + manager = DummyThreadLocalManager() + config.manager = manager + config.begin() + self.assertEqual(manager.pushed, + {'registry':config.registry, 'request':None}) + self.assertEqual(manager.popped, False) + + def test_begin_with_request(self): + from pyramid.config import Configurator + config = Configurator() + request = object() + manager = DummyThreadLocalManager() + config.manager = manager + config.begin(request=request) + self.assertEqual(manager.pushed, + {'registry':config.registry, 'request':request}) + self.assertEqual(manager.popped, False) + + def test_end(self): + from pyramid.config import Configurator + config = Configurator() + manager = DummyThreadLocalManager() + config.manager = manager + config.end() + self.assertEqual(manager.pushed, None) + self.assertEqual(manager.popped, True) + + def test_ctor_with_package_registry(self): + import sys + from pyramid.config import Configurator + pkg = sys.modules['pyramid'] + config = Configurator(package=pkg) + self.assertEqual(config.package, pkg) + + def test_ctor_noreg_custom_settings(self): + from pyramid.interfaces import ISettings + settings = {'reload_templates':True, + 'mysetting':True} + config = self._makeOne(settings=settings) + settings = config.registry.getUtility(ISettings) + self.assertEqual(settings['reload_templates'], True) + self.assertEqual(settings['debug_authorization'], False) + self.assertEqual(settings['mysetting'], True) + + def test_ctor_noreg_debug_logger_None_default(self): + from pyramid.interfaces import IDebugLogger + config = self._makeOne() + logger = config.registry.getUtility(IDebugLogger) + self.assertEqual(logger.name, 'pyramid.debug') + + def test_ctor_noreg_debug_logger_non_None(self): + from pyramid.interfaces import IDebugLogger + logger = object() + config = self._makeOne(debug_logger=logger) + result = config.registry.getUtility(IDebugLogger) + self.assertEqual(logger, result) + + def test_ctor_authentication_policy(self): + from pyramid.interfaces import IAuthenticationPolicy + policy = object() + config = self._makeOne(authentication_policy=policy) + result = config.registry.getUtility(IAuthenticationPolicy) + self.assertEqual(policy, result) + + def test_ctor_authorization_policy_only(self): + from pyramid.exceptions import ConfigurationError + policy = object() + self.assertRaises(ConfigurationError, + self._makeOne, authorization_policy=policy) + + def test_ctor_no_root_factory(self): + from pyramid.interfaces import IRootFactory + config = self._makeOne() + self.failUnless(config.registry.getUtility(IRootFactory)) + + def test_ctor_alternate_renderers(self): + from pyramid.interfaces import IRendererFactory + renderer = object() + config = self._makeOne(renderers=[('yeah', renderer)]) + self.assertEqual(config.registry.getUtility(IRendererFactory, 'yeah'), + renderer) + + def test_ctor_default_permission(self): + from pyramid.interfaces import IDefaultPermission + config = self._makeOne(default_permission='view') + self.assertEqual(config.registry.getUtility(IDefaultPermission), 'view') + + def test_ctor_session_factory(self): + from pyramid.interfaces import ISessionFactory + config = self._makeOne(session_factory='factory') + self.assertEqual(config.registry.getUtility(ISessionFactory), 'factory') + + def test_with_package_module(self): + from pyramid.tests import test_configuration + import pyramid.tests + config = self._makeOne() + newconfig = config.with_package(test_configuration) + self.assertEqual(newconfig.package, pyramid.tests) + + def test_with_package_package(self): + import pyramid.tests + config = self._makeOne() + newconfig = config.with_package(pyramid.tests) + self.assertEqual(newconfig.package, pyramid.tests) + + def test_maybe_dotted_string_success(self): + import pyramid.tests + config = self._makeOne() + result = config.maybe_dotted('pyramid.tests') + self.assertEqual(result, pyramid.tests) + + def test_maybe_dotted_string_fail(self): + config = self._makeOne() + self.assertRaises(ImportError, + config.maybe_dotted, 'cant.be.found') + + def test_maybe_dotted_notstring_success(self): + import pyramid.tests + config = self._makeOne() + result = config.maybe_dotted(pyramid.tests) + self.assertEqual(result, pyramid.tests) + + def test_absolute_resource_spec_already_absolute(self): + import pyramid.tests + config = self._makeOne(package=pyramid.tests) + result = config.absolute_resource_spec('already:absolute') + self.assertEqual(result, 'already:absolute') + + def test_absolute_resource_spec_notastring(self): + import pyramid.tests + config = self._makeOne(package=pyramid.tests) + result = config.absolute_resource_spec(None) + self.assertEqual(result, None) + + def test_absolute_resource_spec_relative(self): + import pyramid.tests + config = self._makeOne(package=pyramid.tests) + result = config.absolute_resource_spec('templates') + self.assertEqual(result, 'pyramid.tests:templates') + + def test_setup_registry_fixed(self): + class DummyRegistry(object): + def subscribers(self, events, name): + self.events = events + return events + def registerUtility(self, *arg, **kw): + pass + reg = DummyRegistry() + config = self._makeOne(reg) + config.add_view = lambda *arg, **kw: False + config.setup_registry() + self.assertEqual(reg.has_listeners, True) + self.assertEqual(reg.notify(1), None) + self.assertEqual(reg.events, (1,)) + + def test_setup_registry_registers_default_exceptionresponse_view(self): + from pyramid.interfaces import IExceptionResponse + from pyramid.view import default_exceptionresponse_view + class DummyRegistry(object): + def registerUtility(self, *arg, **kw): + pass + reg = DummyRegistry() + config = self._makeOne(reg) + views = [] + config.add_view = lambda *arg, **kw: views.append((arg, kw)) + config.setup_registry() + self.assertEqual(views[0], ((default_exceptionresponse_view,), + {'context':IExceptionResponse})) + + def test_setup_registry_explicit_notfound_trumps_iexceptionresponse(self): + from zope.interface import implementedBy + from pyramid.interfaces import IRequest + from pyramid.exceptions import NotFound + from pyramid.registry import Registry + reg = Registry() + config = self._makeOne(reg, autocommit=True) + config.setup_registry() # registers IExceptionResponse default view + def myview(context, request): + return 'OK' + config.add_view(myview, context=NotFound) + request = self._makeRequest(config) + view = self._getViewCallable(config, ctx_iface=implementedBy(NotFound), + request_iface=IRequest) + result = view(None, request) + self.assertEqual(result, 'OK') + + def test_setup_registry_custom_settings(self): + from pyramid.registry import Registry + from pyramid.interfaces import ISettings + settings = {'reload_templates':True, + 'mysetting':True} + reg = Registry() + config = self._makeOne(reg) + config.setup_registry(settings=settings) + settings = reg.getUtility(ISettings) + self.assertEqual(settings['reload_templates'], True) + self.assertEqual(settings['debug_authorization'], False) + self.assertEqual(settings['mysetting'], True) + + def test_setup_registry_debug_logger_None_default(self): + from pyramid.registry import Registry + from pyramid.interfaces import IDebugLogger + reg = Registry() + config = self._makeOne(reg) + config.setup_registry() + logger = reg.getUtility(IDebugLogger) + self.assertEqual(logger.name, 'pyramid.debug') + + def test_setup_registry_debug_logger_non_None(self): + from pyramid.registry import Registry + from pyramid.interfaces import IDebugLogger + logger = object() + reg = Registry() + config = self._makeOne(reg) + config.setup_registry(debug_logger=logger) + result = reg.getUtility(IDebugLogger) + self.assertEqual(logger, result) + + def test_setup_registry_debug_logger_dottedname(self): + from pyramid.registry import Registry + from pyramid.interfaces import IDebugLogger + reg = Registry() + config = self._makeOne(reg) + config.setup_registry(debug_logger='pyramid.tests') + result = reg.getUtility(IDebugLogger) + import pyramid.tests + self.assertEqual(result, pyramid.tests) + + def test_setup_registry_authentication_policy(self): + from pyramid.registry import Registry + from pyramid.interfaces import IAuthenticationPolicy + policy = object() + reg = Registry() + config = self._makeOne(reg) + config.setup_registry(authentication_policy=policy) + result = reg.getUtility(IAuthenticationPolicy) + self.assertEqual(policy, result) + + def test_setup_registry_authentication_policy_dottedname(self): + from pyramid.registry import Registry + from pyramid.interfaces import IAuthenticationPolicy + reg = Registry() + config = self._makeOne(reg) + config.setup_registry(authentication_policy='pyramid.tests') + result = reg.getUtility(IAuthenticationPolicy) + import pyramid.tests + self.assertEqual(result, pyramid.tests) + + def test_setup_registry_authorization_policy_dottedname(self): + from pyramid.registry import Registry + from pyramid.interfaces import IAuthorizationPolicy + reg = Registry() + config = self._makeOne(reg) + dummy = object() + config.setup_registry(authentication_policy=dummy, + authorization_policy='pyramid.tests') + result = reg.getUtility(IAuthorizationPolicy) + import pyramid.tests + self.assertEqual(result, pyramid.tests) + + def test_setup_registry_authorization_policy_only(self): + from pyramid.registry import Registry + from pyramid.exceptions import ConfigurationError + policy = object() + reg = Registry() + config = self._makeOne(reg) + config = self.assertRaises(ConfigurationError, + config.setup_registry, + authorization_policy=policy) + + def test_setup_registry_default_root_factory(self): + from pyramid.registry import Registry + from pyramid.interfaces import IRootFactory + reg = Registry() + config = self._makeOne(reg) + config.setup_registry() + self.failUnless(reg.getUtility(IRootFactory)) + + def test_setup_registry_dottedname_root_factory(self): + from pyramid.registry import Registry + from pyramid.interfaces import IRootFactory + reg = Registry() + config = self._makeOne(reg) + import pyramid.tests + config.setup_registry(root_factory='pyramid.tests') + self.assertEqual(reg.getUtility(IRootFactory), pyramid.tests) + + def test_setup_registry_locale_negotiator_dottedname(self): + from pyramid.registry import Registry + from pyramid.interfaces import ILocaleNegotiator + reg = Registry() + config = self._makeOne(reg) + import pyramid.tests + config.setup_registry(locale_negotiator='pyramid.tests') + utility = reg.getUtility(ILocaleNegotiator) + self.assertEqual(utility, pyramid.tests) + + def test_setup_registry_locale_negotiator(self): + from pyramid.registry import Registry + from pyramid.interfaces import ILocaleNegotiator + reg = Registry() + config = self._makeOne(reg) + negotiator = object() + config.setup_registry(locale_negotiator=negotiator) + utility = reg.getUtility(ILocaleNegotiator) + self.assertEqual(utility, negotiator) + + def test_setup_registry_request_factory(self): + from pyramid.registry import Registry + from pyramid.interfaces import IRequestFactory + reg = Registry() + config = self._makeOne(reg) + factory = object() + config.setup_registry(request_factory=factory) + utility = reg.getUtility(IRequestFactory) + self.assertEqual(utility, factory) + + def test_setup_registry_request_factory_dottedname(self): + from pyramid.registry import Registry + from pyramid.interfaces import IRequestFactory + reg = Registry() + config = self._makeOne(reg) + import pyramid.tests + config.setup_registry(request_factory='pyramid.tests') + utility = reg.getUtility(IRequestFactory) + self.assertEqual(utility, pyramid.tests) + + def test_setup_registry_renderer_globals_factory(self): + from pyramid.registry import Registry + from pyramid.interfaces import IRendererGlobalsFactory + reg = Registry() + config = self._makeOne(reg) + factory = object() + config.setup_registry(renderer_globals_factory=factory) + utility = reg.getUtility(IRendererGlobalsFactory) + self.assertEqual(utility, factory) + + def test_setup_registry_renderer_globals_factory_dottedname(self): + from pyramid.registry import Registry + from pyramid.interfaces import IRendererGlobalsFactory + reg = Registry() + config = self._makeOne(reg) + import pyramid.tests + config.setup_registry(renderer_globals_factory='pyramid.tests') + utility = reg.getUtility(IRendererGlobalsFactory) + self.assertEqual(utility, pyramid.tests) + + def test_setup_registry_alternate_renderers(self): + from pyramid.registry import Registry + from pyramid.interfaces import IRendererFactory + renderer = object() + reg = Registry() + config = self._makeOne(reg) + config.setup_registry(renderers=[('yeah', renderer)]) + self.assertEqual(reg.getUtility(IRendererFactory, 'yeah'), + renderer) + + def test_setup_registry_default_permission(self): + from pyramid.registry import Registry + from pyramid.interfaces import IDefaultPermission + reg = Registry() + config = self._makeOne(reg) + config.setup_registry(default_permission='view') + self.assertEqual(reg.getUtility(IDefaultPermission), 'view') + + def test_get_settings_nosettings(self): + from pyramid.registry import Registry + reg = Registry() + config = self._makeOne(reg) + self.assertEqual(config.get_settings(), None) + + def test_get_settings_withsettings(self): + settings = {'a':1} + config = self._makeOne() + config.registry.settings = settings + self.assertEqual(config.get_settings(), settings) + + def test_add_settings_settings_already_registered(self): + from pyramid.registry import Registry + reg = Registry() + config = self._makeOne(reg) + config._set_settings({'a':1}) + config.add_settings({'b':2}) + settings = reg.settings + self.assertEqual(settings['a'], 1) + self.assertEqual(settings['b'], 2) + + def test_add_settings_settings_not_yet_registered(self): + from pyramid.registry import Registry + from pyramid.interfaces import ISettings + reg = Registry() + config = self._makeOne(reg) + config.add_settings({'a':1}) + settings = reg.getUtility(ISettings) + self.assertEqual(settings['a'], 1) + + def test_add_subscriber_defaults(self): + from zope.interface import implements + from zope.interface import Interface + class IEvent(Interface): + pass + class Event: + implements(IEvent) + L = [] + def subscriber(event): + L.append(event) + config = self._makeOne(autocommit=True) + config.add_subscriber(subscriber) + event = Event() + config.registry.notify(event) + self.assertEqual(len(L), 1) + self.assertEqual(L[0], event) + config.registry.notify(object()) + self.assertEqual(len(L), 2) + + def test_add_subscriber_iface_specified(self): + from zope.interface import implements + from zope.interface import Interface + class IEvent(Interface): + pass + class Event: + implements(IEvent) + L = [] + def subscriber(event): + L.append(event) + config = self._makeOne(autocommit=True) + config.add_subscriber(subscriber, IEvent) + event = Event() + config.registry.notify(event) + self.assertEqual(len(L), 1) + self.assertEqual(L[0], event) + config.registry.notify(object()) + self.assertEqual(len(L), 1) + + def test_add_subscriber_dottednames(self): + import pyramid.tests + from pyramid.interfaces import INewRequest + config = self._makeOne(autocommit=True) + config.add_subscriber('pyramid.tests', + 'pyramid.interfaces.INewRequest') + handlers = list(config.registry.registeredHandlers()) + self.assertEqual(len(handlers), 1) + handler = handlers[0] + self.assertEqual(handler.handler, pyramid.tests) + self.assertEqual(handler.required, (INewRequest,)) + + def test_add_object_event_subscriber(self): + from zope.interface import implements + from zope.interface import Interface + class IEvent(Interface): + pass + class Event: + object = 'foo' + implements(IEvent) + event = Event() + L = [] + def subscriber(object, event): + L.append(event) + config = self._makeOne(autocommit=True) + config.add_subscriber(subscriber, (Interface, IEvent)) + config.registry.subscribers((event.object, event), None) + self.assertEqual(len(L), 1) + self.assertEqual(L[0], event) + config.registry.subscribers((event.object, IDummy), None) + self.assertEqual(len(L), 1) + + def test_make_wsgi_app(self): + from pyramid.router import Router + from pyramid.interfaces import IApplicationCreated + manager = DummyThreadLocalManager() + config = self._makeOne() + subscriber = self._registerEventListener(config, IApplicationCreated) + config.manager = manager + app = config.make_wsgi_app() + self.assertEqual(app.__class__, Router) + self.assertEqual(manager.pushed['registry'], config.registry) + self.assertEqual(manager.pushed['request'], None) + self.failUnless(manager.popped) + self.assertEqual(len(subscriber), 1) + self.failUnless(IApplicationCreated.providedBy(subscriber[0])) + + def test_load_zcml_default(self): + import pyramid.tests.fixtureapp + config = self._makeOne(package=pyramid.tests.fixtureapp, + autocommit=True) + registry = config.load_zcml() + from pyramid.tests.fixtureapp.models import IFixture + self.failUnless(registry.queryUtility(IFixture)) # only in c.zcml + + def test_load_zcml_routesapp(self): + from pyramid.interfaces import IRoutesMapper + config = self._makeOne(autocommit=True) + config.load_zcml('pyramid.tests.routesapp:configure.zcml') + self.failUnless(config.registry.getUtility(IRoutesMapper)) + + def test_load_zcml_fixtureapp(self): + from pyramid.tests.fixtureapp.models import IFixture + config = self._makeOne(autocommit=True) + config.load_zcml('pyramid.tests.fixtureapp:configure.zcml') + self.failUnless(config.registry.queryUtility(IFixture)) # only in c.zcml + + def test_load_zcml_as_relative_filename(self): + import pyramid.tests.fixtureapp + config = self._makeOne(package=pyramid.tests.fixtureapp, + autocommit=True) + registry = config.load_zcml('configure.zcml') + from pyramid.tests.fixtureapp.models import IFixture + self.failUnless(registry.queryUtility(IFixture)) # only in c.zcml + + def test_load_zcml_as_absolute_filename(self): + import os + import pyramid.tests.fixtureapp + config = self._makeOne(package=pyramid.tests.fixtureapp, + autocommit=True) + dn = os.path.dirname(pyramid.tests.fixtureapp.__file__) + c_z = os.path.join(dn, 'configure.zcml') + registry = config.load_zcml(c_z) + from pyramid.tests.fixtureapp.models import IFixture + self.failUnless(registry.queryUtility(IFixture)) # only in c.zcml + + def test_load_zcml_lock_and_unlock(self): + config = self._makeOne(autocommit=True) + dummylock = DummyLock() + config.load_zcml( + 'pyramid.tests.fixtureapp:configure.zcml', + lock=dummylock) + self.assertEqual(dummylock.acquired, True) + self.assertEqual(dummylock.released, True) + + def test_include_with_dotted_name(self): + from pyramid import tests + config = self._makeOne() + context_before = config._make_context() + config._ctx = context_before + config.include('pyramid.tests.test_config.dummy_include') + context_after = config._ctx + actions = context_after.actions + self.assertEqual(len(actions), 1) + self.assertEqual( + context_after.actions[0][:3], + ('discrim', None, tests), + ) + self.assertEqual(context_after.basepath, None) + self.assertEqual(context_after.includepath, ()) + self.failUnless(context_after is context_before) + + def test_include_with_python_callable(self): + from pyramid import tests + config = self._makeOne() + context_before = config._make_context() + config._ctx = context_before + config.include(dummy_include) + context_after = config._ctx + actions = context_after.actions + self.assertEqual(len(actions), 1) + self.assertEqual( + actions[0][:3], + ('discrim', None, tests), + ) + self.assertEqual(context_after.basepath, None) + self.assertEqual(context_after.includepath, ()) + self.failUnless(context_after is context_before) + + def test_add_view_view_callable_None_no_renderer(self): + from pyramid.exceptions import ConfigurationError + config = self._makeOne(autocommit=True) + self.assertRaises(ConfigurationError, config.add_view) + + def test_add_view_with_request_type_and_route_name(self): + from pyramid.exceptions import ConfigurationError + config = self._makeOne(autocommit=True) + view = lambda *arg: 'OK' + self.assertRaises(ConfigurationError, config.add_view, view, '', None, + None, True, True) + + def test_add_view_with_request_type(self): + from zope.interface import directlyProvides + from pyramid.interfaces import IRequest + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, + request_type='pyramid.interfaces.IRequest') + wrapper = self._getViewCallable(config) + request = DummyRequest() + self._assertNotFound(wrapper, None, request) + directlyProvides(request, IRequest) + result = wrapper(None, request) + self.assertEqual(result, 'OK') + + def test_add_view_view_callable_None_with_renderer(self): + config = self._makeOne(autocommit=True) + self._registerRenderer(config, name='dummy') + config.add_view(renderer='dummy') + view = self._getViewCallable(config) + self.failUnless('Hello!' in view(None, None).body) + + def test_add_view_wrapped_view_is_decorated(self): + def view(request): # request-only wrapper + """ """ + config = self._makeOne(autocommit=True) + config.add_view(view=view) + wrapper = self._getViewCallable(config) + self.assertEqual(wrapper.__module__, view.__module__) + self.assertEqual(wrapper.__name__, view.__name__) + self.assertEqual(wrapper.__doc__, view.__doc__) + + def test_add_view_view_callable_dottedname(self): + config = self._makeOne(autocommit=True) + config.add_view(view='pyramid.tests.test_config.dummy_view') + wrapper = self._getViewCallable(config) + self.assertEqual(wrapper(None, None), 'OK') + + def test_add_view_with_function_callable(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view) + wrapper = self._getViewCallable(config) + result = wrapper(None, None) + self.assertEqual(result, 'OK') + + def test_add_view_with_function_callable_requestonly(self): + def view(request): + return 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view) + wrapper = self._getViewCallable(config) + result = wrapper(None, None) + self.assertEqual(result, 'OK') + + def test_add_view_as_instance(self): + class AView: + def __call__(self, context, request): + """ """ + return 'OK' + view = AView() + config = self._makeOne(autocommit=True) + config.add_view(view=view) + wrapper = self._getViewCallable(config) + result = wrapper(None, None) + self.assertEqual(result, 'OK') + + def test_add_view_as_instance_requestonly(self): + class AView: + def __call__(self, request): + """ """ + return 'OK' + view = AView() + config = self._makeOne(autocommit=True) + config.add_view(view=view) + wrapper = self._getViewCallable(config) + result = wrapper(None, None) + self.assertEqual(result, 'OK') + + def test_add_view_as_oldstyle_class(self): + class view: + def __init__(self, context, request): + self.context = context + self.request = request + + def __call__(self): + return 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view) + wrapper = self._getViewCallable(config) + result = wrapper(None, None) + self.assertEqual(result, 'OK') + + def test_add_view_as_oldstyle_class_requestonly(self): + class view: + def __init__(self, request): + self.request = request + + def __call__(self): + return 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view) + wrapper = self._getViewCallable(config) + result = wrapper(None, None) + self.assertEqual(result, 'OK') + + def test_add_view_context_as_class(self): + from zope.interface import implementedBy + view = lambda *arg: 'OK' + class Foo: + pass + config = self._makeOne(autocommit=True) + config.add_view(context=Foo, view=view) + foo = implementedBy(Foo) + wrapper = self._getViewCallable(config, foo) + self.assertEqual(wrapper, view) + + def test_add_view_context_as_iface(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(context=IDummy, view=view) + wrapper = self._getViewCallable(config, IDummy) + self.assertEqual(wrapper, view) + + def test_add_view_context_as_dottedname(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(context='pyramid.tests.test_config.IDummy', + view=view) + wrapper = self._getViewCallable(config, IDummy) + self.assertEqual(wrapper, view) + + def test_add_view_for__as_dottedname(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(for_='pyramid.tests.test_config.IDummy', + view=view) + wrapper = self._getViewCallable(config, IDummy) + self.assertEqual(wrapper, view) + + def test_add_view_for_as_class(self): + # ``for_`` is older spelling for ``context`` + from zope.interface import implementedBy + view = lambda *arg: 'OK' + class Foo: + pass + config = self._makeOne(autocommit=True) + config.add_view(for_=Foo, view=view) + foo = implementedBy(Foo) + wrapper = self._getViewCallable(config, foo) + self.assertEqual(wrapper, view) + + def test_add_view_for_as_iface(self): + # ``for_`` is older spelling for ``context`` + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(for_=IDummy, view=view) + wrapper = self._getViewCallable(config, IDummy) + self.assertEqual(wrapper, view) + + def test_add_view_context_trumps_for(self): + # ``for_`` is older spelling for ``context`` + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + class Foo: + pass + config.add_view(context=IDummy, for_=Foo, view=view) + wrapper = self._getViewCallable(config, IDummy) + self.assertEqual(wrapper, view) + + def test_add_view_register_secured_view(self): + from zope.interface import Interface + from pyramid.interfaces import IRequest + from pyramid.interfaces import ISecuredView + from pyramid.interfaces import IViewClassifier + view = lambda *arg: 'OK' + view.__call_permissive__ = view + config = self._makeOne(autocommit=True) + config.add_view(view=view) + wrapper = config.registry.adapters.lookup( + (IViewClassifier, IRequest, Interface), + ISecuredView, name='', default=None) + self.assertEqual(wrapper, view) + + def test_add_view_exception_register_secured_view(self): + from zope.interface import implementedBy + from pyramid.interfaces import IRequest + from pyramid.interfaces import IView + from pyramid.interfaces import IExceptionViewClassifier + view = lambda *arg: 'OK' + view.__call_permissive__ = view + config = self._makeOne(autocommit=True) + config.add_view(view=view, context=RuntimeError) + wrapper = config.registry.adapters.lookup( + (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), + IView, name='', default=None) + self.assertEqual(wrapper, view) + + def test_add_view_same_phash_overrides_existing_single_view(self): + from pyramid.compat import md5 + from zope.interface import Interface + from pyramid.interfaces import IRequest + from pyramid.interfaces import IView + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IMultiView + phash = md5() + phash.update('xhr:True') + view = lambda *arg: 'NOT OK' + view.__phash__ = phash.hexdigest() + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, (IViewClassifier, IRequest, Interface), IView, name='') + def newview(context, request): + return 'OK' + config.add_view(view=newview, xhr=True) + wrapper = self._getViewCallable(config) + self.failIf(IMultiView.providedBy(wrapper)) + request = DummyRequest() + request.is_xhr = True + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_exc_same_phash_overrides_existing_single_view(self): + from pyramid.compat import md5 + from zope.interface import implementedBy + from pyramid.interfaces import IRequest + from pyramid.interfaces import IView + from pyramid.interfaces import IExceptionViewClassifier + from pyramid.interfaces import IMultiView + phash = md5() + phash.update('xhr:True') + view = lambda *arg: 'NOT OK' + view.__phash__ = phash.hexdigest() + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, + (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), + IView, name='') + def newview(context, request): + return 'OK' + config.add_view(view=newview, xhr=True, context=RuntimeError) + wrapper = self._getViewCallable( + config, ctx_iface=implementedBy(RuntimeError), exception_view=True) + self.failIf(IMultiView.providedBy(wrapper)) + request = DummyRequest() + request.is_xhr = True + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_default_phash_overrides_no_phash(self): + from zope.interface import Interface + from pyramid.interfaces import IRequest + from pyramid.interfaces import IView + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IMultiView + view = lambda *arg: 'NOT OK' + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, (IViewClassifier, IRequest, Interface), IView, name='') + def newview(context, request): + return 'OK' + config.add_view(view=newview) + wrapper = self._getViewCallable(config) + self.failIf(IMultiView.providedBy(wrapper)) + request = DummyRequest() + request.is_xhr = True + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_exc_default_phash_overrides_no_phash(self): + from zope.interface import implementedBy + from pyramid.interfaces import IRequest + from pyramid.interfaces import IView + from pyramid.interfaces import IExceptionViewClassifier + from pyramid.interfaces import IMultiView + view = lambda *arg: 'NOT OK' + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, + (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), + IView, name='') + def newview(context, request): + return 'OK' + config.add_view(view=newview, context=RuntimeError) + wrapper = self._getViewCallable( + config, ctx_iface=implementedBy(RuntimeError), exception_view=True) + self.failIf(IMultiView.providedBy(wrapper)) + request = DummyRequest() + request.is_xhr = True + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_default_phash_overrides_default_phash(self): + from pyramid.config import DEFAULT_PHASH + from zope.interface import Interface + from pyramid.interfaces import IRequest + from pyramid.interfaces import IView + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IMultiView + view = lambda *arg: 'NOT OK' + view.__phash__ = DEFAULT_PHASH + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, (IViewClassifier, IRequest, Interface), IView, name='') + def newview(context, request): + return 'OK' + config.add_view(view=newview) + wrapper = self._getViewCallable(config) + self.failIf(IMultiView.providedBy(wrapper)) + request = DummyRequest() + request.is_xhr = True + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_exc_default_phash_overrides_default_phash(self): + from pyramid.config import DEFAULT_PHASH + from zope.interface import implementedBy + from pyramid.interfaces import IRequest + from pyramid.interfaces import IView + from pyramid.interfaces import IExceptionViewClassifier + from pyramid.interfaces import IMultiView + view = lambda *arg: 'NOT OK' + view.__phash__ = DEFAULT_PHASH + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, + (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), + IView, name='') + def newview(context, request): + return 'OK' + config.add_view(view=newview, context=RuntimeError) + wrapper = self._getViewCallable( + config, ctx_iface=implementedBy(RuntimeError), exception_view=True) + self.failIf(IMultiView.providedBy(wrapper)) + request = DummyRequest() + request.is_xhr = True + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_multiview_replaces_existing_view(self): + from zope.interface import Interface + from pyramid.interfaces import IRequest + from pyramid.interfaces import IView + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IMultiView + view = lambda *arg: 'OK' + view.__phash__ = 'abc' + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, (IViewClassifier, IRequest, Interface), IView, name='') + config.add_view(view=view) + wrapper = self._getViewCallable(config) + self.failUnless(IMultiView.providedBy(wrapper)) + self.assertEqual(wrapper(None, None), 'OK') + + def test_add_view_exc_multiview_replaces_existing_view(self): + from zope.interface import implementedBy + from pyramid.interfaces import IRequest + from pyramid.interfaces import IView + from pyramid.interfaces import IExceptionViewClassifier + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IMultiView + view = lambda *arg: 'OK' + view.__phash__ = 'abc' + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, + (IViewClassifier, IRequest, implementedBy(RuntimeError)), + IView, name='') + config.registry.registerAdapter( + view, + (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), + IView, name='') + config.add_view(view=view, context=RuntimeError) + wrapper = self._getViewCallable( + config, ctx_iface=implementedBy(RuntimeError), exception_view=True) + self.failUnless(IMultiView.providedBy(wrapper)) + self.assertEqual(wrapper(None, None), 'OK') + + def test_add_view_multiview_replaces_existing_securedview(self): + from zope.interface import Interface + from pyramid.interfaces import IRequest + from pyramid.interfaces import ISecuredView + from pyramid.interfaces import IMultiView + from pyramid.interfaces import IViewClassifier + view = lambda *arg: 'OK' + view.__phash__ = 'abc' + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, (IViewClassifier, IRequest, Interface), + ISecuredView, name='') + config.add_view(view=view) + wrapper = self._getViewCallable(config) + self.failUnless(IMultiView.providedBy(wrapper)) + self.assertEqual(wrapper(None, None), 'OK') + + def test_add_view_exc_multiview_replaces_existing_securedview(self): + from zope.interface import implementedBy + from pyramid.interfaces import IRequest + from pyramid.interfaces import ISecuredView + from pyramid.interfaces import IMultiView + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IExceptionViewClassifier + view = lambda *arg: 'OK' + view.__phash__ = 'abc' + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, + (IViewClassifier, IRequest, implementedBy(RuntimeError)), + ISecuredView, name='') + config.registry.registerAdapter( + view, + (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), + ISecuredView, name='') + config.add_view(view=view, context=RuntimeError) + wrapper = self._getViewCallable( + config, ctx_iface=implementedBy(RuntimeError), exception_view=True) + self.failUnless(IMultiView.providedBy(wrapper)) + self.assertEqual(wrapper(None, None), 'OK') + + def test_add_view_with_accept_multiview_replaces_existing_view(self): + from zope.interface import Interface + from pyramid.interfaces import IRequest + from pyramid.interfaces import IView + from pyramid.interfaces import IMultiView + from pyramid.interfaces import IViewClassifier + def view(context, request): + return 'OK' + def view2(context, request): + return 'OK2' + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, (IViewClassifier, IRequest, Interface), IView, name='') + config.add_view(view=view2, accept='text/html') + wrapper = self._getViewCallable(config) + self.failUnless(IMultiView.providedBy(wrapper)) + self.assertEqual(len(wrapper.views), 1) + self.assertEqual(len(wrapper.media_views), 1) + self.assertEqual(wrapper(None, None), 'OK') + request = DummyRequest() + request.accept = DummyAccept('text/html', 'text/html') + self.assertEqual(wrapper(None, request), 'OK2') + + def test_add_view_exc_with_accept_multiview_replaces_existing_view(self): + from zope.interface import implementedBy + from pyramid.interfaces import IRequest + from pyramid.interfaces import IView + from pyramid.interfaces import IMultiView + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IExceptionViewClassifier + def view(context, request): + return 'OK' + def view2(context, request): + return 'OK2' + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, + (IViewClassifier, IRequest, implementedBy(RuntimeError)), + IView, name='') + config.registry.registerAdapter( + view, + (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), + IView, name='') + config.add_view(view=view2, accept='text/html', context=RuntimeError) + wrapper = self._getViewCallable( + config, ctx_iface=implementedBy(RuntimeError), exception_view=True) + self.failUnless(IMultiView.providedBy(wrapper)) + self.assertEqual(len(wrapper.views), 1) + self.assertEqual(len(wrapper.media_views), 1) + self.assertEqual(wrapper(None, None), 'OK') + request = DummyRequest() + request.accept = DummyAccept('text/html', 'text/html') + self.assertEqual(wrapper(None, request), 'OK2') + + def test_add_view_multiview_replaces_existing_view_with___accept__(self): + from zope.interface import Interface + from pyramid.interfaces import IRequest + from pyramid.interfaces import IView + from pyramid.interfaces import IMultiView + from pyramid.interfaces import IViewClassifier + def view(context, request): + return 'OK' + def view2(context, request): + return 'OK2' + view.__accept__ = 'text/html' + view.__phash__ = 'abc' + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, (IViewClassifier, IRequest, Interface), IView, name='') + config.add_view(view=view2) + wrapper = self._getViewCallable(config) + self.failUnless(IMultiView.providedBy(wrapper)) + self.assertEqual(len(wrapper.views), 1) + self.assertEqual(len(wrapper.media_views), 1) + self.assertEqual(wrapper(None, None), 'OK2') + request = DummyRequest() + request.accept = DummyAccept('text/html') + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_exc_mulview_replaces_existing_view_with___accept__(self): + from zope.interface import implementedBy + from pyramid.interfaces import IRequest + from pyramid.interfaces import IView + from pyramid.interfaces import IMultiView + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IExceptionViewClassifier + def view(context, request): + return 'OK' + def view2(context, request): + return 'OK2' + view.__accept__ = 'text/html' + view.__phash__ = 'abc' + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, + (IViewClassifier, IRequest, implementedBy(RuntimeError)), + IView, name='') + config.registry.registerAdapter( + view, + (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), + IView, name='') + config.add_view(view=view2, context=RuntimeError) + wrapper = self._getViewCallable( + config, ctx_iface=implementedBy(RuntimeError), exception_view=True) + self.failUnless(IMultiView.providedBy(wrapper)) + self.assertEqual(len(wrapper.views), 1) + self.assertEqual(len(wrapper.media_views), 1) + self.assertEqual(wrapper(None, None), 'OK2') + request = DummyRequest() + request.accept = DummyAccept('text/html') + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_multiview_replaces_multiview(self): + from zope.interface import Interface + from pyramid.interfaces import IRequest + from pyramid.interfaces import IMultiView + from pyramid.interfaces import IViewClassifier + view = DummyMultiView() + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, (IViewClassifier, IRequest, Interface), + IMultiView, name='') + view2 = lambda *arg: 'OK2' + config.add_view(view=view2) + wrapper = self._getViewCallable(config) + self.failUnless(IMultiView.providedBy(wrapper)) + self.assertEqual([x[:2] for x in wrapper.views], [(view2, None)]) + self.assertEqual(wrapper(None, None), 'OK1') + + def test_add_view_exc_multiview_replaces_multiview(self): + from zope.interface import implementedBy + from pyramid.interfaces import IRequest + from pyramid.interfaces import IMultiView + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IExceptionViewClassifier + view = DummyMultiView() + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, + (IViewClassifier, IRequest, implementedBy(RuntimeError)), + IMultiView, name='') + config.registry.registerAdapter( + view, + (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), + IMultiView, name='') + view2 = lambda *arg: 'OK2' + config.add_view(view=view2, context=RuntimeError) + wrapper = self._getViewCallable( + config, ctx_iface=implementedBy(RuntimeError), exception_view=True) + self.failUnless(IMultiView.providedBy(wrapper)) + self.assertEqual([x[:2] for x in wrapper.views], [(view2, None)]) + self.assertEqual(wrapper(None, None), 'OK1') + + def test_add_view_multiview_context_superclass_then_subclass(self): + from zope.interface import Interface + from pyramid.interfaces import IRequest + from pyramid.interfaces import IView + from pyramid.interfaces import IMultiView + from pyramid.interfaces import IViewClassifier + class ISuper(Interface): + pass + class ISub(ISuper): + pass + view = lambda *arg: 'OK' + view2 = lambda *arg: 'OK2' + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, (IViewClassifier, IRequest, ISuper), IView, name='') + config.add_view(view=view2, for_=ISub) + wrapper = self._getViewCallable(config, ISuper, IRequest) + self.failIf(IMultiView.providedBy(wrapper)) + self.assertEqual(wrapper(None, None), 'OK') + wrapper = self._getViewCallable(config, ISub, IRequest) + self.failIf(IMultiView.providedBy(wrapper)) + self.assertEqual(wrapper(None, None), 'OK2') + + def test_add_view_multiview_exception_superclass_then_subclass(self): + from zope.interface import implementedBy + from pyramid.interfaces import IRequest + from pyramid.interfaces import IView + from pyramid.interfaces import IMultiView + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IExceptionViewClassifier + class Super(Exception): + pass + class Sub(Super): + pass + view = lambda *arg: 'OK' + view2 = lambda *arg: 'OK2' + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, (IViewClassifier, IRequest, Super), IView, name='') + config.registry.registerAdapter( + view, (IExceptionViewClassifier, IRequest, Super), IView, name='') + config.add_view(view=view2, for_=Sub) + wrapper = self._getViewCallable( + config, implementedBy(Super), IRequest) + wrapper_exc_view = self._getViewCallable( + config, implementedBy(Super), IRequest, exception_view=True) + self.assertEqual(wrapper_exc_view, wrapper) + self.failIf(IMultiView.providedBy(wrapper_exc_view)) + self.assertEqual(wrapper_exc_view(None, None), 'OK') + wrapper = self._getViewCallable( + config, implementedBy(Sub), IRequest) + wrapper_exc_view = self._getViewCallable( + config, implementedBy(Sub), IRequest, exception_view=True) + self.assertEqual(wrapper_exc_view, wrapper) + self.failIf(IMultiView.providedBy(wrapper_exc_view)) + self.assertEqual(wrapper_exc_view(None, None), 'OK2') + + def test_add_view_multiview_call_ordering(self): + from zope.interface import directlyProvides + def view1(context, request): return 'view1' + def view2(context, request): return 'view2' + def view3(context, request): return 'view3' + def view4(context, request): return 'view4' + def view5(context, request): return 'view5' + def view6(context, request): return 'view6' + def view7(context, request): return 'view7' + def view8(context, request): return 'view8' + config = self._makeOne(autocommit=True) + config.add_view(view=view1) + config.add_view(view=view2, request_method='POST') + config.add_view(view=view3,request_param='param') + config.add_view(view=view4, containment=IDummy) + config.add_view(view=view5, request_method='POST',request_param='param') + config.add_view(view=view6, request_method='POST', containment=IDummy) + config.add_view(view=view7, request_param='param', containment=IDummy) + config.add_view(view=view8, request_method='POST',request_param='param', + containment=IDummy) + + + wrapper = self._getViewCallable(config) + + ctx = DummyContext() + request = self._makeRequest(config) + request.method = 'GET' + request.params = {} + self.assertEqual(wrapper(ctx, request), 'view1') + + ctx = DummyContext() + request = self._makeRequest(config) + request.params = {} + request.method = 'POST' + self.assertEqual(wrapper(ctx, request), 'view2') + + ctx = DummyContext() + request = self._makeRequest(config) + request.params = {'param':'1'} + request.method = 'GET' + self.assertEqual(wrapper(ctx, request), 'view3') + + ctx = DummyContext() + directlyProvides(ctx, IDummy) + request = self._makeRequest(config) + request.method = 'GET' + request.params = {} + self.assertEqual(wrapper(ctx, request), 'view4') + + ctx = DummyContext() + request = self._makeRequest(config) + request.method = 'POST' + request.params = {'param':'1'} + self.assertEqual(wrapper(ctx, request), 'view5') + + ctx = DummyContext() + directlyProvides(ctx, IDummy) + request = self._makeRequest(config) + request.params = {} + request.method = 'POST' + self.assertEqual(wrapper(ctx, request), 'view6') + + ctx = DummyContext() + directlyProvides(ctx, IDummy) + request = self._makeRequest(config) + request.method = 'GET' + request.params = {'param':'1'} + self.assertEqual(wrapper(ctx, request), 'view7') + + ctx = DummyContext() + directlyProvides(ctx, IDummy) + request = self._makeRequest(config) + request.method = 'POST' + request.params = {'param':'1'} + self.assertEqual(wrapper(ctx, request), 'view8') + + def test_add_view_with_template_renderer(self): + import pyramid.tests + from pyramid.interfaces import ISettings + class view(object): + def __init__(self, context, request): + self.request = request + self.context = context + + def __call__(self): + return {'a':'1'} + config = self._makeOne(autocommit=True) + renderer = self._registerRenderer(config) + fixture = 'pyramid.tests:fixtures/minimal.txt' + config.add_view(view=view, renderer=fixture) + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + result = wrapper(None, request) + self.assertEqual(result.body, 'Hello!') + settings = config.registry.queryUtility(ISettings) + result = renderer.info + self.assertEqual(result.registry, config.registry) + self.assertEqual(result.type, '.txt') + self.assertEqual(result.package, pyramid.tests) + self.assertEqual(result.name, fixture) + self.assertEqual(result.settings, settings) + + def test_add_view_with_template_renderer_no_callable(self): + import pyramid.tests + from pyramid.interfaces import ISettings + config = self._makeOne(autocommit=True) + renderer = self._registerRenderer(config) + fixture = 'pyramid.tests:fixtures/minimal.txt' + config.add_view(view=None, renderer=fixture) + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + result = wrapper(None, request) + self.assertEqual(result.body, 'Hello!') + settings = config.registry.queryUtility(ISettings) + result = renderer.info + self.assertEqual(result.registry, config.registry) + self.assertEqual(result.type, '.txt') + self.assertEqual(result.package, pyramid.tests) + self.assertEqual(result.name, fixture) + self.assertEqual(result.settings, settings) + + def test_add_view_with_request_type_as_iface(self): + from zope.interface import directlyProvides + def view(context, request): + return 'OK' + config = self._makeOne(autocommit=True) + config.add_view(request_type=IDummy, view=view) + wrapper = self._getViewCallable(config, None) + request = self._makeRequest(config) + directlyProvides(request, IDummy) + result = wrapper(None, request) + self.assertEqual(result, 'OK') + + def test_add_view_with_request_type_as_noniface(self): + from pyramid.exceptions import ConfigurationError + view = lambda *arg: 'OK' + config = self._makeOne() + self.assertRaises(ConfigurationError, + config.add_view, view, '', None, None, object) + + def test_add_view_with_route_name(self): + from zope.component import ComponentLookupError + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, route_name='foo') + self.assertEqual(len(config.registry.deferred_route_views), 1) + infos = config.registry.deferred_route_views['foo'] + self.assertEqual(len(infos), 1) + info = infos[0] + self.assertEqual(info['route_name'], 'foo') + self.assertEqual(info['view'], view) + self.assertRaises(ComponentLookupError, + self._getRouteRequestIface, config, 'foo') + wrapper = self._getViewCallable(config, None) + self.assertEqual(wrapper, None) + config.add_route('foo', '/a/b') + request_iface = self._getRouteRequestIface(config, 'foo') + self.failIfEqual(request_iface, None) + wrapper = self._getViewCallable(config, request_iface=request_iface) + self.failIfEqual(wrapper, None) + self.assertEqual(wrapper(None, None), 'OK') + + def test_deferred_route_views_retains_custom_predicates(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, route_name='foo', custom_predicates=('123',)) + self.assertEqual(len(config.registry.deferred_route_views), 1) + infos = config.registry.deferred_route_views['foo'] + self.assertEqual(len(infos), 1) + info = infos[0] + self.assertEqual(info['route_name'], 'foo') + self.assertEqual(info['custom_predicates'], ('123',)) + + def test_add_view_with_route_name_exception(self): + from zope.interface import implementedBy + from zope.component import ComponentLookupError + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, route_name='foo', context=RuntimeError) + self.assertEqual(len(config.registry.deferred_route_views), 1) + infos = config.registry.deferred_route_views['foo'] + self.assertEqual(len(infos), 1) + info = infos[0] + self.assertEqual(info['route_name'], 'foo') + self.assertEqual(info['view'], view) + self.assertRaises(ComponentLookupError, + self._getRouteRequestIface, config, 'foo') + wrapper_exc_view = self._getViewCallable( + config, ctx_iface=implementedBy(RuntimeError), + exception_view=True) + self.assertEqual(wrapper_exc_view, None) + config.add_route('foo', '/a/b') + request_iface = self._getRouteRequestIface(config, 'foo') + self.failIfEqual(request_iface, None) + wrapper_exc_view = self._getViewCallable( + config, ctx_iface=implementedBy(RuntimeError), + request_iface=request_iface, exception_view=True) + self.failIfEqual(wrapper_exc_view, None) + wrapper = self._getViewCallable( + config, ctx_iface=implementedBy(RuntimeError), + request_iface=request_iface) + self.assertEqual(wrapper_exc_view, wrapper) + self.assertEqual(wrapper_exc_view(None, None), 'OK') + + def test_add_view_with_request_method_true(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, request_method='POST') + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.method = 'POST' + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_with_request_method_false(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, request_method='POST') + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.method = 'GET' + self._assertNotFound(wrapper, None, request) + + def test_add_view_with_request_param_noval_true(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, request_param='abc') + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.params = {'abc':''} + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_with_request_param_noval_false(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, request_param='abc') + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.params = {} + self._assertNotFound(wrapper, None, request) + + def test_add_view_with_request_param_val_true(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, request_param='abc=123') + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.params = {'abc':'123'} + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_with_request_param_val_false(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, request_param='abc=123') + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.params = {'abc':''} + self._assertNotFound(wrapper, None, request) + + def test_add_view_with_xhr_true(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, xhr=True) + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.is_xhr = True + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_with_xhr_false(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, xhr=True) + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.is_xhr = False + self._assertNotFound(wrapper, None, request) + + def test_add_view_with_header_badregex(self): + from pyramid.exceptions import ConfigurationError + view = lambda *arg: 'OK' + config = self._makeOne() + self.assertRaises(ConfigurationError, + config.add_view, view=view, header='Host:a\\') + + def test_add_view_with_header_noval_match(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, header='Host') + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.headers = {'Host':'whatever'} + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_with_header_noval_nomatch(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, header='Host') + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.headers = {'NotHost':'whatever'} + self._assertNotFound(wrapper, None, request) + + def test_add_view_with_header_val_match(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, header=r'Host:\d') + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.headers = {'Host':'1'} + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_with_header_val_nomatch(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, header=r'Host:\d') + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.headers = {'Host':'abc'} + self._assertNotFound(wrapper, None, request) + + def test_add_view_with_header_val_missing(self): + from pyramid.exceptions import NotFound + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, header=r'Host:\d') + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.headers = {'NoHost':'1'} + self.assertRaises(NotFound, wrapper, None, request) + + def test_add_view_with_accept_match(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, accept='text/xml') + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.accept = ['text/xml'] + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_with_accept_nomatch(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, accept='text/xml') + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.accept = ['text/html'] + self._assertNotFound(wrapper, None, request) + + def test_add_view_with_containment_true(self): + from zope.interface import directlyProvides + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, containment=IDummy) + wrapper = self._getViewCallable(config) + context = DummyContext() + directlyProvides(context, IDummy) + self.assertEqual(wrapper(context, None), 'OK') + + def test_add_view_with_containment_false(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, containment=IDummy) + wrapper = self._getViewCallable(config) + context = DummyContext() + self._assertNotFound(wrapper, context, None) + + def test_add_view_with_containment_dottedname(self): + from zope.interface import directlyProvides + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view( + view=view, + containment='pyramid.tests.test_config.IDummy') + wrapper = self._getViewCallable(config) + context = DummyContext() + directlyProvides(context, IDummy) + self.assertEqual(wrapper(context, None), 'OK') + + def test_add_view_with_path_info_badregex(self): + from pyramid.exceptions import ConfigurationError + view = lambda *arg: 'OK' + config = self._makeOne() + self.assertRaises(ConfigurationError, + config.add_view, view=view, path_info='\\') + + def test_add_view_with_path_info_match(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, path_info='/foo') + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.path_info = '/foo' + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_with_path_info_nomatch(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, path_info='/foo') + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.path_info = '/' + self._assertNotFound(wrapper, None, request) + + def test_add_view_with_custom_predicates_match(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + def pred1(context, request): + return True + def pred2(context, request): + return True + predicates = (pred1, pred2) + config.add_view(view=view, custom_predicates=predicates) + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_with_custom_predicates_nomatch(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + def pred1(context, request): + return True + def pred2(context, request): + return False + predicates = (pred1, pred2) + config.add_view(view=view, custom_predicates=predicates) + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + self._assertNotFound(wrapper, None, request) + + def test_add_view_custom_predicate_bests_standard_predicate(self): + view = lambda *arg: 'OK' + view2 = lambda *arg: 'NOT OK' + config = self._makeOne(autocommit=True) + def pred1(context, request): + return True + config.add_view(view=view, custom_predicates=(pred1,)) + config.add_view(view=view2, request_method='GET') + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.method = 'GET' + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_custom_more_preds_first_bests_fewer_preds_last(self): + view = lambda *arg: 'OK' + view2 = lambda *arg: 'NOT OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, request_method='GET', xhr=True) + config.add_view(view=view2, request_method='GET') + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.method = 'GET' + request.is_xhr = True + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_same_predicates(self): + from zope.configuration.config import ConfigurationConflictError + view2 = lambda *arg: 'second' + view1 = lambda *arg: 'first' + config = self._makeOne() + config.add_view(view=view1) + config.add_view(view=view2) + self.assertRaises(ConfigurationConflictError, config.commit) + + def test_add_view_with_permission(self): + view1 = lambda *arg: 'OK' + outerself = self + class DummyPolicy(object): + def effective_principals(self, r): + outerself.assertEqual(r, request) + return ['abc'] + def permits(self, context, principals, permission): + outerself.assertEqual(context, None) + outerself.assertEqual(principals, ['abc']) + outerself.assertEqual(permission, 'view') + return True + policy = DummyPolicy() + config = self._makeOne(authorization_policy=policy, + authentication_policy=policy, + autocommit=True) + config.add_view(view=view1, permission='view') + view = self._getViewCallable(config) + request = self._makeRequest(config) + self.assertEqual(view(None, request), 'OK') + + def test_add_view_with_default_permission_no_explicit_permission(self): + view1 = lambda *arg: 'OK' + outerself = self + class DummyPolicy(object): + def effective_principals(self, r): + outerself.assertEqual(r, request) + return ['abc'] + def permits(self, context, principals, permission): + outerself.assertEqual(context, None) + outerself.assertEqual(principals, ['abc']) + outerself.assertEqual(permission, 'view') + return True + policy = DummyPolicy() + config = self._makeOne(authorization_policy=policy, + authentication_policy=policy, + default_permission='view', + autocommit=True) + config.add_view(view=view1) + view = self._getViewCallable(config) + request = self._makeRequest(config) + self.assertEqual(view(None, request), 'OK') + + def test_add_view_with_no_default_permission_no_explicit_permission(self): + view1 = lambda *arg: 'OK' + class DummyPolicy(object): pass # wont be called + policy = DummyPolicy() + config = self._makeOne(authorization_policy=policy, + authentication_policy=policy, + autocommit=True) + config.add_view(view=view1) + view = self._getViewCallable(config) + request = self._makeRequest(config) + self.assertEqual(view(None, request), 'OK') + + def test_add_handler_action_in_route_pattern(self): + config = self._makeOne(autocommit=True) + views = [] + def dummy_add_view(**kw): + views.append(kw) + config.add_view = dummy_add_view + config.add_handler('name', '/:action', DummyHandler) + self._assertRoute(config, 'name', '/:action', 0) + self.assertEqual(len(views), 2) + + view = views[0] + preds = view['custom_predicates'] + self.assertEqual(len(preds), 1) + pred = preds[0] + request = DummyRequest() + self.assertEqual(pred(None, request), False) + request.matchdict = {'action':'action1'} + self.assertEqual(pred(None, request), True) + self.assertEqual(view['route_name'], 'name') + self.assertEqual(view['attr'], 'action1') + self.assertEqual(view['view'], DummyHandler) + + view = views[1] + preds = view['custom_predicates'] + self.assertEqual(len(preds), 1) + pred = preds[0] + request = DummyRequest() + self.assertEqual(pred(None, request), False) + request.matchdict = {'action':'action2'} + self.assertEqual(pred(None, request), True) + self.assertEqual(view['route_name'], 'name') + self.assertEqual(view['attr'], 'action2') + self.assertEqual(view['view'], DummyHandler) + + def test_add_handler_with_view_overridden_autoexpose_None(self): + config = self._makeOne(autocommit=True) + views = [] + def dummy_add_view(**kw): + views.append(kw) # pragma: no cover + config.add_view = dummy_add_view + class MyView(DummyHandler): + __autoexpose__ = None + config.add_handler('name', '/:action', MyView) + self._assertRoute(config, 'name', '/:action', 0) + self.assertEqual(len(views), 0) + + def test_add_handler_with_view_overridden_autoexpose_broken_regex1(self): + from pyramid.exceptions import ConfigurationError + config = self._makeOne() + def dummy_add_view(**kw): + """ """ + config.add_view = dummy_add_view + class MyView(DummyHandler): + __autoexpose__ = 1 + self.assertRaises(ConfigurationError, config.add_handler, + 'name', '/{action}', MyView) + + def test_add_handler_with_view_overridden_autoexpose_broken_regex2(self): + from pyramid.exceptions import ConfigurationError + config = self._makeOne() + def dummy_add_view(**kw): + """ """ + config.add_view = dummy_add_view + class MyView(DummyHandler): + __autoexpose__ = 'a\\' + self.assertRaises(ConfigurationError, config.add_handler, + 'name', '/{action}', MyView) + + def test_add_handler_with_view_method_has_expose_config(self): + config = self._makeOne(autocommit=True) + views = [] + def dummy_add_view(**kw): + views.append(kw) + config.add_view = dummy_add_view + class MyView(object): + def action(self): # pragma: no cover + return 'response' + action.__exposed__ = [{'custom_predicates':(1,)}] + config.add_handler('name', '/:action', MyView) + self._assertRoute(config, 'name', '/:action', 0) + self.assertEqual(len(views), 1) + view = views[0] + preds = view['custom_predicates'] + self.assertEqual(len(preds), 2) + self.assertEqual(view['route_name'], 'name') + self.assertEqual(view['attr'], 'action') + self.assertEqual(view['view'], MyView) + + def test_add_handler_with_view_method_has_expose_config_with_action(self): + config = self._makeOne(autocommit=True) + views = [] + def dummy_add_view(**kw): + views.append(kw) + config.add_view = dummy_add_view + class MyView(object): + def action(self): # pragma: no cover + return 'response' + action.__exposed__ = [{'name':'action3000'}] + config.add_handler('name', '/:action', MyView) + self._assertRoute(config, 'name', '/:action', 0) + self.assertEqual(len(views), 1) + view = views[0] + preds = view['custom_predicates'] + self.assertEqual(len(preds), 1) + pred = preds[0] + request = DummyRequest() + self.assertEqual(pred(None, request), False) + request.matchdict = {'action':'action3000'} + self.assertEqual(pred(None, request), True) + self.assertEqual(view['route_name'], 'name') + self.assertEqual(view['attr'], 'action') + self.assertEqual(view['view'], MyView) + + def test_add_handler_with_view_method_has_expose_config_with_action_regex( + self): + config = self._makeOne(autocommit=True) + views = [] + def dummy_add_view(**kw): + views.append(kw) + config.add_view = dummy_add_view + class MyView(object): + def action(self): # pragma: no cover + return 'response' + action.__exposed__ = [{'name':'^action3000$'}] + config.add_handler('name', '/:action', MyView) + self._assertRoute(config, 'name', '/:action', 0) + self.assertEqual(len(views), 1) + view = views[0] + preds = view['custom_predicates'] + self.assertEqual(len(preds), 1) + pred = preds[0] + request = DummyRequest() + self.assertEqual(pred(None, request), False) + request.matchdict = {'action':'action3000'} + self.assertEqual(pred(None, request), True) + self.assertEqual(view['route_name'], 'name') + self.assertEqual(view['attr'], 'action') + self.assertEqual(view['view'], MyView) + + def test_add_handler_doesnt_mutate_expose_dict(self): + config = self._makeOne(autocommit=True) + views = [] + def dummy_add_view(**kw): + views.append(kw) + config.add_view = dummy_add_view + exposed = [{'name':'^action3000$'}] + class MyView(object): + def action(self): # pragma: no cover + return 'response' + action.__exposed__ = exposed + config.add_handler('name', '/{action}', MyView) + self.assertEqual(exposed[0], {'name':'^action3000$'}) # not mutated + + def test_add_handler_with_action_and_action_in_path(self): + from pyramid.exceptions import ConfigurationError + config = self._makeOne() + self.assertRaises(ConfigurationError, config.add_handler, + 'name', '/{action}', DummyHandler, action='abc') + + def test_add_handler_with_explicit_action(self): + config = self._makeOne(autocommit=True) + class DummyHandler(object): + def index(self): pass + index.__exposed__ = [{'a':'1'}] + views = [] + def dummy_add_view(**kw): + views.append(kw) + config.add_view = dummy_add_view + config.add_handler('name', '/abc', DummyHandler, action='index') + self.assertEqual(len(views), 1) + view = views[0] + self.assertEqual(view['a'], '1') + self.assertEqual(view['attr'], 'index') + self.assertEqual(view['route_name'], 'name') + self.assertEqual(view['view'], DummyHandler) + + def test_add_handler_with_implicit_action(self): + config = self._makeOne(autocommit=True) + class DummyHandler(object): + def __call__(self): pass + __call__.__exposed__ = [{'a':'1'}] + views = [] + def dummy_add_view(**kw): + views.append(kw) + config.add_view = dummy_add_view + config.add_handler('name', '/abc', DummyHandler) + self.assertEqual(len(views), 1) + view = views[0] + self.assertEqual(view['a'], '1') + self.assertEqual(view['attr'], None) + self.assertEqual(view['route_name'], 'name') + self.assertEqual(view['view'], DummyHandler) + + def test_add_handler_with_multiple_action(self): + config = self._makeOne(autocommit=True) + class DummyHandler(object): + def index(self): pass + def create(self): pass + create.__exposed__ = [{'name': 'index'}] + views = [] + def dummy_add_view(**kw): + views.append(kw) + config.add_view = dummy_add_view + config.add_handler('name', '/abc', DummyHandler, action='index') + self.assertEqual(len(views), 2) + view = views[0] + self.assertEqual(view['attr'], 'create') + self.assertEqual(view['route_name'], 'name') + self.assertEqual(view['view'], DummyHandler) + view = views[1] + self.assertEqual(view['attr'], 'index') + + def test_add_handler_string(self): + import pyramid + views = [] + config = self._makeOne(autocommit=True) + def dummy_add_view(**kw): + views.append(kw) + config.add_view = dummy_add_view + config.add_handler('name', '/abc', 'pyramid') + self.assertEqual(len(views), 1) + view = views[0] + self.assertEqual(view['view'], pyramid) + + def test_add_handler_pattern_None_no_previous_route(self): + from pyramid.exceptions import ConfigurationError + config = self._makeOne() + self.assertRaises(ConfigurationError, config.add_handler, + 'name', None, 'pyramid') + + def test_add_handler_pattern_None_with_previous_route(self): + import pyramid + config = self._makeOne(autocommit=True) + config.add_route('name', ':def') + views = [] + def dummy_add_view(**kw): + views.append(kw) + config.add_view = dummy_add_view + config.add_route = None # shouldn't be called + config.add_handler('name', None, 'pyramid') + self.assertEqual(len(views), 1) + view = views[0] + self.assertEqual(view['view'], pyramid) + + + def _assertRoute(self, config, name, path, num_predicates=0): + from pyramid.interfaces import IRoutesMapper + mapper = config.registry.getUtility(IRoutesMapper) + routes = mapper.get_routes() + route = routes[0] + self.assertEqual(len(routes), 1) + self.assertEqual(route.name, name) + self.assertEqual(route.path, path) + self.assertEqual(len(routes[0].predicates), num_predicates) + return route + + def test_get_routes_mapper_not_yet_registered(self): + config = self._makeOne() + mapper = config.get_routes_mapper() + self.assertEqual(mapper.routelist, []) + + def test_get_routes_mapper_already_registered(self): + from pyramid.interfaces import IRoutesMapper + config = self._makeOne() + mapper = object() + config.registry.registerUtility(mapper, IRoutesMapper) + result = config.get_routes_mapper() + self.assertEqual(result, mapper) + + def test_add_route_defaults(self): + config = self._makeOne(autocommit=True) + route = config.add_route('name', 'path') + self._assertRoute(config, 'name', 'path') + self.assertEqual(route.name, 'name') + + def test_add_route_with_factory(self): + config = self._makeOne(autocommit=True) + factory = object() + route = config.add_route('name', 'path', factory=factory) + self.assertEqual(route.factory, factory) + + def test_add_route_with_factory_dottedname(self): + config = self._makeOne(autocommit=True) + route = config.add_route( + 'name', 'path', + factory='pyramid.tests.test_config.dummyfactory') + self.assertEqual(route.factory, dummyfactory) + + def test_add_route_with_xhr(self): + config = self._makeOne(autocommit=True) + config.add_route('name', 'path', xhr=True) + route = self._assertRoute(config, 'name', 'path', 1) + predicate = route.predicates[0] + request = self._makeRequest(config) + request.is_xhr = True + self.assertEqual(predicate(None, request), True) + request = self._makeRequest(config) + request.is_xhr = False + self.assertEqual(predicate(None, request), False) + + def test_add_route_with_request_method(self): + config = self._makeOne(autocommit=True) + config.add_route('name', 'path', request_method='GET') + route = self._assertRoute(config, 'name', 'path', 1) + predicate = route.predicates[0] + request = self._makeRequest(config) + request.method = 'GET' + self.assertEqual(predicate(None, request), True) + request = self._makeRequest(config) + request.method = 'POST' + self.assertEqual(predicate(None, request), False) + + def test_add_route_with_path_info(self): + config = self._makeOne(autocommit=True) + config.add_route('name', 'path', path_info='/foo') + route = self._assertRoute(config, 'name', 'path', 1) + predicate = route.predicates[0] + request = self._makeRequest(config) + request.path_info = '/foo' + self.assertEqual(predicate(None, request), True) + request = self._makeRequest(config) + request.path_info = '/' + self.assertEqual(predicate(None, request), False) + + def test_add_route_with_request_param(self): + config = self._makeOne(autocommit=True) + config.add_route('name', 'path', request_param='abc') + route = self._assertRoute(config, 'name', 'path', 1) + predicate = route.predicates[0] + request = self._makeRequest(config) + request.params = {'abc':'123'} + self.assertEqual(predicate(None, request), True) + request = self._makeRequest(config) + request.params = {} + self.assertEqual(predicate(None, request), False) + + def test_add_route_with_custom_predicates(self): + config = self._makeOne(autocommit=True) + def pred1(context, request): pass + def pred2(context, request): pass + config.add_route('name', 'path', custom_predicates=(pred1, pred2)) + route = self._assertRoute(config, 'name', 'path', 2) + self.assertEqual(route.predicates, [pred1, pred2]) + + def test_add_route_with_header(self): + config = self._makeOne(autocommit=True) + config.add_route('name', 'path', header='Host') + route = self._assertRoute(config, 'name', 'path', 1) + predicate = route.predicates[0] + request = self._makeRequest(config) + request.headers = {'Host':'example.com'} + self.assertEqual(predicate(None, request), True) + request = self._makeRequest(config) + request.headers = {} + self.assertEqual(predicate(None, request), False) + + def test_add_route_with_accept(self): + config = self._makeOne(autocommit=True) + config.add_route('name', 'path', accept='text/xml') + route = self._assertRoute(config, 'name', 'path', 1) + predicate = route.predicates[0] + request = self._makeRequest(config) + request.accept = ['text/xml'] + self.assertEqual(predicate(None, request), True) + request = self._makeRequest(config) + request.accept = ['text/html'] + self.assertEqual(predicate(None, request), False) + + def test_add_route_with_view(self): + config = self._makeOne(autocommit=True) + view = lambda *arg: 'OK' + config.add_route('name', 'path', view=view) + request_type = self._getRouteRequestIface(config, 'name') + wrapper = self._getViewCallable(config, None, request_type) + self.assertEqual(wrapper(None, None), 'OK') + self._assertRoute(config, 'name', 'path') + + def test_add_route_with_view_context(self): + config = self._makeOne(autocommit=True) + view = lambda *arg: 'OK' + config.add_route('name', 'path', view=view, view_context=IDummy) + request_type = self._getRouteRequestIface(config, 'name') + wrapper = self._getViewCallable(config, IDummy, request_type) + self.assertEqual(wrapper(None, None), 'OK') + self._assertRoute(config, 'name', 'path') + wrapper = self._getViewCallable(config, IOther, request_type) + self.assertEqual(wrapper, None) + + def test_add_route_with_view_exception(self): + from zope.interface import implementedBy + config = self._makeOne(autocommit=True) + view = lambda *arg: 'OK' + config.add_route('name', 'path', view=view, view_context=RuntimeError) + request_type = self._getRouteRequestIface(config, 'name') + wrapper = self._getViewCallable( + config, ctx_iface=implementedBy(RuntimeError), + request_iface=request_type, exception_view=True) + self.assertEqual(wrapper(None, None), 'OK') + self._assertRoute(config, 'name', 'path') + wrapper = self._getViewCallable( + config, ctx_iface=IOther, + request_iface=request_type, exception_view=True) + self.assertEqual(wrapper, None) + + def test_add_route_with_view_for(self): + config = self._makeOne(autocommit=True) + view = lambda *arg: 'OK' + config.add_route('name', 'path', view=view, view_for=IDummy) + request_type = self._getRouteRequestIface(config, 'name') + wrapper = self._getViewCallable(config, IDummy, request_type) + self.assertEqual(wrapper(None, None), 'OK') + self._assertRoute(config, 'name', 'path') + wrapper = self._getViewCallable(config, IOther, request_type) + self.assertEqual(wrapper, None) + + def test_add_route_with_for_(self): + config = self._makeOne(autocommit=True) + view = lambda *arg: 'OK' + config.add_route('name', 'path', view=view, for_=IDummy) + request_type = self._getRouteRequestIface(config, 'name') + wrapper = self._getViewCallable(config, IDummy, request_type) + self.assertEqual(wrapper(None, None), 'OK') + self._assertRoute(config, 'name', 'path') + wrapper = self._getViewCallable(config, IOther, request_type) + self.assertEqual(wrapper, None) + + def test_add_route_with_view_renderer(self): + config = self._makeOne(autocommit=True) + self._registerRenderer(config) + view = lambda *arg: 'OK' + config.add_route('name', 'path', view=view, + view_renderer='fixtures/minimal.txt') + request_type = self._getRouteRequestIface(config, 'name') + wrapper = self._getViewCallable(config, None, request_type) + self._assertRoute(config, 'name', 'path') + self.assertEqual(wrapper(None, None).body, 'Hello!') + + def test_add_route_with_view_attr(self): + config = self._makeOne(autocommit=True) + self._registerRenderer(config) + class View(object): + def __init__(self, context, request): + pass + def alt(self): + return 'OK' + config.add_route('name', 'path', view=View, view_attr='alt') + request_type = self._getRouteRequestIface(config, 'name') + wrapper = self._getViewCallable(config, None, request_type) + self._assertRoute(config, 'name', 'path') + self.assertEqual(wrapper(None, None), 'OK') + + def test_add_route_with_view_renderer_alias(self): + config = self._makeOne(autocommit=True) + self._registerRenderer(config) + view = lambda *arg: 'OK' + config.add_route('name', 'path', view=view, + renderer='fixtures/minimal.txt') + request_type = self._getRouteRequestIface(config, 'name') + wrapper = self._getViewCallable(config, None, request_type) + self._assertRoute(config, 'name', 'path') + self.assertEqual(wrapper(None, None).body, 'Hello!') + + def test_add_route_with_view_permission(self): + from pyramid.interfaces import IAuthenticationPolicy + from pyramid.interfaces import IAuthorizationPolicy + config = self._makeOne(autocommit=True) + policy = lambda *arg: None + config.registry.registerUtility(policy, IAuthenticationPolicy) + config.registry.registerUtility(policy, IAuthorizationPolicy) + view = lambda *arg: 'OK' + config.add_route('name', 'path', view=view, view_permission='edit') + request_type = self._getRouteRequestIface(config, 'name') + wrapper = self._getViewCallable(config, None, request_type) + self._assertRoute(config, 'name', 'path') + self.failUnless(hasattr(wrapper, '__call_permissive__')) + + def test_add_route_with_view_permission_alias(self): + from pyramid.interfaces import IAuthenticationPolicy + from pyramid.interfaces import IAuthorizationPolicy + config = self._makeOne(autocommit=True) + policy = lambda *arg: None + config.registry.registerUtility(policy, IAuthenticationPolicy) + config.registry.registerUtility(policy, IAuthorizationPolicy) + view = lambda *arg: 'OK' + config.add_route('name', 'path', view=view, permission='edit') + request_type = self._getRouteRequestIface(config, 'name') + wrapper = self._getViewCallable(config, None, request_type) + self._assertRoute(config, 'name', 'path') + self.failUnless(hasattr(wrapper, '__call_permissive__')) + + def test_add_route_no_pattern_with_path(self): + config = self._makeOne(autocommit=True) + route = config.add_route('name', path='path') + self._assertRoute(config, 'name', 'path') + self.assertEqual(route.name, 'name') + + def test_add_route_no_path_no_pattern(self): + from pyramid.exceptions import ConfigurationError + config = self._makeOne() + self.assertRaises(ConfigurationError, config.add_route, 'name') + + def test_add_route_with_pregenerator(self): + config = self._makeOne(autocommit=True) + route = config.add_route('name', 'pattern', pregenerator='123') + self.assertEqual(route.pregenerator, '123') + + def test__override_not_yet_registered(self): + from pyramid.interfaces import IPackageOverrides + package = DummyPackage('package') + opackage = DummyPackage('opackage') + config = self._makeOne() + config._override(package, 'path', opackage, 'oprefix', + PackageOverrides=DummyOverrides) + overrides = config.registry.queryUtility(IPackageOverrides, + name='package') + self.assertEqual(overrides.inserted, [('path', 'opackage', 'oprefix')]) + self.assertEqual(overrides.package, package) + + def test__override_already_registered(self): + from pyramid.interfaces import IPackageOverrides + package = DummyPackage('package') + opackage = DummyPackage('opackage') + overrides = DummyOverrides(package) + config = self._makeOne() + config.registry.registerUtility(overrides, IPackageOverrides, + name='package') + config._override(package, 'path', opackage, 'oprefix', + PackageOverrides=DummyOverrides) + self.assertEqual(overrides.inserted, [('path', 'opackage', 'oprefix')]) + self.assertEqual(overrides.package, package) + + def test_add_static_here_no_utility_registered(self): + from pyramid.static import PackageURLParser + from zope.interface import implementedBy + from pyramid.static import StaticURLInfo + from pyramid.interfaces import IView + from pyramid.interfaces import IViewClassifier + config = self._makeOne(autocommit=True) + config.add_static_view('static', 'fixtures/static') + request_type = self._getRouteRequestIface(config, 'static/') + route = self._assertRoute(config, 'static/', 'static/*subpath') + self.assertEqual(route.factory.__class__, type(lambda x: x)) + iface = implementedBy(StaticURLInfo) + wrapped = config.registry.adapters.lookup( + (IViewClassifier, request_type, iface), IView, name='') + request = self._makeRequest(config) + self.assertEqual(wrapped(None, request).__class__, PackageURLParser) + + def test_add_static_view_package_relative(self): + from pyramid.interfaces import IStaticURLInfo + info = DummyStaticURLInfo() + 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', {})]) + + def test_add_static_view_package_here_relative(self): + from pyramid.interfaces import IStaticURLInfo + info = DummyStaticURLInfo() + 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', {})]) + + def test_add_static_view_absolute(self): + import os + from pyramid.interfaces import IStaticURLInfo + info = DummyStaticURLInfo() + config = self._makeOne(autocommit=True) + config.registry.registerUtility(info, IStaticURLInfo) + here = os.path.dirname(__file__) + static_path = os.path.join(here, 'fixtures', 'static') + config.add_static_view('static', static_path) + self.assertEqual(info.added, + [('static', static_path, {})]) + + def test_set_notfound_view(self): + from zope.interface import implementedBy + from pyramid.interfaces import IRequest + from pyramid.exceptions import NotFound + config = self._makeOne(autocommit=True) + view = lambda *arg: arg + config.set_notfound_view(view) + request = self._makeRequest(config) + view = self._getViewCallable(config, ctx_iface=implementedBy(NotFound), + request_iface=IRequest) + result = view(None, request) + self.assertEqual(result, (None, request)) + + def test_set_notfound_view_request_has_context(self): + from zope.interface import implementedBy + from pyramid.interfaces import IRequest + from pyramid.exceptions import NotFound + config = self._makeOne(autocommit=True) + view = lambda *arg: arg + config.set_notfound_view(view) + request = self._makeRequest(config) + request.context = 'abc' + view = self._getViewCallable(config, ctx_iface=implementedBy(NotFound), + request_iface=IRequest) + result = view(None, request) + self.assertEqual(result, ('abc', request)) + + def test_set_forbidden_view(self): + from zope.interface import implementedBy + from pyramid.interfaces import IRequest + from pyramid.exceptions import Forbidden + config = self._makeOne(autocommit=True) + view = lambda *arg: 'OK' + config.set_forbidden_view(view) + request = self._makeRequest(config) + view = self._getViewCallable(config, ctx_iface=implementedBy(Forbidden), + request_iface=IRequest) + result = view(None, request) + self.assertEqual(result, 'OK') + + def test_set_forbidden_view_request_has_context(self): + from zope.interface import implementedBy + from pyramid.interfaces import IRequest + from pyramid.exceptions import Forbidden + config = self._makeOne(autocommit=True) + view = lambda *arg: arg + config.set_forbidden_view(view) + request = self._makeRequest(config) + request.context = 'abc' + view = self._getViewCallable(config, ctx_iface=implementedBy(Forbidden), + request_iface=IRequest) + result = view(None, request) + self.assertEqual(result, ('abc', request)) + + def test__set_authentication_policy(self): + from pyramid.interfaces import IAuthenticationPolicy + config = self._makeOne(autocommit=True) + policy = object() + config._set_authentication_policy(policy) + self.assertEqual( + config.registry.getUtility(IAuthenticationPolicy), policy) + + def test__set_authorization_policy(self): + from pyramid.interfaces import IAuthorizationPolicy + config = self._makeOne(autocommit=True) + policy = object() + config._set_authorization_policy(policy) + self.assertEqual( + config.registry.getUtility(IAuthorizationPolicy), policy) + + def test_set_locale_negotiator(self): + from pyramid.interfaces import ILocaleNegotiator + config = self._makeOne(autocommit=True) + def negotiator(request): pass + config.set_locale_negotiator(negotiator) + self.assertEqual(config.registry.getUtility(ILocaleNegotiator), + negotiator) + + def test_set_locale_negotiator_dottedname(self): + from pyramid.interfaces import ILocaleNegotiator + config = self._makeOne(autocommit=True) + config.set_locale_negotiator( + 'pyramid.tests.test_config.dummyfactory') + self.assertEqual(config.registry.getUtility(ILocaleNegotiator), + dummyfactory) + + def test_set_request_factory(self): + from pyramid.interfaces import IRequestFactory + config = self._makeOne(autocommit=True) + factory = object() + config.set_request_factory(factory) + self.assertEqual(config.registry.getUtility(IRequestFactory), factory) + + def test_set_request_factory_dottedname(self): + from pyramid.interfaces import IRequestFactory + config = self._makeOne(autocommit=True) + config.set_request_factory( + 'pyramid.tests.test_config.dummyfactory') + self.assertEqual(config.registry.getUtility(IRequestFactory), + dummyfactory) + + def test_set_renderer_globals_factory(self): + from pyramid.interfaces import IRendererGlobalsFactory + config = self._makeOne(autocommit=True) + factory = object() + config.set_renderer_globals_factory(factory) + self.assertEqual(config.registry.getUtility(IRendererGlobalsFactory), + factory) + + def test_set_renderer_globals_factory_dottedname(self): + from pyramid.interfaces import IRendererGlobalsFactory + config = self._makeOne(autocommit=True) + config.set_renderer_globals_factory( + 'pyramid.tests.test_config.dummyfactory') + self.assertEqual(config.registry.getUtility(IRendererGlobalsFactory), + dummyfactory) + + def test_set_default_permission(self): + from pyramid.interfaces import IDefaultPermission + config = self._makeOne(autocommit=True) + config.set_default_permission('view') + self.assertEqual(config.registry.getUtility(IDefaultPermission), + 'view') + + def test_set_session_factory(self): + from pyramid.interfaces import ISessionFactory + config = self._makeOne(autocommit=True) + config.set_session_factory('factory') + self.assertEqual(config.registry.getUtility(ISessionFactory), + 'factory') + + def test_add_translation_dirs_missing_dir(self): + from pyramid.exceptions import ConfigurationError + config = self._makeOne() + self.assertRaises(ConfigurationError, + config.add_translation_dirs, + '/wont/exist/on/my/system') + + def test_add_translation_dirs_resource_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_registers_chameleon_translate(self): + from pyramid.interfaces import IChameleonTranslate + from pyramid.threadlocal import manager + request = DummyRequest() + config = self._makeOne(autocommit=True) + manager.push({'request':request, 'registry':config.registry}) + try: + config.add_translation_dirs('pyramid.tests.localeapp:locale') + translate = config.registry.getUtility(IChameleonTranslate) + self.assertEqual(translate('Approve'), u'Approve') + finally: + 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]) + + def test_derive_view_function(self): + def view(request): + return 'OK' + config = self._makeOne() + result = config.derive_view(view) + self.failIf(result is view) + self.assertEqual(result(None, None), 'OK') + + def test_derive_view_dottedname(self): + config = self._makeOne() + result = config.derive_view( + 'pyramid.tests.test_config.dummy_view') + self.failIf(result is dummy_view) + self.assertEqual(result(None, None), 'OK') + + def test_derive_view_with_renderer(self): + def view(request): + return 'OK' + config = self._makeOne(autocommit=True) + class moo(object): + def __init__(self, *arg, **kw): + pass + def __call__(self, *arg, **kw): + return 'moo' + config.add_renderer('moo', moo) + result = config.derive_view(view, renderer='moo') + self.failIf(result is view) + self.assertEqual(result(None, None).body, 'moo') + + def test_derive_view_with_default_renderer_no_explicit_renderer(self): + def view(request): + return 'OK' + config = self._makeOne(autocommit=True) + class moo(object): + def __init__(self, *arg, **kw): + pass + def __call__(self, *arg, **kw): + return 'moo' + config.add_renderer(None, moo) + result = config.derive_view(view) + self.failIf(result is view) + self.assertEqual(result(None, None).body, 'moo') + + def test_derive_view_with_default_renderer_with_explicit_renderer(self): + def view(request): + return 'OK' + config = self._makeOne(autocommit=True) + class moo(object): pass + class foo(object): + def __init__(self, *arg, **kw): + pass + def __call__(self, *arg, **kw): + return 'foo' + config.add_renderer(None, moo) + config.add_renderer('foo', foo) + result = config.derive_view(view, renderer='foo') + self.failIf(result is view) + self.assertEqual(result(None, None).body, 'foo') + + def test_derive_view_class_without_attr(self): + class View(object): + def __init__(self, request): + pass + def __call__(self): + return 'OK' + config = self._makeOne() + result = config.derive_view(View) + self.assertEqual(result(None, None), 'OK') + + def test_derive_view_class_with_attr(self): + class View(object): + def __init__(self, request): + pass + def another(self): + return 'OK' + config = self._makeOne() + result = config.derive_view(View, attr='another') + self.assertEqual(result(None, None), 'OK') + + def test__derive_view_as_function_context_and_request(self): + def view(context, request): + return 'OK' + config = self._makeOne() + result = config._derive_view(view) + self.failUnless(result is view) + self.failIf(hasattr(result, '__call_permissive__')) + self.assertEqual(view(None, None), 'OK') + + def test__derive_view_as_function_requestonly(self): + def view(request): + return 'OK' + config = self._makeOne() + result = config._derive_view(view) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.failIf(hasattr(result, '__call_permissive__')) + self.assertEqual(result(None, None), 'OK') + + def test__derive_view_as_newstyle_class_context_and_request(self): + class view(object): + def __init__(self, context, request): + pass + def __call__(self): + return 'OK' + config = self._makeOne() + result = config._derive_view(view) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.failIf(hasattr(result, '__call_permissive__')) + self.assertEqual(result(None, None), 'OK') + + def test__derive_view_as_newstyle_class_requestonly(self): + class view(object): + def __init__(self, context, request): + pass + def __call__(self): + return 'OK' + config = self._makeOne() + result = config._derive_view(view) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.failIf(hasattr(result, '__call_permissive__')) + self.assertEqual(result(None, None), 'OK') + + def test__derive_view_as_oldstyle_class_context_and_request(self): + class view: + def __init__(self, context, request): + pass + def __call__(self): + return 'OK' + config = self._makeOne() + result = config._derive_view(view) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.failIf(hasattr(result, '__call_permissive__')) + self.assertEqual(result(None, None), 'OK') + + def test__derive_view_as_oldstyle_class_requestonly(self): + class view: + def __init__(self, context, request): + pass + def __call__(self): + return 'OK' + config = self._makeOne() + result = config._derive_view(view) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.failIf(hasattr(result, '__call_permissive__')) + self.assertEqual(result(None, None), 'OK') + + def test__derive_view_as_instance_context_and_request(self): + class View: + def __call__(self, context, request): + return 'OK' + view = View() + config = self._makeOne() + result = config._derive_view(view) + self.failUnless(result is view) + self.failIf(hasattr(result, '__call_permissive__')) + self.assertEqual(result(None, None), 'OK') + + def test__derive_view_as_instance_requestonly(self): + class View: + def __call__(self, request): + return 'OK' + view = View() + config = self._makeOne() + result = config._derive_view(view) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.failUnless('instance' in result.__name__) + self.failIf(hasattr(result, '__call_permissive__')) + self.assertEqual(result(None, None), 'OK') + + def test__derive_view_with_debug_authorization_no_authpol(self): + view = lambda *arg: 'OK' + config = self._makeOne() + self._registerSettings(config, + debug_authorization=True, reload_templates=True) + logger = self._registerLogger(config) + result = config._derive_view(view, permission='view') + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.failIf(hasattr(result, '__call_permissive__')) + request = self._makeRequest(config) + request.view_name = 'view_name' + request.url = 'url' + self.assertEqual(result(None, request), 'OK') + 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__derive_view_with_debug_authorization_no_permission(self): + view = lambda *arg: 'OK' + config = self._makeOne() + self._registerSettings(config, + debug_authorization=True, reload_templates=True) + self._registerSecurityPolicy(config, True) + logger = self._registerLogger(config) + result = config._derive_view(view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.failIf(hasattr(result, '__call_permissive__')) + request = self._makeRequest(config) + request.view_name = 'view_name' + request.url = 'url' + self.assertEqual(result(None, request), 'OK') + 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__derive_view_debug_auth_permission_authpol_permitted(self): + view = lambda *arg: 'OK' + config = self._makeOne() + self._registerSettings(config, debug_authorization=True, + reload_templates=True) + logger = self._registerLogger(config) + self._registerSecurityPolicy(config, True) + result = config._derive_view(view, permission='view') + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.assertEqual(result.__call_permissive__, view) + request = self._makeRequest(config) + request.view_name = 'view_name' + request.url = 'url' + self.assertEqual(result(None, request), 'OK') + 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__derive_view_debug_auth_permission_authpol_denied(self): + from pyramid.exceptions import Forbidden + view = lambda *arg: 'OK' + config = self._makeOne() + self._registerSettings(config, + debug_authorization=True, reload_templates=True) + logger = self._registerLogger(config) + self._registerSecurityPolicy(config, False) + result = config._derive_view(view, permission='view') + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.assertEqual(result.__call_permissive__, view) + request = self._makeRequest(config) + request.view_name = 'view_name' + request.url = 'url' + self.assertRaises(Forbidden, 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__derive_view_debug_auth_permission_authpol_denied2(self): + view = lambda *arg: 'OK' + config = self._makeOne() + self._registerSettings(config, + debug_authorization=True, reload_templates=True) + self._registerLogger(config) + self._registerSecurityPolicy(config, False) + result = config._derive_view(view, permission='view') + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + request = self._makeRequest(config) + request.view_name = 'view_name' + request.url = 'url' + permitted = result.__permitted__(None, None) + self.assertEqual(permitted, False) + + def test__derive_view_debug_auth_permission_authpol_overridden(self): + view = lambda *arg: 'OK' + config = self._makeOne() + self._registerSettings(config, + debug_authorization=True, reload_templates=True) + logger = self._registerLogger(config) + self._registerSecurityPolicy(config, False) + result = config._derive_view(view, + permission='__no_permission_required__') + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.failIf(hasattr(result, '__call_permissive__')) + request = self._makeRequest(config) + request.view_name = 'view_name' + request.url = 'url' + self.assertEqual(result(None, request), 'OK') + 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__derive_view_with_predicates_all(self): + view = lambda *arg: 'OK' + predicates = [] + def predicate1(context, request): + predicates.append(True) + return True + def predicate2(context, request): + predicates.append(True) + return True + config = self._makeOne() + result = config._derive_view(view, predicates=[predicate1, predicate2]) + request = self._makeRequest(config) + request.method = 'POST' + next = result(None, None) + self.assertEqual(next, 'OK') + self.assertEqual(predicates, [True, True]) + + def test__derive_view_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 + config = self._makeOne() + result = config._derive_view(view, predicates=[predicate1, predicate2]) + request = self._makeRequest(config) + request.method = 'POST' + next = result.__predicated__(None, None) + self.assertEqual(next, True) + self.assertEqual(predicates, [True, True]) + + def test__derive_view_with_predicates_notall(self): + from pyramid.exceptions import NotFound + view = lambda *arg: 'OK' + predicates = [] + def predicate1(context, request): + predicates.append(True) + return True + def predicate2(context, request): + predicates.append(True) + return False + config = self._makeOne() + result = config._derive_view(view, predicates=[predicate1, predicate2]) + request = self._makeRequest(config) + request.method = 'POST' + self.assertRaises(NotFound, result, None, None) + self.assertEqual(predicates, [True, True]) + + def test__derive_view_with_wrapper_viewname(self): + from webob 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, inner_view) + return Response('outer ' + request.wrapped_body) + config = self._makeOne() + config.registry.registerAdapter( + outer_view, (IViewClassifier, None, None), IView, 'owrap') + result = config._derive_view(inner_view, viewname='inner', + wrapper_viewname='owrap') + self.failIf(result is inner_view) + self.assertEqual(inner_view.__module__, result.__module__) + self.assertEqual(inner_view.__doc__, result.__doc__) + request = self._makeRequest(config) + request.registry = config.registry + response = result(None, request) + self.assertEqual(response.body, 'outer OK') + + def test__derive_view_with_wrapper_viewname_notfound(self): + from webob import Response + inner_response = Response('OK') + def inner_view(context, request): + return inner_response + config = self._makeOne() + request = self._makeRequest(config) + request.registry = config.registry + wrapped = config._derive_view( + inner_view, viewname='inner', wrapper_viewname='owrap') + self.assertRaises(ValueError, wrapped, None, request) + + def test_override_resource_samename(self): + from pyramid.exceptions import ConfigurationError + config = self._makeOne() + self.assertRaises(ConfigurationError, config.override_resource,'a', 'a') + + def test_override_resource_directory_with_file(self): + from pyramid.exceptions import ConfigurationError + config = self._makeOne() + self.assertRaises(ConfigurationError, config.override_resource, + 'a:foo/', 'a:foo.pt') + + def test_override_resource_file_with_directory(self): + from pyramid.exceptions import ConfigurationError + config = self._makeOne() + self.assertRaises(ConfigurationError, config.override_resource, + 'a:foo.pt', 'a:foo/') + + def test_override_resource_success(self): + config = self._makeOne(autocommit=True) + override = DummyUnderOverride() + config.override_resource( + 'pyramid.tests.fixtureapp:templates/foo.pt', + 'pyramid.tests.fixtureapp.subpackage:templates/bar.pt', + _override=override) + from pyramid.tests import fixtureapp + from pyramid.tests.fixtureapp import subpackage + self.assertEqual(override.package, fixtureapp) + self.assertEqual(override.path, 'templates/foo.pt') + self.assertEqual(override.override_package, subpackage) + self.assertEqual(override.override_prefix, 'templates/bar.pt') + + def test_add_renderer(self): + from pyramid.interfaces import IRendererFactory + config = self._makeOne(autocommit=True) + renderer = object() + config.add_renderer('name', renderer) + self.assertEqual(config.registry.getUtility(IRendererFactory, 'name'), + renderer) + + def test_add_renderer_dottedname_factory(self): + from pyramid.interfaces import IRendererFactory + config = self._makeOne(autocommit=True) + import pyramid.tests + config.add_renderer('name', 'pyramid.tests') + self.assertEqual(config.registry.getUtility(IRendererFactory, 'name'), + pyramid.tests) + + def test_scan_integration(self): + import os + from zope.interface import alsoProvides + from pyramid.interfaces import IRequest + from pyramid.view import render_view_to_response + import pyramid.tests.grokkedapp as package + config = self._makeOne(autocommit=True) + config.scan(package) + + ctx = DummyContext() + req = DummyRequest() + alsoProvides(req, IRequest) + req.registry = config.registry + + req.method = 'GET' + result = render_view_to_response(ctx, req, '') + self.assertEqual(result, 'grokked') + + req.method = 'POST' + result = render_view_to_response(ctx, req, '') + self.assertEqual(result, 'grokked_post') + + result= render_view_to_response(ctx, req, 'grokked_class') + self.assertEqual(result, 'grokked_class') + + result= render_view_to_response(ctx, req, 'grokked_instance') + self.assertEqual(result, 'grokked_instance') + + result= render_view_to_response(ctx, req, 'oldstyle_grokked_class') + self.assertEqual(result, 'oldstyle_grokked_class') + + req.method = 'GET' + result = render_view_to_response(ctx, req, 'another') + self.assertEqual(result, 'another_grokked') + + req.method = 'POST' + result = render_view_to_response(ctx, req, 'another') + self.assertEqual(result, 'another_grokked_post') + + result= render_view_to_response(ctx, req, 'another_grokked_class') + self.assertEqual(result, 'another_grokked_class') + + result= render_view_to_response(ctx, req, 'another_grokked_instance') + self.assertEqual(result, 'another_grokked_instance') + + result= render_view_to_response(ctx, req, + 'another_oldstyle_grokked_class') + self.assertEqual(result, 'another_oldstyle_grokked_class') + + result = render_view_to_response(ctx, req, 'stacked1') + self.assertEqual(result, 'stacked') + + result = render_view_to_response(ctx, req, 'stacked2') + self.assertEqual(result, 'stacked') + + result = render_view_to_response(ctx, req, 'another_stacked1') + self.assertEqual(result, 'another_stacked') + + result = render_view_to_response(ctx, req, 'another_stacked2') + self.assertEqual(result, 'another_stacked') + + result = render_view_to_response(ctx, req, 'stacked_class1') + self.assertEqual(result, 'stacked_class') + + result = render_view_to_response(ctx, req, 'stacked_class2') + self.assertEqual(result, 'stacked_class') + + result = render_view_to_response(ctx, req, 'another_stacked_class1') + self.assertEqual(result, 'another_stacked_class') + + result = render_view_to_response(ctx, req, 'another_stacked_class2') + self.assertEqual(result, 'another_stacked_class') + + if not os.name.startswith('java'): + # on Jython, a class without an __init__ apparently accepts + # any number of arguments without raising a TypeError. + + self.assertRaises(TypeError, + render_view_to_response, ctx, req, 'basemethod') + + result = render_view_to_response(ctx, req, 'method1') + self.assertEqual(result, 'method1') + + result = render_view_to_response(ctx, req, 'method2') + self.assertEqual(result, 'method2') + + result = render_view_to_response(ctx, req, 'stacked_method1') + self.assertEqual(result, 'stacked_method') + + result = render_view_to_response(ctx, req, 'stacked_method2') + self.assertEqual(result, 'stacked_method') + + result = render_view_to_response(ctx, req, 'subpackage_init') + self.assertEqual(result, 'subpackage_init') + + result = render_view_to_response(ctx, req, 'subpackage_notinit') + self.assertEqual(result, 'subpackage_notinit') + + result = render_view_to_response(ctx, req, 'subsubpackage_init') + self.assertEqual(result, 'subsubpackage_init') + + result = render_view_to_response(ctx, req, 'pod_notinit') + self.assertEqual(result, None) + + def test_scan_integration_dottedname_package(self): + from zope.interface import alsoProvides + from pyramid.interfaces import IRequest + from pyramid.view import render_view_to_response + config = self._makeOne(autocommit=True) + config.scan('pyramid.tests.grokkedapp') + + ctx = DummyContext() + req = DummyRequest() + alsoProvides(req, IRequest) + req.registry = config.registry + + req.method = 'GET' + result = render_view_to_response(ctx, req, '') + self.assertEqual(result, 'grokked') + + def test_testing_securitypolicy(self): + from pyramid.testing import DummySecurityPolicy + config = self._makeOne(autocommit=True) + config.testing_securitypolicy('user', ('group1', 'group2'), + permissive=False) + from pyramid.interfaces import IAuthenticationPolicy + from pyramid.interfaces import IAuthorizationPolicy + ut = config.registry.getUtility(IAuthenticationPolicy) + self.failUnless(isinstance(ut, DummySecurityPolicy)) + ut = config.registry.getUtility(IAuthorizationPolicy) + self.assertEqual(ut.userid, 'user') + self.assertEqual(ut.groupids, ('group1', 'group2')) + self.assertEqual(ut.permissive, False) + + def test_testing_models(self): + from pyramid.traversal import find_model + from pyramid.interfaces import ITraverser + ob1 = object() + ob2 = object() + models = {'/ob1':ob1, '/ob2':ob2} + config = self._makeOne(autocommit=True) + config.testing_models(models) + adapter = config.registry.getAdapter(None, ITraverser) + result = adapter({'PATH_INFO':'/ob1'}) + self.assertEqual(result['context'], ob1) + self.assertEqual(result['view_name'], '') + self.assertEqual(result['subpath'], ()) + self.assertEqual(result['traversed'], (u'ob1',)) + self.assertEqual(result['virtual_root'], ob1) + self.assertEqual(result['virtual_root_path'], ()) + result = adapter({'PATH_INFO':'/ob2'}) + self.assertEqual(result['context'], ob2) + self.assertEqual(result['view_name'], '') + self.assertEqual(result['subpath'], ()) + self.assertEqual(result['traversed'], (u'ob2',)) + self.assertEqual(result['virtual_root'], ob2) + self.assertEqual(result['virtual_root_path'], ()) + self.assertRaises(KeyError, adapter, {'PATH_INFO':'/ob3'}) + try: + config.begin() + self.assertEqual(find_model(None, '/ob1'), ob1) + finally: + config.end() + + def test_testing_add_subscriber_single(self): + config = self._makeOne(autocommit=True) + L = config.testing_add_subscriber(IDummy) + event = DummyEvent() + config.registry.notify(event) + self.assertEqual(len(L), 1) + self.assertEqual(L[0], event) + config.registry.notify(object()) + self.assertEqual(len(L), 1) + + def test_testing_add_subscriber_dottedname(self): + config = self._makeOne(autocommit=True) + L = config.testing_add_subscriber( + 'pyramid.tests.test_config.IDummy') + event = DummyEvent() + config.registry.notify(event) + self.assertEqual(len(L), 1) + self.assertEqual(L[0], event) + config.registry.notify(object()) + self.assertEqual(len(L), 1) + + def test_testing_add_subscriber_multiple(self): + config = self._makeOne(autocommit=True) + L = config.testing_add_subscriber((Interface, IDummy)) + event = DummyEvent() + event.object = 'foo' + # the below is the equivalent of z.c.event.objectEventNotify(event) + config.registry.subscribers((event.object, event), None) + self.assertEqual(len(L), 2) + self.assertEqual(L[0], 'foo') + self.assertEqual(L[1], event) + + def test_testing_add_subscriber_defaults(self): + config = self._makeOne(autocommit=True) + L = config.testing_add_subscriber() + event = object() + config.registry.notify(event) + self.assertEqual(L[-1], event) + event2 = object() + config.registry.notify(event2) + self.assertEqual(L[-1], event2) + + def test_hook_zca(self): + from pyramid.threadlocal import get_current_registry + gsm = DummyGetSiteManager() + config = self._makeOne() + config.hook_zca(getSiteManager=gsm) + self.assertEqual(gsm.hook, get_current_registry) + + def test_unhook_zca(self): + gsm = DummyGetSiteManager() + config = self._makeOne() + config.unhook_zca(getSiteManager=gsm) + self.assertEqual(gsm.unhooked, True) + + def test_testing_add_renderer(self): + config = self._makeOne(autocommit=True) + renderer = config.testing_add_renderer('templates/foo.pt') + from pyramid.testing import DummyTemplateRenderer + self.failUnless(isinstance(renderer, DummyTemplateRenderer)) + from pyramid.renderers import render_to_response + # must provide request to pass in registry (this is a functest) + request = DummyRequest() + request.registry = config.registry + render_to_response( + 'templates/foo.pt', {'foo':1, 'bar':2}, request=request) + renderer.assert_(foo=1) + renderer.assert_(bar=2) + renderer.assert_(request=request) + + def test_testing_add_renderer_explicitrenderer(self): + config = self._makeOne(autocommit=True) + class E(Exception): pass + def renderer(kw, system): + self.assertEqual(kw, {'foo':1, 'bar':2}) + raise E + renderer = config.testing_add_renderer('templates/foo.pt', renderer) + from pyramid.renderers import render_to_response + # must provide request to pass in registry (this is a functest) + request = DummyRequest() + request.registry = config.registry + try: + render_to_response( + 'templates/foo.pt', {'foo':1, 'bar':2}, request=request) + except E: + pass + else: # pragma: no cover + raise AssertionError + + def test_testing_add_template(self): + config = self._makeOne(autocommit=True) + renderer = config.testing_add_template('templates/foo.pt') + from pyramid.testing import DummyTemplateRenderer + self.failUnless(isinstance(renderer, DummyTemplateRenderer)) + from pyramid.renderers import render_to_response + # must provide request to pass in registry (this is a functest) + request = DummyRequest() + request.registry = config.registry + render_to_response('templates/foo.pt', dict(foo=1, bar=2), + request=request) + renderer.assert_(foo=1) + renderer.assert_(bar=2) + renderer.assert_(request=request) + + def test_commit_conflict_simple(self): + from zope.configuration.config import ConfigurationConflictError + config = self._makeOne() + def view1(request): pass + def view2(request): pass + config.add_view(view1) + config.add_view(view2) + self.assertRaises(ConfigurationConflictError, config.commit) + + def test_commit_conflict_resolved_with_include(self): + config = self._makeOne() + def view1(request): pass + def view2(request): pass + def includeme(config): + config.add_view(view2) + config.add_view(view1) + config.include(includeme) + config.commit() + registeredview = self._getViewCallable(config) + self.assertEqual(registeredview.__name__, 'view1') + + def test_commit_conflict_with_two_includes(self): + from zope.configuration.config import ConfigurationConflictError + config = self._makeOne() + def view1(request): pass + def view2(request): pass + def includeme1(config): + config.add_view(view1) + def includeme2(config): + config.add_view(view2) + config.include(includeme1) + config.include(includeme2) + self.assertRaises(ConfigurationConflictError, config.commit) + + def test_commit_conflict_resolved_with_two_includes_and_local(self): + config = self._makeOne() + def view1(request): pass + def view2(request): pass + def view3(request): pass + def includeme1(config): + config.add_view(view1) + def includeme2(config): + config.add_view(view2) + config.include(includeme1) + config.include(includeme2) + config.add_view(view3) + config.commit() + registeredview = self._getViewCallable(config) + self.assertEqual(registeredview.__name__, 'view3') + + def test_autocommit_no_conflicts(self): + config = self._makeOne(autocommit=True) + def view1(request): pass + def view2(request): pass + def view3(request): pass + config.add_view(view1) + config.add_view(view2) + config.add_view(view3) + config.commit() + registeredview = self._getViewCallable(config) + self.assertEqual(registeredview.__name__, 'view3') + +class Test__map_view(unittest.TestCase): + def setUp(self): + from pyramid.registry import Registry + self.registry = Registry() + testing.setUp(registry=self.registry) + + def tearDown(self): + del self.registry + testing.tearDown() + + def _registerRenderer(self, typ='.txt'): + from pyramid.interfaces import IRendererFactory + from pyramid.interfaces import ITemplateRenderer + from zope.interface import implements + class Renderer: + implements(ITemplateRenderer) + spec = 'abc' + typ + def __init__(self, path): + self.__class__.path = path + def __call__(self, *arg): + return 'Hello!' + self.registry.registerUtility(Renderer, IRendererFactory, name=typ) + return Renderer + + def _makeRequest(self): + request = DummyRequest() + request.registry = self.registry + return request + + def _callFUT(self, view, **kw): + from pyramid.config import _map_view + return _map_view(view, self.registry, **kw) + + def test__map_view_as_function_context_and_request(self): + def view(context, request): + return 'OK' + result = self._callFUT(view) + self.failUnless(result is view) + self.assertEqual(result(None, None), 'OK') + + def test__map_view_as_function_with_attr(self): + def view(context, request): + """ """ + result = self._callFUT(view, attr='__name__') + self.failIf(result is view) + self.assertRaises(TypeError, result, None, None) + + def test__map_view_as_function_with_attr_and_renderer(self): + renderer = self._registerRenderer() + view = lambda *arg: 'OK' + info = {'name':renderer.spec, 'package':None} + result = self._callFUT(view, attr='__name__', renderer=info) + self.failIf(result is view) + self.assertRaises(TypeError, result, None, None) + + def test__map_view_as_function_requestonly(self): + def view(request): + return 'OK' + result = self._callFUT(view) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.assertEqual(result(None, None), 'OK') + + def test__map_view_as_function_requestonly_with_attr(self): + def view(request): + """ """ + result = self._callFUT(view, attr='__name__') + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.assertRaises(TypeError, result, None, None) + + def test__map_view_as_newstyle_class_context_and_request(self): + class view(object): + def __init__(self, context, request): + pass + def __call__(self): + return 'OK' + result = self._callFUT(view) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.assertEqual(result(None, None), 'OK') + + def test__map_view_as_newstyle_class_context_and_request_with_attr(self): + class view(object): + def __init__(self, context, request): + pass + def index(self): + return 'OK' + result = self._callFUT(view, attr='index') + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.assertEqual(result(None, None), 'OK') + + def test__map_view_as_newstyle_class_context_and_request_attr_and_renderer( + self): + renderer = self._registerRenderer() + class view(object): + def __init__(self, context, request): + pass + def index(self): + return {'a':'1'} + info = {'name':renderer.spec, 'package':None} + result = self._callFUT(view, attr='index', renderer=info) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + request = self._makeRequest() + self.assertEqual(result(None, request).body, 'Hello!') + + def test__map_view_as_newstyle_class_requestonly(self): + class view(object): + def __init__(self, request): + pass + def __call__(self): + return 'OK' + result = self._callFUT(view) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.assertEqual(result(None, None), 'OK') + + def test__map_view_as_newstyle_class_requestonly_with_attr(self): + class view(object): + def __init__(self, request): + pass + def index(self): + return 'OK' + result = self._callFUT(view, attr='index') + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.assertEqual(result(None, None), 'OK') + + def test__map_view_as_newstyle_class_requestonly_attr_and_renderer(self): + renderer = self._registerRenderer() + class view(object): + def __init__(self, request): + pass + def index(self): + return {'a':'1'} + info = {'name':renderer.spec, 'package':None} + result = self._callFUT(view, attr='index', renderer=info) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + request = self._makeRequest() + self.assertEqual(result(None, request).body, 'Hello!') + + def test__map_view_as_oldstyle_class_context_and_request(self): + class view: + def __init__(self, context, request): + pass + def __call__(self): + return 'OK' + result = self._callFUT(view) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.assertEqual(result(None, None), 'OK') + + def test__map_view_as_oldstyle_class_context_and_request_with_attr(self): + class view: + def __init__(self, context, request): + pass + def index(self): + return 'OK' + result = self._callFUT(view, attr='index') + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.assertEqual(result(None, None), 'OK') + + def test__map_view_as_oldstyle_cls_context_request_attr_and_renderer(self): + renderer = self._registerRenderer() + class view: + def __init__(self, context, request): + pass + def index(self): + return {'a':'1'} + info = {'name':renderer.spec, 'package':None} + result = self._callFUT(view, attr='index', renderer=info) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + request = self._makeRequest() + self.assertEqual(result(None, request).body, 'Hello!') + + def test__map_view_as_oldstyle_class_requestonly(self): + class view: + def __init__(self, request): + pass + def __call__(self): + return 'OK' + result = self._callFUT(view) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.assertEqual(result(None, None), 'OK') + + def test__map_view_as_oldstyle_class_requestonly_with_attr(self): + class view: + def __init__(self, request): + pass + def index(self): + return 'OK' + result = self._callFUT(view, attr='index') + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.assertEqual(result(None, None), 'OK') + + def test__map_view_as_oldstyle_class_requestonly_attr_and_renderer(self): + renderer = self._registerRenderer() + class view: + def __init__(self, request): + pass + def index(self): + return {'a':'1'} + info = {'name':renderer.spec, 'package':None} + result = self._callFUT(view, attr='index', renderer=info) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + request = self._makeRequest() + self.assertEqual(result(None, request).body, 'Hello!') + + def test__map_view_as_instance_context_and_request(self): + class View: + def __call__(self, context, request): + return 'OK' + view = View() + result = self._callFUT(view) + self.failUnless(result is view) + self.assertEqual(result(None, None), 'OK') + + def test__map_view_as_instance_context_and_request_and_attr(self): + class View: + def index(self, context, request): + return 'OK' + view = View() + result = self._callFUT(view, attr='index') + self.failIf(result is view) + self.assertEqual(result(None, None), 'OK') + + def test__map_view_as_instance_context_and_request_attr_and_renderer(self): + renderer = self._registerRenderer() + class View: + def index(self, context, request): + return {'a':'1'} + view = View() + info = {'name':renderer.spec, 'package':None} + result = self._callFUT(view, attr='index', renderer=info) + self.failIf(result is view) + request = self._makeRequest() + self.assertEqual(result(None, request).body, 'Hello!') + + def test__map_view_as_instance_requestonly(self): + class View: + def __call__(self, request): + return 'OK' + view = View() + result = self._callFUT(view) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.failUnless('instance' in result.__name__) + self.assertEqual(result(None, None), 'OK') + + def test__map_view_as_instance_requestonly_with_attr(self): + class View: + def index(self, request): + return 'OK' + view = View() + result = self._callFUT(view, attr='index') + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.failUnless('instance' in result.__name__) + self.assertEqual(result(None, None), 'OK') + + def test__map_view_as_instance_requestonly_with_attr_and_renderer(self): + renderer = self._registerRenderer() + class View: + def index(self, request): + return {'a':'1'} + view = View() + info = {'name':renderer.spec, 'package':None} + result = self._callFUT(view, attr='index', renderer=info) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.failUnless('instance' in result.__name__) + request = self._makeRequest() + self.assertEqual(result(None, request).body, 'Hello!') + + def test__map_view_rendereronly(self): + renderer = self._registerRenderer() + def view(context, request): + return {'a':'1'} + info = {'name':renderer.spec, 'package':None} + result = self._callFUT(view, renderer=info) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + request = self._makeRequest() + self.assertEqual(result(None, request).body, 'Hello!') + + def test__map_view_with_registry(self): + renderer = self._registerRenderer() + def view(context, request): + return {'a':'1'} + info = {'name':renderer.spec, 'package':None} + result = self._callFUT(view, renderer=info) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + request = self._makeRequest() + self.assertEqual(result(None, request).body, 'Hello!') + +class Test_decorate_view(unittest.TestCase): + def _callFUT(self, wrapped, original): + from pyramid.config import decorate_view + return decorate_view(wrapped, original) + + def test_it_same(self): + def view(context, request): + """ """ + result = self._callFUT(view, view) + self.assertEqual(result, False) + + def test_it_different(self): + class DummyView1: + """ 1 """ + __name__ = '1' + __module__ = '1' + def __call__(self, context, request): + """ """ + def __call_permissive__(self, context, reuqest): + """ """ + def __predicated__(self, context, reuqest): + """ """ + def __permitted__(self, context, request): + """ """ + class DummyView2: + """ 2 """ + __name__ = '2' + __module__ = '2' + def __call__(self, context, request): + """ """ + def __call_permissive__(self, context, reuqest): + """ """ + def __predicated__(self, context, reuqest): + """ """ + def __permitted__(self, context, request): + """ """ + view1 = DummyView1() + view2 = DummyView2() + result = self._callFUT(view1, view2) + self.assertEqual(result, True) + self.failUnless(view1.__doc__ is view2.__doc__) + self.failUnless(view1.__module__ is view2.__module__) + self.failUnless(view1.__name__ is view2.__name__) + self.failUnless(view1.__call_permissive__.im_func is + view2.__call_permissive__.im_func) + self.failUnless(view1.__permitted__.im_func is + view2.__permitted__.im_func) + self.failUnless(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.failUnless(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=('a',) + ) + 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=('a',) + ) + 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.failUnless(order3 > order2) + self.failUnless(order4 > order3) + self.failUnless(order5 > order4) + self.failUnless(order6 > order5) + self.failUnless(order7 > order6) + self.failUnless(order8 > order7) + self.failUnless(order9 > order8) + self.failUnless(order10 > order9) + self.failUnless(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=('a',), + ) + self.failUnless(order1 > order2) + self.failUnless(order2 > order3) + self.failUnless(order3 > order4) + self.failUnless(order4 > order5) + self.failUnless(order5 > order6) + self.failUnless(order6 > order7) + self.failUnless(order7 > order8) + self.failUnless(order8 > order9) + + def test_ordering_importance_and_number(self): + order1, _, _ = self._callFUT( + xhr='xhr', + request_method='request_method', + ) + order2, _, _ = self._callFUT( + custom=('a',), + ) + self.failUnless(order1 < order2) + + order1, _, _ = self._callFUT( + xhr='xhr', + request_method='request_method', + ) + order2, _, _ = self._callFUT( + request_method='request_method', + custom=('a',), + ) + self.failUnless(order1 > order2) + + order1, _, _ = self._callFUT( + xhr='xhr', + request_method='request_method', + path_info='path_info', + ) + order2, _, _ = self._callFUT( + request_method='request_method', + custom=('a',), + ) + self.failUnless(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=('a',), + ) + self.failUnless(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')}}) + +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.exceptions import NotFound + mv = self._makeOne() + context = DummyContext() + request = DummyRequest() + self.assertRaises(NotFound, mv.match, context, request) + + def test_match_predicate_fails(self): + from pyramid.exceptions import NotFound + mv = self._makeOne() + def view(context, request): + """ """ + view.__predicated__ = lambda *arg: False + mv.views = [(100, view, None)] + context = DummyContext() + request = DummyRequest() + self.assertRaises(NotFound, 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.exceptions import NotFound + mv = self._makeOne() + context = DummyContext() + request = DummyRequest() + self.assertRaises(NotFound, 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.exceptions import NotFound + mv = self._makeOne() + context = DummyContext() + request = DummyRequest() + self.assertRaises(NotFound, 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.exceptions import NotFound + mv = self._makeOne() + context = DummyContext() + request = DummyRequest() + request.view_name = '' + def view1(context, request): + raise NotFound + def view2(context, request): + """ """ + mv.views = [(100, view1, None), (99, view2, None)] + self.assertRaises(NotFound, 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.exceptions import NotFound + mv = self._makeOne() + context = DummyContext() + request = DummyRequest() + self.assertRaises(NotFound, 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 TestRequestOnly(unittest.TestCase): + def _callFUT(self, arg): + from pyramid.config import requestonly + return requestonly(arg) + + def test_newstyle_class_no_init(self): + class foo(object): + """ """ + self.assertFalse(self._callFUT(foo)) + + def test_newstyle_class_init_toomanyargs(self): + class foo(object): + def __init__(self, context, request): + """ """ + self.assertFalse(self._callFUT(foo)) + + def test_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_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_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 TestActionPredicate(unittest.TestCase): + def _getTargetClass(self): + from pyramid.config import ActionPredicate + return ActionPredicate + + def _makeOne(self, action='myaction'): + return self._getTargetClass()(action) + + def test_bad_action_regex_string(self): + from pyramid.exceptions import ConfigurationError + cls = self._getTargetClass() + self.assertRaises(ConfigurationError, cls, '[a-z') + + def test_bad_action_regex_None(self): + from pyramid.exceptions import ConfigurationError + cls = self._getTargetClass() + self.assertRaises(ConfigurationError, cls, None) + + def test___call__no_matchdict(self): + pred = self._makeOne() + request = DummyRequest() + self.assertEqual(pred(None, request), False) + + def test___call__no_action_in_matchdict(self): + pred = self._makeOne() + request = DummyRequest() + request.matchdict = {} + self.assertEqual(pred(None, request), False) + + def test___call__action_does_not_match(self): + pred = self._makeOne() + request = DummyRequest() + request.matchdict = {'action':'notmyaction'} + self.assertEqual(pred(None, request), False) + + def test___call__action_matches(self): + pred = self._makeOne() + request = DummyRequest() + request.matchdict = {'action':'myaction'} + self.assertEqual(pred(None, request), True) + + def test___hash__(self): + pred1 = self._makeOne() + pred2 = self._makeOne() + pred3 = self._makeOne(action='notthesame') + self.assertEqual(hash(pred1), hash(pred2)) + self.assertNotEqual(hash(pred1), hash(pred3)) + self.assertNotEqual(hash(pred2), hash(pred3)) + + + +class DummyRequest: + subpath = () + matchdict = None + def __init__(self): + self.environ = {'PATH_INFO':'/static'} + self.params = {} + self.cookies = {} + def copy(self): + return self + def get_response(self, app): + return app + +class DummyContext: + pass + +class DummyLock: + def acquire(self): + self.acquired = True + + def release(self): + self.released = True + +class DummyPackage: + def __init__(self, name): + self.__name__ = name + +class DummyOverrides: + def __init__(self, package): + self.package = package + self.inserted = [] + + def insert(self, path, package, prefix): + self.inserted.append((path, package, prefix)) + +class DummyUnderOverride: + def __call__(self, package, path, override_package, override_prefix, + _info=u''): + self.package = package + self.path = path + self.override_package = override_package + self.override_prefix = override_prefix + +from zope.interface import Interface +class IDummy(Interface): + pass + +class IOther(Interface): + pass + +class DummyResponse: + status = '200 OK' + headerlist = () + app_iter = () + body = '' + +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 + +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: + implements(IMultiView) + def __init__(self): + self.views = [] + self.name = 'name' + def add(self, view, order, accept=None, phash=None): + self.views.append((view, accept, phash)) + def __call__(self, context, request): + return 'OK1' + def __permitted__(self, context, request): + """ """ + +class DummyGetSiteManager(object): + def sethook(self, hook): + self.hook = hook + def reset(self): + self.unhooked = True + +class DummyThreadLocalManager(object): + pushed = None + popped = False + def push(self, d): + self.pushed = d + def pop(self): + self.popped = True + +class IFactory(Interface): + pass + +class DummyFactory(object): + implements(IFactory) + def __call__(self): + """ """ + +class DummyEvent: + implements(IDummy) + +class DummyStaticURLInfo: + def __init__(self): + self.added = [] + + def add(self, name, spec, **kw): + self.added.append((name, spec, kw)) + +def dummy_view(request): + return 'OK' + +def dummyfactory(request): + """ """ + +class DummyHandler(object): # pragma: no cover + def __init__(self, request): + self.request = request + + def action1(self): + return 'response 1' + + def action2(self): + return 'response 2' + +def dummy_include(config): + config._action('discrim', None, config.package) + diff --git a/pyramid/tests/test_configuration.py b/pyramid/tests/test_configuration.py index 1e3f74a7e..786c7539b 100644 --- a/pyramid/tests/test_configuration.py +++ b/pyramid/tests/test_configuration.py @@ -1,4641 +1,21 @@ import unittest -from pyramid import testing - -try: - import __pypy__ -except: - __pypy__ = None - -class ConfigTests(unittest.TestCase): - def _makeOne(self, *arg, **kw): - from pyramid.configuration import Config - return Config(*arg, **kw) - - def _registerRenderer(self, config, name='.txt'): - from pyramid.interfaces import IRendererFactory - from pyramid.interfaces import ITemplateRenderer - from zope.interface import implements - class Renderer: - implements(ITemplateRenderer) - def __init__(self, info): - self.__class__.info = info - def __call__(self, *arg): - return 'Hello!' - config.registry.registerUtility(Renderer, IRendererFactory, name=name) - return Renderer - - def _getViewCallable(self, config, ctx_iface=None, request_iface=None, - name='', exception_view=False): - from zope.interface import Interface - from pyramid.interfaces import IRequest - from pyramid.interfaces import IView - from pyramid.interfaces import IViewClassifier - from pyramid.interfaces import IExceptionViewClassifier - if exception_view: - classifier = IExceptionViewClassifier - else: - classifier = IViewClassifier - if ctx_iface is None: - ctx_iface = Interface - if request_iface is None: - request_iface = IRequest - return config.registry.adapters.lookup( - (classifier, request_iface, ctx_iface), IView, name=name, - default=None) - - def _getRouteRequestIface(self, config, name): - from pyramid.interfaces import IRouteRequest - iface = config.registry.getUtility(IRouteRequest, name) - return iface - - def _assertNotFound(self, wrapper, *arg): - from pyramid.exceptions import NotFound - self.assertRaises(NotFound, wrapper, *arg) - - def _registerEventListener(self, config, event_iface=None): - if event_iface is None: # pragma: no cover - from zope.interface import Interface - event_iface = Interface - L = [] - def subscriber(*event): - L.extend(event) - config.registry.registerHandler(subscriber, (event_iface,)) - return L - - def _registerLogger(self, config): - from pyramid.interfaces import IDebugLogger - logger = DummyLogger() - config.registry.registerUtility(logger, IDebugLogger) - return logger - - def _makeRequest(self, config): - request = DummyRequest() - request.registry = config.registry - return request - - def _registerSecurityPolicy(self, config, permissive): - from pyramid.interfaces import IAuthenticationPolicy - from pyramid.interfaces import IAuthorizationPolicy - policy = DummySecurityPolicy(permissive) - config.registry.registerUtility(policy, IAuthenticationPolicy) - config.registry.registerUtility(policy, IAuthorizationPolicy) - - def _registerSettings(self, config, **settings): - config.registry.settings = settings - - def test_ctor_no_registry(self): - import sys - from pyramid.interfaces import ISettings - from pyramid.configuration import Config - from pyramid.interfaces import IRendererFactory - config = Config() - this_pkg = sys.modules['pyramid.tests'] - self.failUnless(config.registry.getUtility(ISettings)) - self.assertEqual(config.package, this_pkg) - self.failUnless(config.registry.getUtility(IRendererFactory, 'json')) - self.failUnless(config.registry.getUtility(IRendererFactory, 'string')) - if not __pypy__: - self.failUnless(config.registry.getUtility(IRendererFactory, '.pt')) - self.failUnless(config.registry.getUtility(IRendererFactory,'.txt')) - self.failUnless(config.registry.getUtility(IRendererFactory, '.mak')) - self.failUnless(config.registry.getUtility(IRendererFactory, '.mako')) - - def test_begin(self): - from pyramid.configuration import Config - config = Config() - manager = DummyThreadLocalManager() - config.manager = manager - config.begin() - self.assertEqual(manager.pushed, - {'registry':config.registry, 'request':None}) - self.assertEqual(manager.popped, False) - - def test_begin_with_request(self): - from pyramid.configuration import Config - config = Config() - request = object() - manager = DummyThreadLocalManager() - config.manager = manager - config.begin(request=request) - self.assertEqual(manager.pushed, - {'registry':config.registry, 'request':request}) - self.assertEqual(manager.popped, False) - - def test_end(self): - from pyramid.configuration import Config - config = Config() - manager = DummyThreadLocalManager() - config.manager = manager - config.end() - self.assertEqual(manager.pushed, None) - self.assertEqual(manager.popped, True) - - def test_ctor_with_package_registry(self): - import sys - from pyramid.configuration import Config - pkg = sys.modules['pyramid'] - config = Config(package=pkg) - self.assertEqual(config.package, pkg) - - def test_ctor_noreg_custom_settings(self): - from pyramid.interfaces import ISettings - settings = {'reload_templates':True, - 'mysetting':True} - config = self._makeOne(settings=settings) - settings = config.registry.getUtility(ISettings) - self.assertEqual(settings['reload_templates'], True) - self.assertEqual(settings['debug_authorization'], False) - self.assertEqual(settings['mysetting'], True) - - def test_ctor_noreg_debug_logger_None_default(self): - from pyramid.interfaces import IDebugLogger - config = self._makeOne() - logger = config.registry.getUtility(IDebugLogger) - self.assertEqual(logger.name, 'pyramid.debug') - - def test_ctor_noreg_debug_logger_non_None(self): - from pyramid.interfaces import IDebugLogger - logger = object() - config = self._makeOne(debug_logger=logger) - result = config.registry.getUtility(IDebugLogger) - self.assertEqual(logger, result) - - def test_ctor_authentication_policy(self): - from pyramid.interfaces import IAuthenticationPolicy - policy = object() - config = self._makeOne(authentication_policy=policy) - result = config.registry.getUtility(IAuthenticationPolicy) - self.assertEqual(policy, result) - - def test_ctor_authorization_policy_only(self): - from pyramid.exceptions import ConfigurationError - policy = object() - self.assertRaises(ConfigurationError, - self._makeOne, authorization_policy=policy) - - def test_ctor_no_root_factory(self): - from pyramid.interfaces import IRootFactory - config = self._makeOne() - self.failUnless(config.registry.getUtility(IRootFactory)) - - def test_ctor_alternate_renderers(self): - from pyramid.interfaces import IRendererFactory - renderer = object() - config = self._makeOne(renderers=[('yeah', renderer)]) - self.assertEqual(config.registry.getUtility(IRendererFactory, 'yeah'), - renderer) - - def test_ctor_default_permission(self): - from pyramid.interfaces import IDefaultPermission - config = self._makeOne(default_permission='view') - self.assertEqual(config.registry.getUtility(IDefaultPermission), 'view') - - def test_ctor_session_factory(self): - from pyramid.interfaces import ISessionFactory - config = self._makeOne(session_factory='factory') - self.assertEqual(config.registry.getUtility(ISessionFactory), 'factory') - - def test_with_package_module(self): - from pyramid.tests import test_configuration - import pyramid.tests - config = self._makeOne() - newconfig = config.with_package(test_configuration) - self.assertEqual(newconfig.package, pyramid.tests) - - def test_with_package_package(self): - import pyramid.tests - config = self._makeOne() - newconfig = config.with_package(pyramid.tests) - self.assertEqual(newconfig.package, pyramid.tests) - - def test_maybe_dotted_string_success(self): - import pyramid.tests - config = self._makeOne() - result = config.maybe_dotted('pyramid.tests') - self.assertEqual(result, pyramid.tests) - - def test_maybe_dotted_string_fail(self): - config = self._makeOne() - self.assertRaises(ImportError, - config.maybe_dotted, 'cant.be.found') - - def test_maybe_dotted_notstring_success(self): - import pyramid.tests - config = self._makeOne() - result = config.maybe_dotted(pyramid.tests) - self.assertEqual(result, pyramid.tests) - - def test_absolute_resource_spec_already_absolute(self): - import pyramid.tests - config = self._makeOne(package=pyramid.tests) - result = config.absolute_resource_spec('already:absolute') - self.assertEqual(result, 'already:absolute') - - def test_absolute_resource_spec_notastring(self): - import pyramid.tests - config = self._makeOne(package=pyramid.tests) - result = config.absolute_resource_spec(None) - self.assertEqual(result, None) - - def test_absolute_resource_spec_relative(self): - import pyramid.tests - config = self._makeOne(package=pyramid.tests) - result = config.absolute_resource_spec('templates') - self.assertEqual(result, 'pyramid.tests:templates') - - def test_setup_registry_fixed(self): - class DummyRegistry(object): - def subscribers(self, events, name): - self.events = events - return events - def registerUtility(self, *arg, **kw): - pass - reg = DummyRegistry() - config = self._makeOne(reg) - config.add_view = lambda *arg, **kw: False - config.setup_registry() - self.assertEqual(reg.has_listeners, True) - self.assertEqual(reg.notify(1), None) - self.assertEqual(reg.events, (1,)) - - def test_setup_registry_registers_default_exceptionresponse_view(self): - from pyramid.interfaces import IExceptionResponse - from pyramid.view import default_exceptionresponse_view - class DummyRegistry(object): - def registerUtility(self, *arg, **kw): - pass - reg = DummyRegistry() - config = self._makeOne(reg) - views = [] - config.add_view = lambda *arg, **kw: views.append((arg, kw)) - config.setup_registry() - self.assertEqual(views[0], ((default_exceptionresponse_view,), - {'context':IExceptionResponse})) - - def test_setup_registry_explicit_notfound_trumps_iexceptionresponse(self): - from zope.interface import implementedBy - from pyramid.interfaces import IRequest - from pyramid.exceptions import NotFound - from pyramid.registry import Registry - reg = Registry() - config = self._makeOne(reg, autocommit=True) - config.setup_registry() # registers IExceptionResponse default view - def myview(context, request): - return 'OK' - config.add_view(myview, context=NotFound) - request = self._makeRequest(config) - view = self._getViewCallable(config, ctx_iface=implementedBy(NotFound), - request_iface=IRequest) - result = view(None, request) - self.assertEqual(result, 'OK') - - def test_setup_registry_custom_settings(self): - from pyramid.registry import Registry - from pyramid.interfaces import ISettings - settings = {'reload_templates':True, - 'mysetting':True} - reg = Registry() - config = self._makeOne(reg) - config.setup_registry(settings=settings) - settings = reg.getUtility(ISettings) - self.assertEqual(settings['reload_templates'], True) - self.assertEqual(settings['debug_authorization'], False) - self.assertEqual(settings['mysetting'], True) - - def test_setup_registry_debug_logger_None_default(self): - from pyramid.registry import Registry - from pyramid.interfaces import IDebugLogger - reg = Registry() - config = self._makeOne(reg) - config.setup_registry() - logger = reg.getUtility(IDebugLogger) - self.assertEqual(logger.name, 'pyramid.debug') - - def test_setup_registry_debug_logger_non_None(self): - from pyramid.registry import Registry - from pyramid.interfaces import IDebugLogger - logger = object() - reg = Registry() - config = self._makeOne(reg) - config.setup_registry(debug_logger=logger) - result = reg.getUtility(IDebugLogger) - self.assertEqual(logger, result) - - def test_setup_registry_debug_logger_dottedname(self): - from pyramid.registry import Registry - from pyramid.interfaces import IDebugLogger - reg = Registry() - config = self._makeOne(reg) - config.setup_registry(debug_logger='pyramid.tests') - result = reg.getUtility(IDebugLogger) - import pyramid.tests - self.assertEqual(result, pyramid.tests) - - def test_setup_registry_authentication_policy(self): - from pyramid.registry import Registry - from pyramid.interfaces import IAuthenticationPolicy - policy = object() - reg = Registry() - config = self._makeOne(reg) - config.setup_registry(authentication_policy=policy) - result = reg.getUtility(IAuthenticationPolicy) - self.assertEqual(policy, result) - - def test_setup_registry_authentication_policy_dottedname(self): - from pyramid.registry import Registry - from pyramid.interfaces import IAuthenticationPolicy - reg = Registry() - config = self._makeOne(reg) - config.setup_registry(authentication_policy='pyramid.tests') - result = reg.getUtility(IAuthenticationPolicy) - import pyramid.tests - self.assertEqual(result, pyramid.tests) - - def test_setup_registry_authorization_policy_dottedname(self): - from pyramid.registry import Registry - from pyramid.interfaces import IAuthorizationPolicy - reg = Registry() - config = self._makeOne(reg) - dummy = object() - config.setup_registry(authentication_policy=dummy, - authorization_policy='pyramid.tests') - result = reg.getUtility(IAuthorizationPolicy) - import pyramid.tests - self.assertEqual(result, pyramid.tests) - - def test_setup_registry_authorization_policy_only(self): - from pyramid.registry import Registry - from pyramid.exceptions import ConfigurationError - policy = object() - reg = Registry() - config = self._makeOne(reg) - config = self.assertRaises(ConfigurationError, - config.setup_registry, - authorization_policy=policy) - - def test_setup_registry_default_root_factory(self): - from pyramid.registry import Registry - from pyramid.interfaces import IRootFactory - reg = Registry() - config = self._makeOne(reg) - config.setup_registry() - self.failUnless(reg.getUtility(IRootFactory)) - - def test_setup_registry_dottedname_root_factory(self): - from pyramid.registry import Registry - from pyramid.interfaces import IRootFactory - reg = Registry() - config = self._makeOne(reg) - import pyramid.tests - config.setup_registry(root_factory='pyramid.tests') - self.assertEqual(reg.getUtility(IRootFactory), pyramid.tests) - - def test_setup_registry_locale_negotiator_dottedname(self): - from pyramid.registry import Registry - from pyramid.interfaces import ILocaleNegotiator - reg = Registry() - config = self._makeOne(reg) - import pyramid.tests - config.setup_registry(locale_negotiator='pyramid.tests') - utility = reg.getUtility(ILocaleNegotiator) - self.assertEqual(utility, pyramid.tests) - - def test_setup_registry_locale_negotiator(self): - from pyramid.registry import Registry - from pyramid.interfaces import ILocaleNegotiator - reg = Registry() - config = self._makeOne(reg) - negotiator = object() - config.setup_registry(locale_negotiator=negotiator) - utility = reg.getUtility(ILocaleNegotiator) - self.assertEqual(utility, negotiator) - - def test_setup_registry_request_factory(self): - from pyramid.registry import Registry - from pyramid.interfaces import IRequestFactory - reg = Registry() - config = self._makeOne(reg) - factory = object() - config.setup_registry(request_factory=factory) - utility = reg.getUtility(IRequestFactory) - self.assertEqual(utility, factory) - - def test_setup_registry_request_factory_dottedname(self): - from pyramid.registry import Registry - from pyramid.interfaces import IRequestFactory - reg = Registry() - config = self._makeOne(reg) - import pyramid.tests - config.setup_registry(request_factory='pyramid.tests') - utility = reg.getUtility(IRequestFactory) - self.assertEqual(utility, pyramid.tests) - - def test_setup_registry_renderer_globals_factory(self): - from pyramid.registry import Registry - from pyramid.interfaces import IRendererGlobalsFactory - reg = Registry() - config = self._makeOne(reg) - factory = object() - config.setup_registry(renderer_globals_factory=factory) - utility = reg.getUtility(IRendererGlobalsFactory) - self.assertEqual(utility, factory) - - def test_setup_registry_renderer_globals_factory_dottedname(self): - from pyramid.registry import Registry - from pyramid.interfaces import IRendererGlobalsFactory - reg = Registry() - config = self._makeOne(reg) - import pyramid.tests - config.setup_registry(renderer_globals_factory='pyramid.tests') - utility = reg.getUtility(IRendererGlobalsFactory) - self.assertEqual(utility, pyramid.tests) - - def test_setup_registry_alternate_renderers(self): - from pyramid.registry import Registry - from pyramid.interfaces import IRendererFactory - renderer = object() - reg = Registry() - config = self._makeOne(reg) - config.setup_registry(renderers=[('yeah', renderer)]) - self.assertEqual(reg.getUtility(IRendererFactory, 'yeah'), - renderer) - - def test_setup_registry_default_permission(self): - from pyramid.registry import Registry - from pyramid.interfaces import IDefaultPermission - reg = Registry() - config = self._makeOne(reg) - config.setup_registry(default_permission='view') - self.assertEqual(reg.getUtility(IDefaultPermission), 'view') - - def test_get_settings_nosettings(self): - from pyramid.registry import Registry - reg = Registry() - config = self._makeOne(reg) - self.assertEqual(config.get_settings(), None) - - def test_get_settings_withsettings(self): - settings = {'a':1} - config = self._makeOne() - config.registry.settings = settings - self.assertEqual(config.get_settings(), settings) - - def test_add_settings_settings_already_registered(self): - from pyramid.registry import Registry - reg = Registry() - config = self._makeOne(reg) - config._set_settings({'a':1}) - config.add_settings({'b':2}) - settings = reg.settings - self.assertEqual(settings['a'], 1) - self.assertEqual(settings['b'], 2) - - def test_add_settings_settings_not_yet_registered(self): - from pyramid.registry import Registry - from pyramid.interfaces import ISettings - reg = Registry() - config = self._makeOne(reg) - config.add_settings({'a':1}) - settings = reg.getUtility(ISettings) - self.assertEqual(settings['a'], 1) - - def test_add_subscriber_defaults(self): - from zope.interface import implements - from zope.interface import Interface - class IEvent(Interface): - pass - class Event: - implements(IEvent) - L = [] - def subscriber(event): - L.append(event) - config = self._makeOne(autocommit=True) - config.add_subscriber(subscriber) - event = Event() - config.registry.notify(event) - self.assertEqual(len(L), 1) - self.assertEqual(L[0], event) - config.registry.notify(object()) - self.assertEqual(len(L), 2) - - def test_add_subscriber_iface_specified(self): - from zope.interface import implements - from zope.interface import Interface - class IEvent(Interface): - pass - class Event: - implements(IEvent) - L = [] - def subscriber(event): - L.append(event) - config = self._makeOne(autocommit=True) - config.add_subscriber(subscriber, IEvent) - event = Event() - config.registry.notify(event) - self.assertEqual(len(L), 1) - self.assertEqual(L[0], event) - config.registry.notify(object()) - self.assertEqual(len(L), 1) - - def test_add_subscriber_dottednames(self): - import pyramid.tests - from pyramid.interfaces import INewRequest - config = self._makeOne(autocommit=True) - config.add_subscriber('pyramid.tests', - 'pyramid.interfaces.INewRequest') - handlers = list(config.registry.registeredHandlers()) - self.assertEqual(len(handlers), 1) - handler = handlers[0] - self.assertEqual(handler.handler, pyramid.tests) - self.assertEqual(handler.required, (INewRequest,)) - - def test_add_object_event_subscriber(self): - from zope.interface import implements - from zope.interface import Interface - class IEvent(Interface): - pass - class Event: - object = 'foo' - implements(IEvent) - event = Event() - L = [] - def subscriber(object, event): - L.append(event) - config = self._makeOne(autocommit=True) - config.add_subscriber(subscriber, (Interface, IEvent)) - config.registry.subscribers((event.object, event), None) - self.assertEqual(len(L), 1) - self.assertEqual(L[0], event) - config.registry.subscribers((event.object, IDummy), None) - self.assertEqual(len(L), 1) - - def test_make_wsgi_app(self): - from pyramid.router import Router - from pyramid.interfaces import IApplicationCreated - manager = DummyThreadLocalManager() - config = self._makeOne() - subscriber = self._registerEventListener(config, IApplicationCreated) - config.manager = manager - app = config.make_wsgi_app() - self.assertEqual(app.__class__, Router) - self.assertEqual(manager.pushed['registry'], config.registry) - self.assertEqual(manager.pushed['request'], None) - self.failUnless(manager.popped) - self.assertEqual(len(subscriber), 1) - self.failUnless(IApplicationCreated.providedBy(subscriber[0])) - - def test_load_zcml_default(self): - import pyramid.tests.fixtureapp - config = self._makeOne(package=pyramid.tests.fixtureapp, - autocommit=True) - registry = config.load_zcml() - from pyramid.tests.fixtureapp.models import IFixture - self.failUnless(registry.queryUtility(IFixture)) # only in c.zcml - - def test_load_zcml_routesapp(self): - from pyramid.interfaces import IRoutesMapper - config = self._makeOne(autocommit=True) - config.load_zcml('pyramid.tests.routesapp:configure.zcml') - self.failUnless(config.registry.getUtility(IRoutesMapper)) - - def test_load_zcml_fixtureapp(self): - from pyramid.tests.fixtureapp.models import IFixture - config = self._makeOne(autocommit=True) - config.load_zcml('pyramid.tests.fixtureapp:configure.zcml') - self.failUnless(config.registry.queryUtility(IFixture)) # only in c.zcml - - def test_load_zcml_as_relative_filename(self): - import pyramid.tests.fixtureapp - config = self._makeOne(package=pyramid.tests.fixtureapp, - autocommit=True) - registry = config.load_zcml('configure.zcml') - from pyramid.tests.fixtureapp.models import IFixture - self.failUnless(registry.queryUtility(IFixture)) # only in c.zcml - - def test_load_zcml_as_absolute_filename(self): - import os - import pyramid.tests.fixtureapp - config = self._makeOne(package=pyramid.tests.fixtureapp, - autocommit=True) - dn = os.path.dirname(pyramid.tests.fixtureapp.__file__) - c_z = os.path.join(dn, 'configure.zcml') - registry = config.load_zcml(c_z) - from pyramid.tests.fixtureapp.models import IFixture - self.failUnless(registry.queryUtility(IFixture)) # only in c.zcml - - def test_load_zcml_lock_and_unlock(self): - config = self._makeOne(autocommit=True) - dummylock = DummyLock() - config.load_zcml( - 'pyramid.tests.fixtureapp:configure.zcml', - lock=dummylock) - self.assertEqual(dummylock.acquired, True) - self.assertEqual(dummylock.released, True) - - def test_include_with_dotted_name(self): - from pyramid import tests - config = self._makeOne() - context_before = config._make_context() - config._ctx = context_before - config.include('pyramid.tests.test_configuration.dummy_include') - context_after = config._ctx - actions = context_after.actions - self.assertEqual(len(actions), 1) - self.assertEqual( - context_after.actions[0][:3], - ('discrim', None, tests), - ) - self.assertEqual(context_after.basepath, None) - self.assertEqual(context_after.includepath, ()) - self.failUnless(context_after is context_before) - - def test_include_with_python_callable(self): - from pyramid import tests - config = self._makeOne() - context_before = config._make_context() - config._ctx = context_before - config.include(dummy_include) - context_after = config._ctx - actions = context_after.actions - self.assertEqual(len(actions), 1) - self.assertEqual( - actions[0][:3], - ('discrim', None, tests), - ) - self.assertEqual(context_after.basepath, None) - self.assertEqual(context_after.includepath, ()) - self.failUnless(context_after is context_before) - - def test_add_view_view_callable_None_no_renderer(self): - from pyramid.exceptions import ConfigurationError - config = self._makeOne(autocommit=True) - self.assertRaises(ConfigurationError, config.add_view) - - def test_add_view_with_request_type_and_route_name(self): - from pyramid.exceptions import ConfigurationError - config = self._makeOne(autocommit=True) - view = lambda *arg: 'OK' - self.assertRaises(ConfigurationError, config.add_view, view, '', None, - None, True, True) - - def test_add_view_with_request_type(self): - from zope.interface import directlyProvides - from pyramid.interfaces import IRequest - view = lambda *arg: 'OK' - config = self._makeOne(autocommit=True) - config.add_view(view=view, - request_type='pyramid.interfaces.IRequest') - wrapper = self._getViewCallable(config) - request = DummyRequest() - self._assertNotFound(wrapper, None, request) - directlyProvides(request, IRequest) - result = wrapper(None, request) - self.assertEqual(result, 'OK') - - def test_add_view_view_callable_None_with_renderer(self): - config = self._makeOne(autocommit=True) - self._registerRenderer(config, name='dummy') - config.add_view(renderer='dummy') - view = self._getViewCallable(config) - self.failUnless('Hello!' in view(None, None).body) - - def test_add_view_wrapped_view_is_decorated(self): - def view(request): # request-only wrapper - """ """ - config = self._makeOne(autocommit=True) - config.add_view(view=view) - wrapper = self._getViewCallable(config) - self.assertEqual(wrapper.__module__, view.__module__) - self.assertEqual(wrapper.__name__, view.__name__) - self.assertEqual(wrapper.__doc__, view.__doc__) - - def test_add_view_view_callable_dottedname(self): - config = self._makeOne(autocommit=True) - config.add_view(view='pyramid.tests.test_configuration.dummy_view') - wrapper = self._getViewCallable(config) - self.assertEqual(wrapper(None, None), 'OK') - - def test_add_view_with_function_callable(self): - view = lambda *arg: 'OK' - config = self._makeOne(autocommit=True) - config.add_view(view=view) - wrapper = self._getViewCallable(config) - result = wrapper(None, None) - self.assertEqual(result, 'OK') - - def test_add_view_with_function_callable_requestonly(self): - def view(request): - return 'OK' - config = self._makeOne(autocommit=True) - config.add_view(view=view) - wrapper = self._getViewCallable(config) - result = wrapper(None, None) - self.assertEqual(result, 'OK') - - def test_add_view_as_instance(self): - class AView: - def __call__(self, context, request): - """ """ - return 'OK' - view = AView() - config = self._makeOne(autocommit=True) - config.add_view(view=view) - wrapper = self._getViewCallable(config) - result = wrapper(None, None) - self.assertEqual(result, 'OK') - - def test_add_view_as_instance_requestonly(self): - class AView: - def __call__(self, request): - """ """ - return 'OK' - view = AView() - config = self._makeOne(autocommit=True) - config.add_view(view=view) - wrapper = self._getViewCallable(config) - result = wrapper(None, None) - self.assertEqual(result, 'OK') - - def test_add_view_as_oldstyle_class(self): - class view: - def __init__(self, context, request): - self.context = context - self.request = request - - def __call__(self): - return 'OK' - config = self._makeOne(autocommit=True) - config.add_view(view=view) - wrapper = self._getViewCallable(config) - result = wrapper(None, None) - self.assertEqual(result, 'OK') - - def test_add_view_as_oldstyle_class_requestonly(self): - class view: - def __init__(self, request): - self.request = request - - def __call__(self): - return 'OK' - config = self._makeOne(autocommit=True) - config.add_view(view=view) - wrapper = self._getViewCallable(config) - result = wrapper(None, None) - self.assertEqual(result, 'OK') - - def test_add_view_context_as_class(self): - from zope.interface import implementedBy - view = lambda *arg: 'OK' - class Foo: - pass - config = self._makeOne(autocommit=True) - config.add_view(context=Foo, view=view) - foo = implementedBy(Foo) - wrapper = self._getViewCallable(config, foo) - self.assertEqual(wrapper, view) - - def test_add_view_context_as_iface(self): - view = lambda *arg: 'OK' - config = self._makeOne(autocommit=True) - config.add_view(context=IDummy, view=view) - wrapper = self._getViewCallable(config, IDummy) - self.assertEqual(wrapper, view) - - def test_add_view_context_as_dottedname(self): - view = lambda *arg: 'OK' - config = self._makeOne(autocommit=True) - config.add_view(context='pyramid.tests.test_configuration.IDummy', - view=view) - wrapper = self._getViewCallable(config, IDummy) - self.assertEqual(wrapper, view) - - def test_add_view_for__as_dottedname(self): - view = lambda *arg: 'OK' - config = self._makeOne(autocommit=True) - config.add_view(for_='pyramid.tests.test_configuration.IDummy', - view=view) - wrapper = self._getViewCallable(config, IDummy) - self.assertEqual(wrapper, view) - - def test_add_view_for_as_class(self): - # ``for_`` is older spelling for ``context`` - from zope.interface import implementedBy - view = lambda *arg: 'OK' - class Foo: - pass - config = self._makeOne(autocommit=True) - config.add_view(for_=Foo, view=view) - foo = implementedBy(Foo) - wrapper = self._getViewCallable(config, foo) - self.assertEqual(wrapper, view) - - def test_add_view_for_as_iface(self): - # ``for_`` is older spelling for ``context`` - view = lambda *arg: 'OK' - config = self._makeOne(autocommit=True) - config.add_view(for_=IDummy, view=view) - wrapper = self._getViewCallable(config, IDummy) - self.assertEqual(wrapper, view) - - def test_add_view_context_trumps_for(self): - # ``for_`` is older spelling for ``context`` - view = lambda *arg: 'OK' - config = self._makeOne(autocommit=True) - class Foo: - pass - config.add_view(context=IDummy, for_=Foo, view=view) - wrapper = self._getViewCallable(config, IDummy) - self.assertEqual(wrapper, view) - - def test_add_view_register_secured_view(self): - from zope.interface import Interface - from pyramid.interfaces import IRequest - from pyramid.interfaces import ISecuredView - from pyramid.interfaces import IViewClassifier - view = lambda *arg: 'OK' - view.__call_permissive__ = view - config = self._makeOne(autocommit=True) - config.add_view(view=view) - wrapper = config.registry.adapters.lookup( - (IViewClassifier, IRequest, Interface), - ISecuredView, name='', default=None) - self.assertEqual(wrapper, view) - - def test_add_view_exception_register_secured_view(self): - from zope.interface import implementedBy - from pyramid.interfaces import IRequest - from pyramid.interfaces import IView - from pyramid.interfaces import IExceptionViewClassifier - view = lambda *arg: 'OK' - view.__call_permissive__ = view - config = self._makeOne(autocommit=True) - config.add_view(view=view, context=RuntimeError) - wrapper = config.registry.adapters.lookup( - (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), - IView, name='', default=None) - self.assertEqual(wrapper, view) - - def test_add_view_same_phash_overrides_existing_single_view(self): - from pyramid.compat import md5 - from zope.interface import Interface - from pyramid.interfaces import IRequest - from pyramid.interfaces import IView - from pyramid.interfaces import IViewClassifier - from pyramid.interfaces import IMultiView - phash = md5() - phash.update('xhr:True') - view = lambda *arg: 'NOT OK' - view.__phash__ = phash.hexdigest() - config = self._makeOne(autocommit=True) - config.registry.registerAdapter( - view, (IViewClassifier, IRequest, Interface), IView, name='') - def newview(context, request): - return 'OK' - config.add_view(view=newview, xhr=True) - wrapper = self._getViewCallable(config) - self.failIf(IMultiView.providedBy(wrapper)) - request = DummyRequest() - request.is_xhr = True - self.assertEqual(wrapper(None, request), 'OK') - - def test_add_view_exc_same_phash_overrides_existing_single_view(self): - from pyramid.compat import md5 - from zope.interface import implementedBy - from pyramid.interfaces import IRequest - from pyramid.interfaces import IView - from pyramid.interfaces import IExceptionViewClassifier - from pyramid.interfaces import IMultiView - phash = md5() - phash.update('xhr:True') - view = lambda *arg: 'NOT OK' - view.__phash__ = phash.hexdigest() - config = self._makeOne(autocommit=True) - config.registry.registerAdapter( - view, - (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), - IView, name='') - def newview(context, request): - return 'OK' - config.add_view(view=newview, xhr=True, context=RuntimeError) - wrapper = self._getViewCallable( - config, ctx_iface=implementedBy(RuntimeError), exception_view=True) - self.failIf(IMultiView.providedBy(wrapper)) - request = DummyRequest() - request.is_xhr = True - self.assertEqual(wrapper(None, request), 'OK') - - def test_add_view_default_phash_overrides_no_phash(self): - from zope.interface import Interface - from pyramid.interfaces import IRequest - from pyramid.interfaces import IView - from pyramid.interfaces import IViewClassifier - from pyramid.interfaces import IMultiView - view = lambda *arg: 'NOT OK' - config = self._makeOne(autocommit=True) - config.registry.registerAdapter( - view, (IViewClassifier, IRequest, Interface), IView, name='') - def newview(context, request): - return 'OK' - config.add_view(view=newview) - wrapper = self._getViewCallable(config) - self.failIf(IMultiView.providedBy(wrapper)) - request = DummyRequest() - request.is_xhr = True - self.assertEqual(wrapper(None, request), 'OK') - - def test_add_view_exc_default_phash_overrides_no_phash(self): - from zope.interface import implementedBy - from pyramid.interfaces import IRequest - from pyramid.interfaces import IView - from pyramid.interfaces import IExceptionViewClassifier - from pyramid.interfaces import IMultiView - view = lambda *arg: 'NOT OK' - config = self._makeOne(autocommit=True) - config.registry.registerAdapter( - view, - (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), - IView, name='') - def newview(context, request): - return 'OK' - config.add_view(view=newview, context=RuntimeError) - wrapper = self._getViewCallable( - config, ctx_iface=implementedBy(RuntimeError), exception_view=True) - self.failIf(IMultiView.providedBy(wrapper)) - request = DummyRequest() - request.is_xhr = True - self.assertEqual(wrapper(None, request), 'OK') - - def test_add_view_default_phash_overrides_default_phash(self): - from pyramid.configuration import DEFAULT_PHASH - from zope.interface import Interface - from pyramid.interfaces import IRequest - from pyramid.interfaces import IView - from pyramid.interfaces import IViewClassifier - from pyramid.interfaces import IMultiView - view = lambda *arg: 'NOT OK' - view.__phash__ = DEFAULT_PHASH - config = self._makeOne(autocommit=True) - config.registry.registerAdapter( - view, (IViewClassifier, IRequest, Interface), IView, name='') - def newview(context, request): - return 'OK' - config.add_view(view=newview) - wrapper = self._getViewCallable(config) - self.failIf(IMultiView.providedBy(wrapper)) - request = DummyRequest() - request.is_xhr = True - self.assertEqual(wrapper(None, request), 'OK') - - def test_add_view_exc_default_phash_overrides_default_phash(self): - from pyramid.configuration import DEFAULT_PHASH - from zope.interface import implementedBy - from pyramid.interfaces import IRequest - from pyramid.interfaces import IView - from pyramid.interfaces import IExceptionViewClassifier - from pyramid.interfaces import IMultiView - view = lambda *arg: 'NOT OK' - view.__phash__ = DEFAULT_PHASH - config = self._makeOne(autocommit=True) - config.registry.registerAdapter( - view, - (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), - IView, name='') - def newview(context, request): - return 'OK' - config.add_view(view=newview, context=RuntimeError) - wrapper = self._getViewCallable( - config, ctx_iface=implementedBy(RuntimeError), exception_view=True) - self.failIf(IMultiView.providedBy(wrapper)) - request = DummyRequest() - request.is_xhr = True - self.assertEqual(wrapper(None, request), 'OK') - - def test_add_view_multiview_replaces_existing_view(self): - from zope.interface import Interface - from pyramid.interfaces import IRequest - from pyramid.interfaces import IView - from pyramid.interfaces import IViewClassifier - from pyramid.interfaces import IMultiView - view = lambda *arg: 'OK' - view.__phash__ = 'abc' - config = self._makeOne(autocommit=True) - config.registry.registerAdapter( - view, (IViewClassifier, IRequest, Interface), IView, name='') - config.add_view(view=view) - wrapper = self._getViewCallable(config) - self.failUnless(IMultiView.providedBy(wrapper)) - self.assertEqual(wrapper(None, None), 'OK') - - def test_add_view_exc_multiview_replaces_existing_view(self): - from zope.interface import implementedBy - from pyramid.interfaces import IRequest - from pyramid.interfaces import IView - from pyramid.interfaces import IExceptionViewClassifier - from pyramid.interfaces import IViewClassifier - from pyramid.interfaces import IMultiView - view = lambda *arg: 'OK' - view.__phash__ = 'abc' - config = self._makeOne(autocommit=True) - config.registry.registerAdapter( - view, - (IViewClassifier, IRequest, implementedBy(RuntimeError)), - IView, name='') - config.registry.registerAdapter( - view, - (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), - IView, name='') - config.add_view(view=view, context=RuntimeError) - wrapper = self._getViewCallable( - config, ctx_iface=implementedBy(RuntimeError), exception_view=True) - self.failUnless(IMultiView.providedBy(wrapper)) - self.assertEqual(wrapper(None, None), 'OK') - - def test_add_view_multiview_replaces_existing_securedview(self): - from zope.interface import Interface - from pyramid.interfaces import IRequest - from pyramid.interfaces import ISecuredView - from pyramid.interfaces import IMultiView - from pyramid.interfaces import IViewClassifier - view = lambda *arg: 'OK' - view.__phash__ = 'abc' - config = self._makeOne(autocommit=True) - config.registry.registerAdapter( - view, (IViewClassifier, IRequest, Interface), - ISecuredView, name='') - config.add_view(view=view) - wrapper = self._getViewCallable(config) - self.failUnless(IMultiView.providedBy(wrapper)) - self.assertEqual(wrapper(None, None), 'OK') - - def test_add_view_exc_multiview_replaces_existing_securedview(self): - from zope.interface import implementedBy - from pyramid.interfaces import IRequest - from pyramid.interfaces import ISecuredView - from pyramid.interfaces import IMultiView - from pyramid.interfaces import IViewClassifier - from pyramid.interfaces import IExceptionViewClassifier - view = lambda *arg: 'OK' - view.__phash__ = 'abc' - config = self._makeOne(autocommit=True) - config.registry.registerAdapter( - view, - (IViewClassifier, IRequest, implementedBy(RuntimeError)), - ISecuredView, name='') - config.registry.registerAdapter( - view, - (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), - ISecuredView, name='') - config.add_view(view=view, context=RuntimeError) - wrapper = self._getViewCallable( - config, ctx_iface=implementedBy(RuntimeError), exception_view=True) - self.failUnless(IMultiView.providedBy(wrapper)) - self.assertEqual(wrapper(None, None), 'OK') - - def test_add_view_with_accept_multiview_replaces_existing_view(self): - from zope.interface import Interface - from pyramid.interfaces import IRequest - from pyramid.interfaces import IView - from pyramid.interfaces import IMultiView - from pyramid.interfaces import IViewClassifier - def view(context, request): - return 'OK' - def view2(context, request): - return 'OK2' - config = self._makeOne(autocommit=True) - config.registry.registerAdapter( - view, (IViewClassifier, IRequest, Interface), IView, name='') - config.add_view(view=view2, accept='text/html') - wrapper = self._getViewCallable(config) - self.failUnless(IMultiView.providedBy(wrapper)) - self.assertEqual(len(wrapper.views), 1) - self.assertEqual(len(wrapper.media_views), 1) - self.assertEqual(wrapper(None, None), 'OK') - request = DummyRequest() - request.accept = DummyAccept('text/html', 'text/html') - self.assertEqual(wrapper(None, request), 'OK2') - - def test_add_view_exc_with_accept_multiview_replaces_existing_view(self): - from zope.interface import implementedBy - from pyramid.interfaces import IRequest - from pyramid.interfaces import IView - from pyramid.interfaces import IMultiView - from pyramid.interfaces import IViewClassifier - from pyramid.interfaces import IExceptionViewClassifier - def view(context, request): - return 'OK' - def view2(context, request): - return 'OK2' - config = self._makeOne(autocommit=True) - config.registry.registerAdapter( - view, - (IViewClassifier, IRequest, implementedBy(RuntimeError)), - IView, name='') - config.registry.registerAdapter( - view, - (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), - IView, name='') - config.add_view(view=view2, accept='text/html', context=RuntimeError) - wrapper = self._getViewCallable( - config, ctx_iface=implementedBy(RuntimeError), exception_view=True) - self.failUnless(IMultiView.providedBy(wrapper)) - self.assertEqual(len(wrapper.views), 1) - self.assertEqual(len(wrapper.media_views), 1) - self.assertEqual(wrapper(None, None), 'OK') - request = DummyRequest() - request.accept = DummyAccept('text/html', 'text/html') - self.assertEqual(wrapper(None, request), 'OK2') - - def test_add_view_multiview_replaces_existing_view_with___accept__(self): - from zope.interface import Interface - from pyramid.interfaces import IRequest - from pyramid.interfaces import IView - from pyramid.interfaces import IMultiView - from pyramid.interfaces import IViewClassifier - def view(context, request): - return 'OK' - def view2(context, request): - return 'OK2' - view.__accept__ = 'text/html' - view.__phash__ = 'abc' - config = self._makeOne(autocommit=True) - config.registry.registerAdapter( - view, (IViewClassifier, IRequest, Interface), IView, name='') - config.add_view(view=view2) - wrapper = self._getViewCallable(config) - self.failUnless(IMultiView.providedBy(wrapper)) - self.assertEqual(len(wrapper.views), 1) - self.assertEqual(len(wrapper.media_views), 1) - self.assertEqual(wrapper(None, None), 'OK2') - request = DummyRequest() - request.accept = DummyAccept('text/html') - self.assertEqual(wrapper(None, request), 'OK') - - def test_add_view_exc_mulview_replaces_existing_view_with___accept__(self): - from zope.interface import implementedBy - from pyramid.interfaces import IRequest - from pyramid.interfaces import IView - from pyramid.interfaces import IMultiView - from pyramid.interfaces import IViewClassifier - from pyramid.interfaces import IExceptionViewClassifier - def view(context, request): - return 'OK' - def view2(context, request): - return 'OK2' - view.__accept__ = 'text/html' - view.__phash__ = 'abc' - config = self._makeOne(autocommit=True) - config.registry.registerAdapter( - view, - (IViewClassifier, IRequest, implementedBy(RuntimeError)), - IView, name='') - config.registry.registerAdapter( - view, - (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), - IView, name='') - config.add_view(view=view2, context=RuntimeError) - wrapper = self._getViewCallable( - config, ctx_iface=implementedBy(RuntimeError), exception_view=True) - self.failUnless(IMultiView.providedBy(wrapper)) - self.assertEqual(len(wrapper.views), 1) - self.assertEqual(len(wrapper.media_views), 1) - self.assertEqual(wrapper(None, None), 'OK2') - request = DummyRequest() - request.accept = DummyAccept('text/html') - self.assertEqual(wrapper(None, request), 'OK') - - def test_add_view_multiview_replaces_multiview(self): - from zope.interface import Interface - from pyramid.interfaces import IRequest - from pyramid.interfaces import IMultiView - from pyramid.interfaces import IViewClassifier - view = DummyMultiView() - config = self._makeOne(autocommit=True) - config.registry.registerAdapter( - view, (IViewClassifier, IRequest, Interface), - IMultiView, name='') - view2 = lambda *arg: 'OK2' - config.add_view(view=view2) - wrapper = self._getViewCallable(config) - self.failUnless(IMultiView.providedBy(wrapper)) - self.assertEqual([x[:2] for x in wrapper.views], [(view2, None)]) - self.assertEqual(wrapper(None, None), 'OK1') - - def test_add_view_exc_multiview_replaces_multiview(self): - from zope.interface import implementedBy - from pyramid.interfaces import IRequest - from pyramid.interfaces import IMultiView - from pyramid.interfaces import IViewClassifier - from pyramid.interfaces import IExceptionViewClassifier - view = DummyMultiView() - config = self._makeOne(autocommit=True) - config.registry.registerAdapter( - view, - (IViewClassifier, IRequest, implementedBy(RuntimeError)), - IMultiView, name='') - config.registry.registerAdapter( - view, - (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), - IMultiView, name='') - view2 = lambda *arg: 'OK2' - config.add_view(view=view2, context=RuntimeError) - wrapper = self._getViewCallable( - config, ctx_iface=implementedBy(RuntimeError), exception_view=True) - self.failUnless(IMultiView.providedBy(wrapper)) - self.assertEqual([x[:2] for x in wrapper.views], [(view2, None)]) - self.assertEqual(wrapper(None, None), 'OK1') - - def test_add_view_multiview_context_superclass_then_subclass(self): - from zope.interface import Interface - from pyramid.interfaces import IRequest - from pyramid.interfaces import IView - from pyramid.interfaces import IMultiView - from pyramid.interfaces import IViewClassifier - class ISuper(Interface): - pass - class ISub(ISuper): - pass - view = lambda *arg: 'OK' - view2 = lambda *arg: 'OK2' - config = self._makeOne(autocommit=True) - config.registry.registerAdapter( - view, (IViewClassifier, IRequest, ISuper), IView, name='') - config.add_view(view=view2, for_=ISub) - wrapper = self._getViewCallable(config, ISuper, IRequest) - self.failIf(IMultiView.providedBy(wrapper)) - self.assertEqual(wrapper(None, None), 'OK') - wrapper = self._getViewCallable(config, ISub, IRequest) - self.failIf(IMultiView.providedBy(wrapper)) - self.assertEqual(wrapper(None, None), 'OK2') - - def test_add_view_multiview_exception_superclass_then_subclass(self): - from zope.interface import implementedBy - from pyramid.interfaces import IRequest - from pyramid.interfaces import IView - from pyramid.interfaces import IMultiView - from pyramid.interfaces import IViewClassifier - from pyramid.interfaces import IExceptionViewClassifier - class Super(Exception): - pass - class Sub(Super): - pass - view = lambda *arg: 'OK' - view2 = lambda *arg: 'OK2' - config = self._makeOne(autocommit=True) - config.registry.registerAdapter( - view, (IViewClassifier, IRequest, Super), IView, name='') - config.registry.registerAdapter( - view, (IExceptionViewClassifier, IRequest, Super), IView, name='') - config.add_view(view=view2, for_=Sub) - wrapper = self._getViewCallable( - config, implementedBy(Super), IRequest) - wrapper_exc_view = self._getViewCallable( - config, implementedBy(Super), IRequest, exception_view=True) - self.assertEqual(wrapper_exc_view, wrapper) - self.failIf(IMultiView.providedBy(wrapper_exc_view)) - self.assertEqual(wrapper_exc_view(None, None), 'OK') - wrapper = self._getViewCallable( - config, implementedBy(Sub), IRequest) - wrapper_exc_view = self._getViewCallable( - config, implementedBy(Sub), IRequest, exception_view=True) - self.assertEqual(wrapper_exc_view, wrapper) - self.failIf(IMultiView.providedBy(wrapper_exc_view)) - self.assertEqual(wrapper_exc_view(None, None), 'OK2') - - def test_add_view_multiview_call_ordering(self): - from zope.interface import directlyProvides - def view1(context, request): return 'view1' - def view2(context, request): return 'view2' - def view3(context, request): return 'view3' - def view4(context, request): return 'view4' - def view5(context, request): return 'view5' - def view6(context, request): return 'view6' - def view7(context, request): return 'view7' - def view8(context, request): return 'view8' - config = self._makeOne(autocommit=True) - config.add_view(view=view1) - config.add_view(view=view2, request_method='POST') - config.add_view(view=view3,request_param='param') - config.add_view(view=view4, containment=IDummy) - config.add_view(view=view5, request_method='POST',request_param='param') - config.add_view(view=view6, request_method='POST', containment=IDummy) - config.add_view(view=view7, request_param='param', containment=IDummy) - config.add_view(view=view8, request_method='POST',request_param='param', - containment=IDummy) - - - wrapper = self._getViewCallable(config) - - ctx = DummyContext() - request = self._makeRequest(config) - request.method = 'GET' - request.params = {} - self.assertEqual(wrapper(ctx, request), 'view1') - - ctx = DummyContext() - request = self._makeRequest(config) - request.params = {} - request.method = 'POST' - self.assertEqual(wrapper(ctx, request), 'view2') - - ctx = DummyContext() - request = self._makeRequest(config) - request.params = {'param':'1'} - request.method = 'GET' - self.assertEqual(wrapper(ctx, request), 'view3') - - ctx = DummyContext() - directlyProvides(ctx, IDummy) - request = self._makeRequest(config) - request.method = 'GET' - request.params = {} - self.assertEqual(wrapper(ctx, request), 'view4') - - ctx = DummyContext() - request = self._makeRequest(config) - request.method = 'POST' - request.params = {'param':'1'} - self.assertEqual(wrapper(ctx, request), 'view5') - - ctx = DummyContext() - directlyProvides(ctx, IDummy) - request = self._makeRequest(config) - request.params = {} - request.method = 'POST' - self.assertEqual(wrapper(ctx, request), 'view6') - - ctx = DummyContext() - directlyProvides(ctx, IDummy) - request = self._makeRequest(config) - request.method = 'GET' - request.params = {'param':'1'} - self.assertEqual(wrapper(ctx, request), 'view7') - - ctx = DummyContext() - directlyProvides(ctx, IDummy) - request = self._makeRequest(config) - request.method = 'POST' - request.params = {'param':'1'} - self.assertEqual(wrapper(ctx, request), 'view8') - - def test_add_view_with_template_renderer(self): - import pyramid.tests - from pyramid.interfaces import ISettings - class view(object): - def __init__(self, context, request): - self.request = request - self.context = context - - def __call__(self): - return {'a':'1'} - config = self._makeOne(autocommit=True) - renderer = self._registerRenderer(config) - fixture = 'pyramid.tests:fixtures/minimal.txt' - config.add_view(view=view, renderer=fixture) - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - result = wrapper(None, request) - self.assertEqual(result.body, 'Hello!') - settings = config.registry.queryUtility(ISettings) - result = renderer.info - self.assertEqual(result.registry, config.registry) - self.assertEqual(result.type, '.txt') - self.assertEqual(result.package, pyramid.tests) - self.assertEqual(result.name, fixture) - self.assertEqual(result.settings, settings) - - def test_add_view_with_template_renderer_no_callable(self): - import pyramid.tests - from pyramid.interfaces import ISettings - config = self._makeOne(autocommit=True) - renderer = self._registerRenderer(config) - fixture = 'pyramid.tests:fixtures/minimal.txt' - config.add_view(view=None, renderer=fixture) - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - result = wrapper(None, request) - self.assertEqual(result.body, 'Hello!') - settings = config.registry.queryUtility(ISettings) - result = renderer.info - self.assertEqual(result.registry, config.registry) - self.assertEqual(result.type, '.txt') - self.assertEqual(result.package, pyramid.tests) - self.assertEqual(result.name, fixture) - self.assertEqual(result.settings, settings) - - def test_add_view_with_request_type_as_iface(self): - from zope.interface import directlyProvides - def view(context, request): - return 'OK' - config = self._makeOne(autocommit=True) - config.add_view(request_type=IDummy, view=view) - wrapper = self._getViewCallable(config, None) - request = self._makeRequest(config) - directlyProvides(request, IDummy) - result = wrapper(None, request) - self.assertEqual(result, 'OK') - - def test_add_view_with_request_type_as_noniface(self): - from pyramid.exceptions import ConfigurationError - view = lambda *arg: 'OK' - config = self._makeOne() - self.assertRaises(ConfigurationError, - config.add_view, view, '', None, None, object) - - def test_add_view_with_route_name(self): - from zope.component import ComponentLookupError - view = lambda *arg: 'OK' - config = self._makeOne(autocommit=True) - config.add_view(view=view, route_name='foo') - self.assertEqual(len(config.registry.deferred_route_views), 1) - infos = config.registry.deferred_route_views['foo'] - self.assertEqual(len(infos), 1) - info = infos[0] - self.assertEqual(info['route_name'], 'foo') - self.assertEqual(info['view'], view) - self.assertRaises(ComponentLookupError, - self._getRouteRequestIface, config, 'foo') - wrapper = self._getViewCallable(config, None) - self.assertEqual(wrapper, None) - config.add_route('foo', '/a/b') - request_iface = self._getRouteRequestIface(config, 'foo') - self.failIfEqual(request_iface, None) - wrapper = self._getViewCallable(config, request_iface=request_iface) - self.failIfEqual(wrapper, None) - self.assertEqual(wrapper(None, None), 'OK') - - def test_deferred_route_views_retains_custom_predicates(self): - view = lambda *arg: 'OK' - config = self._makeOne(autocommit=True) - config.add_view(view=view, route_name='foo', custom_predicates=('123',)) - self.assertEqual(len(config.registry.deferred_route_views), 1) - infos = config.registry.deferred_route_views['foo'] - self.assertEqual(len(infos), 1) - info = infos[0] - self.assertEqual(info['route_name'], 'foo') - self.assertEqual(info['custom_predicates'], ('123',)) - - def test_add_view_with_route_name_exception(self): - from zope.interface import implementedBy - from zope.component import ComponentLookupError - view = lambda *arg: 'OK' - config = self._makeOne(autocommit=True) - config.add_view(view=view, route_name='foo', context=RuntimeError) - self.assertEqual(len(config.registry.deferred_route_views), 1) - infos = config.registry.deferred_route_views['foo'] - self.assertEqual(len(infos), 1) - info = infos[0] - self.assertEqual(info['route_name'], 'foo') - self.assertEqual(info['view'], view) - self.assertRaises(ComponentLookupError, - self._getRouteRequestIface, config, 'foo') - wrapper_exc_view = self._getViewCallable( - config, ctx_iface=implementedBy(RuntimeError), - exception_view=True) - self.assertEqual(wrapper_exc_view, None) - config.add_route('foo', '/a/b') - request_iface = self._getRouteRequestIface(config, 'foo') - self.failIfEqual(request_iface, None) - wrapper_exc_view = self._getViewCallable( - config, ctx_iface=implementedBy(RuntimeError), - request_iface=request_iface, exception_view=True) - self.failIfEqual(wrapper_exc_view, None) - wrapper = self._getViewCallable( - config, ctx_iface=implementedBy(RuntimeError), - request_iface=request_iface) - self.assertEqual(wrapper_exc_view, wrapper) - self.assertEqual(wrapper_exc_view(None, None), 'OK') - - def test_add_view_with_request_method_true(self): - view = lambda *arg: 'OK' - config = self._makeOne(autocommit=True) - config.add_view(view=view, request_method='POST') - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - request.method = 'POST' - self.assertEqual(wrapper(None, request), 'OK') - - def test_add_view_with_request_method_false(self): - view = lambda *arg: 'OK' - config = self._makeOne(autocommit=True) - config.add_view(view=view, request_method='POST') - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - request.method = 'GET' - self._assertNotFound(wrapper, None, request) - - def test_add_view_with_request_param_noval_true(self): - view = lambda *arg: 'OK' - config = self._makeOne(autocommit=True) - config.add_view(view=view, request_param='abc') - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - request.params = {'abc':''} - self.assertEqual(wrapper(None, request), 'OK') - - def test_add_view_with_request_param_noval_false(self): - view = lambda *arg: 'OK' - config = self._makeOne(autocommit=True) - config.add_view(view=view, request_param='abc') - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - request.params = {} - self._assertNotFound(wrapper, None, request) - - def test_add_view_with_request_param_val_true(self): - view = lambda *arg: 'OK' - config = self._makeOne(autocommit=True) - config.add_view(view=view, request_param='abc=123') - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - request.params = {'abc':'123'} - self.assertEqual(wrapper(None, request), 'OK') - - def test_add_view_with_request_param_val_false(self): - view = lambda *arg: 'OK' - config = self._makeOne(autocommit=True) - config.add_view(view=view, request_param='abc=123') - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - request.params = {'abc':''} - self._assertNotFound(wrapper, None, request) - - def test_add_view_with_xhr_true(self): - view = lambda *arg: 'OK' - config = self._makeOne(autocommit=True) - config.add_view(view=view, xhr=True) - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - request.is_xhr = True - self.assertEqual(wrapper(None, request), 'OK') - - def test_add_view_with_xhr_false(self): - view = lambda *arg: 'OK' - config = self._makeOne(autocommit=True) - config.add_view(view=view, xhr=True) - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - request.is_xhr = False - self._assertNotFound(wrapper, None, request) - - def test_add_view_with_header_badregex(self): - from pyramid.exceptions import ConfigurationError - view = lambda *arg: 'OK' - config = self._makeOne() - self.assertRaises(ConfigurationError, - config.add_view, view=view, header='Host:a\\') - - def test_add_view_with_header_noval_match(self): - view = lambda *arg: 'OK' - config = self._makeOne(autocommit=True) - config.add_view(view=view, header='Host') - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - request.headers = {'Host':'whatever'} - self.assertEqual(wrapper(None, request), 'OK') - - def test_add_view_with_header_noval_nomatch(self): - view = lambda *arg: 'OK' - config = self._makeOne(autocommit=True) - config.add_view(view=view, header='Host') - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - request.headers = {'NotHost':'whatever'} - self._assertNotFound(wrapper, None, request) - - def test_add_view_with_header_val_match(self): - view = lambda *arg: 'OK' - config = self._makeOne(autocommit=True) - config.add_view(view=view, header=r'Host:\d') - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - request.headers = {'Host':'1'} - self.assertEqual(wrapper(None, request), 'OK') - - def test_add_view_with_header_val_nomatch(self): - view = lambda *arg: 'OK' - config = self._makeOne(autocommit=True) - config.add_view(view=view, header=r'Host:\d') - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - request.headers = {'Host':'abc'} - self._assertNotFound(wrapper, None, request) - - def test_add_view_with_header_val_missing(self): - from pyramid.exceptions import NotFound - view = lambda *arg: 'OK' - config = self._makeOne(autocommit=True) - config.add_view(view=view, header=r'Host:\d') - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - request.headers = {'NoHost':'1'} - self.assertRaises(NotFound, wrapper, None, request) - - def test_add_view_with_accept_match(self): - view = lambda *arg: 'OK' - config = self._makeOne(autocommit=True) - config.add_view(view=view, accept='text/xml') - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - request.accept = ['text/xml'] - self.assertEqual(wrapper(None, request), 'OK') - - def test_add_view_with_accept_nomatch(self): - view = lambda *arg: 'OK' - config = self._makeOne(autocommit=True) - config.add_view(view=view, accept='text/xml') - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - request.accept = ['text/html'] - self._assertNotFound(wrapper, None, request) - - def test_add_view_with_containment_true(self): - from zope.interface import directlyProvides - view = lambda *arg: 'OK' - config = self._makeOne(autocommit=True) - config.add_view(view=view, containment=IDummy) - wrapper = self._getViewCallable(config) - context = DummyContext() - directlyProvides(context, IDummy) - self.assertEqual(wrapper(context, None), 'OK') - - def test_add_view_with_containment_false(self): - view = lambda *arg: 'OK' - config = self._makeOne(autocommit=True) - config.add_view(view=view, containment=IDummy) - wrapper = self._getViewCallable(config) - context = DummyContext() - self._assertNotFound(wrapper, context, None) - - def test_add_view_with_containment_dottedname(self): - from zope.interface import directlyProvides - view = lambda *arg: 'OK' - config = self._makeOne(autocommit=True) - config.add_view( - view=view, - containment='pyramid.tests.test_configuration.IDummy') - wrapper = self._getViewCallable(config) - context = DummyContext() - directlyProvides(context, IDummy) - self.assertEqual(wrapper(context, None), 'OK') - - def test_add_view_with_path_info_badregex(self): - from pyramid.exceptions import ConfigurationError - view = lambda *arg: 'OK' - config = self._makeOne() - self.assertRaises(ConfigurationError, - config.add_view, view=view, path_info='\\') - - def test_add_view_with_path_info_match(self): - view = lambda *arg: 'OK' - config = self._makeOne(autocommit=True) - config.add_view(view=view, path_info='/foo') - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - request.path_info = '/foo' - self.assertEqual(wrapper(None, request), 'OK') - - def test_add_view_with_path_info_nomatch(self): - view = lambda *arg: 'OK' - config = self._makeOne(autocommit=True) - config.add_view(view=view, path_info='/foo') - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - request.path_info = '/' - self._assertNotFound(wrapper, None, request) - - def test_add_view_with_custom_predicates_match(self): - view = lambda *arg: 'OK' - config = self._makeOne(autocommit=True) - def pred1(context, request): - return True - def pred2(context, request): - return True - predicates = (pred1, pred2) - config.add_view(view=view, custom_predicates=predicates) - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - self.assertEqual(wrapper(None, request), 'OK') - - def test_add_view_with_custom_predicates_nomatch(self): - view = lambda *arg: 'OK' - config = self._makeOne(autocommit=True) - def pred1(context, request): - return True - def pred2(context, request): - return False - predicates = (pred1, pred2) - config.add_view(view=view, custom_predicates=predicates) - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - self._assertNotFound(wrapper, None, request) - - def test_add_view_custom_predicate_bests_standard_predicate(self): - view = lambda *arg: 'OK' - view2 = lambda *arg: 'NOT OK' - config = self._makeOne(autocommit=True) - def pred1(context, request): - return True - config.add_view(view=view, custom_predicates=(pred1,)) - config.add_view(view=view2, request_method='GET') - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - request.method = 'GET' - self.assertEqual(wrapper(None, request), 'OK') - - def test_add_view_custom_more_preds_first_bests_fewer_preds_last(self): - view = lambda *arg: 'OK' - view2 = lambda *arg: 'NOT OK' - config = self._makeOne(autocommit=True) - config.add_view(view=view, request_method='GET', xhr=True) - config.add_view(view=view2, request_method='GET') - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - request.method = 'GET' - request.is_xhr = True - self.assertEqual(wrapper(None, request), 'OK') - - def test_add_view_same_predicates(self): - from zope.configuration.config import ConfigurationConflictError - view2 = lambda *arg: 'second' - view1 = lambda *arg: 'first' - config = self._makeOne() - config.add_view(view=view1) - config.add_view(view=view2) - self.assertRaises(ConfigurationConflictError, config.commit) - - def test_add_view_with_permission(self): - view1 = lambda *arg: 'OK' - outerself = self - class DummyPolicy(object): - def effective_principals(self, r): - outerself.assertEqual(r, request) - return ['abc'] - def permits(self, context, principals, permission): - outerself.assertEqual(context, None) - outerself.assertEqual(principals, ['abc']) - outerself.assertEqual(permission, 'view') - return True - policy = DummyPolicy() - config = self._makeOne(authorization_policy=policy, - authentication_policy=policy, - autocommit=True) - config.add_view(view=view1, permission='view') - view = self._getViewCallable(config) - request = self._makeRequest(config) - self.assertEqual(view(None, request), 'OK') - - def test_add_view_with_default_permission_no_explicit_permission(self): - view1 = lambda *arg: 'OK' - outerself = self - class DummyPolicy(object): - def effective_principals(self, r): - outerself.assertEqual(r, request) - return ['abc'] - def permits(self, context, principals, permission): - outerself.assertEqual(context, None) - outerself.assertEqual(principals, ['abc']) - outerself.assertEqual(permission, 'view') - return True - policy = DummyPolicy() - config = self._makeOne(authorization_policy=policy, - authentication_policy=policy, - default_permission='view', - autocommit=True) - config.add_view(view=view1) - view = self._getViewCallable(config) - request = self._makeRequest(config) - self.assertEqual(view(None, request), 'OK') - - def test_add_view_with_no_default_permission_no_explicit_permission(self): - view1 = lambda *arg: 'OK' - class DummyPolicy(object): pass # wont be called - policy = DummyPolicy() - config = self._makeOne(authorization_policy=policy, - authentication_policy=policy, - autocommit=True) - config.add_view(view=view1) - view = self._getViewCallable(config) - request = self._makeRequest(config) - self.assertEqual(view(None, request), 'OK') - - def test_add_handler_action_in_route_pattern(self): - config = self._makeOne(autocommit=True) - views = [] - def dummy_add_view(**kw): - views.append(kw) - config.add_view = dummy_add_view - config.add_handler('name', '/:action', DummyHandler) - self._assertRoute(config, 'name', '/:action', 0) - self.assertEqual(len(views), 2) - - view = views[0] - preds = view['custom_predicates'] - self.assertEqual(len(preds), 1) - pred = preds[0] - request = DummyRequest() - self.assertEqual(pred(None, request), False) - request.matchdict = {'action':'action1'} - self.assertEqual(pred(None, request), True) - self.assertEqual(view['route_name'], 'name') - self.assertEqual(view['attr'], 'action1') - self.assertEqual(view['view'], DummyHandler) - - view = views[1] - preds = view['custom_predicates'] - self.assertEqual(len(preds), 1) - pred = preds[0] - request = DummyRequest() - self.assertEqual(pred(None, request), False) - request.matchdict = {'action':'action2'} - self.assertEqual(pred(None, request), True) - self.assertEqual(view['route_name'], 'name') - self.assertEqual(view['attr'], 'action2') - self.assertEqual(view['view'], DummyHandler) - - def test_add_handler_with_view_overridden_autoexpose_None(self): - config = self._makeOne(autocommit=True) - views = [] - def dummy_add_view(**kw): - views.append(kw) # pragma: no cover - config.add_view = dummy_add_view - class MyView(DummyHandler): - __autoexpose__ = None - config.add_handler('name', '/:action', MyView) - self._assertRoute(config, 'name', '/:action', 0) - self.assertEqual(len(views), 0) - - def test_add_handler_with_view_overridden_autoexpose_broken_regex1(self): - from pyramid.exceptions import ConfigurationError - config = self._makeOne() - def dummy_add_view(**kw): - """ """ - config.add_view = dummy_add_view - class MyView(DummyHandler): - __autoexpose__ = 1 - self.assertRaises(ConfigurationError, config.add_handler, - 'name', '/{action}', MyView) - - def test_add_handler_with_view_overridden_autoexpose_broken_regex2(self): - from pyramid.exceptions import ConfigurationError - config = self._makeOne() - def dummy_add_view(**kw): - """ """ - config.add_view = dummy_add_view - class MyView(DummyHandler): - __autoexpose__ = 'a\\' - self.assertRaises(ConfigurationError, config.add_handler, - 'name', '/{action}', MyView) - - def test_add_handler_with_view_method_has_expose_config(self): - config = self._makeOne(autocommit=True) - views = [] - def dummy_add_view(**kw): - views.append(kw) - config.add_view = dummy_add_view - class MyView(object): - def action(self): # pragma: no cover - return 'response' - action.__exposed__ = [{'custom_predicates':(1,)}] - config.add_handler('name', '/:action', MyView) - self._assertRoute(config, 'name', '/:action', 0) - self.assertEqual(len(views), 1) - view = views[0] - preds = view['custom_predicates'] - self.assertEqual(len(preds), 2) - self.assertEqual(view['route_name'], 'name') - self.assertEqual(view['attr'], 'action') - self.assertEqual(view['view'], MyView) - - def test_add_handler_with_view_method_has_expose_config_with_action(self): - config = self._makeOne(autocommit=True) - views = [] - def dummy_add_view(**kw): - views.append(kw) - config.add_view = dummy_add_view - class MyView(object): - def action(self): # pragma: no cover - return 'response' - action.__exposed__ = [{'name':'action3000'}] - config.add_handler('name', '/:action', MyView) - self._assertRoute(config, 'name', '/:action', 0) - self.assertEqual(len(views), 1) - view = views[0] - preds = view['custom_predicates'] - self.assertEqual(len(preds), 1) - pred = preds[0] - request = DummyRequest() - self.assertEqual(pred(None, request), False) - request.matchdict = {'action':'action3000'} - self.assertEqual(pred(None, request), True) - self.assertEqual(view['route_name'], 'name') - self.assertEqual(view['attr'], 'action') - self.assertEqual(view['view'], MyView) - - def test_add_handler_with_view_method_has_expose_config_with_action_regex( - self): - config = self._makeOne(autocommit=True) - views = [] - def dummy_add_view(**kw): - views.append(kw) - config.add_view = dummy_add_view - class MyView(object): - def action(self): # pragma: no cover - return 'response' - action.__exposed__ = [{'name':'^action3000$'}] - config.add_handler('name', '/:action', MyView) - self._assertRoute(config, 'name', '/:action', 0) - self.assertEqual(len(views), 1) - view = views[0] - preds = view['custom_predicates'] - self.assertEqual(len(preds), 1) - pred = preds[0] - request = DummyRequest() - self.assertEqual(pred(None, request), False) - request.matchdict = {'action':'action3000'} - self.assertEqual(pred(None, request), True) - self.assertEqual(view['route_name'], 'name') - self.assertEqual(view['attr'], 'action') - self.assertEqual(view['view'], MyView) - - def test_add_handler_doesnt_mutate_expose_dict(self): - config = self._makeOne(autocommit=True) - views = [] - def dummy_add_view(**kw): - views.append(kw) - config.add_view = dummy_add_view - exposed = [{'name':'^action3000$'}] - class MyView(object): - def action(self): # pragma: no cover - return 'response' - action.__exposed__ = exposed - config.add_handler('name', '/{action}', MyView) - self.assertEqual(exposed[0], {'name':'^action3000$'}) # not mutated - - def test_add_handler_with_action_and_action_in_path(self): - from pyramid.exceptions import ConfigurationError - config = self._makeOne() - self.assertRaises(ConfigurationError, config.add_handler, - 'name', '/{action}', DummyHandler, action='abc') - - def test_add_handler_with_explicit_action(self): - config = self._makeOne(autocommit=True) - class DummyHandler(object): - def index(self): pass - index.__exposed__ = [{'a':'1'}] - views = [] - def dummy_add_view(**kw): - views.append(kw) - config.add_view = dummy_add_view - config.add_handler('name', '/abc', DummyHandler, action='index') - self.assertEqual(len(views), 1) - view = views[0] - self.assertEqual(view['a'], '1') - self.assertEqual(view['attr'], 'index') - self.assertEqual(view['route_name'], 'name') - self.assertEqual(view['view'], DummyHandler) - - def test_add_handler_with_implicit_action(self): - config = self._makeOne(autocommit=True) - class DummyHandler(object): - def __call__(self): pass - __call__.__exposed__ = [{'a':'1'}] - views = [] - def dummy_add_view(**kw): - views.append(kw) - config.add_view = dummy_add_view - config.add_handler('name', '/abc', DummyHandler) - self.assertEqual(len(views), 1) - view = views[0] - self.assertEqual(view['a'], '1') - self.assertEqual(view['attr'], None) - self.assertEqual(view['route_name'], 'name') - self.assertEqual(view['view'], DummyHandler) - - def test_add_handler_with_multiple_action(self): - config = self._makeOne(autocommit=True) - class DummyHandler(object): - def index(self): pass - def create(self): pass - create.__exposed__ = [{'name': 'index'}] - views = [] - def dummy_add_view(**kw): - views.append(kw) - config.add_view = dummy_add_view - config.add_handler('name', '/abc', DummyHandler, action='index') - self.assertEqual(len(views), 2) - view = views[0] - self.assertEqual(view['attr'], 'create') - self.assertEqual(view['route_name'], 'name') - self.assertEqual(view['view'], DummyHandler) - view = views[1] - self.assertEqual(view['attr'], 'index') - - def test_add_handler_string(self): - import pyramid - views = [] - config = self._makeOne(autocommit=True) - def dummy_add_view(**kw): - views.append(kw) - config.add_view = dummy_add_view - config.add_handler('name', '/abc', 'pyramid') - self.assertEqual(len(views), 1) - view = views[0] - self.assertEqual(view['view'], pyramid) - - def test_add_handler_pattern_None_no_previous_route(self): - from pyramid.exceptions import ConfigurationError - config = self._makeOne() - self.assertRaises(ConfigurationError, config.add_handler, - 'name', None, 'pyramid') - - def test_add_handler_pattern_None_with_previous_route(self): - import pyramid - config = self._makeOne(autocommit=True) - config.add_route('name', ':def') - views = [] - def dummy_add_view(**kw): - views.append(kw) - config.add_view = dummy_add_view - config.add_route = None # shouldn't be called - config.add_handler('name', None, 'pyramid') - self.assertEqual(len(views), 1) - view = views[0] - self.assertEqual(view['view'], pyramid) - - - def _assertRoute(self, config, name, path, num_predicates=0): - from pyramid.interfaces import IRoutesMapper - mapper = config.registry.getUtility(IRoutesMapper) - routes = mapper.get_routes() - route = routes[0] - self.assertEqual(len(routes), 1) - self.assertEqual(route.name, name) - self.assertEqual(route.path, path) - self.assertEqual(len(routes[0].predicates), num_predicates) - return route - - def test_get_routes_mapper_not_yet_registered(self): - config = self._makeOne() - mapper = config.get_routes_mapper() - self.assertEqual(mapper.routelist, []) - - def test_get_routes_mapper_already_registered(self): - from pyramid.interfaces import IRoutesMapper - config = self._makeOne() - mapper = object() - config.registry.registerUtility(mapper, IRoutesMapper) - result = config.get_routes_mapper() - self.assertEqual(result, mapper) - - def test_add_route_defaults(self): - config = self._makeOne(autocommit=True) - route = config.add_route('name', 'path') - self._assertRoute(config, 'name', 'path') - self.assertEqual(route.name, 'name') - - def test_add_route_with_factory(self): - config = self._makeOne(autocommit=True) - factory = object() - route = config.add_route('name', 'path', factory=factory) - self.assertEqual(route.factory, factory) - - def test_add_route_with_factory_dottedname(self): - config = self._makeOne(autocommit=True) - route = config.add_route( - 'name', 'path', - factory='pyramid.tests.test_configuration.dummyfactory') - self.assertEqual(route.factory, dummyfactory) - - def test_add_route_with_xhr(self): - config = self._makeOne(autocommit=True) - config.add_route('name', 'path', xhr=True) - route = self._assertRoute(config, 'name', 'path', 1) - predicate = route.predicates[0] - request = self._makeRequest(config) - request.is_xhr = True - self.assertEqual(predicate(None, request), True) - request = self._makeRequest(config) - request.is_xhr = False - self.assertEqual(predicate(None, request), False) - - def test_add_route_with_request_method(self): - config = self._makeOne(autocommit=True) - config.add_route('name', 'path', request_method='GET') - route = self._assertRoute(config, 'name', 'path', 1) - predicate = route.predicates[0] - request = self._makeRequest(config) - request.method = 'GET' - self.assertEqual(predicate(None, request), True) - request = self._makeRequest(config) - request.method = 'POST' - self.assertEqual(predicate(None, request), False) - - def test_add_route_with_path_info(self): - config = self._makeOne(autocommit=True) - config.add_route('name', 'path', path_info='/foo') - route = self._assertRoute(config, 'name', 'path', 1) - predicate = route.predicates[0] - request = self._makeRequest(config) - request.path_info = '/foo' - self.assertEqual(predicate(None, request), True) - request = self._makeRequest(config) - request.path_info = '/' - self.assertEqual(predicate(None, request), False) - - def test_add_route_with_request_param(self): - config = self._makeOne(autocommit=True) - config.add_route('name', 'path', request_param='abc') - route = self._assertRoute(config, 'name', 'path', 1) - predicate = route.predicates[0] - request = self._makeRequest(config) - request.params = {'abc':'123'} - self.assertEqual(predicate(None, request), True) - request = self._makeRequest(config) - request.params = {} - self.assertEqual(predicate(None, request), False) - - def test_add_route_with_custom_predicates(self): - config = self._makeOne(autocommit=True) - def pred1(context, request): pass - def pred2(context, request): pass - config.add_route('name', 'path', custom_predicates=(pred1, pred2)) - route = self._assertRoute(config, 'name', 'path', 2) - self.assertEqual(route.predicates, [pred1, pred2]) - - def test_add_route_with_header(self): - config = self._makeOne(autocommit=True) - config.add_route('name', 'path', header='Host') - route = self._assertRoute(config, 'name', 'path', 1) - predicate = route.predicates[0] - request = self._makeRequest(config) - request.headers = {'Host':'example.com'} - self.assertEqual(predicate(None, request), True) - request = self._makeRequest(config) - request.headers = {} - self.assertEqual(predicate(None, request), False) - - def test_add_route_with_accept(self): - config = self._makeOne(autocommit=True) - config.add_route('name', 'path', accept='text/xml') - route = self._assertRoute(config, 'name', 'path', 1) - predicate = route.predicates[0] - request = self._makeRequest(config) - request.accept = ['text/xml'] - self.assertEqual(predicate(None, request), True) - request = self._makeRequest(config) - request.accept = ['text/html'] - self.assertEqual(predicate(None, request), False) - - def test_add_route_with_view(self): - config = self._makeOne(autocommit=True) - view = lambda *arg: 'OK' - config.add_route('name', 'path', view=view) - request_type = self._getRouteRequestIface(config, 'name') - wrapper = self._getViewCallable(config, None, request_type) - self.assertEqual(wrapper(None, None), 'OK') - self._assertRoute(config, 'name', 'path') - - def test_add_route_with_view_context(self): - config = self._makeOne(autocommit=True) - view = lambda *arg: 'OK' - config.add_route('name', 'path', view=view, view_context=IDummy) - request_type = self._getRouteRequestIface(config, 'name') - wrapper = self._getViewCallable(config, IDummy, request_type) - self.assertEqual(wrapper(None, None), 'OK') - self._assertRoute(config, 'name', 'path') - wrapper = self._getViewCallable(config, IOther, request_type) - self.assertEqual(wrapper, None) - - def test_add_route_with_view_exception(self): - from zope.interface import implementedBy - config = self._makeOne(autocommit=True) - view = lambda *arg: 'OK' - config.add_route('name', 'path', view=view, view_context=RuntimeError) - request_type = self._getRouteRequestIface(config, 'name') - wrapper = self._getViewCallable( - config, ctx_iface=implementedBy(RuntimeError), - request_iface=request_type, exception_view=True) - self.assertEqual(wrapper(None, None), 'OK') - self._assertRoute(config, 'name', 'path') - wrapper = self._getViewCallable( - config, ctx_iface=IOther, - request_iface=request_type, exception_view=True) - self.assertEqual(wrapper, None) - - def test_add_route_with_view_for(self): - config = self._makeOne(autocommit=True) - view = lambda *arg: 'OK' - config.add_route('name', 'path', view=view, view_for=IDummy) - request_type = self._getRouteRequestIface(config, 'name') - wrapper = self._getViewCallable(config, IDummy, request_type) - self.assertEqual(wrapper(None, None), 'OK') - self._assertRoute(config, 'name', 'path') - wrapper = self._getViewCallable(config, IOther, request_type) - self.assertEqual(wrapper, None) - - def test_add_route_with_for_(self): - config = self._makeOne(autocommit=True) - view = lambda *arg: 'OK' - config.add_route('name', 'path', view=view, for_=IDummy) - request_type = self._getRouteRequestIface(config, 'name') - wrapper = self._getViewCallable(config, IDummy, request_type) - self.assertEqual(wrapper(None, None), 'OK') - self._assertRoute(config, 'name', 'path') - wrapper = self._getViewCallable(config, IOther, request_type) - self.assertEqual(wrapper, None) - - def test_add_route_with_view_renderer(self): - config = self._makeOne(autocommit=True) - self._registerRenderer(config) - view = lambda *arg: 'OK' - config.add_route('name', 'path', view=view, - view_renderer='fixtures/minimal.txt') - request_type = self._getRouteRequestIface(config, 'name') - wrapper = self._getViewCallable(config, None, request_type) - self._assertRoute(config, 'name', 'path') - self.assertEqual(wrapper(None, None).body, 'Hello!') - - def test_add_route_with_view_attr(self): - config = self._makeOne(autocommit=True) - self._registerRenderer(config) - class View(object): - def __init__(self, context, request): - pass - def alt(self): - return 'OK' - config.add_route('name', 'path', view=View, view_attr='alt') - request_type = self._getRouteRequestIface(config, 'name') - wrapper = self._getViewCallable(config, None, request_type) - self._assertRoute(config, 'name', 'path') - self.assertEqual(wrapper(None, None), 'OK') - - def test_add_route_with_view_renderer_alias(self): - config = self._makeOne(autocommit=True) - self._registerRenderer(config) - view = lambda *arg: 'OK' - config.add_route('name', 'path', view=view, - renderer='fixtures/minimal.txt') - request_type = self._getRouteRequestIface(config, 'name') - wrapper = self._getViewCallable(config, None, request_type) - self._assertRoute(config, 'name', 'path') - self.assertEqual(wrapper(None, None).body, 'Hello!') - - def test_add_route_with_view_permission(self): - from pyramid.interfaces import IAuthenticationPolicy - from pyramid.interfaces import IAuthorizationPolicy - config = self._makeOne(autocommit=True) - policy = lambda *arg: None - config.registry.registerUtility(policy, IAuthenticationPolicy) - config.registry.registerUtility(policy, IAuthorizationPolicy) - view = lambda *arg: 'OK' - config.add_route('name', 'path', view=view, view_permission='edit') - request_type = self._getRouteRequestIface(config, 'name') - wrapper = self._getViewCallable(config, None, request_type) - self._assertRoute(config, 'name', 'path') - self.failUnless(hasattr(wrapper, '__call_permissive__')) - - def test_add_route_with_view_permission_alias(self): - from pyramid.interfaces import IAuthenticationPolicy - from pyramid.interfaces import IAuthorizationPolicy - config = self._makeOne(autocommit=True) - policy = lambda *arg: None - config.registry.registerUtility(policy, IAuthenticationPolicy) - config.registry.registerUtility(policy, IAuthorizationPolicy) - view = lambda *arg: 'OK' - config.add_route('name', 'path', view=view, permission='edit') - request_type = self._getRouteRequestIface(config, 'name') - wrapper = self._getViewCallable(config, None, request_type) - self._assertRoute(config, 'name', 'path') - self.failUnless(hasattr(wrapper, '__call_permissive__')) - - def test_add_route_no_pattern_with_path(self): - config = self._makeOne(autocommit=True) - route = config.add_route('name', path='path') - self._assertRoute(config, 'name', 'path') - self.assertEqual(route.name, 'name') - - def test_add_route_no_path_no_pattern(self): - from pyramid.exceptions import ConfigurationError - config = self._makeOne() - self.assertRaises(ConfigurationError, config.add_route, 'name') - - def test_add_route_with_pregenerator(self): - config = self._makeOne(autocommit=True) - route = config.add_route('name', 'pattern', pregenerator='123') - self.assertEqual(route.pregenerator, '123') - - def test__override_not_yet_registered(self): - from pyramid.interfaces import IPackageOverrides - package = DummyPackage('package') - opackage = DummyPackage('opackage') - config = self._makeOne() - config._override(package, 'path', opackage, 'oprefix', - PackageOverrides=DummyOverrides) - overrides = config.registry.queryUtility(IPackageOverrides, - name='package') - self.assertEqual(overrides.inserted, [('path', 'opackage', 'oprefix')]) - self.assertEqual(overrides.package, package) - - def test__override_already_registered(self): - from pyramid.interfaces import IPackageOverrides - package = DummyPackage('package') - opackage = DummyPackage('opackage') - overrides = DummyOverrides(package) - config = self._makeOne() - config.registry.registerUtility(overrides, IPackageOverrides, - name='package') - config._override(package, 'path', opackage, 'oprefix', - PackageOverrides=DummyOverrides) - self.assertEqual(overrides.inserted, [('path', 'opackage', 'oprefix')]) - self.assertEqual(overrides.package, package) - - def test_add_static_here_no_utility_registered(self): - from pyramid.static import PackageURLParser - from zope.interface import implementedBy - from pyramid.static import StaticURLInfo - from pyramid.interfaces import IView - from pyramid.interfaces import IViewClassifier - config = self._makeOne(autocommit=True) - config.add_static_view('static', 'fixtures/static') - request_type = self._getRouteRequestIface(config, 'static/') - route = self._assertRoute(config, 'static/', 'static/*subpath') - self.assertEqual(route.factory.__class__, type(lambda x: x)) - iface = implementedBy(StaticURLInfo) - wrapped = config.registry.adapters.lookup( - (IViewClassifier, request_type, iface), IView, name='') - request = self._makeRequest(config) - self.assertEqual(wrapped(None, request).__class__, PackageURLParser) - - def test_add_static_view_package_relative(self): - from pyramid.interfaces import IStaticURLInfo - info = DummyStaticURLInfo() - 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', {})]) - - def test_add_static_view_package_here_relative(self): - from pyramid.interfaces import IStaticURLInfo - info = DummyStaticURLInfo() - 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', {})]) - - def test_add_static_view_absolute(self): - import os - from pyramid.interfaces import IStaticURLInfo - info = DummyStaticURLInfo() - config = self._makeOne(autocommit=True) - config.registry.registerUtility(info, IStaticURLInfo) - here = os.path.dirname(__file__) - static_path = os.path.join(here, 'fixtures', 'static') - config.add_static_view('static', static_path) - self.assertEqual(info.added, - [('static', static_path, {})]) - - def test_set_notfound_view(self): - from zope.interface import implementedBy - from pyramid.interfaces import IRequest - from pyramid.exceptions import NotFound - config = self._makeOne(autocommit=True) - view = lambda *arg: arg - config.set_notfound_view(view) - request = self._makeRequest(config) - view = self._getViewCallable(config, ctx_iface=implementedBy(NotFound), - request_iface=IRequest) - result = view(None, request) - self.assertEqual(result, (None, request)) - - def test_set_notfound_view_request_has_context(self): - from zope.interface import implementedBy - from pyramid.interfaces import IRequest - from pyramid.exceptions import NotFound - config = self._makeOne(autocommit=True) - view = lambda *arg: arg - config.set_notfound_view(view) - request = self._makeRequest(config) - request.context = 'abc' - view = self._getViewCallable(config, ctx_iface=implementedBy(NotFound), - request_iface=IRequest) - result = view(None, request) - self.assertEqual(result, ('abc', request)) - - def test_set_forbidden_view(self): - from zope.interface import implementedBy - from pyramid.interfaces import IRequest - from pyramid.exceptions import Forbidden - config = self._makeOne(autocommit=True) - view = lambda *arg: 'OK' - config.set_forbidden_view(view) - request = self._makeRequest(config) - view = self._getViewCallable(config, ctx_iface=implementedBy(Forbidden), - request_iface=IRequest) - result = view(None, request) - self.assertEqual(result, 'OK') - - def test_set_forbidden_view_request_has_context(self): - from zope.interface import implementedBy - from pyramid.interfaces import IRequest - from pyramid.exceptions import Forbidden - config = self._makeOne(autocommit=True) - view = lambda *arg: arg - config.set_forbidden_view(view) - request = self._makeRequest(config) - request.context = 'abc' - view = self._getViewCallable(config, ctx_iface=implementedBy(Forbidden), - request_iface=IRequest) - result = view(None, request) - self.assertEqual(result, ('abc', request)) - - def test__set_authentication_policy(self): - from pyramid.interfaces import IAuthenticationPolicy - config = self._makeOne(autocommit=True) - policy = object() - config._set_authentication_policy(policy) - self.assertEqual( - config.registry.getUtility(IAuthenticationPolicy), policy) - - def test__set_authorization_policy(self): - from pyramid.interfaces import IAuthorizationPolicy - config = self._makeOne(autocommit=True) - policy = object() - config._set_authorization_policy(policy) - self.assertEqual( - config.registry.getUtility(IAuthorizationPolicy), policy) - - def test_set_locale_negotiator(self): - from pyramid.interfaces import ILocaleNegotiator - config = self._makeOne(autocommit=True) - def negotiator(request): pass - config.set_locale_negotiator(negotiator) - self.assertEqual(config.registry.getUtility(ILocaleNegotiator), - negotiator) - - def test_set_locale_negotiator_dottedname(self): - from pyramid.interfaces import ILocaleNegotiator - config = self._makeOne(autocommit=True) - config.set_locale_negotiator( - 'pyramid.tests.test_configuration.dummyfactory') - self.assertEqual(config.registry.getUtility(ILocaleNegotiator), - dummyfactory) - - def test_set_request_factory(self): - from pyramid.interfaces import IRequestFactory - config = self._makeOne(autocommit=True) - factory = object() - config.set_request_factory(factory) - self.assertEqual(config.registry.getUtility(IRequestFactory), factory) - - def test_set_request_factory_dottedname(self): - from pyramid.interfaces import IRequestFactory - config = self._makeOne(autocommit=True) - config.set_request_factory( - 'pyramid.tests.test_configuration.dummyfactory') - self.assertEqual(config.registry.getUtility(IRequestFactory), - dummyfactory) - - def test_set_renderer_globals_factory(self): - from pyramid.interfaces import IRendererGlobalsFactory - config = self._makeOne(autocommit=True) - factory = object() - config.set_renderer_globals_factory(factory) - self.assertEqual(config.registry.getUtility(IRendererGlobalsFactory), - factory) - - def test_set_renderer_globals_factory_dottedname(self): - from pyramid.interfaces import IRendererGlobalsFactory - config = self._makeOne(autocommit=True) - config.set_renderer_globals_factory( - 'pyramid.tests.test_configuration.dummyfactory') - self.assertEqual(config.registry.getUtility(IRendererGlobalsFactory), - dummyfactory) - - def test_set_default_permission(self): - from pyramid.interfaces import IDefaultPermission - config = self._makeOne(autocommit=True) - config.set_default_permission('view') - self.assertEqual(config.registry.getUtility(IDefaultPermission), - 'view') - - def test_set_session_factory(self): - from pyramid.interfaces import ISessionFactory - config = self._makeOne(autocommit=True) - config.set_session_factory('factory') - self.assertEqual(config.registry.getUtility(ISessionFactory), - 'factory') - - def test_add_translation_dirs_missing_dir(self): - from pyramid.exceptions import ConfigurationError - config = self._makeOne() - self.assertRaises(ConfigurationError, - config.add_translation_dirs, - '/wont/exist/on/my/system') - - def test_add_translation_dirs_resource_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_registers_chameleon_translate(self): - from pyramid.interfaces import IChameleonTranslate - from pyramid.threadlocal import manager - request = DummyRequest() - config = self._makeOne(autocommit=True) - manager.push({'request':request, 'registry':config.registry}) - try: - config.add_translation_dirs('pyramid.tests.localeapp:locale') - translate = config.registry.getUtility(IChameleonTranslate) - self.assertEqual(translate('Approve'), u'Approve') - finally: - 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]) - - def test_derive_view_function(self): - def view(request): - return 'OK' - config = self._makeOne() - result = config.derive_view(view) - self.failIf(result is view) - self.assertEqual(result(None, None), 'OK') - - def test_derive_view_dottedname(self): - config = self._makeOne() - result = config.derive_view( - 'pyramid.tests.test_configuration.dummy_view') - self.failIf(result is dummy_view) - self.assertEqual(result(None, None), 'OK') - - def test_derive_view_with_renderer(self): - def view(request): - return 'OK' - config = self._makeOne(autocommit=True) - class moo(object): - def __init__(self, *arg, **kw): - pass - def __call__(self, *arg, **kw): - return 'moo' - config.add_renderer('moo', moo) - result = config.derive_view(view, renderer='moo') - self.failIf(result is view) - self.assertEqual(result(None, None).body, 'moo') - - def test_derive_view_with_default_renderer_no_explicit_renderer(self): - def view(request): - return 'OK' - config = self._makeOne(autocommit=True) - class moo(object): - def __init__(self, *arg, **kw): - pass - def __call__(self, *arg, **kw): - return 'moo' - config.add_renderer(None, moo) - result = config.derive_view(view) - self.failIf(result is view) - self.assertEqual(result(None, None).body, 'moo') - - def test_derive_view_with_default_renderer_with_explicit_renderer(self): - def view(request): - return 'OK' - config = self._makeOne(autocommit=True) - class moo(object): pass - class foo(object): - def __init__(self, *arg, **kw): - pass - def __call__(self, *arg, **kw): - return 'foo' - config.add_renderer(None, moo) - config.add_renderer('foo', foo) - result = config.derive_view(view, renderer='foo') - self.failIf(result is view) - self.assertEqual(result(None, None).body, 'foo') - - def test_derive_view_class_without_attr(self): - class View(object): - def __init__(self, request): - pass - def __call__(self): - return 'OK' - config = self._makeOne() - result = config.derive_view(View) - self.assertEqual(result(None, None), 'OK') - - def test_derive_view_class_with_attr(self): - class View(object): - def __init__(self, request): - pass - def another(self): - return 'OK' - config = self._makeOne() - result = config.derive_view(View, attr='another') - self.assertEqual(result(None, None), 'OK') - - def test__derive_view_as_function_context_and_request(self): - def view(context, request): - return 'OK' - config = self._makeOne() - result = config._derive_view(view) - self.failUnless(result is view) - self.failIf(hasattr(result, '__call_permissive__')) - self.assertEqual(view(None, None), 'OK') - - def test__derive_view_as_function_requestonly(self): - def view(request): - return 'OK' - config = self._makeOne() - result = config._derive_view(view) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.failIf(hasattr(result, '__call_permissive__')) - self.assertEqual(result(None, None), 'OK') - - def test__derive_view_as_newstyle_class_context_and_request(self): - class view(object): - def __init__(self, context, request): - pass - def __call__(self): - return 'OK' - config = self._makeOne() - result = config._derive_view(view) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.failIf(hasattr(result, '__call_permissive__')) - self.assertEqual(result(None, None), 'OK') - - def test__derive_view_as_newstyle_class_requestonly(self): - class view(object): - def __init__(self, context, request): - pass - def __call__(self): - return 'OK' - config = self._makeOne() - result = config._derive_view(view) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.failIf(hasattr(result, '__call_permissive__')) - self.assertEqual(result(None, None), 'OK') - - def test__derive_view_as_oldstyle_class_context_and_request(self): - class view: - def __init__(self, context, request): - pass - def __call__(self): - return 'OK' - config = self._makeOne() - result = config._derive_view(view) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.failIf(hasattr(result, '__call_permissive__')) - self.assertEqual(result(None, None), 'OK') - - def test__derive_view_as_oldstyle_class_requestonly(self): - class view: - def __init__(self, context, request): - pass - def __call__(self): - return 'OK' - config = self._makeOne() - result = config._derive_view(view) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.failIf(hasattr(result, '__call_permissive__')) - self.assertEqual(result(None, None), 'OK') - - def test__derive_view_as_instance_context_and_request(self): - class View: - def __call__(self, context, request): - return 'OK' - view = View() - config = self._makeOne() - result = config._derive_view(view) - self.failUnless(result is view) - self.failIf(hasattr(result, '__call_permissive__')) - self.assertEqual(result(None, None), 'OK') - - def test__derive_view_as_instance_requestonly(self): - class View: - def __call__(self, request): - return 'OK' - view = View() - config = self._makeOne() - result = config._derive_view(view) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.failUnless('instance' in result.__name__) - self.failIf(hasattr(result, '__call_permissive__')) - self.assertEqual(result(None, None), 'OK') - - def test__derive_view_with_debug_authorization_no_authpol(self): - view = lambda *arg: 'OK' - config = self._makeOne() - self._registerSettings(config, - debug_authorization=True, reload_templates=True) - logger = self._registerLogger(config) - result = config._derive_view(view, permission='view') - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.failIf(hasattr(result, '__call_permissive__')) - request = self._makeRequest(config) - request.view_name = 'view_name' - request.url = 'url' - self.assertEqual(result(None, request), 'OK') - 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__derive_view_with_debug_authorization_no_permission(self): - view = lambda *arg: 'OK' - config = self._makeOne() - self._registerSettings(config, - debug_authorization=True, reload_templates=True) - self._registerSecurityPolicy(config, True) - logger = self._registerLogger(config) - result = config._derive_view(view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.failIf(hasattr(result, '__call_permissive__')) - request = self._makeRequest(config) - request.view_name = 'view_name' - request.url = 'url' - self.assertEqual(result(None, request), 'OK') - 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__derive_view_debug_auth_permission_authpol_permitted(self): - view = lambda *arg: 'OK' - config = self._makeOne() - self._registerSettings(config, debug_authorization=True, - reload_templates=True) - logger = self._registerLogger(config) - self._registerSecurityPolicy(config, True) - result = config._derive_view(view, permission='view') - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.assertEqual(result.__call_permissive__, view) - request = self._makeRequest(config) - request.view_name = 'view_name' - request.url = 'url' - self.assertEqual(result(None, request), 'OK') - 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__derive_view_debug_auth_permission_authpol_denied(self): - from pyramid.exceptions import Forbidden - view = lambda *arg: 'OK' - config = self._makeOne() - self._registerSettings(config, - debug_authorization=True, reload_templates=True) - logger = self._registerLogger(config) - self._registerSecurityPolicy(config, False) - result = config._derive_view(view, permission='view') - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.assertEqual(result.__call_permissive__, view) - request = self._makeRequest(config) - request.view_name = 'view_name' - request.url = 'url' - self.assertRaises(Forbidden, 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__derive_view_debug_auth_permission_authpol_denied2(self): - view = lambda *arg: 'OK' - config = self._makeOne() - self._registerSettings(config, - debug_authorization=True, reload_templates=True) - self._registerLogger(config) - self._registerSecurityPolicy(config, False) - result = config._derive_view(view, permission='view') - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - request = self._makeRequest(config) - request.view_name = 'view_name' - request.url = 'url' - permitted = result.__permitted__(None, None) - self.assertEqual(permitted, False) - - def test__derive_view_debug_auth_permission_authpol_overridden(self): - view = lambda *arg: 'OK' - config = self._makeOne() - self._registerSettings(config, - debug_authorization=True, reload_templates=True) - logger = self._registerLogger(config) - self._registerSecurityPolicy(config, False) - result = config._derive_view(view, - permission='__no_permission_required__') - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.failIf(hasattr(result, '__call_permissive__')) - request = self._makeRequest(config) - request.view_name = 'view_name' - request.url = 'url' - self.assertEqual(result(None, request), 'OK') - 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__derive_view_with_predicates_all(self): - view = lambda *arg: 'OK' - predicates = [] - def predicate1(context, request): - predicates.append(True) - return True - def predicate2(context, request): - predicates.append(True) - return True - config = self._makeOne() - result = config._derive_view(view, predicates=[predicate1, predicate2]) - request = self._makeRequest(config) - request.method = 'POST' - next = result(None, None) - self.assertEqual(next, 'OK') - self.assertEqual(predicates, [True, True]) - - def test__derive_view_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 - config = self._makeOne() - result = config._derive_view(view, predicates=[predicate1, predicate2]) - request = self._makeRequest(config) - request.method = 'POST' - next = result.__predicated__(None, None) - self.assertEqual(next, True) - self.assertEqual(predicates, [True, True]) - - def test__derive_view_with_predicates_notall(self): - from pyramid.exceptions import NotFound - view = lambda *arg: 'OK' - predicates = [] - def predicate1(context, request): - predicates.append(True) - return True - def predicate2(context, request): - predicates.append(True) - return False - config = self._makeOne() - result = config._derive_view(view, predicates=[predicate1, predicate2]) - request = self._makeRequest(config) - request.method = 'POST' - self.assertRaises(NotFound, result, None, None) - self.assertEqual(predicates, [True, True]) - - def test__derive_view_with_wrapper_viewname(self): - from webob 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, inner_view) - return Response('outer ' + request.wrapped_body) - config = self._makeOne() - config.registry.registerAdapter( - outer_view, (IViewClassifier, None, None), IView, 'owrap') - result = config._derive_view(inner_view, viewname='inner', - wrapper_viewname='owrap') - self.failIf(result is inner_view) - self.assertEqual(inner_view.__module__, result.__module__) - self.assertEqual(inner_view.__doc__, result.__doc__) - request = self._makeRequest(config) - request.registry = config.registry - response = result(None, request) - self.assertEqual(response.body, 'outer OK') - - def test__derive_view_with_wrapper_viewname_notfound(self): - from webob import Response - inner_response = Response('OK') - def inner_view(context, request): - return inner_response - config = self._makeOne() - request = self._makeRequest(config) - request.registry = config.registry - wrapped = config._derive_view( - inner_view, viewname='inner', wrapper_viewname='owrap') - self.assertRaises(ValueError, wrapped, None, request) - - def test_override_resource_samename(self): - from pyramid.exceptions import ConfigurationError - config = self._makeOne() - self.assertRaises(ConfigurationError, config.override_resource,'a', 'a') - - def test_override_resource_directory_with_file(self): - from pyramid.exceptions import ConfigurationError - config = self._makeOne() - self.assertRaises(ConfigurationError, config.override_resource, - 'a:foo/', 'a:foo.pt') - - def test_override_resource_file_with_directory(self): - from pyramid.exceptions import ConfigurationError - config = self._makeOne() - self.assertRaises(ConfigurationError, config.override_resource, - 'a:foo.pt', 'a:foo/') - - def test_override_resource_success(self): - config = self._makeOne(autocommit=True) - override = DummyUnderOverride() - config.override_resource( - 'pyramid.tests.fixtureapp:templates/foo.pt', - 'pyramid.tests.fixtureapp.subpackage:templates/bar.pt', - _override=override) - from pyramid.tests import fixtureapp - from pyramid.tests.fixtureapp import subpackage - self.assertEqual(override.package, fixtureapp) - self.assertEqual(override.path, 'templates/foo.pt') - self.assertEqual(override.override_package, subpackage) - self.assertEqual(override.override_prefix, 'templates/bar.pt') - - def test_add_renderer(self): - from pyramid.interfaces import IRendererFactory - config = self._makeOne(autocommit=True) - renderer = object() - config.add_renderer('name', renderer) - self.assertEqual(config.registry.getUtility(IRendererFactory, 'name'), - renderer) - - def test_add_renderer_dottedname_factory(self): - from pyramid.interfaces import IRendererFactory - config = self._makeOne(autocommit=True) - import pyramid.tests - config.add_renderer('name', 'pyramid.tests') - self.assertEqual(config.registry.getUtility(IRendererFactory, 'name'), - pyramid.tests) - - def test_scan_integration(self): - import os - from zope.interface import alsoProvides - from pyramid.interfaces import IRequest - from pyramid.view import render_view_to_response - import pyramid.tests.grokkedapp as package - config = self._makeOne(autocommit=True) - config.scan(package) - - ctx = DummyContext() - req = DummyRequest() - alsoProvides(req, IRequest) - req.registry = config.registry - - req.method = 'GET' - result = render_view_to_response(ctx, req, '') - self.assertEqual(result, 'grokked') - - req.method = 'POST' - result = render_view_to_response(ctx, req, '') - self.assertEqual(result, 'grokked_post') - - result= render_view_to_response(ctx, req, 'grokked_class') - self.assertEqual(result, 'grokked_class') - - result= render_view_to_response(ctx, req, 'grokked_instance') - self.assertEqual(result, 'grokked_instance') - - result= render_view_to_response(ctx, req, 'oldstyle_grokked_class') - self.assertEqual(result, 'oldstyle_grokked_class') - - req.method = 'GET' - result = render_view_to_response(ctx, req, 'another') - self.assertEqual(result, 'another_grokked') - - req.method = 'POST' - result = render_view_to_response(ctx, req, 'another') - self.assertEqual(result, 'another_grokked_post') - - result= render_view_to_response(ctx, req, 'another_grokked_class') - self.assertEqual(result, 'another_grokked_class') - - result= render_view_to_response(ctx, req, 'another_grokked_instance') - self.assertEqual(result, 'another_grokked_instance') - - result= render_view_to_response(ctx, req, - 'another_oldstyle_grokked_class') - self.assertEqual(result, 'another_oldstyle_grokked_class') - - result = render_view_to_response(ctx, req, 'stacked1') - self.assertEqual(result, 'stacked') - - result = render_view_to_response(ctx, req, 'stacked2') - self.assertEqual(result, 'stacked') - - result = render_view_to_response(ctx, req, 'another_stacked1') - self.assertEqual(result, 'another_stacked') - - result = render_view_to_response(ctx, req, 'another_stacked2') - self.assertEqual(result, 'another_stacked') - - result = render_view_to_response(ctx, req, 'stacked_class1') - self.assertEqual(result, 'stacked_class') - - result = render_view_to_response(ctx, req, 'stacked_class2') - self.assertEqual(result, 'stacked_class') - - result = render_view_to_response(ctx, req, 'another_stacked_class1') - self.assertEqual(result, 'another_stacked_class') - - result = render_view_to_response(ctx, req, 'another_stacked_class2') - self.assertEqual(result, 'another_stacked_class') - - if not os.name.startswith('java'): - # on Jython, a class without an __init__ apparently accepts - # any number of arguments without raising a TypeError. - - self.assertRaises(TypeError, - render_view_to_response, ctx, req, 'basemethod') - - result = render_view_to_response(ctx, req, 'method1') - self.assertEqual(result, 'method1') - - result = render_view_to_response(ctx, req, 'method2') - self.assertEqual(result, 'method2') - - result = render_view_to_response(ctx, req, 'stacked_method1') - self.assertEqual(result, 'stacked_method') - - result = render_view_to_response(ctx, req, 'stacked_method2') - self.assertEqual(result, 'stacked_method') - - result = render_view_to_response(ctx, req, 'subpackage_init') - self.assertEqual(result, 'subpackage_init') - - result = render_view_to_response(ctx, req, 'subpackage_notinit') - self.assertEqual(result, 'subpackage_notinit') - - result = render_view_to_response(ctx, req, 'subsubpackage_init') - self.assertEqual(result, 'subsubpackage_init') - - result = render_view_to_response(ctx, req, 'pod_notinit') - self.assertEqual(result, None) - - def test_scan_integration_dottedname_package(self): - from zope.interface import alsoProvides - from pyramid.interfaces import IRequest - from pyramid.view import render_view_to_response - config = self._makeOne(autocommit=True) - config.scan('pyramid.tests.grokkedapp') - - ctx = DummyContext() - req = DummyRequest() - alsoProvides(req, IRequest) - req.registry = config.registry - - req.method = 'GET' - result = render_view_to_response(ctx, req, '') - self.assertEqual(result, 'grokked') - - def test_testing_securitypolicy(self): - from pyramid.testing import DummySecurityPolicy - config = self._makeOne(autocommit=True) - config.testing_securitypolicy('user', ('group1', 'group2'), - permissive=False) - from pyramid.interfaces import IAuthenticationPolicy - from pyramid.interfaces import IAuthorizationPolicy - ut = config.registry.getUtility(IAuthenticationPolicy) - self.failUnless(isinstance(ut, DummySecurityPolicy)) - ut = config.registry.getUtility(IAuthorizationPolicy) - self.assertEqual(ut.userid, 'user') - self.assertEqual(ut.groupids, ('group1', 'group2')) - self.assertEqual(ut.permissive, False) - - def test_testing_models(self): - from pyramid.traversal import find_model - from pyramid.interfaces import ITraverser - ob1 = object() - ob2 = object() - models = {'/ob1':ob1, '/ob2':ob2} - config = self._makeOne(autocommit=True) - config.testing_models(models) - adapter = config.registry.getAdapter(None, ITraverser) - result = adapter({'PATH_INFO':'/ob1'}) - self.assertEqual(result['context'], ob1) - self.assertEqual(result['view_name'], '') - self.assertEqual(result['subpath'], ()) - self.assertEqual(result['traversed'], (u'ob1',)) - self.assertEqual(result['virtual_root'], ob1) - self.assertEqual(result['virtual_root_path'], ()) - result = adapter({'PATH_INFO':'/ob2'}) - self.assertEqual(result['context'], ob2) - self.assertEqual(result['view_name'], '') - self.assertEqual(result['subpath'], ()) - self.assertEqual(result['traversed'], (u'ob2',)) - self.assertEqual(result['virtual_root'], ob2) - self.assertEqual(result['virtual_root_path'], ()) - self.assertRaises(KeyError, adapter, {'PATH_INFO':'/ob3'}) - try: - config.begin() - self.assertEqual(find_model(None, '/ob1'), ob1) - finally: - config.end() - - def test_testing_add_subscriber_single(self): - config = self._makeOne(autocommit=True) - L = config.testing_add_subscriber(IDummy) - event = DummyEvent() - config.registry.notify(event) - self.assertEqual(len(L), 1) - self.assertEqual(L[0], event) - config.registry.notify(object()) - self.assertEqual(len(L), 1) - - def test_testing_add_subscriber_dottedname(self): - config = self._makeOne(autocommit=True) - L = config.testing_add_subscriber( - 'pyramid.tests.test_configuration.IDummy') - event = DummyEvent() - config.registry.notify(event) - self.assertEqual(len(L), 1) - self.assertEqual(L[0], event) - config.registry.notify(object()) - self.assertEqual(len(L), 1) - - def test_testing_add_subscriber_multiple(self): - config = self._makeOne(autocommit=True) - L = config.testing_add_subscriber((Interface, IDummy)) - event = DummyEvent() - event.object = 'foo' - # the below is the equivalent of z.c.event.objectEventNotify(event) - config.registry.subscribers((event.object, event), None) - self.assertEqual(len(L), 2) - self.assertEqual(L[0], 'foo') - self.assertEqual(L[1], event) - - def test_testing_add_subscriber_defaults(self): - config = self._makeOne(autocommit=True) - L = config.testing_add_subscriber() - event = object() - config.registry.notify(event) - self.assertEqual(L[-1], event) - event2 = object() - config.registry.notify(event2) - self.assertEqual(L[-1], event2) - - def test_hook_zca(self): - from pyramid.threadlocal import get_current_registry - gsm = DummyGetSiteManager() - config = self._makeOne() - config.hook_zca(getSiteManager=gsm) - self.assertEqual(gsm.hook, get_current_registry) - - def test_unhook_zca(self): - gsm = DummyGetSiteManager() - config = self._makeOne() - config.unhook_zca(getSiteManager=gsm) - self.assertEqual(gsm.unhooked, True) - - def test_testing_add_renderer(self): - config = self._makeOne(autocommit=True) - renderer = config.testing_add_renderer('templates/foo.pt') - from pyramid.testing import DummyTemplateRenderer - self.failUnless(isinstance(renderer, DummyTemplateRenderer)) - from pyramid.renderers import render_to_response - # must provide request to pass in registry (this is a functest) - request = DummyRequest() - request.registry = config.registry - render_to_response( - 'templates/foo.pt', {'foo':1, 'bar':2}, request=request) - renderer.assert_(foo=1) - renderer.assert_(bar=2) - renderer.assert_(request=request) - - def test_testing_add_renderer_explicitrenderer(self): - config = self._makeOne(autocommit=True) - class E(Exception): pass - def renderer(kw, system): - self.assertEqual(kw, {'foo':1, 'bar':2}) - raise E - renderer = config.testing_add_renderer('templates/foo.pt', renderer) - from pyramid.renderers import render_to_response - # must provide request to pass in registry (this is a functest) - request = DummyRequest() - request.registry = config.registry - try: - render_to_response( - 'templates/foo.pt', {'foo':1, 'bar':2}, request=request) - except E: - pass - else: # pragma: no cover - raise AssertionError - - def test_testing_add_template(self): - config = self._makeOne(autocommit=True) - renderer = config.testing_add_template('templates/foo.pt') - from pyramid.testing import DummyTemplateRenderer - self.failUnless(isinstance(renderer, DummyTemplateRenderer)) - from pyramid.renderers import render_to_response - # must provide request to pass in registry (this is a functest) - request = DummyRequest() - request.registry = config.registry - render_to_response('templates/foo.pt', dict(foo=1, bar=2), - request=request) - renderer.assert_(foo=1) - renderer.assert_(bar=2) - renderer.assert_(request=request) - - def test_commit_conflict_simple(self): - from zope.configuration.config import ConfigurationConflictError - config = self._makeOne() - def view1(request): pass - def view2(request): pass - config.add_view(view1) - config.add_view(view2) - self.assertRaises(ConfigurationConflictError, config.commit) - - def test_commit_conflict_resolved_with_include(self): - config = self._makeOne() - def view1(request): pass - def view2(request): pass - def includeme(config): - config.add_view(view2) - config.add_view(view1) - config.include(includeme) - config.commit() - registeredview = self._getViewCallable(config) - self.assertEqual(registeredview.__name__, 'view1') - - def test_commit_conflict_with_two_includes(self): - from zope.configuration.config import ConfigurationConflictError - config = self._makeOne() - def view1(request): pass - def view2(request): pass - def includeme1(config): - config.add_view(view1) - def includeme2(config): - config.add_view(view2) - config.include(includeme1) - config.include(includeme2) - self.assertRaises(ConfigurationConflictError, config.commit) - - def test_commit_conflict_resolved_with_two_includes_and_local(self): - config = self._makeOne() - def view1(request): pass - def view2(request): pass - def view3(request): pass - def includeme1(config): - config.add_view(view1) - def includeme2(config): - config.add_view(view2) - config.include(includeme1) - config.include(includeme2) - config.add_view(view3) - config.commit() - registeredview = self._getViewCallable(config) - self.assertEqual(registeredview.__name__, 'view3') - - def test_autocommit_no_conflicts(self): - config = self._makeOne(autocommit=True) - def view1(request): pass - def view2(request): pass - def view3(request): pass - config.add_view(view1) - config.add_view(view2) - config.add_view(view3) - config.commit() - registeredview = self._getViewCallable(config) - self.assertEqual(registeredview.__name__, 'view3') - -class Test__map_view(unittest.TestCase): - def setUp(self): - from pyramid.registry import Registry - self.registry = Registry() - testing.setUp(registry=self.registry) - - def tearDown(self): - del self.registry - testing.tearDown() - - def _registerRenderer(self, typ='.txt'): - from pyramid.interfaces import IRendererFactory - from pyramid.interfaces import ITemplateRenderer - from zope.interface import implements - class Renderer: - implements(ITemplateRenderer) - spec = 'abc' + typ - def __init__(self, path): - self.__class__.path = path - def __call__(self, *arg): - return 'Hello!' - self.registry.registerUtility(Renderer, IRendererFactory, name=typ) - return Renderer - - def _makeRequest(self): - request = DummyRequest() - request.registry = self.registry - return request - - def _callFUT(self, view, **kw): - from pyramid.configuration import _map_view - return _map_view(view, self.registry, **kw) - - def test__map_view_as_function_context_and_request(self): - def view(context, request): - return 'OK' - result = self._callFUT(view) - self.failUnless(result is view) - self.assertEqual(result(None, None), 'OK') - - def test__map_view_as_function_with_attr(self): - def view(context, request): - """ """ - result = self._callFUT(view, attr='__name__') - self.failIf(result is view) - self.assertRaises(TypeError, result, None, None) - - def test__map_view_as_function_with_attr_and_renderer(self): - renderer = self._registerRenderer() - view = lambda *arg: 'OK' - info = {'name':renderer.spec, 'package':None} - result = self._callFUT(view, attr='__name__', renderer=info) - self.failIf(result is view) - self.assertRaises(TypeError, result, None, None) - - def test__map_view_as_function_requestonly(self): - def view(request): - return 'OK' - result = self._callFUT(view) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.assertEqual(result(None, None), 'OK') - - def test__map_view_as_function_requestonly_with_attr(self): - def view(request): - """ """ - result = self._callFUT(view, attr='__name__') - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.assertRaises(TypeError, result, None, None) - - def test__map_view_as_newstyle_class_context_and_request(self): - class view(object): - def __init__(self, context, request): - pass - def __call__(self): - return 'OK' - result = self._callFUT(view) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.assertEqual(result(None, None), 'OK') - - def test__map_view_as_newstyle_class_context_and_request_with_attr(self): - class view(object): - def __init__(self, context, request): - pass - def index(self): - return 'OK' - result = self._callFUT(view, attr='index') - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.assertEqual(result(None, None), 'OK') - - def test__map_view_as_newstyle_class_context_and_request_attr_and_renderer( - self): - renderer = self._registerRenderer() - class view(object): - def __init__(self, context, request): - pass - def index(self): - return {'a':'1'} - info = {'name':renderer.spec, 'package':None} - result = self._callFUT(view, attr='index', renderer=info) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - request = self._makeRequest() - self.assertEqual(result(None, request).body, 'Hello!') - - def test__map_view_as_newstyle_class_requestonly(self): - class view(object): - def __init__(self, request): - pass - def __call__(self): - return 'OK' - result = self._callFUT(view) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.assertEqual(result(None, None), 'OK') - - def test__map_view_as_newstyle_class_requestonly_with_attr(self): - class view(object): - def __init__(self, request): - pass - def index(self): - return 'OK' - result = self._callFUT(view, attr='index') - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.assertEqual(result(None, None), 'OK') - - def test__map_view_as_newstyle_class_requestonly_attr_and_renderer(self): - renderer = self._registerRenderer() - class view(object): - def __init__(self, request): - pass - def index(self): - return {'a':'1'} - info = {'name':renderer.spec, 'package':None} - result = self._callFUT(view, attr='index', renderer=info) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - request = self._makeRequest() - self.assertEqual(result(None, request).body, 'Hello!') - - def test__map_view_as_oldstyle_class_context_and_request(self): - class view: - def __init__(self, context, request): - pass - def __call__(self): - return 'OK' - result = self._callFUT(view) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.assertEqual(result(None, None), 'OK') - - def test__map_view_as_oldstyle_class_context_and_request_with_attr(self): - class view: - def __init__(self, context, request): - pass - def index(self): - return 'OK' - result = self._callFUT(view, attr='index') - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.assertEqual(result(None, None), 'OK') - - def test__map_view_as_oldstyle_cls_context_request_attr_and_renderer(self): - renderer = self._registerRenderer() - class view: - def __init__(self, context, request): - pass - def index(self): - return {'a':'1'} - info = {'name':renderer.spec, 'package':None} - result = self._callFUT(view, attr='index', renderer=info) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - request = self._makeRequest() - self.assertEqual(result(None, request).body, 'Hello!') - - def test__map_view_as_oldstyle_class_requestonly(self): - class view: - def __init__(self, request): - pass - def __call__(self): - return 'OK' - result = self._callFUT(view) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.assertEqual(result(None, None), 'OK') - - def test__map_view_as_oldstyle_class_requestonly_with_attr(self): - class view: - def __init__(self, request): - pass - def index(self): - return 'OK' - result = self._callFUT(view, attr='index') - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.assertEqual(result(None, None), 'OK') - - def test__map_view_as_oldstyle_class_requestonly_attr_and_renderer(self): - renderer = self._registerRenderer() - class view: - def __init__(self, request): - pass - def index(self): - return {'a':'1'} - info = {'name':renderer.spec, 'package':None} - result = self._callFUT(view, attr='index', renderer=info) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - request = self._makeRequest() - self.assertEqual(result(None, request).body, 'Hello!') - - def test__map_view_as_instance_context_and_request(self): - class View: - def __call__(self, context, request): - return 'OK' - view = View() - result = self._callFUT(view) - self.failUnless(result is view) - self.assertEqual(result(None, None), 'OK') - - def test__map_view_as_instance_context_and_request_and_attr(self): - class View: - def index(self, context, request): - return 'OK' - view = View() - result = self._callFUT(view, attr='index') - self.failIf(result is view) - self.assertEqual(result(None, None), 'OK') - - def test__map_view_as_instance_context_and_request_attr_and_renderer(self): - renderer = self._registerRenderer() - class View: - def index(self, context, request): - return {'a':'1'} - view = View() - info = {'name':renderer.spec, 'package':None} - result = self._callFUT(view, attr='index', renderer=info) - self.failIf(result is view) - request = self._makeRequest() - self.assertEqual(result(None, request).body, 'Hello!') - - def test__map_view_as_instance_requestonly(self): - class View: - def __call__(self, request): - return 'OK' - view = View() - result = self._callFUT(view) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.failUnless('instance' in result.__name__) - self.assertEqual(result(None, None), 'OK') - - def test__map_view_as_instance_requestonly_with_attr(self): - class View: - def index(self, request): - return 'OK' - view = View() - result = self._callFUT(view, attr='index') - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.failUnless('instance' in result.__name__) - self.assertEqual(result(None, None), 'OK') - - def test__map_view_as_instance_requestonly_with_attr_and_renderer(self): - renderer = self._registerRenderer() - class View: - def index(self, request): - return {'a':'1'} - view = View() - info = {'name':renderer.spec, 'package':None} - result = self._callFUT(view, attr='index', renderer=info) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.failUnless('instance' in result.__name__) - request = self._makeRequest() - self.assertEqual(result(None, request).body, 'Hello!') - - def test__map_view_rendereronly(self): - renderer = self._registerRenderer() - def view(context, request): - return {'a':'1'} - info = {'name':renderer.spec, 'package':None} - result = self._callFUT(view, renderer=info) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - request = self._makeRequest() - self.assertEqual(result(None, request).body, 'Hello!') - - def test__map_view_with_registry(self): - renderer = self._registerRenderer() - def view(context, request): - return {'a':'1'} - info = {'name':renderer.spec, 'package':None} - result = self._callFUT(view, renderer=info) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - request = self._makeRequest() - self.assertEqual(result(None, request).body, 'Hello!') - -class Test_decorate_view(unittest.TestCase): - def _callFUT(self, wrapped, original): - from pyramid.configuration import decorate_view - return decorate_view(wrapped, original) - - def test_it_same(self): - def view(context, request): - """ """ - result = self._callFUT(view, view) - self.assertEqual(result, False) - - def test_it_different(self): - class DummyView1: - """ 1 """ - __name__ = '1' - __module__ = '1' - def __call__(self, context, request): - """ """ - def __call_permissive__(self, context, reuqest): - """ """ - def __predicated__(self, context, reuqest): - """ """ - def __permitted__(self, context, request): - """ """ - class DummyView2: - """ 2 """ - __name__ = '2' - __module__ = '2' - def __call__(self, context, request): - """ """ - def __call_permissive__(self, context, reuqest): - """ """ - def __predicated__(self, context, reuqest): - """ """ - def __permitted__(self, context, request): - """ """ - view1 = DummyView1() - view2 = DummyView2() - result = self._callFUT(view1, view2) - self.assertEqual(result, True) - self.failUnless(view1.__doc__ is view2.__doc__) - self.failUnless(view1.__module__ is view2.__module__) - self.failUnless(view1.__name__ is view2.__name__) - self.failUnless(view1.__call_permissive__.im_func is - view2.__call_permissive__.im_func) - self.failUnless(view1.__permitted__.im_func is - view2.__permitted__.im_func) - self.failUnless(view1.__predicated__.im_func is - view2.__predicated__.im_func) - -class Test__make_predicates(unittest.TestCase): - def _callFUT(self, **kw): - from pyramid.configuration 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.failUnless(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=('a',) - ) - 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=('a',) - ) - 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.failUnless(order3 > order2) - self.failUnless(order4 > order3) - self.failUnless(order5 > order4) - self.failUnless(order6 > order5) - self.failUnless(order7 > order6) - self.failUnless(order8 > order7) - self.failUnless(order9 > order8) - self.failUnless(order10 > order9) - self.failUnless(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=('a',), - ) - self.failUnless(order1 > order2) - self.failUnless(order2 > order3) - self.failUnless(order3 > order4) - self.failUnless(order4 > order5) - self.failUnless(order5 > order6) - self.failUnless(order6 > order7) - self.failUnless(order7 > order8) - self.failUnless(order8 > order9) - - def test_ordering_importance_and_number(self): - order1, _, _ = self._callFUT( - xhr='xhr', - request_method='request_method', - ) - order2, _, _ = self._callFUT( - custom=('a',), - ) - self.failUnless(order1 < order2) - - order1, _, _ = self._callFUT( - xhr='xhr', - request_method='request_method', - ) - order2, _, _ = self._callFUT( - request_method='request_method', - custom=('a',), - ) - self.failUnless(order1 > order2) - - order1, _, _ = self._callFUT( - xhr='xhr', - request_method='request_method', - path_info='path_info', - ) - order2, _, _ = self._callFUT( - request_method='request_method', - custom=('a',), - ) - self.failUnless(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=('a',), - ) - self.failUnless(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')}}) - -class TestMultiView(unittest.TestCase): - def _getTargetClass(self): - from pyramid.configuration 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.exceptions import NotFound - mv = self._makeOne() - context = DummyContext() - request = DummyRequest() - self.assertRaises(NotFound, mv.match, context, request) - - def test_match_predicate_fails(self): - from pyramid.exceptions import NotFound - mv = self._makeOne() - def view(context, request): - """ """ - view.__predicated__ = lambda *arg: False - mv.views = [(100, view, None)] - context = DummyContext() - request = DummyRequest() - self.assertRaises(NotFound, 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.exceptions import NotFound - mv = self._makeOne() - context = DummyContext() - request = DummyRequest() - self.assertRaises(NotFound, 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.exceptions import NotFound - mv = self._makeOne() - context = DummyContext() - request = DummyRequest() - self.assertRaises(NotFound, 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.exceptions import NotFound - mv = self._makeOne() - context = DummyContext() - request = DummyRequest() - request.view_name = '' - def view1(context, request): - raise NotFound - def view2(context, request): - """ """ - mv.views = [(100, view1, None), (99, view2, None)] - self.assertRaises(NotFound, 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.exceptions import NotFound - mv = self._makeOne() - context = DummyContext() - request = DummyRequest() - self.assertRaises(NotFound, 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 TestRequestOnly(unittest.TestCase): - def _callFUT(self, arg): - from pyramid.configuration import requestonly - return requestonly(arg) - - def test_newstyle_class_no_init(self): - class foo(object): - """ """ - self.assertFalse(self._callFUT(foo)) - - def test_newstyle_class_init_toomanyargs(self): - class foo(object): - def __init__(self, context, request): - """ """ - self.assertFalse(self._callFUT(foo)) - - def test_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_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_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 TestMakeApp(unittest.TestCase): +class ConfiguratorTests(unittest.TestCase): def setUp(self): - testing.setUp() + from zope.deprecation import __show__ + __show__.off() def tearDown(self): - testing.tearDown() - - def _callFUT(self, *arg, **kw): - from pyramid.configuration import make_app - return make_app(*arg, **kw) - - def test_it(self): - settings = {'a':1} - rootfactory = object() - app = self._callFUT(rootfactory, settings=settings, - Configurator=DummyConfigurator) - self.assertEqual(app.root_factory, rootfactory) - self.assertEqual(app.settings, settings) - self.assertEqual(app.zcml_file, 'configure.zcml') - self.assertEqual(app.zca_hooked, True) - - def test_it_options_means_settings(self): - settings = {'a':1} - rootfactory = object() - app = self._callFUT(rootfactory, options=settings, - Configurator=DummyConfigurator) - self.assertEqual(app.root_factory, rootfactory) - self.assertEqual(app.settings, settings) - self.assertEqual(app.zcml_file, 'configure.zcml') - - def test_it_with_package(self): - package = object() - rootfactory = object() - app = self._callFUT(rootfactory, package=package, - Configurator=DummyConfigurator) - self.assertEqual(app.package, package) - - def test_it_with_custom_configure_zcml(self): - rootfactory = object() - settings = {'configure_zcml':'2.zcml'} - app = self._callFUT(rootfactory, filename='1.zcml', settings=settings, - Configurator=DummyConfigurator) - self.assertEqual(app.zcml_file, '2.zcml') - -class Test_isexception(unittest.TestCase): - def _callFUT(self, ob): - from pyramid.configuration 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 TestActionPredicate(unittest.TestCase): - def _getTargetClass(self): - from pyramid.configuration import ActionPredicate - return ActionPredicate - - def _makeOne(self, action='myaction'): - return self._getTargetClass()(action) - - def test_bad_action_regex_string(self): - from pyramid.exceptions import ConfigurationError - cls = self._getTargetClass() - self.assertRaises(ConfigurationError, cls, '[a-z') - - def test_bad_action_regex_None(self): - from pyramid.exceptions import ConfigurationError - cls = self._getTargetClass() - self.assertRaises(ConfigurationError, cls, None) - - def test___call__no_matchdict(self): - pred = self._makeOne() - request = DummyRequest() - self.assertEqual(pred(None, request), False) - - def test___call__no_action_in_matchdict(self): - pred = self._makeOne() - request = DummyRequest() - request.matchdict = {} - self.assertEqual(pred(None, request), False) - - def test___call__action_does_not_match(self): - pred = self._makeOne() - request = DummyRequest() - request.matchdict = {'action':'notmyaction'} - self.assertEqual(pred(None, request), False) - - def test___call__action_matches(self): - pred = self._makeOne() - request = DummyRequest() - request.matchdict = {'action':'myaction'} - self.assertEqual(pred(None, request), True) - - def test___hash__(self): - pred1 = self._makeOne() - pred2 = self._makeOne() - pred3 = self._makeOne(action='notthesame') - self.assertEqual(hash(pred1), hash(pred2)) - self.assertNotEqual(hash(pred1), hash(pred3)) - self.assertNotEqual(hash(pred2), hash(pred3)) - - - -class DummyRequest: - subpath = () - matchdict = None - def __init__(self): - self.environ = {'PATH_INFO':'/static'} - self.params = {} - self.cookies = {} - def copy(self): - return self - def get_response(self, app): - return app - -class DummyContext: - pass - -class DummyLock: - def acquire(self): - self.acquired = True - - def release(self): - self.released = True - -class DummyPackage: - def __init__(self, name): - self.__name__ = name - -class DummyOverrides: - def __init__(self, package): - self.package = package - self.inserted = [] - - def insert(self, path, package, prefix): - self.inserted.append((path, package, prefix)) - -class DummyUnderOverride: - def __call__(self, package, path, override_package, override_prefix, - _info=u''): - self.package = package - self.path = path - self.override_package = override_package - self.override_prefix = override_prefix - -from zope.interface import Interface -class IDummy(Interface): - pass - -class IOther(Interface): - pass - -class DummyResponse: - status = '200 OK' - headerlist = () - app_iter = () - body = '' - -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 - -class DummyConfigurator(object): - def __init__(self, registry=None, package=None, root_factory=None, - settings=None): - self.root_factory = root_factory - self.package = package - self.settings = settings - - def begin(self, request=None): - self.begun = True - self.request = request - - def end(self): - self.ended = True - - def load_zcml(self, filename): - self.zcml_file = filename - - def make_wsgi_app(self): - return self - - def hook_zca(self): - self.zca_hooked = True - - -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: - implements(IMultiView) - def __init__(self): - self.views = [] - self.name = 'name' - def add(self, view, order, accept=None, phash=None): - self.views.append((view, accept, phash)) - def __call__(self, context, request): - return 'OK1' - def __permitted__(self, context, request): - """ """ - -class DummyGetSiteManager(object): - def sethook(self, hook): - self.hook = hook - def reset(self): - self.unhooked = True - -class DummyThreadLocalManager(object): - pushed = None - popped = False - def push(self, d): - self.pushed = d - def pop(self): - self.popped = True - -class IFactory(Interface): - pass - -class DummyFactory(object): - implements(IFactory) - def __call__(self): - """ """ - -class DummyEvent: - implements(IDummy) - -class DummyStaticURLInfo: - def __init__(self): - self.added = [] - - def add(self, name, spec, **kw): - self.added.append((name, spec, kw)) - -def dummy_view(request): - return 'OK' - -def dummyfactory(request): - """ """ - -class DummyHandler(object): # pragma: no cover - def __init__(self, request): - self.request = request + from zope.deprecation import __show__ + __show__.on() + + def _makeOne(self, *arg, **kw): + from pyramid.configuration import Configurator + return Configurator(*arg, **kw) - def action1(self): - return 'response 1' + def test_autocommit_true(self): + config = self._makeOne() + self.assertEqual(config.autocommit, True) + - def action2(self): - return 'response 2' -def dummy_include(config): - config._action('discrim', None, config.package) - diff --git a/pyramid/tests/test_integration.py b/pyramid/tests/test_integration.py index f7654de78..2c8892eef 100644 --- a/pyramid/tests/test_integration.py +++ b/pyramid/tests/test_integration.py @@ -31,9 +31,9 @@ class WGSIAppPlusViewConfigTests(unittest.TestCase): from pyramid.interfaces import IRequest from pyramid.interfaces import IView from pyramid.interfaces import IViewClassifier - from pyramid.configuration import Config + from pyramid.config import Configurator from pyramid.tests import test_integration - config = Config() + config = Configurator() config.scan(test_integration) config.commit() reg = config.registry @@ -67,8 +67,8 @@ class TestStaticApp(unittest.TestCase): class IntegrationBase(unittest.TestCase): root_factory = None def setUp(self): - from pyramid.configuration import Config - config = Config(root_factory=self.root_factory) + from pyramid.config import Configurator + config = Configurator(root_factory=self.root_factory) config.begin() config.load_zcml(self.config) config.commit() @@ -243,8 +243,8 @@ class TestExceptionViewsApp(IntegrationBase): class ImperativeIncludeConfigurationTest(unittest.TestCase): def setUp(self): - from pyramid.configuration import Config - config = Config() + from pyramid.config import Configurator + config = Configurator() from pyramid.tests.includeapp1.root import configure configure(config) app = config.make_wsgi_app() diff --git a/pyramid/tests/test_router.py b/pyramid/tests/test_router.py index fc2d87630..0ed3d79cf 100644 --- a/pyramid/tests/test_router.py +++ b/pyramid/tests/test_router.py @@ -965,6 +965,79 @@ class TestRouter(unittest.TestCase): start_response = DummyStartResponse() self.assertRaises(RuntimeError, router, environ, start_response) +class TestMakeApp(unittest.TestCase): + def setUp(self): + from zope.deprecation import __show__ + __show__.off() + testing.setUp() + + def tearDown(self): + from zope.deprecation import __show__ + __show__.on() + testing.tearDown() + + def _callFUT(self, *arg, **kw): + from pyramid.router import make_app + return make_app(*arg, **kw) + + def test_it(self): + settings = {'a':1} + rootfactory = object() + app = self._callFUT(rootfactory, settings=settings, + Configurator=DummyConfigurator) + self.assertEqual(app.root_factory, rootfactory) + self.assertEqual(app.settings, settings) + self.assertEqual(app.zcml_file, 'configure.zcml') + self.assertEqual(app.zca_hooked, True) + + def test_it_options_means_settings(self): + settings = {'a':1} + rootfactory = object() + app = self._callFUT(rootfactory, options=settings, + Configurator=DummyConfigurator) + self.assertEqual(app.root_factory, rootfactory) + self.assertEqual(app.settings, settings) + self.assertEqual(app.zcml_file, 'configure.zcml') + + def test_it_with_package(self): + package = object() + rootfactory = object() + app = self._callFUT(rootfactory, package=package, + Configurator=DummyConfigurator) + self.assertEqual(app.package, package) + + def test_it_with_custom_configure_zcml(self): + rootfactory = object() + settings = {'configure_zcml':'2.zcml'} + app = self._callFUT(rootfactory, filename='1.zcml', settings=settings, + Configurator=DummyConfigurator) + self.assertEqual(app.zcml_file, '2.zcml') + +class DummyConfigurator(object): + def __init__(self, registry=None, package=None, root_factory=None, + settings=None, autocommit=True): + self.root_factory = root_factory + self.package = package + self.settings = settings + self.autocommit = autocommit + + def begin(self, request=None): + self.begun = True + self.request = request + + def end(self): + self.ended = True + + def load_zcml(self, filename): + self.zcml_file = filename + + def make_wsgi_app(self): + return self + + def hook_zca(self): + self.zca_hooked = True + + class DummyContext: pass diff --git a/pyramid/tests/test_util.py b/pyramid/tests/test_util.py index 2929f888f..47aab948a 100644 --- a/pyramid/tests/test_util.py +++ b/pyramid/tests/test_util.py @@ -154,7 +154,7 @@ class TestDottedNameResolver(unittest.TestCase): self.assertEqual(typ.package_name, 'pyramid.tests') def test_ctor_string_irresolveable(self): - from pyramid.configuration import ConfigurationError + from pyramid.config import ConfigurationError self.assertRaises(ConfigurationError, self._makeOne, 'cant.be.found') def test_ctor_module(self): diff --git a/pyramid/tests/test_zcml.py b/pyramid/tests/test_zcml.py index a5aea32ec..33a67873f 100644 --- a/pyramid/tests/test_zcml.py +++ b/pyramid/tests/test_zcml.py @@ -163,9 +163,9 @@ class TestNotFoundDirective(unittest.TestCase): from pyramid.interfaces import IView from pyramid.interfaces import IViewClassifier from pyramid.exceptions import NotFound - from pyramid.configuration import Config + from pyramid.config import Configurator reg = self.config.registry - config = Config(reg) + config = Configurator(reg) def dummy_renderer_factory(*arg, **kw): return lambda *arg, **kw: 'OK' config.add_renderer('.pt', dummy_renderer_factory) @@ -232,9 +232,9 @@ class TestForbiddenDirective(unittest.TestCase): from pyramid.interfaces import IView from pyramid.interfaces import IViewClassifier from pyramid.exceptions import Forbidden - from pyramid.configuration import Config + from pyramid.config import Configurator reg = self.config.registry - config = Config(reg) + config = Configurator(reg) def dummy_renderer_factory(*arg, **kw): return lambda *arg, **kw: 'OK' config.add_renderer('.pt', dummy_renderer_factory) diff --git a/pyramid/zcml.py b/pyramid/zcml.py index 6f2fa30e2..06e8cee4b 100644 --- a/pyramid/zcml.py +++ b/pyramid/zcml.py @@ -19,7 +19,7 @@ from pyramid.authentication import AuthTktAuthenticationPolicy from pyramid.authentication import RemoteUserAuthenticationPolicy from pyramid.authentication import RepozeWho1AuthenticationPolicy from pyramid.authorization import ACLAuthorizationPolicy -from pyramid.configuration import Config +from pyramid.config import Configurator from pyramid.exceptions import ConfigurationError from pyramid.resource import resource_spec_from_abspath from pyramid.threadlocal import get_current_registry @@ -167,7 +167,7 @@ def view( context = context or for_ - config = Config.with_context(_context) + config = Configurator.with_context(_context) config.add_view( permission=permission, context=context, view=view, name=name, request_type=request_type, route_name=route_name, @@ -266,7 +266,7 @@ def route(_context, if pattern is None: raise ConfigurationError('route directive must include a "pattern"') - config = Config.with_context(_context) + config = Configurator.with_context(_context) config.add_route( name, pattern, @@ -315,7 +315,7 @@ def notfound(_context, renderer=None, wrapper=None): - config = Config.with_context(_context) + config = Configurator.with_context(_context) config.set_notfound_view(view=view, attr=attr, renderer=renderer, wrapper=wrapper) @@ -326,7 +326,7 @@ def forbidden(_context, renderer=None, wrapper=None): - config = Config.with_context(_context) + config = Configurator.with_context(_context) config.set_forbidden_view(view=view, attr=attr, renderer=renderer, wrapper=wrapper) @@ -346,7 +346,7 @@ class IResourceDirective(Interface): required=True) def resource(_context, to_override, override_with, _override=None): - config = Config.with_context(_context) + config = Configurator.with_context(_context) config.override_resource(to_override, override_with, _override=_override) class IRepozeWho1AuthenticationPolicyDirective(Interface): @@ -360,7 +360,7 @@ def repozewho1authenticationpolicy(_context, identifier_name='auth_tkt', callback=callback) # authentication policies must be registered eagerly so they can # be found by the view registration machinery - config = Config.with_context(_context) + config = Configurator.with_context(_context) config._set_authentication_policy(policy) class IRemoteUserAuthenticationPolicyDirective(Interface): @@ -374,7 +374,7 @@ def remoteuserauthenticationpolicy(_context, environ_key='REMOTE_USER', callback=callback) # authentication policies must be registered eagerly so they can # be found by the view registration machinery - config = Config.with_context(_context) + config = Configurator.with_context(_context) config._set_authentication_policy(policy) class IAuthTktAuthenticationPolicyDirective(Interface): @@ -416,7 +416,7 @@ def authtktauthenticationpolicy(_context, raise ConfigurationError(str(why)) # authentication policies must be registered eagerly so they can # be found by the view registration machinery - config = Config.with_context(_context) + config = Configurator.with_context(_context) config._set_authentication_policy(policy) class IACLAuthorizationPolicyDirective(Interface): @@ -426,7 +426,7 @@ def aclauthorizationpolicy(_context): policy = ACLAuthorizationPolicy() # authorization policies must be registered eagerly so they can be # found by the view registration machinery - config = Config.with_context(_context) + config = Configurator.with_context(_context) config._set_authorization_policy(policy) class IRendererDirective(Interface): @@ -441,7 +441,7 @@ class IRendererDirective(Interface): def renderer(_context, factory, name=''): # renderer factories must be registered eagerly so they can be # found by the view machinery - config = Config.with_context(_context) + config = Configurator.with_context(_context) config.add_renderer(name, factory) class IStaticDirective(Interface): @@ -472,7 +472,7 @@ def static(_context, name, path, cache_max_age=3600, permission='__no_permission_required__'): """ Handle ``static`` ZCML directives """ - config = Config.with_context(_context) + config = Configurator.with_context(_context) config.add_static_view(name, path, cache_max_age=cache_max_age, permission=permission) @@ -483,7 +483,7 @@ class IScanDirective(Interface): ) def scan(_context, package): - config = Config.with_context(_context) + config = Configurator.with_context(_context) config.scan(package) class ITranslationDirDirective(Interface): @@ -495,7 +495,7 @@ class ITranslationDirDirective(Interface): def translationdir(_context, dir): path = path_spec(_context, dir) - config = Config.with_context(_context) + config = Configurator.with_context(_context) config.add_translation_dirs(path) class ILocaleNegotiatorDirective(Interface): @@ -506,7 +506,7 @@ class ILocaleNegotiatorDirective(Interface): ) def localenegotiator(_context, negotiator): - config = Config.with_context(_context) + config = Configurator.with_context(_context) config.set_locale_negotiator(negotiator) class IAdapterDirective(Interface): @@ -645,7 +645,7 @@ def subscriber(_context, for_=None, factory=None, handler=None, provides=None): for_ = tuple(for_) - config = Config.with_context(_context) + config = Configurator.with_context(_context) if handler is not None: config.add_subscriber(handler, for_) @@ -732,7 +732,7 @@ def default_permission(_context, name): """ Register a default permission name """ # the default permission must be registered eagerly so it can # be found by the view registration machinery - config = Config.with_context(_context) + config = Configurator.with_context(_context) config.set_default_permission(name) def path_spec(context, path): @@ -753,7 +753,7 @@ def zcml_configure(name, package): """ registry = get_current_registry() - configurator = Config(registry=registry, package=package) + configurator = Configurator(registry=registry, package=package) configurator.load_zcml(name) actions = configurator._ctx.actions[:] configurator.commit() -- cgit v1.2.3