Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/validators/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
19 changes: 18 additions & 1 deletion tests/test_validation_failure.py
Original file line number Diff line number Diff line change
@@ -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"

Expand Down Expand Up @@ -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)