Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/wtforms/fields/core.py: 27%
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 inspect
2import itertools
4from markupsafe import escape
5from markupsafe import Markup
7from wtforms import widgets
8from wtforms.i18n import DummyTranslations
9from wtforms.utils import unset_value
10from wtforms.validators import StopValidation
11from wtforms.validators import ValidationError
14class Field:
15 """
16 Field base class
17 """
19 errors = tuple()
20 process_errors = tuple()
21 raw_data = None
22 validators = tuple()
23 widget = None
24 _formfield = True
25 _translations = DummyTranslations()
26 do_not_call_in_templates = True # Allow Django 1.4 traversal
28 def __new__(cls, *args, **kwargs):
29 if "_form" in kwargs:
30 return super().__new__(cls)
31 else:
32 return UnboundField(cls, *args, **kwargs)
34 def __init__(
35 self,
36 label=None,
37 validators=None,
38 filters=(),
39 description="",
40 id=None,
41 default=None,
42 invalid_value_message=None,
43 widget=None,
44 render_kw=None,
45 name=None,
46 datalist=None,
47 _form=None,
48 _prefix="",
49 _translations=None,
50 _meta=None,
51 ):
52 """
53 Construct a new field.
55 :param label:
56 The label of the field.
57 :param validators:
58 A sequence of validators to call when `validate` is called.
59 :param filters:
60 A sequence of callable which are run by :meth:`~Field.process`
61 to filter or transform the input data. For example
62 ``StringForm(filters=[lambda x: x.strip() if x is not None else x])``.
63 Note that filters are applied after processing the default and
64 incoming data, but before validation. Filters should handle
65 empty values such as `None`.
66 :param description:
67 A description for the field, typically used for help text.
68 :param id:
69 An id to use for the field. A reasonable default is set by the form,
70 and you shouldn't need to set this manually.
71 :param default:
72 The default value to assign to the field, if no form or object
73 input is provided. May be a callable.
74 :param invalid_value_message:
75 Optional custom message used when processing submitted or Python
76 data fails because the value is invalid for this field.
77 :param widget:
78 If provided, overrides the widget used to render the field.
79 :param dict render_kw:
80 If provided, a dictionary which provides default keywords that
81 will be given to the widget at render time.
82 :param name:
83 The HTML name of this field. The default value is the Python
84 attribute name.
85 :param _form:
86 The form holding this field. It is passed by the form itself during
87 construction. You should never pass this value yourself.
88 :param _prefix:
89 The prefix to prepend to the form name of this field, passed by
90 the enclosing form during construction.
91 :param _translations:
92 A translations object providing message translations. Usually
93 passed by the enclosing form during construction. See
94 :doc:`I18n docs <i18n>` for information on message translations.
95 :param _meta:
96 If provided, this is the 'meta' instance from the form. You usually
97 don't pass this yourself.
99 If `_form` isn't provided, an :class:`UnboundField` will be
100 returned instead. Call its :func:`bind` method with a form instance and
101 a name to construct the field.
102 """
103 if _translations is not None:
104 self._translations = _translations
106 if _meta is not None:
107 self.meta = _meta
108 elif _form is not None:
109 self.meta = _form.meta
110 else:
111 raise TypeError("Must provide one of _form or _meta")
113 self._form = _form
115 self.default = default
116 self.description = description
117 self.invalid_value_message = invalid_value_message
118 self.render_kw = render_kw
119 self.filters = filters
120 self.flags = Flags()
121 self.name = _prefix + name
122 self.short_name = name
123 self.type = type(self).__name__
125 self.check_validators(validators)
126 self.validators = validators or self.validators
128 self.id = id or self.name
129 self.label = Label(
130 self.id,
131 label
132 if label is not None
133 else self.gettext(name.replace("_", " ").title()),
134 )
136 if widget is not None:
137 self.widget = widget
139 self._datalist = None
140 if datalist is not None:
141 if isinstance(datalist, str):
142 self._datalist = datalist
143 else:
144 self._datalist = datalist._clone(id=f"{self.id}-datalist")
146 for v in itertools.chain(self.validators, [self.widget]):
147 flags = getattr(v, "field_flags", {})
149 for k, v in flags.items():
150 setattr(self.flags, k, v)
152 def __str__(self):
153 """
154 Returns a HTML representation of the field. For more powerful rendering,
155 see the `__call__` method.
156 """
157 return self()
159 def __html__(self):
160 """
161 Returns a HTML representation of the field. For more powerful rendering,
162 see the :meth:`__call__` method.
163 """
164 return self()
166 def __call__(self, **kwargs):
167 """
168 Render this field as HTML, using keyword args as additional attributes.
170 This delegates rendering to
171 :meth:`meta.render_field <wtforms.meta.DefaultMeta.render_field>`
172 whose default behavior is to call the field's widget, passing any
173 keyword arguments from this call along to the widget.
175 In all of the WTForms HTML widgets, keyword arguments are turned to
176 HTML attributes, though in theory a widget is free to do anything it
177 wants with the supplied keyword arguments, and widgets don't have to
178 even do anything related to HTML.
179 """
180 return self.meta.render_field(self, kwargs)
182 def datalist(self, **kwargs):
183 """Render the inline ``<datalist>`` bound to this field, or
184 empty markup when there is none."""
185 if self._datalist is None or isinstance(self._datalist, str):
186 return Markup("")
187 return self._datalist(self, **kwargs)
189 @classmethod
190 def check_validators(cls, validators):
191 if validators is not None:
192 for validator in validators:
193 if not callable(validator):
194 raise TypeError(
195 f"{validator} is not a valid validator because it is not "
196 "callable"
197 )
199 if inspect.isclass(validator):
200 raise TypeError(
201 f"{validator} is not a valid validator because it is a class, "
202 "it should be an instance"
203 )
205 def gettext(self, string):
206 """
207 Get a translation for the given message.
209 This proxies for the internal translations object.
211 :param string: A string to be translated.
212 :return: A string which is the translated output.
213 """
214 return self._translations.gettext(string)
216 def ngettext(self, singular, plural, n):
217 """
218 Get a translation for a message which can be pluralized.
220 :param str singular: The singular form of the message.
221 :param str plural: The plural form of the message.
222 :param int n: The number of elements this message is referring to
223 """
224 return self._translations.ngettext(singular, plural, n)
226 def validate(self, form, extra_validators=()):
227 """
228 Validates the field and returns True or False. `self.errors` will
229 contain any errors raised during validation. This is usually only
230 called by `Form.validate`.
232 Subfields shouldn't override this, but rather override either
233 `pre_validate`, `post_validate` or both, depending on needs.
235 :param form: The form the field belongs to.
236 :param extra_validators: A sequence of extra validators to run.
237 """
238 self.errors = list(self.process_errors)
239 stop_validation = False
241 # Check the type of extra_validators
242 self.check_validators(extra_validators)
244 # Call pre_validate
245 try:
246 self.pre_validate(form)
247 except StopValidation as e:
248 if e.args and e.args[0]:
249 self.errors.append(e.args[0])
250 stop_validation = True
251 except ValidationError as e:
252 self.errors.append(e.args[0])
254 # Run validators
255 if not stop_validation:
256 chain = itertools.chain(self.validators, extra_validators)
257 stop_validation = self._run_validation_chain(form, chain)
259 # Call post_validate
260 try:
261 self.post_validate(form, stop_validation)
262 except ValidationError as e:
263 self.errors.append(e.args[0])
265 return len(self.errors) == 0
267 def _run_validation_chain(self, form, validators):
268 """
269 Run a validation chain, stopping if any validator raises StopValidation.
271 :param form: The Form instance this field belongs to.
272 :param validators: a sequence or iterable of validator callables.
273 :return: True if validation was stopped, False otherwise.
274 """
275 for validator in validators:
276 try:
277 validator(form, self)
278 except StopValidation as e:
279 if e.args and e.args[0]:
280 self.errors.append(e.args[0])
281 return True
282 except ValidationError as e:
283 self.errors.append(e.args[0])
285 return False
287 def pre_validate(self, form):
288 """
289 Override if you need field-level validation. Runs before any other
290 validators.
292 :param form: The form the field belongs to.
293 """
294 pass
296 def post_validate(self, form, validation_stopped):
297 """
298 Override if you need to run any field-level validation tasks after
299 normal validation. This shouldn't be needed in most cases.
301 :param form: The form the field belongs to.
302 :param validation_stopped:
303 `True` if any validator raised StopValidation.
304 """
305 pass
307 def process(self, formdata, data=unset_value, extra_filters=None):
308 """
309 Process incoming data, calling process_data, process_formdata as needed,
310 and run filters.
312 If `data` is not provided, process_data will be called on the field's
313 default.
315 Field subclasses usually won't override this, instead overriding the
316 process_formdata and process_data methods. Only override this for
317 special advanced processing, such as when a field encapsulates many
318 inputs.
320 :param extra_filters: A sequence of extra filters to run.
321 """
322 self.process_errors = []
323 if data is unset_value:
324 try:
325 data = self.default()
326 except TypeError:
327 data = self.default
329 self.object_data = data
331 try:
332 self.process_data(data)
333 except ValueError as e:
334 self.process_errors.append(e.args[0])
336 if formdata is not None:
337 if self.name in formdata:
338 self.raw_data = formdata.getlist(self.name)
339 else:
340 self.raw_data = []
342 try:
343 self.process_formdata(self.raw_data)
344 except ValueError as e:
345 self.process_errors.append(e.args[0])
347 try:
348 for filter in itertools.chain(self.filters, extra_filters or []):
349 self.data = filter(self.data)
350 except ValueError as e:
351 self.process_errors.append(e.args[0])
353 def post_process(self):
354 """Hook called after every field in the enclosing form has been processed.
356 Override this when a field needs to read other fields' processed data,
357 for example to resolve dynamic choices that depend on the form state.
358 The base implementation resolves any inline :class:`~wtforms.DataList`
359 attached to the field.
360 """
361 if self._datalist is not None and not isinstance(self._datalist, str):
362 self._datalist._resolve(self)
364 def process_data(self, value):
365 """
366 Process the Python data applied to this field and store the result.
368 This will be called during form construction by the form's `kwargs` or
369 `obj` argument.
371 :param value: The python object containing the value to process.
372 """
373 self.data = value
375 def process_formdata(self, valuelist):
376 """
377 Process data received over the wire from a form.
379 This will be called during form construction with data supplied
380 through the `formdata` argument.
382 :param valuelist: A list of strings to process.
383 """
384 if valuelist:
385 self.data = valuelist[0]
387 def populate_obj(self, obj, name):
388 """
389 Populates `obj.<name>` with the field's data.
391 :note: This is a destructive operation. If `obj.<name>` already exists,
392 it will be overridden. Use with caution.
393 """
394 setattr(obj, name, self.data)
397class UnboundField:
398 _formfield = True
399 creation_counter = 0
401 def __init__(self, field_class, *args, name=None, **kwargs):
402 UnboundField.creation_counter += 1
403 self.field_class = field_class
404 self.args = args
405 self.name = name
406 self.kwargs = kwargs
407 self.creation_counter = UnboundField.creation_counter
408 validators = kwargs.get("validators")
409 if validators:
410 self.field_class.check_validators(validators)
412 def bind(self, form, name, prefix="", translations=None, **kwargs):
413 kw = dict(
414 self.kwargs,
415 name=name,
416 _form=form,
417 _prefix=prefix,
418 _translations=translations,
419 **kwargs,
420 )
421 return self.field_class(*self.args, **kw)
423 def __repr__(self):
424 return (
425 "<UnboundField("
426 f"{self.field_class.__name__}, {self.args!r}, {self.kwargs!r}"
427 ")>"
428 )
431class Flags:
432 """
433 Holds a set of flags as attributes.
435 Accessing a non-existing attribute returns None for its value.
436 """
438 def __getattr__(self, name):
439 if name.startswith("_"):
440 return super().__getattr__(name)
441 return None
443 def __contains__(self, name):
444 return getattr(self, name)
446 def __repr__(self):
447 flags = (
448 f"{name}={getattr(self, name)}"
449 for name in dir(self)
450 if not name.startswith("_")
451 )
452 flags = ", ".join(flags)
453 return f"<wtforms.fields.Flags: {{{flags}}}>"
456class Label:
457 """
458 An HTML :mdn-tag:`label`.
459 """
461 def __init__(self, field_id, text):
462 self.field_id = field_id
463 self.text = text
465 def __str__(self):
466 return self()
468 def __html__(self):
469 return self()
471 def __call__(self, text=None, **kwargs):
472 if "for_" in kwargs:
473 kwargs["for"] = kwargs.pop("for_")
474 else:
475 kwargs.setdefault("for", self.field_id)
477 attributes = widgets.html_params(**kwargs)
478 attributes = f" {attributes}" if attributes else ""
479 text = escape(text or self.text)
480 return Markup(f"<label{attributes}>{text}</label>")
482 def __repr__(self):
483 return f"Label({self.field_id!r}, {self.text!r})"