Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/wtforms/validators.py: 47%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1import ipaddress
2import math
3import re
4import uuid
5from collections.abc import Callable
6from datetime import date
7from datetime import datetime
8from datetime import time
9from urllib.parse import urlparse
11__all__ = (
12 "DataRequired",
13 "data_required",
14 "Email",
15 "email",
16 "EqualTo",
17 "equal_to",
18 "IPAddress",
19 "ip_address",
20 "InputRequired",
21 "input_required",
22 "Length",
23 "length",
24 "NumberRange",
25 "DateRange",
26 "number_range",
27 "date_range",
28 "Optional",
29 "optional",
30 "Regexp",
31 "regexp",
32 "URL",
33 "url",
34 "AnyOf",
35 "any_of",
36 "NoneOf",
37 "none_of",
38 "MacAddress",
39 "mac_address",
40 "UUID",
41 "ValidationError",
42 "StopValidation",
43 "readonly",
44 "ReadOnly",
45 "disabled",
46 "Disabled",
47)
50class ValidationError(ValueError):
51 """
52 Raised when a validator fails to validate its input.
53 """
55 def __init__(self, message="", *args, **kwargs):
56 ValueError.__init__(self, message, *args, **kwargs)
59class StopValidation(Exception):
60 """
61 Causes the validation chain to stop.
63 If StopValidation is raised, no more validators in the validation chain are
64 called. If raised with a message, the message will be added to the errors
65 list.
66 """
68 def __init__(self, message="", *args, **kwargs):
69 Exception.__init__(self, message, *args, **kwargs)
72class EqualTo:
73 """
74 Compares the values of two fields.
76 :param fieldname:
77 The name of the other field to compare to.
78 :param message:
79 Error message to raise in case of a validation error. Can be
80 interpolated with `%(other_label)s` and `%(other_name)s` to provide a
81 more helpful error.
82 """
84 def __init__(self, fieldname, message=None):
85 self.fieldname = fieldname
86 self.message = message
88 def __call__(self, form, field):
89 try:
90 other = form[self.fieldname]
91 except KeyError as exc:
92 raise ValidationError(
93 field.gettext("Invalid field name '%s'.") % self.fieldname
94 ) from exc
95 if field.data == other.data:
96 return
98 d = {
99 "other_label": hasattr(other, "label")
100 and other.label.text
101 or self.fieldname,
102 "other_name": self.fieldname,
103 }
104 message = self.message
105 if message is None:
106 message = field.gettext("Field must be equal to %(other_name)s.")
108 raise ValidationError(message % d)
111class Length:
112 """
113 Validates the length of a string.
115 :param min:
116 The minimum required length of the string. If not provided, minimum
117 length will not be checked.
118 :param max:
119 The maximum length of the string. If not provided, maximum length
120 will not be checked.
121 :param message:
122 Error message to raise in case of a validation error. Can be
123 interpolated using `%(min)d` and `%(max)d` if desired. Useful defaults
124 are provided depending on the existence of min and max.
126 When supported, sets the `minlength` and `maxlength` attributes on widgets.
127 """
129 def __init__(self, min=-1, max=-1, message=None):
130 assert min != -1 or max != -1, (
131 "At least one of `min` or `max` must be specified."
132 )
133 assert max == -1 or min <= max, "`min` cannot be more than `max`."
134 self.min = min
135 self.max = max
136 self.message = message
137 self.field_flags = {}
138 if self.min != -1:
139 self.field_flags["minlength"] = self.min
140 if self.max != -1:
141 self.field_flags["maxlength"] = self.max
143 def __call__(self, form, field):
144 length = field.data and len(field.data) or 0
145 if length >= self.min and (self.max == -1 or length <= self.max):
146 return
148 if self.message is not None:
149 message = self.message
151 elif self.max == -1:
152 message = field.ngettext(
153 "Field must be at least %(min)d character long.",
154 "Field must be at least %(min)d characters long.",
155 self.min,
156 )
157 elif self.min == -1:
158 message = field.ngettext(
159 "Field cannot be longer than %(max)d character.",
160 "Field cannot be longer than %(max)d characters.",
161 self.max,
162 )
163 elif self.min == self.max:
164 message = field.ngettext(
165 "Field must be exactly %(max)d character long.",
166 "Field must be exactly %(max)d characters long.",
167 self.max,
168 )
169 else:
170 message = field.gettext(
171 "Field must be between %(min)d and %(max)d characters long."
172 )
174 raise ValidationError(message % dict(min=self.min, max=self.max, length=length))
177class NumberRange:
178 """
179 Validates that a number is of a minimum and/or maximum value, inclusive.
180 This will work with any comparable number type, such as floats and
181 decimals, not just integers.
183 ``min`` and ``max`` may be callables to compute dynamic bounds.
185 :param min:
186 The minimum required value of the number. If not provided, minimum
187 value will not be checked. Can also be a callable that returns the
188 minimum value.
189 :param max:
190 The maximum value of the number. If not provided, maximum value
191 will not be checked. Can also be a callable that returns the maximum
192 value.
193 :param message:
194 Error message to raise in case of a validation error. Can be
195 interpolated using `%(min)s` and `%(max)s` if desired. Useful defaults
196 are provided depending on the existence of min and max.
198 When supported, sets the `min` and `max` attributes on widgets.
199 """
201 def __init__(self, min=None, max=None, message=None):
202 self.min = min
203 self.max = max
204 self.message = message
205 self.field_flags = {}
206 if self.min is not None:
207 self.field_flags["min"] = self.min
208 if self.max is not None:
209 self.field_flags["max"] = self.max
211 @staticmethod
212 def _resolve(value):
213 return value() if callable(value) else value
215 def __call__(self, form, field):
216 min_value = self._resolve(self.min)
217 max_value = self._resolve(self.max)
218 data = field.data
219 if (
220 data is not None
221 and not math.isnan(data)
222 and (min_value is None or data >= min_value)
223 and (max_value is None or data <= max_value)
224 ):
225 return
227 if self.message is not None:
228 message = self.message
230 # we use %(min)s interpolation to support floats, None, and
231 # Decimals without throwing a formatting exception.
232 elif max_value is None:
233 message = field.gettext("Number must be at least %(min)s.")
235 elif min_value is None:
236 message = field.gettext("Number must be at most %(max)s.")
238 else:
239 message = field.gettext("Number must be between %(min)s and %(max)s.")
241 raise ValidationError(message % dict(min=min_value, max=max_value))
244class DateRange:
245 """
246 Validates that a date or datetime is of a minimum and/or maximum value,
247 inclusive. This will work with dates and datetimes.
249 ``min`` and ``max`` may be callables to compute dynamic bounds.
251 For example::
253 def in_n_days(days):
254 return datetime.now() + timedelta(days=days)
257 cb = partial(in_n_days, 5)
260 class DateForm(Form):
261 date = DateField("date", [DateRange(min=date(2023, 1, 1), max=cb)])
262 datetime = DateTimeLocalField(
263 "datetime-local",
264 [DateRange(min=datetime(2023, 1, 1, 15, 30), max=cb)],
265 )
267 :param min:
268 The minimum required date or datetime. If not provided, minimum
269 date or datetime will not be checked. Can also be a callable that
270 returns a date or datetime.
271 :param max:
272 The maximum date or datetime. If not provided, maximum date or datetime
273 will not be checked. Can also be a callable that returns a date or
274 datetime.
275 :param message:
276 Error message to raise in case of a validation error. Can be
277 interpolated using `%(min)s` and `%(max)s` if desired. Useful defaults
278 are provided depending on the existence of min and max.
280 When supported, sets the `min` and `max` attributes on widgets.
281 """
283 def __init__(
284 self,
285 min=None,
286 max=None,
287 message=None,
288 ):
289 self.min = min
290 self.max = max
291 self.message = message
292 self.field_flags = {}
294 if self.min is not None:
295 self.field_flags["min"] = self.min
296 if self.max is not None:
297 self.field_flags["max"] = self.max
299 @staticmethod
300 def _to_datetime(value):
301 if isinstance(value, datetime):
302 return value
303 if isinstance(value, date):
304 return datetime.combine(value, time())
305 return value
307 @classmethod
308 def _coerce_bound(cls, value):
309 if callable(value):
310 value = value()
311 return cls._to_datetime(value)
313 def __call__(self, form, field):
314 min_value = self._coerce_bound(self.min)
315 max_value = self._coerce_bound(self.max)
317 data = self._to_datetime(field.data)
318 if data is not None and (
319 (min_value is None or data >= min_value)
320 and (max_value is None or data <= max_value)
321 ):
322 return
324 if self.message is not None:
325 message = self.message
327 elif max_value is None:
328 message = field.gettext("Date must be at least %(min)s.")
330 elif min_value is None:
331 message = field.gettext("Date must be at most %(max)s.")
333 else:
334 message = field.gettext("Date must be between %(min)s and %(max)s.")
336 raise ValidationError(message % dict(min=min_value, max=max_value))
339class Optional:
340 """
341 Allows empty input and stops the validation chain from continuing.
343 If input is empty, also removes prior errors (such as processing errors)
344 from the field.
346 :param strip_whitespace:
347 If True (the default) also stop the validation chain on input which
348 consists of only whitespace.
350 Sets the `optional` attribute on widgets.
351 """
353 def __init__(self, strip_whitespace=True):
354 if strip_whitespace:
355 self.string_check = lambda s: s.strip()
356 else:
357 self.string_check = lambda s: s
359 self.field_flags = {"optional": True}
361 def __call__(self, form, field):
362 if (
363 not field.raw_data
364 or isinstance(field.raw_data[0], str)
365 and not self.string_check(field.raw_data[0])
366 ):
367 field.errors[:] = []
368 raise StopValidation()
371class DataRequired:
372 """
373 Checks the field's data is 'truthy' otherwise stops the validation chain.
375 This validator checks that the ``data`` attribute on the field is a 'true'
376 value (effectively, it does ``if field.data``.) Furthermore, if the data
377 is a string type, a string containing only whitespace characters is
378 considered false.
380 If the data is empty, also removes prior errors (such as processing errors)
381 from the field.
383 **NOTE** this validator used to be called `Required` but the way it behaved
384 (requiring coerced data, not input data) meant it functioned in a way
385 which was not symmetric to the `Optional` validator and furthermore caused
386 confusion with certain fields which coerced data to 'falsey' values like
387 ``0``, ``Decimal(0)``, ``time(0)`` etc. Unless a very specific reason
388 exists, we recommend using the :class:`InputRequired` instead.
390 :param message:
391 Error message to raise in case of a validation error.
393 Sets the `required` attribute on widgets.
394 """
396 def __init__(self, message=None):
397 self.message = message
398 self.field_flags = {"required": True}
400 def __call__(self, form, field):
401 if field.data and (not isinstance(field.data, str) or field.data.strip()):
402 return
404 if self.message is None:
405 message = field.gettext("This field is required.")
406 else:
407 message = self.message
409 field.errors[:] = []
410 raise StopValidation(message)
413class InputRequired:
414 """
415 Validates that input was provided for this field.
417 Note there is a distinction between this and DataRequired in that
418 InputRequired looks that form-input data was provided, and DataRequired
419 looks at the post-coercion data. This means that this validator only checks
420 whether non-empty data was sent, not whether non-empty data was coerced
421 from that data. Initially populated data is not considered sent.
423 Sets the `required` attribute on widgets.
424 """
426 def __init__(self, message=None):
427 self.message = message
428 self.field_flags = {"required": True}
430 def __call__(self, form, field):
431 if field.raw_data and field.raw_data[0]:
432 return
434 if self.message is None:
435 message = field.gettext("This field is required.")
436 else:
437 message = self.message
439 field.errors[:] = []
440 raise StopValidation(message)
443class Regexp:
444 """
445 Validates the field against a user provided regexp.
447 :param regex:
448 The regular expression string to use. Can also be a compiled regular
449 expression pattern.
450 :param flags:
451 The regexp flags to use, for example re.IGNORECASE. Ignored if
452 `regex` is not a string.
453 :param message:
454 Error message to raise in case of a validation error.
455 :param matcher:
456 Callable invoked as ``matcher(pattern, value)`` to perform the match.
457 Defaults to :func:`re.match`. Pass :func:`re.search` or
458 :func:`re.fullmatch` to change the anchoring behaviour.
459 :param html_pattern:
460 Controls the HTML ``pattern`` attribute emitted on supporting widgets.
461 Defaults to ``False`` (no attribute). Set to ``True`` to emit the
462 Python pattern source as-is, to a string to emit a custom
463 browser-specific pattern, or to a callable invoked as
464 ``html_pattern(regex)`` returning ``bool`` or ``str`` interpreted by
465 the same rules. Python and JavaScript regex syntaxes differ; emitting
466 a Python regex unchanged may fail in browsers. Note that the HTML
467 ``pattern`` attribute is implicitly anchored at both ends (equivalent
468 to :func:`re.fullmatch`), so a pattern paired with ``matcher=re.match``
469 or ``matcher=re.search`` may be accepted server-side but rejected by
470 the browser.
471 """
473 def __init__(
474 self,
475 regex,
476 flags=0,
477 message=None,
478 matcher=re.match,
479 html_pattern: bool | str | Callable[[re.Pattern], bool | str] = False,
480 ):
481 if isinstance(regex, str):
482 regex = re.compile(regex, flags)
483 self.regex = regex
484 self.message = message
485 self.matcher = matcher
486 self.field_flags = self._resolve_field_flags(html_pattern)
488 def _resolve_field_flags(self, html_pattern):
489 if callable(html_pattern):
490 html_pattern = html_pattern(self.regex)
491 if html_pattern is True:
492 return {"pattern": self.regex.pattern}
493 if isinstance(html_pattern, str):
494 return {"pattern": html_pattern}
495 return {}
497 def __call__(self, form, field, message=None):
498 match = self.matcher(self.regex, field.data or "")
499 if match:
500 return match
502 if message is None:
503 if self.message is None:
504 message = field.gettext("Invalid input.")
505 else:
506 message = self.message
508 raise ValidationError(message)
511class Email:
512 """
513 Validates an email address. Requires email_validator package to be
514 installed. For ex: pip install wtforms[email].
516 Options that default to ``None`` are not forwarded to
517 ``email_validator``, so its module-level defaults (e.g.
518 ``email_validator.TEST_ENVIRONMENT``) take effect. Pass an explicit
519 value to override per-instance.
521 :param message:
522 Error message to raise in case of a validation error.
523 :param granular_message:
524 Use validation failed message from email_validator library
525 (Default False).
526 :param check_deliverability:
527 Perform domain name resolution check (Default False, diverging
528 from ``email_validator``'s default of True for safety on public
529 forms).
530 :param test_environment:
531 Allow `test` and `*.test` domain names, and disable DNS-based
532 deliverability checks (Default: defer to ``email_validator``).
533 :param allow_smtputf8:
534 Fail validation for addresses that would require SMTPUTF8
535 (Default: defer to ``email_validator``).
536 :param allow_empty_local:
537 Allow an empty local part (i.e. @example.com), e.g. for validating
538 Postfix aliases (Default: defer to ``email_validator``).
539 """
541 def __init__(
542 self,
543 message=None,
544 granular_message=False,
545 check_deliverability=False,
546 test_environment=None,
547 allow_smtputf8=None,
548 allow_empty_local=None,
549 ):
550 self.message = message
551 self.granular_message = granular_message
552 self.check_deliverability = check_deliverability
553 self.test_environment = test_environment
554 self.allow_smtputf8 = allow_smtputf8
555 self.allow_empty_local = allow_empty_local
557 def __call__(self, form, field):
558 try:
559 import email_validator
560 except ImportError as exc: # pragma: no cover
561 raise Exception(
562 "Install 'email_validator' for email validation support."
563 ) from exc
565 try:
566 if field.data is None:
567 raise email_validator.EmailNotValidError()
568 email_validator.validate_email(
569 field.data,
570 check_deliverability=self.check_deliverability,
571 test_environment=self.test_environment,
572 allow_smtputf8=self.allow_smtputf8,
573 allow_empty_local=self.allow_empty_local,
574 )
575 except email_validator.EmailNotValidError as e:
576 message = self.message
577 if message is None:
578 if self.granular_message:
579 message = field.gettext(e)
580 else:
581 message = field.gettext("Invalid email address.")
582 raise ValidationError(message) from e
585class IPAddress:
586 """
587 Validates an IP address.
589 :param ipv4:
590 If True, accept IPv4 addresses as valid (default True)
591 :param ipv6:
592 If True, accept IPv6 addresses as valid (default False)
593 :param message:
594 Error message to raise in case of a validation error.
595 """
597 def __init__(self, ipv4=True, ipv6=False, message=None):
598 if not ipv4 and not ipv6:
599 raise ValueError(
600 "IP Address Validator must have at least one of ipv4 or ipv6 enabled."
601 )
602 self.ipv4 = ipv4
603 self.ipv6 = ipv6
604 self.message = message
606 def __call__(self, form, field):
607 value = field.data
608 valid = False
609 if value:
610 valid = (self.ipv4 and self.check_ipv4(value)) or (
611 self.ipv6 and self.check_ipv6(value)
612 )
614 if valid:
615 return
617 message = self.message
618 if message is None:
619 message = field.gettext("Invalid IP address.")
620 raise ValidationError(message)
622 @classmethod
623 def check_ipv4(cls, value):
624 try:
625 address = ipaddress.ip_address(value)
626 except ValueError:
627 return False
629 if not isinstance(address, ipaddress.IPv4Address):
630 return False
632 return True
634 @classmethod
635 def check_ipv6(cls, value):
636 try:
637 address = ipaddress.ip_address(value)
638 except ValueError:
639 return False
641 if not isinstance(address, ipaddress.IPv6Address):
642 return False
644 return True
647class MacAddress(Regexp):
648 """
649 Validates a MAC address.
651 :param message:
652 Error message to raise in case of a validation error.
653 """
655 def __init__(self, message=None):
656 pattern = r"^(?:[0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$"
657 super().__init__(pattern, message=message)
659 def __call__(self, form, field):
660 message = self.message
661 if message is None:
662 message = field.gettext("Invalid Mac address.")
664 super().__call__(form, field, message)
667class URL:
668 """
669 Simple url validation based on :func:`urllib.parse.urlparse`. Much like
670 the email validator, you probably want to validate the url later by
671 other means if the url must resolve.
673 :param require_tld:
674 If true, then the domain-name portion of the URL must contain a .tld
675 suffix. Set this to false if you want to allow domains like
676 `localhost`.
677 :param allow_ip:
678 If false, then giving an ip as host will fail validation.
679 :param allow_userinfo:
680 If true, accept ``username[:password]@`` in the URL. Defaults to
681 false: forms collecting URLs are often displayed back to users, and
682 userinfo is a known phishing vector
683 (e.g. ``https://accounts.example.com@evil.example/``).
684 :param schemes:
685 Iterable of allowed URL schemes. Defaults to ``("http", "https")``.
686 Pass ``None`` to accept any scheme (use with caution: ``javascript:``,
687 ``data:``, etc. would be accepted).
688 :param message:
689 Error message to raise in case of a validation error.
690 """
692 def __init__(
693 self,
694 require_tld=True,
695 allow_ip=True,
696 allow_userinfo=False,
697 schemes=("http", "https"),
698 message=None,
699 ):
700 self.allow_userinfo = allow_userinfo
701 self.schemes = schemes
702 self.message = message
703 self.validate_hostname = HostnameValidation(
704 require_tld=require_tld, allow_ip=allow_ip
705 )
707 def __call__(self, form, field):
708 message = self.message
709 if message is None:
710 message = field.gettext("Invalid URL.")
712 try:
713 r = urlparse(field.data)
714 except ValueError as exc:
715 raise ValidationError(message) from exc
717 if not r.scheme or not r.hostname:
718 raise ValidationError(message)
720 if self.schemes is not None and r.scheme not in self.schemes:
721 raise ValidationError(message)
723 if not self.allow_userinfo and (r.username or r.password):
724 raise ValidationError(message)
726 try:
727 _ = r.port
728 except ValueError as exc:
729 raise ValidationError(message) from exc
731 if not self.validate_hostname(r.hostname):
732 raise ValidationError(message)
735class UUID:
736 """
737 Validates a UUID.
739 :param message:
740 Error message to raise in case of a validation error.
741 """
743 def __init__(self, message=None):
744 self.message = message
746 def __call__(self, form, field):
747 message = self.message
748 if message is None:
749 message = field.gettext("Invalid UUID.")
750 if isinstance(field.data, uuid.UUID):
751 return
752 if not isinstance(field.data, str):
753 raise ValidationError(message)
754 try:
755 uuid.UUID(field.data)
756 except ValueError as exc:
757 raise ValidationError(message) from exc
760class AnyOf:
761 """
762 Compares the incoming data to a sequence of valid inputs.
764 :param values:
765 A sequence of valid inputs.
766 :param message:
767 Error message to raise in case of a validation error. `%(values)s`
768 contains the list of values.
769 :param values_formatter:
770 Function used to format the list of values in the error message.
771 """
773 def __init__(self, values, message=None, values_formatter=None):
774 self.values = values
775 self.message = message
776 if values_formatter is None:
777 values_formatter = self.default_values_formatter
778 self.values_formatter = values_formatter
780 def __call__(self, form, field):
781 data = field.data if isinstance(field.data, list) else [field.data]
782 if any(d in self.values for d in data):
783 return
785 message = self.message
786 if message is None:
787 message = field.gettext("Invalid value, must be one of: %(values)s.")
789 raise ValidationError(message % dict(values=self.values_formatter(self.values)))
791 @staticmethod
792 def default_values_formatter(values):
793 return ", ".join(str(x) for x in values)
796class NoneOf:
797 """
798 Compares the incoming data to a sequence of invalid inputs.
800 :param values:
801 A sequence of invalid inputs.
802 :param message:
803 Error message to raise in case of a validation error. `%(values)s`
804 contains the list of values.
805 :param values_formatter:
806 Function used to format the list of values in the error message.
807 """
809 def __init__(self, values, message=None, values_formatter=None):
810 self.values = values
811 self.message = message
812 if values_formatter is None:
813 values_formatter = self.default_values_formatter
814 self.values_formatter = values_formatter
816 def __call__(self, form, field):
817 data = field.data if isinstance(field.data, list) else [field.data]
818 if not any(d in self.values for d in data):
819 return
821 message = self.message
822 if message is None:
823 message = field.gettext("Invalid value, can't be any of: %(values)s.")
825 raise ValidationError(message % dict(values=self.values_formatter(self.values)))
827 @staticmethod
828 def default_values_formatter(v):
829 return ", ".join(str(x) for x in v)
832class HostnameValidation:
833 """
834 Helper class for checking hostnames for validation.
836 This is not a validator in and of itself, and as such is not exported.
837 """
839 hostname_part = re.compile(r"^(xn-|[a-z0-9_]+)(-[a-z0-9_-]+)*$", re.IGNORECASE)
840 tld_part = re.compile(r"^([a-z]{2,20}|xn--([a-z0-9]+-)*[a-z0-9]+)$", re.IGNORECASE)
842 def __init__(self, require_tld=True, allow_ip=False):
843 self.require_tld = require_tld
844 self.allow_ip = allow_ip
846 def __call__(self, hostname):
847 if self.allow_ip and (
848 IPAddress.check_ipv4(hostname) or IPAddress.check_ipv6(hostname)
849 ):
850 return True
852 # Encode out IDNA hostnames. This makes further validation easier.
853 try:
854 hostname = hostname.encode("idna")
855 except UnicodeError:
856 pass
858 # Turn back into a string in Python 3x
859 if not isinstance(hostname, str):
860 hostname = hostname.decode("ascii")
862 if len(hostname) > 253:
863 return False
865 # Check that all labels in the hostname are valid
866 parts = hostname.split(".")
867 for part in parts:
868 if not part or len(part) > 63:
869 return False
870 if not self.hostname_part.match(part):
871 return False
873 if self.require_tld and (len(parts) < 2 or not self.tld_part.match(parts[-1])):
874 return False
876 return True
879class ReadOnly:
880 """
881 Set a field readonly.
883 Validation fails if the form data is different than the
884 field object data, or if unset, from the field default data.
885 """
887 def __init__(self):
888 self.field_flags = {"readonly": True}
890 def __call__(self, form, field):
891 if field.data != field.object_data:
892 raise ValidationError(field.gettext("This field cannot be edited."))
895class Disabled:
896 """
897 Set a field disabled.
899 Validation fails if the form data has any value.
900 """
902 def __init__(self):
903 self.field_flags = {"disabled": True}
905 def __call__(self, form, field):
906 if field.raw_data:
907 raise ValidationError(
908 field.gettext("This field is disabled and cannot have a value.")
909 )
912email = Email
913equal_to = EqualTo
914ip_address = IPAddress
915mac_address = MacAddress
916length = Length
917number_range = NumberRange
918date_range = DateRange
919optional = Optional
920input_required = InputRequired
921data_required = DataRequired
922regexp = Regexp
923url = URL
924any_of = AnyOf
925none_of = NoneOf
926readonly = ReadOnly
927disabled = Disabled