[predicates] ExpectedValuePredicate now accepts a dict parameter
``match_form_params`` and ``match_kwargs`` benefit from that.
For instance, the following statements are valid::
__select__ = match_form_params('vid', 'subvid')
__select__ = match_form_params(vid='list', subvid='incontext')
__select__ = match_form_params(vid=('list', 'tsearch'))
In the latter cases, not only the parameters (``vid``/``subvid``)
must be in the request form but their corresponding value must also match
the expected values (or one of possible choices if `values` is a sequence).
closes #5484070
--- a/predicates.py Thu Jun 18 18:26:02 2015 +0200
+++ b/predicates.py Wed Jul 01 21:00:13 2015 +0200
@@ -394,7 +394,7 @@
"""
def __init__(self, *expected, **kwargs):
assert expected, self
- if len(expected) == 1 and isinstance(expected[0], set):
+ if len(expected) == 1 and isinstance(expected[0], (set, dict)):
self.expected = expected[0]
else:
self.expected = frozenset(expected)
@@ -409,7 +409,21 @@
def __call__(self, cls, req, **kwargs):
values = self._values_set(cls, req, **kwargs)
- matching = len(values & self.expected)
+ if isinstance(values, dict):
+ if isinstance(self.expected, dict):
+ matching = 0
+ for key, expected_value in self.expected.items():
+ if key in values:
+ if (isinstance(expected_value, (list, tuple, frozenset, set))
+ and values[key] in expected_value):
+ matching += 1
+ elif values[key] == expected_value:
+ matching += 1
+ if isinstance(self.expected, (set, frozenset)):
+ values = frozenset(values)
+ matching = len(values & self.expected)
+ else:
+ matching = len(values & self.expected)
if self.once_is_enough:
return matching
if matching == len(self.expected):
@@ -438,7 +452,7 @@
"""
def _values_set(self, cls, req, **kwargs):
- return frozenset(kwargs)
+ return kwargs
class appobject_selectable(Predicate):
@@ -1435,8 +1449,23 @@
in which case a single matching parameter is enough.
"""
+ def __init__(self, *expected, **kwargs):
+ """override default __init__ to allow either named or positional
+ parameters.
+ """
+ if kwargs and expected:
+ raise ValueError("match_form_params() can't be called with both "
+ "positional and named arguments")
+ if expected:
+ if len(expected) == 1 and not isinstance(expected[0], basestring):
+ raise ValueError("match_form_params() positional arguments "
+ "must be strings")
+ super(match_form_params, self).__init__(*expected)
+ else:
+ super(match_form_params, self).__init__(kwargs)
+
def _values_set(self, cls, req, **kwargs):
- return frozenset(req.form)
+ return req.form
class match_http_method(ExpectedValuePredicate):
--- a/test/unittest_predicates.py Thu Jun 18 18:26:02 2015 +0200
+++ b/test/unittest_predicates.py Wed Jul 01 21:00:13 2015 +0200
@@ -27,7 +27,7 @@
from cubicweb.devtools.testlib import CubicWebTC
from cubicweb.predicates import (is_instance, adaptable, match_kwargs, match_user_groups,
multi_lines_rset, score_entity, is_in_state,
- rql_condition, relation_possible)
+ rql_condition, relation_possible, match_form_params)
from cubicweb.selectors import on_transition # XXX on_transition is deprecated
from cubicweb.view import EntityAdapter
from cubicweb.web import action
@@ -391,6 +391,102 @@
rset = req.execute('Any X WHERE X is IN(CWGroup, CWUser)')
self.assertTrue(selector(None, req, rset=rset))
+
+class MatchFormParamsTC(CubicWebTC):
+ """tests for match_form_params predicate"""
+
+ def test_keyonly_match(self):
+ """test standard usage: ``match_form_params('param1', 'param2')``
+
+ ``param1`` and ``param2`` must be specified in request's form.
+ """
+ web_request = self.admin_access.web_request
+ vid_selector = match_form_params('vid')
+ vid_subvid_selector = match_form_params('vid', 'subvid')
+ # no parameter => KO,KO
+ with web_request() as req:
+ self.assertEqual(vid_selector(None, req), 0)
+ self.assertEqual(vid_subvid_selector(None, req), 0)
+ # one expected parameter found => OK,KO
+ with web_request(vid='foo') as req:
+ self.assertEqual(vid_selector(None, req), 1)
+ self.assertEqual(vid_subvid_selector(None, req), 0)
+ # all expected parameters found => OK,OK
+ with web_request(vid='foo', subvid='bar') as req:
+ self.assertEqual(vid_selector(None, req), 1)
+ self.assertEqual(vid_subvid_selector(None, req), 2)
+
+ def test_keyvalue_match_one_parameter(self):
+ """test dict usage: ``match_form_params(param1=value1)``
+
+ ``param1`` must be specified in the request's form and its value
+ must be ``value1``.
+ """
+ web_request = self.admin_access.web_request
+ # test both positional and named parameters
+ vid_selector = match_form_params(vid='foo')
+ # no parameter => should fail
+ with web_request() as req:
+ self.assertEqual(vid_selector(None, req), 0)
+ # expected parameter found with expected value => OK
+ with web_request(vid='foo', subvid='bar') as req:
+ self.assertEqual(vid_selector(None, req), 1)
+ # expected parameter found but value is incorrect => KO
+ with web_request(vid='bar') as req:
+ self.assertEqual(vid_selector(None, req), 0)
+
+ def test_keyvalue_match_two_parameters(self):
+ """test dict usage: ``match_form_params(param1=value1, param2=value2)``
+
+ ``param1`` and ``param2`` must be specified in the request's form and
+ their respective value must be ``value1`` and ``value2``.
+ """
+ web_request = self.admin_access.web_request
+ vid_subvid_selector = match_form_params(vid='list', subvid='tsearch')
+ # missing one expected parameter => KO
+ with web_request(vid='list') as req:
+ self.assertEqual(vid_subvid_selector(None, req), 0)
+ # expected parameters found but values are incorrect => KO
+ with web_request(vid='list', subvid='foo') as req:
+ self.assertEqual(vid_subvid_selector(None, req), 0)
+ # expected parameters found and values are correct => OK
+ with web_request(vid='list', subvid='tsearch') as req:
+ self.assertEqual(vid_subvid_selector(None, req), 2)
+
+ def test_keyvalue_multiple_match(self):
+ """test dict usage with multiple values
+
+ i.e. as in ``match_form_params(param1=('value1', 'value2'))``
+
+ ``param1`` must be specified in the request's form and its value
+ must be either ``value1`` or ``value2``.
+ """
+ web_request = self.admin_access.web_request
+ vid_subvid_selector = match_form_params(vid='list', subvid=('tsearch', 'listitem'))
+ # expected parameters found and values correct => OK
+ with web_request(vid='list', subvid='tsearch') as req:
+ self.assertEqual(vid_subvid_selector(None, req), 2)
+ with web_request(vid='list', subvid='listitem') as req:
+ self.assertEqual(vid_subvid_selector(None, req), 2)
+ # expected parameters found but values are incorrect => OK
+ with web_request(vid='list', subvid='foo') as req:
+ self.assertEqual(vid_subvid_selector(None, req), 0)
+
+ def test_invalid_calls(self):
+ """checks invalid calls raise a ValueError"""
+ # mixing named and positional arguments should fail
+ with self.assertRaises(ValueError) as cm:
+ match_form_params('list', x='1', y='2')
+ self.assertEqual(str(cm.exception),
+ "match_form_params() can't be called with both "
+ "positional and named arguments")
+ # using a dict as first and unique argument should fail
+ with self.assertRaises(ValueError) as cm:
+ match_form_params({'x': 1})
+ self.assertEqual(str(cm.exception),
+ "match_form_params() positional arguments must be strings")
+
+
if __name__ == '__main__':
unittest_main()