summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris McDonough <chrism@plope.com>2012-08-15 04:05:34 -0400
committerChris McDonough <chrism@plope.com>2012-08-15 04:05:34 -0400
commit8ec8e2d23fab2a9eebbb68d0e0b72a6c94c251aa (patch)
tree9d07390711651a0d169354d9e9d86397272dc785
parentf036f97f00781cf81f8905b6725071f79b31f6c5 (diff)
downloadpyramid-8ec8e2d23fab2a9eebbb68d0e0b72a6c94c251aa.tar.gz
pyramid-8ec8e2d23fab2a9eebbb68d0e0b72a6c94c251aa.tar.bz2
pyramid-8ec8e2d23fab2a9eebbb68d0e0b72a6c94c251aa.zip
- Anonymous predicate keyword list for all add-view-ish methods
and decorators as well as add_route is now named "**predicates" rather than "**other_predicates". - Fix bug in add_view and add_route where pvals was not a copy of other_predicates (caused discriminators to not match up when computed elsewhere). - Various formatting nits fixed. - add_notfound_view and add_forbidden_view now accept **predicates. - view_config, notfound_view_config, forbidden_view_config, and view_defaults now accept **predicates. Their implementations were changed to avoid repetition.
-rw-r--r--pyramid/config/routes.py25
-rw-r--r--pyramid/config/views.py112
-rw-r--r--pyramid/tests/test_config/test_views.py74
-rw-r--r--pyramid/tests/test_view.py6
-rw-r--r--pyramid/view.py68
5 files changed, 192 insertions, 93 deletions
diff --git a/pyramid/config/routes.py b/pyramid/config/routes.py
index 8f7f3612b..18fe39e45 100644
--- a/pyramid/config/routes.py
+++ b/pyramid/config/routes.py
@@ -20,7 +20,7 @@ from pyramid.config.util import (
PredicateList,
)
-from pyramid.config import predicates
+import pyramid.config.predicates
class RoutesConfiguratorMixin(object):
@action_method
@@ -49,7 +49,7 @@ class RoutesConfiguratorMixin(object):
path=None,
pregenerator=None,
static=False,
- **other_predicates):
+ **predicates):
""" 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`
@@ -259,7 +259,7 @@ class RoutesConfiguratorMixin(object):
:ref:`custom_route_predicates` for more information about
``info``.
- other_predicates
+ predicates
Pass a key/value pair here to use a third-party predicate
registered via
@@ -420,7 +420,7 @@ class RoutesConfiguratorMixin(object):
request_iface, IRouteRequest, name=name)
def register_connect():
- pvals = other_predicates
+ pvals = predicates.copy()
pvals.update(
dict(
xhr=xhr,
@@ -513,15 +513,16 @@ class RoutesConfiguratorMixin(object):
order=PHASE1_CONFIG)
def add_default_route_predicates(self):
+ p = pyramid.config.predicates
for (name, factory) in (
- ('xhr', predicates.XHRPredicate),
- ('request_method', predicates.RequestMethodPredicate),
- ('path_info', predicates.PathInfoPredicate),
- ('request_param', predicates.RequestParamPredicate),
- ('header', predicates.HeaderPredicate),
- ('accept', predicates.AcceptPredicate),
- ('custom', predicates.CustomPredicate),
- ('traverse', predicates.TraversePredicate),
+ ('xhr', p.XHRPredicate),
+ ('request_method', p.RequestMethodPredicate),
+ ('path_info', p.PathInfoPredicate),
+ ('request_param', p.RequestParamPredicate),
+ ('header', p.HeaderPredicate),
+ ('accept', p.AcceptPredicate),
+ ('custom', p.CustomPredicate),
+ ('traverse', p.TraversePredicate),
):
self.add_route_predicate(name, factory)
diff --git a/pyramid/config/views.py b/pyramid/config/views.py
index 6fb598847..1c4e20dd6 100644
--- a/pyramid/config/views.py
+++ b/pyramid/config/views.py
@@ -73,7 +73,7 @@ from pyramid.util import (
object_description,
)
-from pyramid.config import predicates
+import pyramid.config.predicates
from pyramid.config.util import (
DEFAULT_PHASH,
@@ -664,7 +664,7 @@ class ViewsConfiguratorMixin(object):
mapper=None,
http_cache=None,
match_param=None,
- **other_predicates):
+ **predicates):
""" Add a :term:`view configuration` to the current
configuration state. Arguments to ``add_view`` are broken
down below into *predicate* arguments and *non-predicate*
@@ -1002,7 +1002,7 @@ class ViewsConfiguratorMixin(object):
``True`` or ``False`` after doing arbitrary evaluation of
the context and/or the request.
- other_predicates
+ predicates
Pass a key/value pair here to use a third-party predicate
registered via
@@ -1048,7 +1048,7 @@ class ViewsConfiguratorMixin(object):
registry = self.registry)
introspectables = []
- pvals = other_predicates
+ pvals = predicates.copy()
pvals.update(
dict(
xhr=xhr,
@@ -1102,7 +1102,7 @@ class ViewsConfiguratorMixin(object):
decorator=decorator,
)
)
- view_intr.update(**other_predicates)
+ view_intr.update(**predicates)
introspectables.append(view_intr)
predlist = self.view_predlist
@@ -1313,7 +1313,7 @@ class ViewsConfiguratorMixin(object):
""" Adds a view predicate factory. The associated view predicate can
later be named as a keyword argument to
:meth:`pyramid.config.Configurator.add_view` in the
- ``other_predicates`` anonyous keyword argument dictionary.
+ ``predicates`` anonyous keyword argument dictionary.
``name`` should be the name of the predicate. It must be a valid
Python identifier (it will be used as a keyword argument to
@@ -1345,17 +1345,18 @@ class ViewsConfiguratorMixin(object):
order=PHASE1_CONFIG) # must be registered before views added
def add_default_view_predicates(self):
+ p = pyramid.config.predicates
for (name, factory) in (
- ('xhr', predicates.XHRPredicate),
- ('request_method', predicates.RequestMethodPredicate),
- ('path_info', predicates.PathInfoPredicate),
- ('request_param', predicates.RequestParamPredicate),
- ('header', predicates.HeaderPredicate),
- ('accept', predicates.AcceptPredicate),
- ('containment', predicates.ContainmentPredicate),
- ('request_type', predicates.RequestTypePredicate),
- ('match_param', predicates.MatchParamPredicate),
- ('custom', predicates.CustomPredicate),
+ ('xhr', p.XHRPredicate),
+ ('request_method', p.RequestMethodPredicate),
+ ('path_info', p.PathInfoPredicate),
+ ('request_param', p.RequestParamPredicate),
+ ('header', p.HeaderPredicate),
+ ('accept', p.AcceptPredicate),
+ ('containment', p.ContainmentPredicate),
+ ('request_type', p.RequestTypePredicate),
+ ('match_param', p.MatchParamPredicate),
+ ('custom', p.CustomPredicate),
):
self.add_view_predicate(name, factory)
@@ -1476,11 +1477,26 @@ class ViewsConfiguratorMixin(object):
@action_method
def add_forbidden_view(
- self, view=None, attr=None, renderer=None, wrapper=None,
- route_name=None, request_type=None, request_method=None,
- request_param=None, containment=None, xhr=None, accept=None,
- header=None, path_info=None, custom_predicates=(), decorator=None,
- mapper=None, match_param=None):
+ self,
+ view=None,
+ attr=None,
+ renderer=None,
+ wrapper=None,
+ route_name=None,
+ request_type=None,
+ request_method=None,
+ request_param=None,
+ containment=None,
+ xhr=None,
+ accept=None,
+ header=None,
+ path_info=None,
+ custom_predicates=(),
+ decorator=None,
+ mapper=None,
+ match_param=None,
+ **predicates
+ ):
""" Add a forbidden view to the current configuration state. The
view will be called when Pyramid or application code raises a
:exc:`pyramid.httpexceptions.HTTPForbidden` exception and the set of
@@ -1497,12 +1513,23 @@ class ViewsConfiguratorMixin(object):
All arguments have the same meaning as
:meth:`pyramid.config.Configurator.add_view` and each predicate
argument restricts the set of circumstances under which this notfound
- view will be invoked.
+ view will be invoked. Unlike
+ :meth:`pyramid.config.Configurator.add_view`, this method will raise
+ an exception if passed ``name``, ``permission``, ``context``,
+ ``for_``, or ``http_cache`` keyword arguments. These argument values
+ make no sense in the context of a forbidden view.
.. note::
This method is new as of Pyramid 1.3.
"""
+ for arg in ('name', 'permission', 'context', 'for_', 'http_cache'):
+ if arg in predicates:
+ raise ConfigurationError(
+ '%s may not be used as an argument to add_forbidden_view'
+ % arg
+ )
+
settings = dict(
view=view,
context=HTTPForbidden,
@@ -1524,17 +1551,34 @@ class ViewsConfiguratorMixin(object):
attr=attr,
renderer=renderer,
)
+ settings.update(predicates)
return self.add_view(**settings)
set_forbidden_view = add_forbidden_view # deprecated sorta-bw-compat alias
@action_method
def add_notfound_view(
- self, view=None, attr=None, renderer=None, wrapper=None,
- route_name=None, request_type=None, request_method=None,
- request_param=None, containment=None, xhr=None, accept=None,
- header=None, path_info=None, custom_predicates=(), decorator=None,
- mapper=None, match_param=None, append_slash=False):
+ self,
+ view=None,
+ attr=None,
+ renderer=None,
+ wrapper=None,
+ route_name=None,
+ request_type=None,
+ request_method=None,
+ request_param=None,
+ containment=None,
+ xhr=None,
+ accept=None,
+ header=None,
+ path_info=None,
+ custom_predicates=(),
+ decorator=None,
+ mapper=None,
+ match_param=None,
+ append_slash=False,
+ **predicates
+ ):
""" Add a default notfound view to the current configuration state.
The view will be called when Pyramid or application code raises an
:exc:`pyramid.httpexceptions.HTTPForbidden` exception (e.g. when a
@@ -1550,7 +1594,11 @@ class ViewsConfiguratorMixin(object):
All arguments except ``append_slash`` have the same meaning as
:meth:`pyramid.config.Configurator.add_view` and each predicate
argument restricts the set of circumstances under which this notfound
- view will be invoked.
+ view will be invoked. Unlike
+ :meth:`pyramid.config.Configurator.add_view`, this method will raise
+ an exception if passed ``name``, ``permission``, ``context``,
+ ``for_``, or ``http_cache`` keyword arguments. These argument values
+ make no sense in the context of a notfound view.
If ``append_slash`` is ``True``, when this notfound view is invoked,
and the current path info does not end in a slash, the notfound logic
@@ -1564,6 +1612,13 @@ class ViewsConfiguratorMixin(object):
This method is new as of Pyramid 1.3.
"""
+ for arg in ('name', 'permission', 'context', 'for_', 'http_cache'):
+ if arg in predicates:
+ raise ConfigurationError(
+ '%s may not be used as an argument to add_notfound_view'
+ % arg
+ )
+
settings = dict(
view=view,
context=HTTPNotFound,
@@ -1583,6 +1638,7 @@ class ViewsConfiguratorMixin(object):
route_name=route_name,
permission=NO_PERMISSION_REQUIRED,
)
+ settings.update(predicates)
if append_slash:
view = self._derive_view(view, attr=attr, renderer=renderer)
view = AppendSlashNotFoundViewFactory(view)
diff --git a/pyramid/tests/test_config/test_views.py b/pyramid/tests/test_config/test_views.py
index f2daf0c34..38f60d79b 100644
--- a/pyramid/tests/test_config/test_views.py
+++ b/pyramid/tests/test_config/test_views.py
@@ -1701,6 +1701,38 @@ class TestViewsConfigurationMixin(unittest.TestCase):
result = view(None, request)
self.assertEqual(result, 'OK')
+ def test_add_forbidden_view_allows_other_predicates(self):
+ from pyramid.renderers import null_renderer
+ config = self._makeOne(autocommit=True)
+ # doesnt blow up
+ config.add_view_predicate('dummy', DummyPredicate)
+ config.add_forbidden_view(renderer=null_renderer, dummy='abc')
+
+ def test_add_forbidden_view_disallows_name(self):
+ config = self._makeOne(autocommit=True)
+ self.assertRaises(ConfigurationError,
+ config.add_forbidden_view, name='foo')
+
+ def test_add_forbidden_view_disallows_permission(self):
+ config = self._makeOne(autocommit=True)
+ self.assertRaises(ConfigurationError,
+ config.add_forbidden_view, permission='foo')
+
+ def test_add_forbidden_view_disallows_context(self):
+ config = self._makeOne(autocommit=True)
+ self.assertRaises(ConfigurationError,
+ config.add_forbidden_view, context='foo')
+
+ def test_add_forbidden_view_disallows_for_(self):
+ config = self._makeOne(autocommit=True)
+ self.assertRaises(ConfigurationError,
+ config.add_forbidden_view, for_='foo')
+
+ def test_add_forbidden_view_disallows_http_cache(self):
+ config = self._makeOne(autocommit=True)
+ self.assertRaises(ConfigurationError,
+ config.add_forbidden_view, http_cache='foo')
+
def test_add_notfound_view(self):
from pyramid.renderers import null_renderer
from zope.interface import implementedBy
@@ -1716,6 +1748,38 @@ class TestViewsConfigurationMixin(unittest.TestCase):
result = view(None, request)
self.assertEqual(result, (None, request))
+ def test_add_notfound_view_allows_other_predicates(self):
+ from pyramid.renderers import null_renderer
+ config = self._makeOne(autocommit=True)
+ # doesnt blow up
+ config.add_view_predicate('dummy', DummyPredicate)
+ config.add_notfound_view(renderer=null_renderer, dummy='abc')
+
+ def test_add_notfound_view_disallows_name(self):
+ config = self._makeOne(autocommit=True)
+ self.assertRaises(ConfigurationError,
+ config.add_notfound_view, name='foo')
+
+ def test_add_notfound_view_disallows_permission(self):
+ config = self._makeOne(autocommit=True)
+ self.assertRaises(ConfigurationError,
+ config.add_notfound_view, permission='foo')
+
+ def test_add_notfound_view_disallows_context(self):
+ config = self._makeOne(autocommit=True)
+ self.assertRaises(ConfigurationError,
+ config.add_notfound_view, context='foo')
+
+ def test_add_notfound_view_disallows_for_(self):
+ config = self._makeOne(autocommit=True)
+ self.assertRaises(ConfigurationError,
+ config.add_notfound_view, for_='foo')
+
+ def test_add_notfound_view_disallows_http_cache(self):
+ config = self._makeOne(autocommit=True)
+ self.assertRaises(ConfigurationError,
+ config.add_notfound_view, http_cache='foo')
+
def test_add_notfound_view_append_slash(self):
from pyramid.response import Response
from pyramid.renderers import null_renderer
@@ -3973,3 +4037,13 @@ class DummyViewDefaultsClass(object):
pass
def __call__(self):
return 'OK'
+
+class DummyPredicate(object):
+ def __init__(self, val, config):
+ self.val = val
+
+ def text(self):
+ return 'dummy'
+
+ phash = text
+
diff --git a/pyramid/tests/test_view.py b/pyramid/tests/test_view.py
index ee4994172..f63e17bd8 100644
--- a/pyramid/tests/test_view.py
+++ b/pyramid/tests/test_view.py
@@ -87,7 +87,7 @@ class Test_notfound_view_config(BaseTest, unittest.TestCase):
config = call_venusian(venusian)
settings = config.settings
self.assertEqual(len(settings), 1)
- self.assertEqual(len(settings[0]), 5)
+ self.assertEqual(len(settings[0]), 4)
self.assertEqual(settings[0]['venusian'], venusian)
self.assertEqual(settings[0]['view'], None) # comes from call_venusian
self.assertEqual(settings[0]['attr'], 'view')
@@ -368,6 +368,10 @@ class TestViewConfigDecorator(unittest.TestCase):
self.assertEqual(decorator.mapper, 'mapper')
self.assertEqual(decorator.decorator, 'decorator')
self.assertEqual(decorator.match_param, 'match_param')
+
+ def test_create_with_other_predicates(self):
+ decorator = self._makeOne(foo=1)
+ self.assertEqual(decorator.foo, 1)
def test_call_function(self):
decorator = self._makeOne()
diff --git a/pyramid/view.py b/pyramid/view.py
index 1df0849c0..12a2efde6 100644
--- a/pyramid/view.py
+++ b/pyramid/view.py
@@ -138,15 +138,6 @@ def render_view(context, request, name='', secure=True):
return None
return ''.join(iterable)
-class _default(object):
- def __nonzero__(self):
- return False
- __bool__ = __nonzero__
- def __repr__(self): # pragma: no cover
- return '(default)'
-
-default = _default()
-
class view_config(object):
""" A function, class or method :term:`decorator` which allows a
developer to create view registrations nearer to a :term:`view
@@ -174,12 +165,12 @@ class view_config(object):
backwards compatibility purposes, as the name
:class:`pyramid.view.bfg_view`.
- The following arguments are supported to
+ The following keyword arguments are supported to
:class:`pyramid.view.view_config`: ``context``, ``permission``, ``name``,
``request_type``, ``route_name``, ``request_method``, ``request_param``,
``containment``, ``xhr``, ``accept``, ``header``, ``path_info``,
``custom_predicates``, ``decorator``, ``mapper``, ``http_cache``,
- and ``match_param``.
+ ``match_param``, and ``predicates``.
The meanings of these arguments are the same as the arguments passed to
:meth:`pyramid.config.Configurator.add_view`. If any argument is left
@@ -190,21 +181,11 @@ class view_config(object):
"""
venusian = venusian # for testing injection
- def __init__(self, name=default, request_type=default, for_=default,
- permission=default, route_name=default,
- request_method=default, request_param=default,
- containment=default, attr=default, renderer=default,
- wrapper=default, xhr=default, accept=default,
- header=default, path_info=default,
- custom_predicates=default, context=default,
- decorator=default, mapper=default, http_cache=default,
- match_param=default):
- L = dict(locals()) # See issue #635 for dict() rationale
- if (context is not default) or (for_ is not default):
- L['context'] = context or for_
- for k, v in L.items():
- if k not in ('self', 'L') and v is not default:
- setattr(self, k, v)
+ def __init__(self, **settings):
+ if 'for_' in settings:
+ if settings.get('context') is None:
+ settings['context'] = settings['for_']
+ self.__dict__.update(settings)
def __call__(self, wrapped):
settings = self.__dict__.copy()
@@ -326,7 +307,7 @@ class notfound_view_config(object):
The notfound_view_config constructor accepts most of the same arguments
as the constructor of :class:`pyramid.view.view_config`. It can be used
in the same places, and behaves in largely the same way, except it always
- registers a not found exception view instead of a "normal" view.
+ registers a not found exception view instead of a 'normal' view.
Example:
@@ -360,17 +341,8 @@ class notfound_view_config(object):
venusian = venusian
- def __init__(self, request_type=default, request_method=default,
- route_name=default, request_param=default, attr=default,
- renderer=default, containment=default, wrapper=default,
- xhr=default, accept=default, header=default,
- path_info=default, custom_predicates=default,
- decorator=default, mapper=default, match_param=default,
- append_slash=False):
- L = dict(locals()) # See issue #635 for dict() rationale
- for k, v in L.items():
- if k not in ('self', 'L') and v is not default:
- self.__dict__[k] = v
+ def __init__(self, **settings):
+ self.__dict__.update(settings)
def __call__(self, wrapped):
settings = self.__dict__.copy()
@@ -400,7 +372,7 @@ class forbidden_view_config(object):
The forbidden_view_config constructor accepts most of the same arguments
as the constructor of :class:`pyramid.view.view_config`. It can be used
in the same places, and behaves in largely the same way, except it always
- registers a forbidden exception view instead of a "normal" view.
+ registers a forbidden exception view instead of a 'normal' view.
Example:
@@ -413,9 +385,9 @@ class forbidden_view_config(object):
def notfound(request):
return Response('You are not allowed', status='401 Unauthorized')
- All have the same meaning as :meth:`pyramid.view.view_config` and each
- predicate argument restricts the set of circumstances under which this
- notfound view will be invoked.
+ All arguments passed to this function have the same meaning as
+ :meth:`pyramid.view.view_config` and each predicate argument restricts
+ the set of circumstances under which this notfound view will be invoked.
See :ref:`changing_the_forbidden_view` for detailed usage information.
@@ -426,16 +398,8 @@ class forbidden_view_config(object):
venusian = venusian
- def __init__(self, request_type=default, request_method=default,
- route_name=default, request_param=default, attr=default,
- renderer=default, containment=default, wrapper=default,
- xhr=default, accept=default, header=default,
- path_info=default, custom_predicates=default,
- decorator=default, mapper=default, match_param=default):
- L = dict(locals()) # See issue #635 for dict() rationale
- for k, v in L.items():
- if k not in ('self', 'L') and v is not default:
- self.__dict__[k] = v
+ def __init__(self, **settings):
+ self.__dict__.update(settings)
def __call__(self, wrapped):
settings = self.__dict__.copy()