diff --git a/CHANGES.rst b/CHANGES.rst index 31a745b9..4c5d97ea 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -28,16 +28,20 @@ Unreleased - HTML5 :class:`~fields.html5.IntegerField` and :class:`~fields.html5.RangeInput` don't render the ``step="1"`` attribute by default. (`#343`_) +- Added a ``check_validators`` method to :class:`~fields.Field` which checks + if the given validators are both callable, and not classes (`#298`_, `#410`_) .. _#238: https://github.com/wtforms/wtforms/issues/238 .. _#257: https://github.com/wtforms/wtforms/issues/257 .. _#266: https://github.com/wtforms/wtforms/pull/266 .. _#288: https://github.com/wtforms/wtforms/pull/288 .. _#289: https://github.com/wtforms/wtforms/issues/289 +.. _#298: https://github.com/wtforms/wtforms/issues/298 .. _#343: https://github.com/wtforms/wtforms/pull/343 .. _#395: https://github.com/wtforms/wtforms/pull/395 .. _#407: https://github.com/wtforms/wtforms/pull/407 -.. _#475: https://github.com/wtforms/wtforms/pull/475/ +.. _#410: https://github.com/wtforms/wtforms/pull/410 +.. _#475: https://github.com/wtforms/wtforms/pull/475 .. _#463: https://github.com/wtforms/wtforms/pull/463 .. _#523: https://github.com/wtforms/wtforms/pull/523 .. _#524: https://github.com/wtforms/wtforms/pull/524 diff --git a/tests/fields.py b/tests/fields.py index 896e77f9..42b1ede7 100644 --- a/tests/fields.py +++ b/tests/fields.py @@ -209,6 +209,19 @@ def test_required_flag(self): form = self.F() self.assertEqual(form.b(), u'') + def test_check_validators(self): + v1 = "Not callable" + v2 = validators.DataRequired + + with self.assertRaisesRegexp(TypeError, "{} is not a valid validator because " + "it is not callable".format(v1)): + Field(validators=[v1]) + + with self.assertRaisesRegexp(TypeError, "{} is not a valid validator because " + "it is a class, it should be an " + "instance".format(v2)): + Field(validators=[v2]) + class PrePostTestField(TextField): def pre_validate(self, form): diff --git a/wtforms/fields/core.py b/wtforms/fields/core.py index 65c3fd32..62e19c74 100644 --- a/wtforms/fields/core.py +++ b/wtforms/fields/core.py @@ -3,6 +3,7 @@ import datetime import decimal import itertools +import inspect from copy import copy @@ -104,7 +105,9 @@ def __init__(self, label=None, validators=None, filters=tuple(), self.name = _prefix + _name self.short_name = _name self.type = type(self).__name__ - self.validators = validators or list(self.validators) + + self.check_validators(validators) + self.validators = validators or self.validators self.id = id or self.name self.label = Label(self.id, label if label is not None else self.gettext(_name.replace('_', ' ').title())) @@ -154,6 +157,18 @@ def __call__(self, **kwargs): """ return self.meta.render_field(self, kwargs) + @classmethod + def check_validators(cls, validators): + if validators is not None: + for validator in validators: + if not callable(validator): + raise TypeError("{} is not a valid validator because it is not " + "callable".format(validator)) + + if inspect.isclass(validator): + raise TypeError("{} is not a valid validator because it is a class, " + "it should be an instance".format(validator)) + def gettext(self, string): """ Get a translation for the given message. @@ -190,6 +205,9 @@ def validate(self, form, extra_validators=tuple()): self.errors = list(self.process_errors) stop_validation = False + # Check the type of extra_validators + self.check_validators(extra_validators) + # Call pre_validate try: self.pre_validate(form) @@ -340,6 +358,9 @@ def __init__(self, field_class, *args, **kwargs): self.args = args self.kwargs = kwargs self.creation_counter = UnboundField.creation_counter + validators = kwargs.get('validators') + if validators: + self.field_class.check_validators(validators) def bind(self, form, name, prefix='', translations=None, **kwargs): kw = dict(