From 718a440916739fd433c8edda3e93a672b42aa371 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Tue, 23 Aug 2011 02:21:40 -0500 Subject: Added support for a ``match_param`` view predicate. --- pyramid/config/util.py | 19 ++++++++++-- pyramid/config/views.py | 26 +++++++++++++--- pyramid/tests/test_config/test_util.py | 55 +++++++++++++++++++++++++++++++--- 3 files changed, 90 insertions(+), 10 deletions(-) diff --git a/pyramid/config/util.py b/pyramid/config/util.py index e6be528bf..c631da44c 100644 --- a/pyramid/config/util.py +++ b/pyramid/config/util.py @@ -35,8 +35,8 @@ def action_method(wrapped): return wrapper def make_predicates(xhr=None, request_method=None, path_info=None, - request_param=None, header=None, accept=None, - containment=None, request_type=None, + request_param=None, match_param=None, header=None, + accept=None, containment=None, request_type=None, traverse=None, custom=()): # PREDICATES @@ -184,6 +184,21 @@ def make_predicates(xhr=None, request_method=None, path_info=None, predicates.append(request_type_predicate) h.update('request_type:%r' % hash(request_type)) + if match_param is not None: + if isinstance(match_param, basestring): + match_param, match_param_val = match_param.split('=', 1) + match_param = {match_param: match_param_val} + text = "match_param %s" % match_param + def match_param_predicate(context, request): + for k, v in match_param.iteritems(): + if request.matchdict.get(k) != v: + return False + return True + match_param_predicate.__text__ = text + weights.append(1 << 9) + predicates.append(match_param_predicate) + h.update('match_param:%r' % match_param) + if traverse is not None: # ``traverse`` can only be used as a *route* "predicate"; it # adds 'traverse' to the matchdict if it's specified in the diff --git a/pyramid/config/views.py b/pyramid/config/views.py index 2d39524ac..8c7106736 100644 --- a/pyramid/config/views.py +++ b/pyramid/config/views.py @@ -531,7 +531,8 @@ class ViewsConfiguratorMixin(object): request_param=None, containment=None, attr=None, renderer=None, wrapper=None, xhr=False, accept=None, header=None, path_info=None, custom_predicates=(), - context=None, decorator=None, mapper=None, http_cache=None): + context=None, decorator=None, mapper=None, http_cache=None, + match_param=None): """ Add a :term:`view configuration` to the current configuration state. Arguments to ``add_view`` are broken down below into *predicate* arguments and *non-predicate* @@ -775,6 +776,23 @@ class ViewsConfiguratorMixin(object): the value must match the right hand side of the expression (``123``) for the view to "match" the current request. + match_param + + .. note:: This feature is new as of :app:`Pyramid` 1.2. + + This param may be either a single string of the format "key=value" + or a dict of key/value pairs. + + A view declaration with this argument ensures that the view will + only be called when the :term:`request` has key/value pairs in + the ``matchdict`` that equal those supplied in the predicate. + e.g. ``match_param="action=edit" would require the ``action`` + parameter in the ``matchdict`` match the right hande side of the + expression (``edit``) for the view to "match" the current request. + + If the ``match_param`` is a dict, every key/value pair must match + for the predicate to pass. + containment This value should be a Python class or :term:`interface` (or a @@ -832,7 +850,6 @@ class ViewsConfiguratorMixin(object): variable. If the regex matches, this predicate will be ``True``. - custom_predicates This value should be a sequence of references to custom @@ -885,6 +902,7 @@ class ViewsConfiguratorMixin(object): 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, ) @@ -896,7 +914,7 @@ class ViewsConfiguratorMixin(object): request_method=request_method, path_info=path_info, request_param=request_param, header=header, accept=accept, containment=containment, request_type=request_type, - custom=custom_predicates) + match_param=match_param, custom=custom_predicates) if context is None: context = for_ @@ -1040,7 +1058,7 @@ class ViewsConfiguratorMixin(object): discriminator = [ 'view', context, name, request_type, IView, containment, - request_param, request_method, route_name, attr, + request_param, request_method, match_param, route_name, attr, xhr, accept, header, path_info] discriminator.extend(sorted(custom_predicates)) discriminator = tuple(discriminator) diff --git a/pyramid/tests/test_config/test_util.py b/pyramid/tests/test_config/test_util.py index 1e43fc6f1..a7119eced 100644 --- a/pyramid/tests/test_config/test_util.py +++ b/pyramid/tests/test_config/test_util.py @@ -16,6 +16,7 @@ class Test__make_predicates(unittest.TestCase): request_method='request_method', path_info='path_info', request_param='param', + match_param='foo=bar', header='header', accept='accept', containment='containment', @@ -27,6 +28,7 @@ class Test__make_predicates(unittest.TestCase): request_method='request_method', path_info='path_info', request_param='param', + match_param='foo=bar', header='header', accept='accept', containment='containment', @@ -38,6 +40,7 @@ class Test__make_predicates(unittest.TestCase): request_method='request_method', path_info='path_info', request_param='param', + match_param='foo=bar', header='header', accept='accept', containment='containment', @@ -48,6 +51,7 @@ class Test__make_predicates(unittest.TestCase): request_method='request_method', path_info='path_info', request_param='param', + match_param='foo=bar', header='header', accept='accept', containment='containment', @@ -57,6 +61,7 @@ class Test__make_predicates(unittest.TestCase): request_method='request_method', path_info='path_info', request_param='param', + match_param='foo=bar', header='header', accept='accept', ) @@ -65,6 +70,7 @@ class Test__make_predicates(unittest.TestCase): request_method='request_method', path_info='path_info', request_param='param', + match_param='foo=bar', header='header', ) order7, _, _ = self._callFUT( @@ -72,20 +78,27 @@ class Test__make_predicates(unittest.TestCase): request_method='request_method', path_info='path_info', request_param='param', + match_param='foo=bar', ) order8, _, _ = self._callFUT( xhr='xhr', request_method='request_method', path_info='path_info', + request_param='param', ) order9, _, _ = self._callFUT( xhr='xhr', request_method='request_method', + path_info='path_info', ) order10, _, _ = self._callFUT( xhr='xhr', + request_method='request_method', ) order11, _, _ = self._callFUT( + xhr='xhr', + ) + order12, _, _ = self._callFUT( ) self.assertEqual(order1, order2) self.assertTrue(order3 > order2) @@ -97,6 +110,7 @@ class Test__make_predicates(unittest.TestCase): self.assertTrue(order9 > order8) self.assertTrue(order10 > order9) self.assertTrue(order11 > order10) + self.assertTrue(order12 > order10) def test_ordering_importance_of_predicates(self): order1, _, _ = self._callFUT( @@ -124,6 +138,9 @@ class Test__make_predicates(unittest.TestCase): request_type='request_type', ) order9, _, _ = self._callFUT( + match_param='foo=bar', + ) + order10, _, _ = self._callFUT( custom=(DummyCustomPredicate(),), ) self.assertTrue(order1 > order2) @@ -134,6 +151,7 @@ class Test__make_predicates(unittest.TestCase): self.assertTrue(order6 > order7) self.assertTrue(order7 > order8) self.assertTrue(order8 > order9) + self.assertTrue(order9 > order10) def test_ordering_importance_and_number(self): order1, _, _ = self._callFUT( @@ -221,7 +239,8 @@ class Test__make_predicates(unittest.TestCase): request_type='request_type', custom=(DummyCustomPredicate(), DummyCustomPredicate.classmethod_predicate, - DummyCustomPredicate.classmethod_predicate_no_text)) + DummyCustomPredicate.classmethod_predicate_no_text), + match_param='foo=bar') self.assertEqual(predicates[0].__text__, 'xhr = True') self.assertEqual(predicates[1].__text__, 'request method = request_method') @@ -231,9 +250,34 @@ class Test__make_predicates(unittest.TestCase): self.assertEqual(predicates[5].__text__, 'accept = accept') self.assertEqual(predicates[6].__text__, 'containment = containment') self.assertEqual(predicates[7].__text__, 'request_type = request_type') - self.assertEqual(predicates[8].__text__, 'custom predicate') - self.assertEqual(predicates[9].__text__, 'classmethod predicate') - self.assertEqual(predicates[10].__text__, '') + self.assertEqual(predicates[8].__text__, "match_param {'foo': 'bar'}") + self.assertEqual(predicates[9].__text__, 'custom predicate') + self.assertEqual(predicates[10].__text__, 'classmethod predicate') + self.assertEqual(predicates[11].__text__, '') + + def test_match_param_from_string(self): + _, predicates, _ = self._callFUT(match_param='foo=bar') + request = DummyRequest() + request.matchdict = {'foo':'bar', 'baz':'bum'} + self.assertTrue(predicates[0](Dummy(), request)) + + def test_match_param_from_string_fails(self): + _, predicates, _ = self._callFUT(match_param='foo=bar') + request = DummyRequest() + request.matchdict = {'foo':'bum', 'baz':'bum'} + self.assertFalse(predicates[0](Dummy(), request)) + + def test_match_param_from_dict(self): + _, predicates, _ = self._callFUT(match_param={'foo':'bar','baz':'bum'}) + request = DummyRequest() + request.matchdict = {'foo':'bar', 'baz':'bum'} + self.assertTrue(predicates[0](Dummy(), request)) + + def test_match_param_from_dict_fails(self): + _, predicates, _ = self._callFUT(match_param={'foo':'bar','baz':'bum'}) + request = DummyRequest() + request.matchdict = {'foo':'bar', 'baz':'foo'} + self.assertFalse(predicates[0](Dummy(), request)) class DummyCustomPredicate(object): def __init__(self): @@ -246,6 +290,9 @@ class DummyCustomPredicate(object): @classmethod def classmethod_predicate_no_text(*args): pass # pragma: no cover +class Dummy: + pass + class DummyRequest: subpath = () matchdict = None -- cgit v1.2.3