Dylan Jay
2011-08-25 c36e1570e997a90ac0666426b7c459adb5cd54bc
Merge branch 'master' of github.com:djay/pyramid
1 files deleted
3 files added
20 files modified
719 ■■■■■ changed files
CHANGES.txt 35 ●●●●● patch | view | raw | blame | history
README.rst 2 ●●●●● patch | view | raw | blame | history
TODO.txt 6 ●●●● patch | view | raw | blame | history
docs/conf.py 2 ●●● patch | view | raw | blame | history
docs/whatsnew-1.2.rst 40 ●●●●● patch | view | raw | blame | history
pyramid/config/__init__.py 32 ●●●●● patch | view | raw | blame | history
pyramid/config/adapters.py 5 ●●●●● patch | view | raw | blame | history
pyramid/config/assets.py 1 ●●●● patch | view | raw | blame | history
pyramid/config/factories.py 7 ●●●●● patch | view | raw | blame | history
pyramid/config/rendering.py 14 ●●●●● patch | view | raw | blame | history
pyramid/config/routes.py 70 ●●●●● patch | view | raw | blame | history
pyramid/config/security.py 40 ●●●●● patch | view | raw | blame | history
pyramid/config/tweens.py 14 ●●●●● patch | view | raw | blame | history
pyramid/config/views.py 54 ●●●●● patch | view | raw | blame | history
pyramid/httpexceptions.py 2 ●●● patch | view | raw | blame | history
pyramid/interfaces.py 10 ●●●●● patch | view | raw | blame | history
pyramid/tests/ccbugapp/__init__.py 16 ●●●● patch | view | raw | blame | history
pyramid/tests/ccbugapp/views.py 10 ●●●●● patch | view | raw | blame | history
pyramid/tests/conflictapp/__init__.py 23 ●●●●● patch | view | raw | blame | history
pyramid/tests/conflictapp/included.py 6 ●●●●● patch | view | raw | blame | history
pyramid/tests/test_config/test_init.py 179 ●●●● patch | view | raw | blame | history
pyramid/tests/test_config/test_security.py 84 ●●●●● patch | view | raw | blame | history
pyramid/tests/test_integration.py 65 ●●●●● patch | view | raw | blame | history
setup.py 2 ●●● patch | view | raw | blame | history
CHANGES.txt
@@ -1,6 +1,14 @@
Next release
============
- When a ``renderers=`` argument is not specified to the Configurator
  constructor, eagerly register and commit the default renderer set.  This
  permits the overriding of the default renderers, which was broken in 1.2a1
  without a commit directly after Configurator construction.
1.2a1 (2011-08-24)
==================
Features
--------
@@ -171,6 +179,33 @@
  instead make a separate call to the method for each callable.  This change
  was introduced to support the ``route_prefix`` feature of include.
- It may be necessary to more strictly order configuration route and view
  statements when using an "autocommitting" Configurator.  In the past, it
  was possible to add a view which named a route name before adding a route
  with that name when you used an autocommitting configurator.  For example::
    config = Configurator(autocommit=True)
    config.add_view('my.pkg.someview', route_name='foo')
    config.add_route('foo', '/foo')
  The above will raise an exception when the view attempts to add itself.
  Now you must add the route before adding the view::
    config = Configurator(autocommit=True)
    config.add_route('foo', '/foo')
    config.add_view('my.pkg.someview', route_name='foo')
  This won't effect "normal" users, only people who have legacy BFG codebases
  that used an autommitting configurator and possibly tests that use the
  configurator API (the configurator returned by ``pyramid.testing.setUp`` is
  an autocommitting configurator).  The right way to get around this is to
  use a non-autocommitting configurator (the default), which does not have
  these directive ordering requirements.
- The ``pyramid.config.Configurator.add_route`` directive no longer returns a
  route object.  This change was required to make route vs. view
  configuration processing work properly.
Documentation
-------------
README.rst
@@ -8,8 +8,6 @@
Pyramid is the newest web framework produced by the `Pylons Project
<http://pylonsproject.org/>`_.
Pyramid was previously known as `repoze.bfg <http://bfg.repoze.org>`_.
Support and Documentation
-------------------------
TODO.txt
@@ -8,12 +8,12 @@
  deploying via proxy:
  https://docs.pylonsproject.org/projects/pyramid_cookbook/dev/deployment/nginx.html#step-2-starting-paster
- Fix conflict behavior for routes and auth policies (define phases and use
  order=).
Nice-to-Have
------------
- Add a default-view-config-params decorator that can be applied to a class
  which names defaults for method-based view_config decorator options.
- Flesh out "paste" chapter.
- Move config-related stuff from "renderers" to config/rendering, and
docs/conf.py
@@ -93,7 +93,7 @@
# other places throughout the built documents.
#
# The short X.Y version.
version = '1.2dev'
version = '1.2a1'
# The full version, including alpha/beta/rc tags.
release = version
docs/whatsnew-1.2.rst
@@ -176,6 +176,46 @@
  :meth:`pyramid.config.Configurator.include`, it will break.  You now must
  now instead make a separate call to the method for each callable.
- It may be necessary to more strictly order configuration route and view
  statements when using an "autocommitting" :term:`Configurator`.  In the
  past, it was possible to add a view which named a route name before adding
  a route with that name when you used an autocommitting configurator.  For
  example:
  .. code-block:: python
     config = Configurator(autocommit=True)
     config.add_view('my.pkg.someview', route_name='foo')
     config.add_route('foo', '/foo')
  The above will raise an exception when the view attempts to add itself.
  Now you must add the route before adding the view:
  .. code-block:: python
     config = Configurator(autocommit=True)
     config.add_route('foo', '/foo')
     config.add_view('my.pkg.someview', route_name='foo')
  This won't effect "normal" users, only people who have legacy BFG codebases
  that used an autommitting configurator and possibly tests that use the
  configurator API (the configurator returned by
  :func:`pyramid.testing.setUp` is an autocommitting configurator).  The
  right way to get around this is to use a default non-autocommitting
  configurator, which does not have these directive ordering requirements:
  .. code-block:: python
     config = Configurator()
     config.add_view('my.pkg.someview', route_name='foo')
     config.add_route('foo', '/foo')
   The above will work fine.
- The :meth:`pyramid.config.Configurator.add_route` directive no longer
  returns a route object.  This change was required to make route vs. view
  configuration processing work properly.
Documentation Enhancements
--------------------------
pyramid/config/__init__.py
@@ -303,29 +303,33 @@
            self.add_view(exceptionresponse_view, context=IExceptionResponse)
            self.add_view(exceptionresponse_view,context=WebobWSGIHTTPException)
        # commit before adding default_view_mapper, as the
        # exceptionresponse_view above requires the superdefault view
        # mapper
        # commit below because:
        #
        # - the default exceptionresponse_view requires the superdefault view
        #   mapper, so we need to configure it before adding default_view_mapper
        #
        # - superdefault renderers should be overrideable without requiring
        #   the user to commit before calling config.add_renderer
        self.commit()
        if default_view_mapper is not None:
            self.set_view_mapper(default_view_mapper)
            self.commit()
        # The following registrations should be treated as if the methods had
        # been called after configurator construction (commit should not be
        # called after this).  Rationale: user-supplied implementations
        # should be preferred rather than add-on author implementations (as
        # per automatic conflict resolution).
        # self.commit() should not be called after this point because the
        # following registrations should be treated as analogues of methods
        # called by the user after configurator construction.  Rationale:
        # user-supplied implementations should be preferred rather than
        # add-on author implementations with the help of automatic conflict
        # resolution.
        if authentication_policy and not authorization_policy:
            authorization_policy = ACLAuthorizationPolicy() # default
        if authentication_policy:
            self.set_authentication_policy(authentication_policy)
        if authorization_policy:
            self.set_authorization_policy(authorization_policy)
        if authentication_policy:
            self.set_authentication_policy(authentication_policy)
        if default_view_mapper is not None:
            self.set_view_mapper(default_view_mapper)
        for name, renderer in renderers:
            self.add_renderer(name, renderer)
pyramid/config/adapters.py
@@ -1,6 +1,7 @@
from zope.interface import Interface
from pyramid.interfaces import IResponse
from pyramid.interfaces import PHASE3_CONFIG
from pyramid.config.util import action_method
@@ -27,7 +28,7 @@
            iface = (iface,)
        def register():
            self.registry.registerHandler(subscriber, iface)
        self.action(None, register)
        self.action(None, register, order=PHASE3_CONFIG)
        return subscriber
    @action_method
@@ -52,7 +53,7 @@
                reg.registerSelfAdapter((type_or_iface,), IResponse)
            else:
                reg.registerAdapter(adapter, (type_or_iface,), IResponse)
        self.action((IResponse, type_or_iface), register)
        self.action((IResponse, type_or_iface), register, order=PHASE3_CONFIG)
    def _register_response_adapters(self):
        # cope with WebOb response objects that aren't decorated with IResponse
pyramid/config/assets.py
@@ -235,6 +235,7 @@
            from_package = sys.modules[package]
            to_package = sys.modules[override_package]
            override(from_package, path, to_package, override_prefix)
        self.action(None, register)
    override_resource = override_asset # bw compat
pyramid/config/factories.py
@@ -4,6 +4,7 @@
from pyramid.interfaces import IRequestFactory
from pyramid.interfaces import IRootFactory
from pyramid.interfaces import ISessionFactory
from pyramid.interfaces import PHASE3_CONFIG
from pyramid.traversal import DefaultRootFactory
@@ -24,7 +25,7 @@
        def register():
            self.registry.registerUtility(factory, IRootFactory)
            self.registry.registerUtility(factory, IDefaultRootFactory) # b/c
        self.action(IRootFactory, register)
        self.action(IRootFactory, register, order=PHASE3_CONFIG)
    _set_root_factory = set_root_factory # bw compat
@@ -41,7 +42,7 @@
        """
        def register():
            self.registry.registerUtility(session_factory, ISessionFactory)
        self.action(ISessionFactory, register)
        self.action(ISessionFactory, register, order=PHASE3_CONFIG)
    @action_method
    def set_request_factory(self, factory):
@@ -60,5 +61,5 @@
        factory = self.maybe_dotted(factory)
        def register():
            self.registry.registerUtility(factory, IRequestFactory)
        self.action(IRequestFactory, register)
        self.action(IRequestFactory, register, order=PHASE3_CONFIG)
pyramid/config/rendering.py
@@ -2,6 +2,8 @@
from pyramid.interfaces import IRendererFactory
from pyramid.interfaces import IRendererGlobalsFactory
from pyramid.interfaces import PHASE1_CONFIG
from pyramid.interfaces import PHASE3_CONFIG
from pyramid.config.util import action_method
@@ -49,10 +51,11 @@
        # as a name
        if not name: 
            name = ''
        # we need to register renderers eagerly because they are used during
        # view configuration
        self.registry.registerUtility(factory, IRendererFactory, name=name)
        self.action((IRendererFactory, name), None)
        def register():
            self.registry.registerUtility(factory, IRendererFactory, name=name)
        # we need to register renderers early (in phase 1) because they are
        # used during view configuration (which happens in phase 3)
        self.action((IRendererFactory, name), register, order=PHASE1_CONFIG)
    @action_method
    def set_renderer_globals_factory(self, factory, warn=True):
@@ -86,5 +89,4 @@
        factory = self.maybe_dotted(factory)
        def register():
            self.registry.registerUtility(factory, IRendererGlobalsFactory)
        self.action(IRendererGlobalsFactory, register)
        self.action(IRendererGlobalsFactory, register, order=PHASE3_CONFIG)
pyramid/config/routes.py
@@ -3,6 +3,7 @@
from pyramid.interfaces import IRequest
from pyramid.interfaces import IRouteRequest
from pyramid.interfaces import IRoutesMapper
from pyramid.interfaces import PHASE2_CONFIG
from pyramid.exceptions import ConfigurationError
from pyramid.request import route_request_iface
@@ -344,21 +345,45 @@
            custom=custom_predicates
            )
        request_iface = self.registry.queryUtility(IRouteRequest, name=name)
        if request_iface is None:
            if use_global_views:
                bases = (IRequest,)
            else:
                bases = ()
            request_iface = route_request_iface(name, bases)
            self.registry.registerUtility(
                request_iface, IRouteRequest, name=name)
            deferred_views = getattr(self.registry, 'deferred_route_views', {})
            view_info = deferred_views.pop(name, ())
            for info in view_info:
                self.add_view(**info)
        factory = self.maybe_dotted(factory)
        if pattern is None:
            pattern = path
        if pattern is None:
            raise ConfigurationError('"pattern" argument may not be None')
        # deprecated adding views from add_route
        if self.route_prefix:
            pattern = self.route_prefix.rstrip('/') + '/' + pattern.lstrip('/')
        mapper = self.get_routes_mapper()
        def register_route_request_iface():
            request_iface = self.registry.queryUtility(IRouteRequest, name=name)
            if request_iface is None:
                if use_global_views:
                    bases = (IRequest,)
                else:
                    bases = ()
                request_iface = route_request_iface(name, bases)
                self.registry.registerUtility(
                    request_iface, IRouteRequest, name=name)
        def register_connect():
            return mapper.connect(name, pattern, factory, predicates=predicates,
                                  pregenerator=pregenerator, static=static)
        # We have to connect routes in the order they were provided;
        # we can't use a phase to do that, because when the actions are
        # sorted, actions in the same phase lose relative ordering
        self.action(('route-connect', name), register_connect)
        # But IRouteRequest interfaces must be registered before we begin to
        # process view registrations (in phase 3)
        self.action(('route', name), register_route_request_iface,
                    order=PHASE2_CONFIG)
        # deprecated adding views from add_route; must come after
        # route registration for purposes of autocommit ordering
        if any([view, view_context, view_permission, view_renderer,
                view_for, for_, permission, renderer, view_attr]):
            self._add_view_from_route(
@@ -369,23 +394,6 @@
                renderer=view_renderer or renderer,
                attr=view_attr,
            )
        mapper = self.get_routes_mapper()
        factory = self.maybe_dotted(factory)
        if pattern is None:
            pattern = path
        if pattern is None:
            raise ConfigurationError('"pattern" argument may not be None')
        if self.route_prefix:
            pattern = self.route_prefix.rstrip('/') + '/' + pattern.lstrip('/')
        discriminator = ('route', name)
        self.action(discriminator, None)
        return mapper.connect(name, pattern, factory, predicates=predicates,
                              pregenerator=pregenerator, static=static)
    def get_routes_mapper(self):
        """ Return the :term:`routes mapper` object associated with
pyramid/config/security.py
@@ -1,6 +1,9 @@
from pyramid.interfaces import IAuthorizationPolicy
from pyramid.interfaces import IAuthenticationPolicy
from pyramid.interfaces import IDefaultPermission
from pyramid.interfaces import PHASE1_CONFIG
from pyramid.interfaces import PHASE2_CONFIG
from pyramid.interfaces import PHASE3_CONFIG
from pyramid.exceptions import ConfigurationError
from pyramid.config.util import action_method
@@ -18,18 +21,16 @@
           can be used to achieve the same purpose.
        
        """
        self._set_authentication_policy(policy)
        def ensure():
            if self.autocommit:
                return
        def register():
            self._set_authentication_policy(policy)
            if self.registry.queryUtility(IAuthorizationPolicy) is None:
                raise ConfigurationError(
                    'Cannot configure an authentication policy without '
                    'also configuring an authorization policy '
                    '(see the set_authorization_policy method)')
        self.action(IAuthenticationPolicy, callable=ensure)
                    '(use the set_authorization_policy method)')
        # authentication policy used by view config (phase 3)
        self.action(IAuthenticationPolicy, register, order=PHASE2_CONFIG)
    @action_method
    def _set_authentication_policy(self, policy):
        policy = self.maybe_dotted(policy)
        self.registry.registerUtility(policy, IAuthenticationPolicy)
@@ -45,16 +46,22 @@
           :class:`pyramid.config.Configurator` constructor
           can be used to achieve the same purpose.
        """
        self._set_authorization_policy(policy)
        def register():
            self._set_authorization_policy(policy)
        def ensure():
            if self.autocommit:
                return
            if self.registry.queryUtility(IAuthenticationPolicy) is None:
                raise ConfigurationError(
                    'Cannot configure an authorization policy without also '
                    'configuring an authentication policy '
                    '(see the set_authentication_policy method)')
        self.action(IAuthorizationPolicy, callable=ensure)
                    'Cannot configure an authorization policy without '
                    'also configuring an authentication policy '
                    '(use the set_authorization_policy method)')
        # authorization policy used by view config (phase 3) and
        # authentication policy (phase 2)
        self.action(IAuthorizationPolicy, register, order=PHASE1_CONFIG)
        self.action(None, ensure, order=PHASE3_CONFIG)
    @action_method
    def _set_authorization_policy(self, policy):
        policy = self.maybe_dotted(policy)
        self.registry.registerUtility(policy, IAuthorizationPolicy)
@@ -96,8 +103,9 @@
           :class:`pyramid.config.Configurator` constructor
           can be used to achieve the same purpose.
        """
        # default permission used during view registration
        self.registry.registerUtility(permission, IDefaultPermission)
        self.action(IDefaultPermission, None)
        # default permission used during view registration (phase 3)
        def register():
            self.registry.registerUtility(permission, IDefaultPermission)
        self.action(IDefaultPermission, register, order=PHASE1_CONFIG)
pyramid/config/tweens.py
@@ -1,6 +1,7 @@
from zope.interface import implements
from pyramid.interfaces import ITweens
from pyramid.interfaces import PHASE3_CONFIG
from pyramid.exceptions import ConfigurationError
from pyramid.tweens import excview_tween_factory
@@ -128,17 +129,20 @@
            raise ConfigurationError('%s cannot be under MAIN' % name)
        registry = self.registry
        tweens = registry.queryUtility(ITweens)
        if tweens is None:
            tweens = Tweens()
            registry.registerUtility(tweens, ITweens)
            tweens.add_implicit(EXCVIEW, excview_tween_factory, over=MAIN)
        if explicit:
            tweens.add_explicit(name, tween_factory)
        else:
            tweens.add_implicit(name, tween_factory, under=under, over=over)
        self.action(('tween', name, explicit))
        def register():
            if explicit:
                tweens.add_explicit(name, tween_factory)
            else:
                tweens.add_implicit(name, tween_factory, under=under, over=over)
        self.action(('tween', name, explicit), register, order=PHASE3_CONFIG)
class CyclicDependencyError(Exception):
    def __init__(self, cycles):
pyramid/config/views.py
@@ -25,6 +25,8 @@
from pyramid.interfaces import IRequest
from pyramid.interfaces import IRouteRequest
from pyramid.interfaces import IRendererFactory
from pyramid.interfaces import PHASE1_CONFIG
from pyramid.interfaces import PHASE3_CONFIG
from pyramid.exceptions import ConfigurationError
from pyramid.exceptions import PredicateMismatch
@@ -885,31 +887,6 @@
                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,
                    match_param=match_param,
                    custom_predicates=custom_predicates, context=context,
                    mapper = mapper, http_cache = http_cache,
                    )
                view_info = deferred_views.setdefault(route_name, [])
                view_info.append(info)
                return
        order, predicates, phash = make_predicates(xhr=xhr,
            request_method=request_method, path_info=path_info,
            request_param=request_param, header=header, accept=accept,
@@ -931,8 +908,19 @@
                registry = self.registry)
        def register(permission=permission, renderer=renderer):
            request_iface = IRequest
            if route_name is not None:
                request_iface = self.registry.queryUtility(IRouteRequest,
                                                           name=route_name)
                if request_iface is None:
                    # route configuration should have already happened in
                    # phase 2
                    raise ConfigurationError(
                        'No route named %s found for view registration' %
                        route_name)
            if renderer is None:
                # use default renderer if one exists
                # use default renderer if one exists (reg'd in phase 1)
                if self.registry.queryUtility(IRendererFactory) is not None:
                    renderer = renderers.RendererHelper(
                        name=None,
@@ -941,6 +929,7 @@
            if permission is None:
                # intent: will be None if no default permission is registered
                # (reg'd in phase 1)
                permission = self.registry.queryUtility(IDefaultPermission)
            # __no_permission_required__ handled by _secure_view
@@ -1058,11 +1047,11 @@
        discriminator = [
            'view', context, name, request_type, IView, containment,
            request_param, request_method, match_param, route_name, attr,
            xhr, accept, header, path_info]
            request_param, request_method, route_name, attr,
            xhr, accept, header, path_info, match_param]
        discriminator.extend(sorted(custom_predicates))
        discriminator = tuple(discriminator)
        self.action(discriminator, register)
        self.action(discriminator, register, order=PHASE3_CONFIG)
    
    def derive_view(self, view, attr=None, renderer=None):
        """
@@ -1281,8 +1270,11 @@
           can be used to achieve the same purpose.
        """
        mapper = self.maybe_dotted(mapper)
        self.registry.registerUtility(mapper, IViewMapperFactory)
        self.action(IViewMapperFactory, None)
        def register():
            self.registry.registerUtility(mapper, IViewMapperFactory)
        # IViewMapperFactory is looked up as the result of view config
        # in phase 3
        self.action(IViewMapperFactory, register, order=PHASE1_CONFIG)
    @action_method
    def add_static_view(self, name, path, **kw):
pyramid/httpexceptions.py
@@ -987,7 +987,7 @@
def exception_response(status_code, **kw):
    """Creates an HTTP exception based on a status code. Example::
        raise responsecode(404) # raises an HTTPNotFound exception.
        raise exception_response(404) # raises an HTTPNotFound exception.
    The values passed as ``kw`` are provided to the exception's constructor.
    """
pyramid/interfaces.py
@@ -847,4 +847,12 @@
                         'renderer was created')
    settings = Attribute('The deployment settings dictionary related '
                         'to the current application')
# configuration phases: a lower phase number means the actions associated
# with this phase will be executed earlier than those with later phase
# numbers
PHASE1_CONFIG = -20
PHASE2_CONFIG = -10
PHASE3_CONFIG = 0
pyramid/tests/ccbugapp/__init__.py
@@ -1,8 +1,16 @@
from webob import Response
def rdf_view(request):
    """ """
    return Response('rdf')
def juri_view(request):
    """ """
    return Response('juri')
def includeme(config):
    config.add_route('rdf', 'licenses/:license_code/:license_version/rdf')
    config.add_route('juri',
                     'licenses/:license_code/:license_version/:jurisdiction')
    config.add_view('.views.rdf_view', route_name='rdf')
    config.add_view('.views.juri_view', route_name='juri')
    config.add_view(rdf_view, route_name='rdf')
    config.add_view(juri_view, route_name='juri')
pyramid/tests/ccbugapp/views.py
File was deleted
pyramid/tests/conflictapp/__init__.py
New file
@@ -0,0 +1,23 @@
from pyramid.response import Response
from pyramid.authentication import AuthTktAuthenticationPolicy
from pyramid.authorization import ACLAuthorizationPolicy
def aview(request):
    return Response('a view')
def routeview(request):
    return Response('route view')
def protectedview(request):
    return Response('protected view')
def includeme(config):
    # purposely sorta-randomly ordered (route comes after view naming it,
    # authz comes after views)
    config.add_view(aview)
    config.add_view(protectedview, name='protected', permission='view')
    config.add_view(routeview, route_name='aroute')
    config.add_route('aroute', '/route')
    config.set_authentication_policy(AuthTktAuthenticationPolicy('seekri1t'))
    config.set_authorization_policy(ACLAuthorizationPolicy())
    config.include('pyramid.tests.conflictapp.included')
pyramid/tests/conflictapp/included.py
New file
@@ -0,0 +1,6 @@
from webob import Response
def bview(request): return Response('b view')
def includeme(config):
    config.add_view(bview)
pyramid/tests/test_config/test_init.py
@@ -103,6 +103,7 @@
        this_pkg = sys.modules['pyramid.tests.test_config']
        self.assertTrue(config.registry.getUtility(ISettings))
        self.assertEqual(config.package, this_pkg)
        config.commit()
        self.assertTrue(config.registry.getUtility(IRendererFactory, 'json'))
        self.assertTrue(config.registry.getUtility(IRendererFactory, 'string'))
        if not __pypy__:
@@ -185,6 +186,7 @@
        from pyramid.interfaces import IAuthenticationPolicy
        policy = object()
        config = self._makeOne(authentication_policy=policy)
        config.commit()
        result = config.registry.getUtility(IAuthenticationPolicy)
        self.assertEqual(policy, result)
@@ -205,12 +207,21 @@
        from pyramid.interfaces import IRendererFactory
        renderer = object()
        config = self._makeOne(renderers=[('yeah', renderer)])
        config.commit()
        self.assertEqual(config.registry.getUtility(IRendererFactory, 'yeah'),
                         renderer)
    def test_ctor_default_renderers(self):
        from pyramid.interfaces import IRendererFactory
        from pyramid.renderers import json_renderer_factory
        config = self._makeOne()
        self.assertEqual(config.registry.getUtility(IRendererFactory, 'json'),
                         json_renderer_factory)
    def test_ctor_default_permission(self):
        from pyramid.interfaces import IDefaultPermission
        config = self._makeOne(default_permission='view')
        config.commit()
        self.assertEqual(config.registry.getUtility(IDefaultPermission), 'view')
    def test_ctor_session_factory(self):
@@ -224,6 +235,7 @@
        from pyramid.interfaces import IViewMapperFactory
        mapper = object()
        config = self._makeOne(default_view_mapper=mapper)
        config.commit()
        self.assertEqual(config.registry.getUtility(IViewMapperFactory),
                         mapper)
@@ -463,6 +475,7 @@
        reg = Registry()
        config = self._makeOne(reg)
        config.setup_registry(authentication_policy=policy)
        config.commit()
        result = reg.getUtility(IAuthenticationPolicy)
        self.assertEqual(policy, result)
@@ -472,6 +485,7 @@
        reg = Registry()
        config = self._makeOne(reg)
        config.setup_registry(authentication_policy='pyramid.tests')
        config.commit()
        result = reg.getUtility(IAuthenticationPolicy)
        import pyramid.tests
        self.assertEqual(result, pyramid.tests)
@@ -484,6 +498,7 @@
        dummy = object()
        config.setup_registry(authentication_policy=dummy,
                              authorization_policy='pyramid.tests')
        config.commit()
        result = reg.getUtility(IAuthorizationPolicy)
        import pyramid.tests
        self.assertEqual(result, pyramid.tests)
@@ -607,6 +622,7 @@
        reg = Registry()
        config = self._makeOne(reg)
        config.setup_registry(renderers=[('yeah', renderer)])
        config.commit()
        self.assertEqual(reg.getUtility(IRendererFactory, 'yeah'),
                         renderer)
@@ -616,6 +632,7 @@
        reg = Registry()
        config = self._makeOne(reg)
        config.setup_registry(default_permission='view')
        config.commit()
        self.assertEqual(reg.getUtility(IDefaultPermission), 'view')
    def test_setup_registry_includes(self):
@@ -1913,71 +1930,33 @@
    def test_add_view_with_route_name(self):
        from pyramid.renderers import null_renderer
        from zope.component import ComponentLookupError
        view = lambda *arg: 'OK'
        config = self._makeOne(autocommit=True)
        config.add_view(view=view, route_name='foo', renderer=null_renderer)
        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')
        config.add_view(view=view, route_name='foo', renderer=null_renderer)
        request_iface = self._getRouteRequestIface(config, 'foo')
        self.assertNotEqual(request_iface, None)
        wrapper = self._getViewCallable(config, request_iface=request_iface)
        self.assertNotEqual(wrapper, None)
        self.assertEqual(wrapper(None, None), 'OK')
    def test_add_view_with_route_name_deferred_views_already_exist(self):
    def test_add_view_with_nonexistant_route_name(self):
        from pyramid.renderers import null_renderer
        from zope.configuration.config import ConfigurationExecutionError
        view = lambda *arg: 'OK'
        config = self._makeOne(autocommit=True)
        config.registry.deferred_route_views = {'bar':[]}
        config.add_view(view=view, route_name='foo')
        self.assertEqual(len(config.registry.deferred_route_views), 2)
        self.assertEqual(config.registry.deferred_route_views['bar'], [])
        infos = config.registry.deferred_route_views['foo']
        self.assertEqual(len(infos), 1)
    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',))
        config = self._makeOne()
        config.add_view(view=view, route_name='foo', renderer=null_renderer)
        self.assertRaises(ConfigurationExecutionError, config.commit)
    def test_add_view_with_route_name_exception(self):
        from pyramid.renderers import null_renderer
        from zope.interface import implementedBy
        from zope.component import ComponentLookupError
        view = lambda *arg: 'OK'
        config = self._makeOne(autocommit=True)
        config.add_route('foo', '/a/b')
        config.add_view(view=view, route_name='foo', context=RuntimeError,
                        renderer=null_renderer)
        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.assertNotEqual(request_iface, None)
        wrapper_exc_view = self._getViewCallable(
            config, ctx_iface=implementedBy(RuntimeError),
            request_iface=request_iface, exception_view=True)
@@ -2351,45 +2330,40 @@
    def test_add_route_defaults(self):
        config = self._makeOne(autocommit=True)
        route = config.add_route('name', 'path')
        config.add_route('name', 'path')
        self._assertRoute(config, 'name', 'path')
        self.assertEqual(route.name, 'name')
    def test_add_route_with_route_prefix(self):
        config = self._makeOne(autocommit=True)
        config.route_prefix = 'root'
        route = config.add_route('name', 'path')
        self.assertEqual(route.name, 'name')
        self.assertEqual(route.pattern, 'root/path')
        config.add_route('name', 'path')
        self._assertRoute(config, 'name', 'root/path')
    def test_add_route_discriminator(self):
        config = self._makeOne()
        route = config.add_route('name', 'path')
        self._assertRoute(config, 'name', 'path')
        self.assertEqual(route.name, 'name')
        config.add_route('name', 'path')
        self.assertEqual(config._ctx.actions[-1][0], ('route', 'name'))
    def test_add_route_with_factory(self):
        config = self._makeOne(autocommit=True)
        factory = object()
        route = config.add_route('name', 'path', factory=factory)
        config.add_route('name', 'path', factory=factory)
        route = self._assertRoute(config, 'name', 'path')
        self.assertEqual(route.factory, factory)
    def test_add_route_with_static(self):
        config = self._makeOne(autocommit=True)
        route = config.add_route('name', 'path/{foo}', static=True)
        self.assertEqual(route.name, 'name')
        config.add_route('name', 'path/{foo}', static=True)
        mapper = config.get_routes_mapper()
        self.assertEqual(len(mapper.get_routes()), 0)
        self.assertEqual(mapper.generate('name', {"foo":"a"}), '/path/a')
    def test_add_route_with_factory_dottedname(self):
        config = self._makeOne(autocommit=True)
        route = config.add_route(
        config.add_route(
            'name', 'path',
            factory='pyramid.tests.test_config.dummyfactory')
        route = self._assertRoute(config, 'name', 'path')
        self.assertEqual(route.factory, dummyfactory)
    def test_add_route_with_xhr(self):
@@ -2474,9 +2448,8 @@
    def test_add_route_no_pattern_with_path(self):
        config = self._makeOne(autocommit=True)
        route = config.add_route('name', path='path')
        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
@@ -2485,7 +2458,8 @@
    def test_add_route_with_pregenerator(self):
        config = self._makeOne(autocommit=True)
        route = config.add_route('name', 'pattern', pregenerator='123')
        config.add_route('name', 'pattern', pregenerator='123')
        route = self._assertRoute(config, 'name', 'pattern')
        self.assertEqual(route.pregenerator, '123')
    def test_add_route_no_view_with_view_attr(self):
@@ -2554,6 +2528,7 @@
            def __call__(self, *arg, **kw):
                return 'moo'
        config.add_renderer(None, moo)
        config.commit()
        def view(request):
            return 'OK'
        result = config.derive_view(view)
@@ -2572,6 +2547,7 @@
        config = self._makeOne()
        config.add_renderer(None, moo)
        config.add_renderer('foo', foo)
        config.commit()
        result = config.derive_view(view, renderer='foo')
        self.assertFalse(result is view)
        request = self._makeRequest(config)
@@ -2786,81 +2762,6 @@
        finally:
            config.end()
        self.assertTrue('div' in result.body)
    def test_set_authentication_policy_no_authz_policy(self):
        from zope.configuration.config import ConfigurationExecutionError
        config = self._makeOne()
        policy = object()
        config.set_authentication_policy(policy)
        self.assertRaises(ConfigurationExecutionError, config.commit)
    def test_set_authentication_policy_no_authz_policy_autocommit(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_authentication_policy_with_authz_policy(self):
        from pyramid.interfaces import IAuthenticationPolicy
        from pyramid.interfaces import IAuthorizationPolicy
        config = self._makeOne()
        authn_policy = object()
        authz_policy = object()
        config.registry.registerUtility(authz_policy, IAuthorizationPolicy)
        config.set_authentication_policy(authn_policy)
        config.commit()
        self.assertEqual(
            config.registry.getUtility(IAuthenticationPolicy), authn_policy)
    def test_set_authentication_policy_with_authz_policy_autocommit(self):
        from pyramid.interfaces import IAuthenticationPolicy
        from pyramid.interfaces import IAuthorizationPolicy
        config = self._makeOne(autocommit=True)
        authn_policy = object()
        authz_policy = object()
        config.registry.registerUtility(authz_policy, IAuthorizationPolicy)
        config.set_authentication_policy(authn_policy)
        config.commit()
        self.assertEqual(
            config.registry.getUtility(IAuthenticationPolicy), authn_policy)
    def test_set_authorization_policy_no_authn_policy(self):
        from zope.configuration.config import ConfigurationExecutionError
        config = self._makeOne()
        policy = object()
        config.set_authorization_policy(policy)
        self.assertRaises(ConfigurationExecutionError, config.commit)
    def test_set_authorization_policy_no_authn_policy_autocommit(self):
        from pyramid.exceptions import ConfigurationError
        config = self._makeOne(autocommit=True)
        policy = object()
        self.assertRaises(ConfigurationError,
                          config.set_authorization_policy, policy)
    def test_set_authorization_policy_with_authn_policy(self):
        from pyramid.interfaces import IAuthorizationPolicy
        from pyramid.interfaces import IAuthenticationPolicy
        config = self._makeOne()
        authn_policy = object()
        authz_policy = object()
        config.registry.registerUtility(authn_policy, IAuthenticationPolicy)
        config.set_authorization_policy(authz_policy)
        self.assertEqual(
            config.registry.getUtility(IAuthorizationPolicy), authz_policy)
    def test_set_authorization_policy_with_authn_policy_autocommit(self):
        from pyramid.interfaces import IAuthorizationPolicy
        from pyramid.interfaces import IAuthenticationPolicy
        config = self._makeOne(autocommit=True)
        authn_policy = object()
        authz_policy = object()
        config.registry.registerUtility(authn_policy, IAuthenticationPolicy)
        config.set_authorization_policy(authz_policy)
        self.assertEqual(
            config.registry.getUtility(IAuthorizationPolicy), authz_policy)
    def test_set_locale_negotiator(self):
        from pyramid.interfaces import ILocaleNegotiator
@@ -3830,11 +3731,13 @@
        try:
            config.commit()
        except ConfigurationConflictError, why:
            c1, c2, c3, c4 = self._conflictFunctions(why)
            c1, c2, c3, c4, c5, c6 = self._conflictFunctions(why)
            self.assertEqual(c1, 'test_conflict_route_with_view')
            self.assertEqual(c2, 'test_conflict_route_with_view')
            self.assertEqual(c3, 'test_conflict_route_with_view')
            self.assertEqual(c4, 'test_conflict_route_with_view')
            self.assertEqual(c5, 'test_conflict_route_with_view')
            self.assertEqual(c6, 'test_conflict_route_with_view')
        else: # pragma: no cover
            raise AssertionError
        
pyramid/tests/test_config/test_security.py
New file
@@ -0,0 +1,84 @@
import unittest
class ConfiguratorSecurityMethodsTests(unittest.TestCase):
    def _makeOne(self, *arg, **kw):
        from pyramid.config import Configurator
        config = Configurator(*arg, **kw)
        return config
    def test_set_authentication_policy_no_authz_policy(self):
        from zope.configuration.config import ConfigurationExecutionError
        config = self._makeOne()
        policy = object()
        config.set_authentication_policy(policy)
        self.assertRaises(ConfigurationExecutionError, config.commit)
    def test_set_authentication_policy_no_authz_policy_autocommit(self):
        from pyramid.exceptions import ConfigurationError
        config = self._makeOne(autocommit=True)
        policy = object()
        self.assertRaises(ConfigurationError,
                          config.set_authentication_policy, policy)
    def test_set_authentication_policy_with_authz_policy(self):
        from pyramid.interfaces import IAuthenticationPolicy
        from pyramid.interfaces import IAuthorizationPolicy
        config = self._makeOne()
        authn_policy = object()
        authz_policy = object()
        config.registry.registerUtility(authz_policy, IAuthorizationPolicy)
        config.set_authentication_policy(authn_policy)
        config.commit()
        self.assertEqual(
            config.registry.getUtility(IAuthenticationPolicy), authn_policy)
    def test_set_authentication_policy_with_authz_policy_autocommit(self):
        from pyramid.interfaces import IAuthenticationPolicy
        from pyramid.interfaces import IAuthorizationPolicy
        config = self._makeOne(autocommit=True)
        authn_policy = object()
        authz_policy = object()
        config.registry.registerUtility(authz_policy, IAuthorizationPolicy)
        config.set_authentication_policy(authn_policy)
        config.commit()
        self.assertEqual(
            config.registry.getUtility(IAuthenticationPolicy), authn_policy)
    def test_set_authorization_policy_no_authn_policy(self):
        from zope.configuration.config import ConfigurationExecutionError
        config = self._makeOne()
        policy = object()
        config.set_authorization_policy(policy)
        self.assertRaises(ConfigurationExecutionError, config.commit)
    def test_set_authorization_policy_no_authn_policy_autocommit(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_authorization_policy_with_authn_policy(self):
        from pyramid.interfaces import IAuthorizationPolicy
        from pyramid.interfaces import IAuthenticationPolicy
        config = self._makeOne()
        authn_policy = object()
        authz_policy = object()
        config.registry.registerUtility(authn_policy, IAuthenticationPolicy)
        config.set_authorization_policy(authz_policy)
        config.commit()
        self.assertEqual(
            config.registry.getUtility(IAuthorizationPolicy), authz_policy)
    def test_set_authorization_policy_with_authn_policy_autocommit(self):
        from pyramid.interfaces import IAuthorizationPolicy
        from pyramid.interfaces import IAuthenticationPolicy
        config = self._makeOne(autocommit=True)
        authn_policy = object()
        authz_policy = object()
        config.registry.registerUtility(authn_policy, IAuthenticationPolicy)
        config.set_authorization_policy(authz_policy)
        self.assertEqual(
            config.registry.getUtility(IAuthorizationPolicy), authz_policy)
pyramid/tests/test_integration.py
@@ -344,6 +344,71 @@
        res = self.testapp.get('/route_raise_exception4', status=200)
        self.assertTrue('whoa' in res.body)
class TestConflictApp(unittest.TestCase):
    package = 'pyramid.tests.conflictapp'
    def _makeConfig(self):
        from pyramid.config import Configurator
        config = Configurator()
        return config
    def test_autoresolved_view(self):
        config = self._makeConfig()
        config.include(self.package)
        app = config.make_wsgi_app()
        from webtest import TestApp
        self.testapp = TestApp(app)
        res = self.testapp.get('/')
        self.assertTrue('a view' in res.body)
        res = self.testapp.get('/route')
        self.assertTrue('route view' in res.body)
    def test_overridden_autoresolved_view(self):
        from pyramid.response import Response
        config = self._makeConfig()
        config.include(self.package)
        def thisview(request):
            return Response('this view')
        config.add_view(thisview)
        app = config.make_wsgi_app()
        from webtest import TestApp
        self.testapp = TestApp(app)
        res = self.testapp.get('/')
        self.assertTrue('this view' in res.body)
    def test_overridden_route_view(self):
        from pyramid.response import Response
        config = self._makeConfig()
        config.include(self.package)
        def thisview(request):
            return Response('this view')
        config.add_view(thisview, route_name='aroute')
        app = config.make_wsgi_app()
        from webtest import TestApp
        self.testapp = TestApp(app)
        res = self.testapp.get('/route')
        self.assertTrue('this view' in res.body)
    def test_nonoverridden_authorization_policy(self):
        config = self._makeConfig()
        config.include(self.package)
        app = config.make_wsgi_app()
        from webtest import TestApp
        self.testapp = TestApp(app)
        res = self.testapp.get('/protected', status=403)
        self.assertTrue('403 Forbidden' in res)
    def test_overridden_authorization_policy(self):
        config = self._makeConfig()
        config.include(self.package)
        from pyramid.testing import DummySecurityPolicy
        config.set_authorization_policy(DummySecurityPolicy('fred'))
        config.set_authentication_policy(DummySecurityPolicy(permissive=True))
        app = config.make_wsgi_app()
        from webtest import TestApp
        self.testapp = TestApp(app)
        res = self.testapp.get('/protected', status=200)
        self.assertTrue('protected view' in res)
class ImperativeIncludeConfigurationTest(unittest.TestCase):
    def setUp(self):
        from pyramid.config import Configurator
setup.py
@@ -53,7 +53,7 @@
    install_requires.append('simplejson')
    
setup(name='pyramid',
      version='1.2dev',
      version='1.2a1',
      description=('The Pyramid web application development framework, a '
                   'Pylons project'),
      long_description=README + '\n\n' +  CHANGES,