diff --git a/CHANGES.rst b/CHANGES.rst index 3ee928e15..5c16afbcd 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -69,6 +69,9 @@ Unreleased - form.errors is not cached and will update if an error is appended to a field after access. (:pr:`568`) - :class:`~wtforms.validators.NumberRange` correctly handle *not a number* values. (:pr:`505`, :pr:`548`) +- :class:`ValueError` raised by a validator are handled like regular exceptions. validators + need to raise :class:`~wtforms.validators.ValidationError` or + :class:`~wtforms.validators.StopValidation` to make a validation fail (:pr:`445`, :pr:`551`). Version 2.2.1 diff --git a/src/wtforms/fields/core.py b/src/wtforms/fields/core.py index 9afd7958e..3cd2c12a2 100644 --- a/src/wtforms/fields/core.py +++ b/src/wtforms/fields/core.py @@ -11,7 +11,7 @@ from wtforms.compat import izip, text_type from wtforms.i18n import DummyTranslations from wtforms.utils import unset_value -from wtforms.validators import StopValidation +from wtforms.validators import StopValidation, ValidationError __all__ = ( "BooleanField", @@ -246,7 +246,7 @@ def validate(self, form, extra_validators=()): if e.args and e.args[0]: self.errors.append(e.args[0]) stop_validation = True - except ValueError as e: + except ValidationError as e: self.errors.append(e.args[0]) # Run validators @@ -257,7 +257,7 @@ def validate(self, form, extra_validators=()): # Call post_validate try: self.post_validate(form, stop_validation) - except ValueError as e: + except ValidationError as e: self.errors.append(e.args[0]) return len(self.errors) == 0 @@ -277,7 +277,7 @@ def _run_validation_chain(self, form, validators): if e.args and e.args[0]: self.errors.append(e.args[0]) return True - except ValueError as e: + except ValidationError as e: self.errors.append(e.args[0]) return False @@ -553,7 +553,7 @@ def pre_validate(self, form): if self.data == v: break else: - raise ValueError(self.gettext("Not a valid choice")) + raise ValidationError(self.gettext("Not a valid choice")) class SelectMultipleField(SelectField): @@ -591,7 +591,7 @@ def pre_validate(self, form): values = list(c[0] for c in self.choices) for d in self.data: if d not in values: - raise ValueError( + raise ValidationError( self.gettext("'%(value)s' is not a valid choice for this field") % dict(value=d) ) diff --git a/tests/test_fields.py b/tests/test_fields.py index 153202639..c415f3a3b 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -293,9 +293,9 @@ def pre_validate(self, form): def post_validate(self, form, stopped): if self.data == "p": - raise ValueError("Post") + raise validators.ValidationError("Post") elif stopped and self.data == "stop-post": - raise ValueError("Post-stopped") + raise validators.ValidationError("Post-stopped") class TestPrePostValidation: @@ -1002,9 +1002,9 @@ def test_min_max_entries(self): def test_validators(self): def validator(form, field): if field.data and field.data[0] == "fail": - raise ValueError("fail") + raise validators.ValidationError("fail") elif len(field.data) > 2: - raise ValueError("too many") + raise validators.ValidationError("too many") F = make_form(a=FieldList(self.t, validators=[validator])) diff --git a/tests/test_validators.py b/tests/test_validators.py index a54a0c1ed..f7e0cc779 100644 --- a/tests/test_validators.py +++ b/tests/test_validators.py @@ -3,7 +3,7 @@ import pytest import re -from wtforms import Label +from wtforms import Label, Form, StringField from wtforms.compat import text_type from wtforms.validators import ( StopValidation, @@ -670,3 +670,17 @@ def test_regexp_message(dummy_form, dummy_field, grab_error_message): validator = regexp("^a", message="foo") dummy_field.data = "f" assert grab_error_message(validator, dummy_form, dummy_field) == "foo" + + +@pytest.mark.parametrize("exc", [IndexError, ZeroDivisionError, ValueError]) +def test_raise_exceptions(exc): + def validate(form, field): + raise exc + + class F(Form): + field = StringField(validators=[validate]) + + f = F() + + with pytest.raises(exc): + f.validate()