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