From 5fc0d36724a6197c8c0106e846d8e78e1219b1fe Mon Sep 17 00:00:00 2001 From: Chris McDonough <chrism@plope.com> Date: Wed, 31 Jul 2013 02:30:07 +0200 Subject: [PATCH] first cut at indicating that only some predicates are negatable --- pyramid/config/util.py | 8 +++ docs/narr/viewconfig.rst | 18 +++++++-- pyramid/config/predicates.py | 8 ++++ pyramid/config/__init__.py | 12 +++++ pyramid/tests/test_config/test_util.py | 26 +++++++++--- 5 files changed, 59 insertions(+), 13 deletions(-) diff --git a/docs/narr/viewconfig.rst b/docs/narr/viewconfig.rst index fd32293..9292548 100644 --- a/docs/narr/viewconfig.rst +++ b/docs/narr/viewconfig.rst @@ -560,8 +560,8 @@ Inverting Predicate Values ~~~~~~~~~~~~~~~~~~~~~~~~~~ -You can invert the meaning of any predicate value by wrapping it in a call to -:class:`pyramid.config.not_`. +You can invert the meaning of a supported predicate value by wrapping it in a +call to :class:`pyramid.config.not_`. .. code-block:: python :linenos: @@ -571,11 +571,21 @@ config.add_view( 'mypackage.views.my_view', route_name='ok', - request_method=not_('POST') + header=not_('X-Foo') ) The above example will ensure that the view is called if the request method -is *not* ``POST``, at least if no other view is more specific. +does not have the ``X-Foo`` header, at least if no other view is more specific. + +A predicate must be *negatable* to use ``not_`` against its value. The current +set of built-in negatable predicates are: ``path_info``, ``request_param``, +``header``, ``containment``, ``request_type``, ``match_param``, +``physical_path``, and ``effective_principals``. If you try to use ``not_`` +against a non-negatable predicate, an error will be raised at startup time. + +You can make sure a custom view predicate added via +:meth:`pyramid.config.Configurator.add_view_predicate` is negatable by adding a +``negatable`` attribute to it that is True. This technique of wrapping a predicate value in ``not_`` can be used anywhere predicate values are accepted: diff --git a/pyramid/config/__init__.py b/pyramid/config/__init__.py index d52ee0e..698c943 100644 --- a/pyramid/config/__init__.py +++ b/pyramid/config/__init__.py @@ -1152,7 +1152,17 @@ # and "i" exist for sorting purposes after conflict resolution. ainfo = (order, i, action) - discriminator = undefer(action['discriminator']) + try: + discriminator = undefer(action['discriminator']) + except: + t, v, tb = sys.exc_info() + try: + reraise(ConfigurationExecutionError, + ConfigurationExecutionError(t, v, action['info']), + tb) + finally: + del t, v, tb + action['discriminator'] = discriminator if discriminator is None: diff --git a/pyramid/config/predicates.py b/pyramid/config/predicates.py index c8f66e8..6caa875 100644 --- a/pyramid/config/predicates.py +++ b/pyramid/config/predicates.py @@ -48,6 +48,7 @@ return request.method in self.val class PathInfoPredicate(object): + negatable = True def __init__(self, val, config): self.orig = val try: @@ -65,6 +66,7 @@ return self.val.match(request.upath_info) is not None class RequestParamPredicate(object): + negatable = True def __init__(self, val, config): val = as_sorted_tuple(val) reqs = [] @@ -95,6 +97,7 @@ return True class HeaderPredicate(object): + negatable = True def __init__(self, val, config): name = val v = None @@ -137,6 +140,7 @@ return self.val in request.accept class ContainmentPredicate(object): + negatable = True def __init__(self, val, config): self.val = config.maybe_dotted(val) @@ -150,6 +154,7 @@ return find_interface(ctx, self.val) is not None class RequestTypePredicate(object): + negatable = True def __init__(self, val, config): self.val = val @@ -162,6 +167,7 @@ return self.val.providedBy(request) class MatchParamPredicate(object): + negatable = True def __init__(self, val, config): val = as_sorted_tuple(val) self.val = val @@ -258,6 +264,7 @@ return True class PhysicalPathPredicate(object): + negatable = True def __init__(self, val, config): if is_nonstr_iter(val): self.val = tuple(val) @@ -276,6 +283,7 @@ return False class EffectivePrincipalsPredicate(object): + negatable = True def __init__(self, val, config): if is_nonstr_iter(val): self.val = set(val) diff --git a/pyramid/config/util.py b/pyramid/config/util.py index a98e71c..a0cbeca 100644 --- a/pyramid/config/util.py +++ b/pyramid/config/util.py @@ -110,7 +110,13 @@ notted = True pred = predicate_factory(realval, config) if notted: - pred = Notted(pred) + if getattr(pred, 'negatable', False): + pred = Notted(pred) + else: + raise ConfigurationError( + '%s is not a negatable predicate' % (pred.text(),), + ) + hashes = pred.phash() if not is_nonstr_iter(hashes): hashes = [hashes] diff --git a/pyramid/tests/test_config/test_util.py b/pyramid/tests/test_config/test_util.py index f6cd414..c813cc5 100644 --- a/pyramid/tests/test_config/test_util.py +++ b/pyramid/tests/test_config/test_util.py @@ -365,19 +365,31 @@ from pyramid.exceptions import ConfigurationError self.assertRaises(ConfigurationError, self._callFUT, unknown=1) - def test_notted(self): + def test_notted_configerror(self): + from pyramid.exceptions import ConfigurationError from pyramid.config import not_ - from pyramid.testing import DummyRequest - request = DummyRequest() - _, predicates, _ = self._callFUT( + self.assertRaises(ConfigurationError, self._callFUT, xhr='xhr', request_method=not_('POST'), header=not_('header'), ) + + def test_notted_ok(self): + from pyramid.config import not_ + _, predicates, _ = self._callFUT( + xhr='xhr', + path_info=not_('/path_info'), + header=not_('header'), + ) + from pyramid.testing import DummyRequest + request = DummyRequest() + request.upath_info = request.path_info + request.is_xhr = False self.assertEqual(predicates[0].text(), 'xhr = True') - self.assertEqual(predicates[1].text(), - "!request_method = POST") - self.assertEqual(predicates[2].text(), '!header header') + self.assertEqual(predicates[1].text(), '!path_info = /path_info') + self.assertEqual(predicates[2].text(), + "!header header") + self.assertEqual(predicates[0](None, request), False) self.assertEqual(predicates[1](None, request), True) self.assertEqual(predicates[2](None, request), True) -- Gitblit v1.9.3