diff --git a/src/validators/utils.py b/src/validators/utils.py index 28d3c857..94ce7226 100644 --- a/src/validators/utils.py +++ b/src/validators/utils.py @@ -91,7 +91,7 @@ def wrapper(*args: Any, **kwargs: Any): if func(*args, **kwargs) else ValidationError(func, _func_args_as_dict(func, *args, **kwargs)) ) - except (ValueError, TypeError, UnicodeError) as exp: + except (ValueError, TypeError, UnicodeError, AttributeError) as exp: if raise_validation_error: raise ValidationError( func, _func_args_as_dict(func, *args, **kwargs), str(exp) diff --git a/tests/test_validation_failure.py b/tests/test_validation_failure.py index 67502000..86e747b8 100644 --- a/tests/test_validation_failure.py +++ b/tests/test_validation_failure.py @@ -1,7 +1,11 @@ """Test validation Failure.""" +# external +import pytest + # local -from validators import between +from validators import between, cron, email, hostname, uuid +from validators.utils import ValidationError failed_obj_repr = "ValidationError(func=between" @@ -31,3 +35,16 @@ def test_arguments_as_properties(self): assert self.is_in_between.__dict__["value"] == 3 assert self.is_in_between.__dict__["min_val"] == 4 assert self.is_in_between.__dict__["max_val"] == 5 + + +@pytest.mark.parametrize("validator", [uuid, email, hostname, cron]) +@pytest.mark.parametrize("value", [123, 1.5, True, ["x"], {"a": 1}]) +def test_returns_validation_error_on_non_string_input(validator, value): + """Wrong-typed input returns ValidationError, not a leaked exception. + + These validators reach for string methods (e.g. ``.replace``/``.count``/ + ``.strip``) on the value, which raises ``AttributeError`` for non-strings. + The decorator must convert that into a ``ValidationError`` like every other + invalid input, rather than letting it escape. + """ + assert isinstance(validator(value), ValidationError)