From 675a2144e3ea6bbe7199d37ec05730496d93a64e Mon Sep 17 00:00:00 2001 From: Ambrose Ling Date: Sun, 8 Dec 2024 12:07:48 -0500 Subject: [PATCH 1/2] Consistent validation with complex number to float type coercion --- src/input/input_python.rs | 12 ++++++++++++ tests/validators/test_float.py | 18 ++++++++++++++++-- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/input/input_python.rs b/src/input/input_python.rs index 1eaa69415..6adea5b2c 100644 --- a/src/input/input_python.rs +++ b/src/input/input_python.rs @@ -291,6 +291,18 @@ impl<'py> Input<'py> for Bound<'py, PyAny> { return Ok(ValidationMatch::exact(EitherFloat::Py(float.clone()))); } + if self.is_instance_of::() { + if !strict { + if let Ok(real) = self.getattr(intern!(self.py(), "real")) { + if let Ok(float) = real.extract::() { + return Ok(ValidationMatch::lax(EitherFloat::F64(float))); + } + } + } else { + return Err(ValError::new(ErrorTypeDefaults::FloatType, self)); + } + } + if !strict { if let Some(s) = maybe_as_string(self, ErrorTypeDefaults::FloatParsing)? { // checking for bytes and string is fast, so do this before isinstance(float) diff --git a/tests/validators/test_float.py b/tests/validators/test_float.py index c397f453c..4da61941b 100644 --- a/tests/validators/test_float.py +++ b/tests/validators/test_float.py @@ -29,6 +29,10 @@ (False, 0), ('wrong', Err('Input should be a valid number, unable to parse string as a number [type=float_parsing')), ([1, 2], Err('Input should be a valid number [type=float_type, input_value=[1, 2], input_type=list]')), + (1 + 0j, 1.0), + (2.5 + 0j, 2.5), + (3 + 1j, 3.0), + (1j, 0), ], ) def test_float(py_and_json: PyAndJson, input_value, expected): @@ -37,7 +41,10 @@ def test_float(py_and_json: PyAndJson, input_value, expected): with pytest.raises(ValidationError, match=re.escape(expected.message)): v.validate_test(input_value) else: - output = v.validate_test(input_value) + if isinstance(input_value, complex): + output = v.validate_python(input_value) + else: + output = v.validate_test(input_value) assert output == expected assert isinstance(output, float) @@ -52,6 +59,10 @@ def test_float(py_and_json: PyAndJson, input_value, expected): (42.5, 42.5), ('42', Err("Input should be a valid number [type=float_type, input_value='42', input_type=str]")), (True, Err('Input should be a valid number [type=float_type, input_value=True, input_type=bool]')), + (1 + 0j, Err('Input should be a valid number [type=float_type, input_value=(1+0j), input_type=complex]')), + (2.5 + 0j, Err('Input should be a valid number [type=float_type, input_value=(2.5+0j), input_type=complex]')), + (3 + 1j, Err('Input should be a valid number [type=float_type, input_value=(3+1j), input_type=complex]')), + (1j, Err('Input should be a valid number [type=float_type, input_value=1j, input_type=complex]')), ], ids=repr, ) @@ -59,7 +70,10 @@ def test_float_strict(py_and_json: PyAndJson, input_value, expected): v = py_and_json({'type': 'float', 'strict': True}) if isinstance(expected, Err): with pytest.raises(ValidationError, match=re.escape(expected.message)): - v.validate_test(input_value) + if isinstance(input_value, complex): + v.validate_python(input_value) + else: + v.validate_test(input_value) else: output = v.validate_test(input_value) assert output == expected From 8e5b5ffafc2df9f618d094a558ddcf48802163b1 Mon Sep 17 00:00:00 2001 From: Ambrose Ling Date: Sun, 8 Dec 2024 13:34:51 -0500 Subject: [PATCH 2/2] Improved patch coverage --- src/input/input_python.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/input/input_python.rs b/src/input/input_python.rs index 6adea5b2c..b07405af2 100644 --- a/src/input/input_python.rs +++ b/src/input/input_python.rs @@ -292,15 +292,12 @@ impl<'py> Input<'py> for Bound<'py, PyAny> { } if self.is_instance_of::() { - if !strict { - if let Ok(real) = self.getattr(intern!(self.py(), "real")) { - if let Ok(float) = real.extract::() { - return Ok(ValidationMatch::lax(EitherFloat::F64(float))); - } - } - } else { + if strict { return Err(ValError::new(ErrorTypeDefaults::FloatType, self)); } + let real = self.getattr(intern!(self.py(), "real")).unwrap(); + let float = real.extract::().unwrap(); + return Ok(ValidationMatch::lax(EitherFloat::F64(float))); } if !strict {