diff --git a/src/input/input_json.rs b/src/input/input_json.rs index a6b6c24d9..81d40098d 100644 --- a/src/input/input_json.rs +++ b/src/input/input_json.rs @@ -62,8 +62,6 @@ impl<'a> Input<'a> for JsonInput { fn lax_str(&'a self) -> ValResult> { match self { JsonInput::String(s) => Ok(s.as_str().into()), - JsonInput::Int(int) => Ok(int.to_string().into()), - JsonInput::Float(float) => Ok(float.to_string().into()), _ => Err(ValError::new(ErrorKind::StrType, self)), } } diff --git a/src/input/input_python.rs b/src/input/input_python.rs index 455d0167e..1b8e25d6e 100644 --- a/src/input/input_python.rs +++ b/src/input/input_python.rs @@ -4,8 +4,8 @@ use std::str::from_utf8; use pyo3::exceptions::PyAttributeError; use pyo3::prelude::*; use pyo3::types::{ - PyBool, PyByteArray, PyBytes, PyDate, PyDateTime, PyDelta, PyDict, PyFrozenSet, PyInt, PyList, PyMapping, - PySequence, PySet, PyString, PyTime, PyTuple, PyType, + PyBool, PyByteArray, PyBytes, PyDate, PyDateTime, PyDelta, PyDict, PyFrozenSet, PyList, PyMapping, PySequence, + PySet, PyString, PyTime, PyTuple, PyType, }; use pyo3::{intern, AsPyPointer}; @@ -113,16 +113,6 @@ impl<'a> Input<'a> for PyAny { Err(_) => return Err(ValError::new(ErrorKind::StrUnicode, self)), }; Ok(str.into()) - } else if self.cast_as::().is_ok() { - // do this before int and float parsing as `False` is cast to `0` and we don't want False to - // be returned as a string - Err(ValError::new(ErrorKind::StrType, self)) - } else if let Ok(int) = self.cast_as::() { - let int = i64::extract(int)?; - Ok(int.to_string().into()) - } else if let Ok(float) = f64::extract(self) { - // don't cast_as here so Decimals are covered - internally f64:extract uses PyFloat_AsDouble - Ok(float.to_string().into()) } else { Err(ValError::new(ErrorKind::StrType, self)) } diff --git a/tests/benchmarks/test_complete_benchmark.py b/tests/benchmarks/test_complete_benchmark.py index 8a22c5f22..64c170a28 100644 --- a/tests/benchmarks/test_complete_benchmark.py +++ b/tests/benchmarks/test_complete_benchmark.py @@ -91,7 +91,7 @@ def test_complete_invalid(): lax_validator = SchemaValidator(lax_schema) with pytest.raises(ValidationError) as exc_info: lax_validator.validate_python(input_data_wrong()) - assert len(exc_info.value.errors()) == 736 + assert len(exc_info.value.errors()) == 738 model = pydantic_model() if model is None: diff --git a/tests/benchmarks/test_micro_benchmarks.py b/tests/benchmarks/test_micro_benchmarks.py index 096dd3144..0ad8d6902 100644 --- a/tests/benchmarks/test_micro_benchmarks.py +++ b/tests/benchmarks/test_micro_benchmarks.py @@ -415,7 +415,7 @@ def test_frozenset_of_ints_core(benchmark): benchmark(v.validate_python, frozenset_of_ints) -dict_of_ints_data = ({i: i for i in range(1000)}, {i: str(i) for i in range(1000)}) +dict_of_ints_data = ({str(i): i for i in range(1000)}, {str(i): str(i) for i in range(1000)}) @skip_pydantic diff --git a/tests/test_json.py b/tests/test_json.py index a9c078da3..227c8f88e 100644 --- a/tests/test_json.py +++ b/tests/test_json.py @@ -29,10 +29,12 @@ def test_null(): def test_str(): - assert SchemaValidator({'type': 'str'}).validate_json('"foobar"') == 'foobar' - assert SchemaValidator({'type': 'str'}).validate_json('123') == '123' + s = SchemaValidator({'type': 'str'}) + assert s.validate_json('"foobar"') == 'foobar' with pytest.raises(ValidationError, match=r'Input should be a valid string \[kind=str_type,'): - SchemaValidator({'type': 'str'}).validate_json('false') + s.validate_json('false') + with pytest.raises(ValidationError, match=r'Input should be a valid string \[kind=str_type,'): + s.validate_json('123') @pytest.mark.parametrize( @@ -53,8 +55,8 @@ def test_model(): ) # language=json - input_str = '{"field_a": 123, "field_b": 1}' - assert v.validate_json(input_str) == {'field_a': '123', 'field_b': 1} + input_str = '{"field_a": "abc", "field_b": 1}' + assert v.validate_json(input_str) == {'field_a': 'abc', 'field_b': 1} def test_float_no_remainder(): diff --git a/tests/validators/test_dict.py b/tests/validators/test_dict.py index cbb48168c..331e55d9f 100644 --- a/tests/validators/test_dict.py +++ b/tests/validators/test_dict.py @@ -24,8 +24,8 @@ def test_dict(py_and_json: PyAndJson): @pytest.mark.parametrize( 'input_value,expected', [ - ({'1': 1, '2': 2}, {'1': '1', '2': '2'}), - (OrderedDict(a=1, b=2), {'a': '1', 'b': '2'}), + ({'1': b'1', '2': b'2'}, {'1': '1', '2': '2'}), + (OrderedDict(a=b'1', b='2'), {'a': '1', 'b': '2'}), ({}, {}), ('foobar', Err("Input should be a valid dictionary [kind=dict_type, input_value='foobar', input_type=str]")), ([], Err('Input should be a valid dictionary [kind=dict_type,')), diff --git a/tests/validators/test_function.py b/tests/validators/test_function.py index 0e7263e7e..81e1320e7 100644 --- a/tests/validators/test_function.py +++ b/tests/validators/test_function.py @@ -168,7 +168,7 @@ def f(input_value, **kwargs): } ) - assert v.validate_python({'field_a': '123', 'field_b': 321}) == {'field_a': 123, 'field_b': '321 Changed'} + assert v.validate_python({'field_a': '123', 'field_b': b'321'}) == {'field_a': 123, 'field_b': '321 Changed'} assert f_kwargs == {'data': {'field_a': 123}, 'config': None, 'context': None} @@ -192,7 +192,7 @@ def f(input_value, **kwargs): {'config_choose_priority': 2}, ) - assert v.validate_python({'test_field': 321}) == {'test_field': '321 Changed'} + assert v.validate_python({'test_field': b'321'}) == {'test_field': '321 Changed'} assert f_kwargs == {'data': {}, 'config': {'config_choose_priority': 2}, 'context': None} @@ -206,7 +206,7 @@ def f(input_value, **kwargs): v = SchemaValidator({'type': 'function', 'mode': 'after', 'function': f, 'schema': {'type': 'str'}}) - assert v.validate_python(123) == '123 Changed' + assert v.validate_python(b'abc') == 'abc Changed' assert f_kwargs == {'data': None, 'config': None, 'context': None} @@ -241,7 +241,7 @@ def f(input_value, **kwargs): m = {'field_a': 'test', 'more': 'foobar'} assert v.validate_python({'field_a': 'test'}) == m - assert v.validate_assignment('field_a', 456, m) == {'field_a': '456', 'more': 'foobar'} + assert v.validate_assignment('field_a', b'abc', m) == {'field_a': 'abc', 'more': 'foobar'} def test_function_wrong_sig(): @@ -279,9 +279,9 @@ def __validate__(cls, input_value, **kwargs): assert isinstance(f, Foobar) assert f.a == 'foofoo' - f = v.validate_python(1) + f = v.validate_python(b'a') assert isinstance(f, Foobar) - assert f.a == '11' + assert f.a == 'aa' with pytest.raises(ValidationError) as exc_info: v.validate_python(True) diff --git a/tests/validators/test_string.py b/tests/validators/test_string.py index 039b88082..06e34dbc1 100644 --- a/tests/validators/test_string.py +++ b/tests/validators/test_string.py @@ -13,8 +13,8 @@ 'input_value,expected', [ ('foobar', 'foobar'), - (123, '123'), - (123.456, '123.456'), + (123, Err('Input should be a valid string [kind=str_type, input_value=123, input_type=int]')), + (123.456, Err('Input should be a valid string [kind=str_type, input_value=123.456, input_type=int]')), (False, Err('Input should be a valid string [kind=str_type')), (True, Err('Input should be a valid string [kind=str_type')), ([], Err('Input should be a valid string [kind=str_type, input_value=[], input_type=list]')), @@ -46,8 +46,11 @@ def test_str(py_and_json: PyAndJson, input_value, expected): ), # null bytes are very annoying, but we can't really block them here (b'\x00', '\x00'), - (123, '123'), - (Decimal('123'), '123'), + (123, Err('Input should be a valid string [kind=str_type, input_value=123, input_type=int]')), + ( + Decimal('123'), + Err("Input should be a valid string [kind=str_type, input_value=Decimal('123'), input_type=int]"), + ), ], ) def test_str_not_json(input_value, expected): @@ -62,9 +65,13 @@ def test_str_not_json(input_value, expected): @pytest.mark.parametrize( 'kwargs,input_value,expected', [ - ({}, 123, '123'), + ({}, b'abc', 'abc'), ({'strict': True}, 'Foobar', 'Foobar'), - ({'strict': True}, 123, Err('Input should be a valid string [kind=str_type, input_value=123, input_type=int]')), + ( + {'strict': True}, + b'abc', + Err('Input should be a valid string [kind=str_type, input_value=123, input_type=int]'), + ), ({'to_upper': True}, 'fooBar', 'FOOBAR'), ({'to_lower': True}, 'fooBar', 'foobar'), ({'strip_whitespace': True}, ' foobar ', 'foobar'), diff --git a/tests/validators/test_tuple.py b/tests/validators/test_tuple.py index d230fbc43..87b116e85 100644 --- a/tests/validators/test_tuple.py +++ b/tests/validators/test_tuple.py @@ -51,8 +51,8 @@ def test_any_no_copy(): 'mode,items,input_value,expected', [ ('variable', {'type': 'int'}, (1, 2, '33'), (1, 2, 33)), - ('variable', {'type': 'str'}, (1, 2, '33'), ('1', '2', '33')), - ('positional', [{'type': 'int'}, {'type': 'str'}, {'type': 'float'}], (1, 2, 33), (1, '2', 33.0)), + ('variable', {'type': 'str'}, (b'1', b'2', '33'), ('1', '2', '33')), + ('positional', [{'type': 'int'}, {'type': 'str'}, {'type': 'float'}], (1, b'a', 33), (1, 'a', 33.0)), ], ) def test_tuple_strict_passes_with_tuple(mode, items, input_value, expected): @@ -227,7 +227,7 @@ def test_union_tuple_list(input_value, expected): [ ((1, 2, 3), (1, 2, 3)), (('a', 'b', 'c'), ('a', 'b', 'c')), - (('a', 1, 'c'), ('a', '1', 'c')), + (('a', b'a', 'c'), ('a', 'a', 'c')), ( [5], Err( @@ -251,6 +251,7 @@ def test_union_tuple_list(input_value, expected): ), ), ], + ids=repr, ) def test_union_tuple_var_len(input_value, expected): v = SchemaValidator( @@ -276,7 +277,6 @@ def test_union_tuple_var_len(input_value, expected): [ ((1, 2, 3), (1, 2, 3)), (('a', 'b', 'c'), ('a', 'b', 'c')), - (('a', 1, 'c'), ('a', '1', 'c')), ( [5, '1', 1], Err( @@ -298,6 +298,7 @@ def test_union_tuple_var_len(input_value, expected): ), ), ], + ids=repr, ) def test_union_tuple_fix_len(input_value, expected): v = SchemaValidator( @@ -349,10 +350,10 @@ def test_tuple_fix_extra(): def test_tuple_fix_extra_any(): v = SchemaValidator({'type': 'tuple', 'mode': 'positional', 'items_schema': ['str'], 'extra_schema': 'any'}) - assert v.validate_python([1]) == ('1',) - assert v.validate_python([1, 2]) == ('1', 2) - assert v.validate_python((1, 2)) == ('1', 2) - assert v.validate_python([1, 2, b'3']) == ('1', 2, b'3') + assert v.validate_python([b'1']) == ('1',) + assert v.validate_python([b'1', 2]) == ('1', 2) + assert v.validate_python((b'1', 2)) == ('1', 2) + assert v.validate_python([b'1', 2, b'3']) == ('1', 2, b'3') with pytest.raises(ValidationError) as exc_info: v.validate_python([]) assert exc_info.value.errors() == [{'kind': 'missing', 'loc': [0], 'message': 'Field required', 'input_value': []}] diff --git a/tests/validators/test_typed_dict.py b/tests/validators/test_typed_dict.py index ead66361d..3a7221f2e 100644 --- a/tests/validators/test_typed_dict.py +++ b/tests/validators/test_typed_dict.py @@ -46,7 +46,7 @@ def test_simple(): } ) - assert v.validate_python({'field_a': 123, 'field_b': 1}) == {'field_a': '123', 'field_b': 1} + assert v.validate_python({'field_a': b'abc', 'field_b': 1}) == {'field_a': 'abc', 'field_b': 1} def test_strict(): @@ -77,9 +77,9 @@ def test_with_default(): } ) - assert v.validate_python({'field_a': 123}) == ({'field_a': '123', 'field_b': 666}, {'field_a'}) - assert v.validate_python({'field_a': 123, 'field_b': 1}) == ( - {'field_a': '123', 'field_b': 1}, + assert v.validate_python({'field_a': b'abc'}) == ({'field_a': 'abc', 'field_b': 666}, {'field_a'}) + assert v.validate_python({'field_a': b'abc', 'field_b': 1}) == ( + {'field_a': 'abc', 'field_b': 1}, {'field_b', 'field_a'}, ) @@ -92,13 +92,13 @@ def test_missing_error(): } ) with pytest.raises(ValidationError) as exc_info: - v.validate_python({'field_a': 123}) + v.validate_python({'field_a': b'abc'}) assert ( str(exc_info.value) == """\ 1 validation error for typed-dict field_b - Field required [kind=missing, input_value={'field_a': 123}, input_type=dict]""" + Field required [kind=missing, input_value={'field_a': b'abc'}, input_type=dict]""" ) @@ -141,7 +141,7 @@ def test_ignore_extra(): } ) - assert v.validate_python({'field_a': 123, 'field_b': 1, 'field_c': 123}) == ( + assert v.validate_python({'field_a': b'123', 'field_b': 1, 'field_c': 123}) == ( {'field_a': '123', 'field_b': 1}, {'field_b', 'field_a'}, ) @@ -158,7 +158,7 @@ def test_forbid_extra(): ) with pytest.raises(ValidationError) as exc_info: - v.validate_python({'field_a': 123, 'field_b': 1}) + v.validate_python({'field_a': 'abc', 'field_b': 1}) assert exc_info.value.errors() == [ {'kind': 'extra_forbidden', 'loc': ['field_b'], 'message': 'Extra inputs are not permitted', 'input_value': 1} @@ -175,8 +175,8 @@ def test_allow_extra(): } ) - assert v.validate_python({'field_a': 123, 'field_b': (1, 2)}) == ( - {'field_a': '123', 'field_b': (1, 2)}, + assert v.validate_python({'field_a': b'abc', 'field_b': (1, 2)}) == ( + {'field_a': 'abc', 'field_b': (1, 2)}, {'field_a', 'field_b'}, ) @@ -238,7 +238,7 @@ def test_validate_assignment(): assert v.validate_python({'field_a': 'test'}) == ({'field_a': 'test'}, {'field_a'}) - assert v.validate_assignment('field_a', 456, {'field_a': 'test'}) == ({'field_a': '456'}, {'field_a'}) + assert v.validate_assignment('field_a', b'abc', {'field_a': 'test'}) == ({'field_a': 'abc'}, {'field_a'}) def test_validate_assignment_functions():