diff options
| author | Chris McDonough <chrism@plope.com> | 2012-08-15 04:05:34 -0400 |
|---|---|---|
| committer | Chris McDonough <chrism@plope.com> | 2012-08-15 04:05:34 -0400 |
| commit | 8ec8e2d23fab2a9eebbb68d0e0b72a6c94c251aa (patch) | |
| tree | 9d07390711651a0d169354d9e9d86397272dc785 | |
| parent | f036f97f00781cf81f8905b6725071f79b31f6c5 (diff) | |
| download | pyramid-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.py | 25 | ||||
| -rw-r--r-- | pyramid/config/views.py | 112 | ||||
| -rw-r--r-- | pyramid/tests/test_config/test_views.py | 74 | ||||
| -rw-r--r-- | pyramid/tests/test_view.py | 6 | ||||
| -rw-r--r-- | pyramid/view.py | 68 |
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() |
