From 53222eec541205c7a9ebc529e14a025e56fb1e9c Mon Sep 17 00:00:00 2001 From: Adam Holmberg Date: Thu, 15 Oct 2015 15:30:16 -0500 Subject: [PATCH 1/2] Make bind ignore extra keys in dict values PYTHON-178 --- cassandra/query.py | 15 +++++++-------- tests/unit/test_parameter_binding.py | 6 ++++-- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/cassandra/query.py b/cassandra/query.py index e1f8000894..679f8ef7e1 100644 --- a/cassandra/query.py +++ b/cassandra/query.py @@ -486,23 +486,24 @@ def bind(self, values): * short sequences will be extended to match bind parameters with UNSET_VALUE * names may be omitted from a dict with UNSET_VALUE implied. + .. versionchanged:: 3.0.0 + + method will not throw if extra keys are present in bound dict (PYTHON-178) """ if values is None: values = () proto_version = self.prepared_statement.protocol_version col_meta = self.prepared_statement.column_metadata - col_meta_len = len(col_meta) - value_len = len(values) # special case for binding dicts if isinstance(values, dict): - unbound_values = values.copy() + values_dict = values values = [] # sort values accordingly for col in col_meta: try: - values.append(unbound_values.pop(col.name)) + values.append(values_dict[col.name]) except KeyError: if proto_version >= 4: values.append(UNSET_VALUE) @@ -511,10 +512,8 @@ def bind(self, values): 'Column name `%s` not found in bound dict.' % (col.name)) - value_len = len(values) - - if unbound_values: - raise ValueError("Unexpected arguments provided to bind(): %s" % unbound_values.keys()) + value_len = len(values) + col_meta_len = len(col_meta) if value_len > col_meta_len: raise ValueError( diff --git a/tests/unit/test_parameter_binding.py b/tests/unit/test_parameter_binding.py index a53a9f6dfb..044e032554 100644 --- a/tests/unit/test_parameter_binding.py +++ b/tests/unit/test_parameter_binding.py @@ -25,6 +25,7 @@ from cassandra.util import OrderedDict from six.moves import xrange +import six class ParamBindingTest(unittest.TestCase): @@ -147,8 +148,9 @@ def test_dict_missing_routing_key(self): def test_missing_value(self): self.assertRaises(KeyError, self.bound.bind, {'rk0': 0, 'rk1': 0, 'ck0': 0}) - def test_dict_extra_value(self): - self.assertRaises(ValueError, self.bound.bind, {'rk0': 0, 'rk1': 0, 'ck0': 0, 'v0': 0, 'should_not_be_here': 123}) + def test_extra_value(self): + self.bound.bind({'rk0': 0, 'rk1': 0, 'ck0': 0, 'v0': 0, 'should_not_be_here': 123}) # okay to have extra keys in dict + self.assertEqual(self.bound.values, [six.b('\x00') * 4] * 4) # four encoded zeros self.assertRaises(ValueError, self.bound.bind, (0, 0, 0, 0, 123)) def test_values_none(self): From 55ebfdccc1275a687fdca653d333316b50cb20f5 Mon Sep 17 00:00:00 2001 From: Adam Holmberg Date: Thu, 15 Oct 2015 16:17:50 -0500 Subject: [PATCH 2/2] Update integration tests allowing unbound keys in dict PYTHON-178 --- tests/integration/standard/test_prepared_statements.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/integration/standard/test_prepared_statements.py b/tests/integration/standard/test_prepared_statements.py index 7218045cb5..b0be36147c 100644 --- a/tests/integration/standard/test_prepared_statements.py +++ b/tests/integration/standard/test_prepared_statements.py @@ -173,7 +173,7 @@ def _run_too_many_bind_values(self, session): self.assertIsInstance(prepared, PreparedStatement) self.assertRaises(ValueError, prepared.bind, (1, 2)) - def test_too_many_bind_values_dicts(self): + def test_imprecise_bind_values_dicts(self): """ Ensure an error is thrown when attempting to bind the wrong values with dict bindings @@ -189,16 +189,16 @@ def test_too_many_bind_values_dicts(self): self.assertIsInstance(prepared, PreparedStatement) - # too many values - self.assertRaises(ValueError, prepared.bind, {'k': 1, 'v': 2, 'v2': 3}) + # too many values is ok - others are ignored + prepared.bind({'k': 1, 'v': 2, 'v2': 3}) # right number, but one does not belong if PROTOCOL_VERSION < 4: # pre v4, the driver bails with key error when 'v' is found missing self.assertRaises(KeyError, prepared.bind, {'k': 1, 'v2': 3}) else: - # post v4, the driver uses UNSET_VALUE for 'v' and bails when 'v2' is unbound - self.assertRaises(ValueError, prepared.bind, {'k': 1, 'v2': 3}) + # post v4, the driver uses UNSET_VALUE for 'v' and 'v2' is ignored + prepared.bind({'k': 1, 'v2': 3}) # also catch too few variables with dicts self.assertIsInstance(prepared, PreparedStatement)