Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/wtforms/fields/core.py: 29%
179 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:32 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:32 +0000
1import inspect
2import itertools
3import warnings
5from markupsafe import escape
6from markupsafe import Markup
8from wtforms import widgets
9from wtforms.i18n import DummyTranslations
10from wtforms.utils import unset_value
11from wtforms.validators import StopValidation
12from wtforms.validators import ValidationError
15class Field:
16 """
17 Field base class
18 """
20 errors = tuple()
21 process_errors = tuple()
22 raw_data = None
23 validators = tuple()
24 widget = None
25 _formfield = True
26 _translations = DummyTranslations()
27 do_not_call_in_templates = True # Allow Django 1.4 traversal
29 def __new__(cls, *args, **kwargs):
30 if "_form" in kwargs:
31 return super().__new__(cls)
32 else:
33 return UnboundField(cls, *args, **kwargs)
35 def __init__(
36 self,
37 label=None,
38 validators=None,
39 filters=(),
40 description="",
41 id=None,
42 default=None,
43 widget=None,
44 render_kw=None,
45 name=None,
46 _form=None,
47 _prefix="",
48 _translations=None,
49 _meta=None,
50 ):
51 """
52 Construct a new field.
54 :param label:
55 The label of the field.
56 :param validators:
57 A sequence of validators to call when `validate` is called.
58 :param filters:
59 A sequence of filters which are run on input data by `process`.
60 :param description:
61 A description for the field, typically used for help text.
62 :param id:
63 An id to use for the field. A reasonable default is set by the form,
64 and you shouldn't need to set this manually.
65 :param default:
66 The default value to assign to the field, if no form or object
67 input is provided. May be a callable.
68 :param widget:
69 If provided, overrides the widget used to render the field.
70 :param dict render_kw:
71 If provided, a dictionary which provides default keywords that
72 will be given to the widget at render time.
73 :param name:
74 The HTML name of this field. The default value is the Python
75 attribute name.
76 :param _form:
77 The form holding this field. It is passed by the form itself during
78 construction. You should never pass this value yourself.
79 :param _prefix:
80 The prefix to prepend to the form name of this field, passed by
81 the enclosing form during construction.
82 :param _translations:
83 A translations object providing message translations. Usually
84 passed by the enclosing form during construction. See
85 :doc:`I18n docs <i18n>` for information on message translations.
86 :param _meta:
87 If provided, this is the 'meta' instance from the form. You usually
88 don't pass this yourself.
90 If `_form` isn't provided, an :class:`UnboundField` will be
91 returned instead. Call its :func:`bind` method with a form instance and
92 a name to construct the field.
93 """
94 if _translations is not None:
95 self._translations = _translations
97 if _meta is not None:
98 self.meta = _meta
99 elif _form is not None:
100 self.meta = _form.meta
101 else:
102 raise TypeError("Must provide one of _form or _meta")
104 self.default = default
105 self.description = description
106 self.render_kw = render_kw
107 self.filters = filters
108 self.flags = Flags()
109 self.name = _prefix + name
110 self.short_name = name
111 self.type = type(self).__name__
113 self.check_validators(validators)
114 self.validators = validators or self.validators
116 self.id = id or self.name
117 self.label = Label(
118 self.id,
119 label
120 if label is not None
121 else self.gettext(name.replace("_", " ").title()),
122 )
124 if widget is not None:
125 self.widget = widget
127 for v in itertools.chain(self.validators, [self.widget]):
128 flags = getattr(v, "field_flags", {})
130 # check for legacy format, remove eventually
131 if isinstance(flags, tuple): # pragma: no cover
132 warnings.warn(
133 "Flags should be stored in dicts and not in tuples. "
134 "The next version of WTForms will abandon support "
135 "for flags in tuples.",
136 DeprecationWarning,
137 stacklevel=2,
138 )
139 flags = {flag_name: True for flag_name in flags}
141 for k, v in flags.items():
142 setattr(self.flags, k, v)
144 def __str__(self):
145 """
146 Returns a HTML representation of the field. For more powerful rendering,
147 see the `__call__` method.
148 """
149 return self()
151 def __html__(self):
152 """
153 Returns a HTML representation of the field. For more powerful rendering,
154 see the :meth:`__call__` method.
155 """
156 return self()
158 def __call__(self, **kwargs):
159 """
160 Render this field as HTML, using keyword args as additional attributes.
162 This delegates rendering to
163 :meth:`meta.render_field <wtforms.meta.DefaultMeta.render_field>`
164 whose default behavior is to call the field's widget, passing any
165 keyword arguments from this call along to the widget.
167 In all of the WTForms HTML widgets, keyword arguments are turned to
168 HTML attributes, though in theory a widget is free to do anything it
169 wants with the supplied keyword arguments, and widgets don't have to
170 even do anything related to HTML.
171 """
172 return self.meta.render_field(self, kwargs)
174 @classmethod
175 def check_validators(cls, validators):
176 if validators is not None:
177 for validator in validators:
178 if not callable(validator):
179 raise TypeError(
180 "{} is not a valid validator because it is not "
181 "callable".format(validator)
182 )
184 if inspect.isclass(validator):
185 raise TypeError(
186 "{} is not a valid validator because it is a class, "
187 "it should be an instance".format(validator)
188 )
190 def gettext(self, string):
191 """
192 Get a translation for the given message.
194 This proxies for the internal translations object.
196 :param string: A string to be translated.
197 :return: A string which is the translated output.
198 """
199 return self._translations.gettext(string)
201 def ngettext(self, singular, plural, n):
202 """
203 Get a translation for a message which can be pluralized.
205 :param str singular: The singular form of the message.
206 :param str plural: The plural form of the message.
207 :param int n: The number of elements this message is referring to
208 """
209 return self._translations.ngettext(singular, plural, n)
211 def validate(self, form, extra_validators=()):
212 """
213 Validates the field and returns True or False. `self.errors` will
214 contain any errors raised during validation. This is usually only
215 called by `Form.validate`.
217 Subfields shouldn't override this, but rather override either
218 `pre_validate`, `post_validate` or both, depending on needs.
220 :param form: The form the field belongs to.
221 :param extra_validators: A sequence of extra validators to run.
222 """
223 self.errors = list(self.process_errors)
224 stop_validation = False
226 # Check the type of extra_validators
227 self.check_validators(extra_validators)
229 # Call pre_validate
230 try:
231 self.pre_validate(form)
232 except StopValidation as e:
233 if e.args and e.args[0]:
234 self.errors.append(e.args[0])
235 stop_validation = True
236 except ValidationError as e:
237 self.errors.append(e.args[0])
239 # Run validators
240 if not stop_validation:
241 chain = itertools.chain(self.validators, extra_validators)
242 stop_validation = self._run_validation_chain(form, chain)
244 # Call post_validate
245 try:
246 self.post_validate(form, stop_validation)
247 except ValidationError as e:
248 self.errors.append(e.args[0])
250 return len(self.errors) == 0
252 def _run_validation_chain(self, form, validators):
253 """
254 Run a validation chain, stopping if any validator raises StopValidation.
256 :param form: The Form instance this field belongs to.
257 :param validators: a sequence or iterable of validator callables.
258 :return: True if validation was stopped, False otherwise.
259 """
260 for validator in validators:
261 try:
262 validator(form, self)
263 except StopValidation as e:
264 if e.args and e.args[0]:
265 self.errors.append(e.args[0])
266 return True
267 except ValidationError as e:
268 self.errors.append(e.args[0])
270 return False
272 def pre_validate(self, form):
273 """
274 Override if you need field-level validation. Runs before any other
275 validators.
277 :param form: The form the field belongs to.
278 """
279 pass
281 def post_validate(self, form, validation_stopped):
282 """
283 Override if you need to run any field-level validation tasks after
284 normal validation. This shouldn't be needed in most cases.
286 :param form: The form the field belongs to.
287 :param validation_stopped:
288 `True` if any validator raised StopValidation.
289 """
290 pass
292 def process(self, formdata, data=unset_value, extra_filters=None):
293 """
294 Process incoming data, calling process_data, process_formdata as needed,
295 and run filters.
297 If `data` is not provided, process_data will be called on the field's
298 default.
300 Field subclasses usually won't override this, instead overriding the
301 process_formdata and process_data methods. Only override this for
302 special advanced processing, such as when a field encapsulates many
303 inputs.
305 :param extra_filters: A sequence of extra filters to run.
306 """
307 self.process_errors = []
308 if data is unset_value:
309 try:
310 data = self.default()
311 except TypeError:
312 data = self.default
314 self.object_data = data
316 try:
317 self.process_data(data)
318 except ValueError as e:
319 self.process_errors.append(e.args[0])
321 if formdata is not None:
322 if self.name in formdata:
323 self.raw_data = formdata.getlist(self.name)
324 else:
325 self.raw_data = []
327 try:
328 self.process_formdata(self.raw_data)
329 except ValueError as e:
330 self.process_errors.append(e.args[0])
332 try:
333 for filter in itertools.chain(self.filters, extra_filters or []):
334 self.data = filter(self.data)
335 except ValueError as e:
336 self.process_errors.append(e.args[0])
338 def process_data(self, value):
339 """
340 Process the Python data applied to this field and store the result.
342 This will be called during form construction by the form's `kwargs` or
343 `obj` argument.
345 :param value: The python object containing the value to process.
346 """
347 self.data = value
349 def process_formdata(self, valuelist):
350 """
351 Process data received over the wire from a form.
353 This will be called during form construction with data supplied
354 through the `formdata` argument.
356 :param valuelist: A list of strings to process.
357 """
358 if valuelist:
359 self.data = valuelist[0]
361 def populate_obj(self, obj, name):
362 """
363 Populates `obj.<name>` with the field's data.
365 :note: This is a destructive operation. If `obj.<name>` already exists,
366 it will be overridden. Use with caution.
367 """
368 setattr(obj, name, self.data)
371class UnboundField:
372 _formfield = True
373 creation_counter = 0
375 def __init__(self, field_class, *args, name=None, **kwargs):
376 UnboundField.creation_counter += 1
377 self.field_class = field_class
378 self.args = args
379 self.name = name
380 self.kwargs = kwargs
381 self.creation_counter = UnboundField.creation_counter
382 validators = kwargs.get("validators")
383 if validators:
384 self.field_class.check_validators(validators)
386 def bind(self, form, name, prefix="", translations=None, **kwargs):
387 kw = dict(
388 self.kwargs,
389 name=name,
390 _form=form,
391 _prefix=prefix,
392 _translations=translations,
393 **kwargs,
394 )
395 return self.field_class(*self.args, **kw)
397 def __repr__(self):
398 return "<UnboundField({}, {!r}, {!r})>".format(
399 self.field_class.__name__, self.args, self.kwargs
400 )
403class Flags:
404 """
405 Holds a set of flags as attributes.
407 Accessing a non-existing attribute returns None for its value.
408 """
410 def __getattr__(self, name):
411 if name.startswith("_"):
412 return super().__getattr__(name)
413 return None
415 def __contains__(self, name):
416 return getattr(self, name)
418 def __repr__(self):
419 flags = (name for name in dir(self) if not name.startswith("_"))
420 return "<wtforms.fields.Flags: {%s}>" % ", ".join(flags)
423class Label:
424 """
425 An HTML form label.
426 """
428 def __init__(self, field_id, text):
429 self.field_id = field_id
430 self.text = text
432 def __str__(self):
433 return self()
435 def __html__(self):
436 return self()
438 def __call__(self, text=None, **kwargs):
439 if "for_" in kwargs:
440 kwargs["for"] = kwargs.pop("for_")
441 else:
442 kwargs.setdefault("for", self.field_id)
444 attributes = widgets.html_params(**kwargs)
445 text = escape(text or self.text)
446 return Markup(f"<label {attributes}>{text}</label>")
448 def __repr__(self):
449 return f"Label({self.field_id!r}, {self.text!r})"