Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/wtforms/widgets/core.py: 52%
211 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
1from markupsafe import escape
2from markupsafe import Markup
4__all__ = (
5 "CheckboxInput",
6 "ColorInput",
7 "DateInput",
8 "DateTimeInput",
9 "DateTimeLocalInput",
10 "EmailInput",
11 "FileInput",
12 "HiddenInput",
13 "ListWidget",
14 "MonthInput",
15 "NumberInput",
16 "Option",
17 "PasswordInput",
18 "RadioInput",
19 "RangeInput",
20 "SearchInput",
21 "Select",
22 "SubmitInput",
23 "TableWidget",
24 "TextArea",
25 "TextInput",
26 "TelInput",
27 "TimeInput",
28 "URLInput",
29 "WeekInput",
30)
33def clean_key(key):
34 key = key.rstrip("_")
35 if key.startswith("data_") or key.startswith("aria_"):
36 key = key.replace("_", "-")
37 return key
40def html_params(**kwargs):
41 """
42 Generate HTML attribute syntax from inputted keyword arguments.
44 The output value is sorted by the passed keys, to provide consistent output
45 each time this function is called with the same parameters. Because of the
46 frequent use of the normally reserved keywords `class` and `for`, suffixing
47 these with an underscore will allow them to be used.
49 In order to facilitate the use of ``data-`` and ``aria-`` attributes, if the
50 name of the attribute begins with ``data_`` or ``aria_``, then every
51 underscore will be replaced with a hyphen in the generated attribute.
53 >>> html_params(data_attr='user.name', aria_labeledby='name')
54 'data-attr="user.name" aria-labeledby="name"'
56 In addition, the values ``True`` and ``False`` are special:
57 * ``attr=True`` generates the HTML compact output of a boolean attribute,
58 e.g. ``checked=True`` will generate simply ``checked``
59 * ``attr=False`` will be ignored and generate no output.
61 >>> html_params(name='text1', id='f', class_='text')
62 'class="text" id="f" name="text1"'
63 >>> html_params(checked=True, readonly=False, name="text1", abc="hello")
64 'abc="hello" checked name="text1"'
66 .. versionchanged:: 3.0
67 ``aria_`` args convert underscores to hyphens like ``data_``
68 args.
70 .. versionchanged:: 2.2
71 ``data_`` args convert all underscores to hyphens, instead of
72 only the first one.
73 """
74 params = []
75 for k, v in sorted(kwargs.items()):
76 k = clean_key(k)
77 if v is True:
78 params.append(k)
79 elif v is False:
80 pass
81 else:
82 params.append(f'{str(k)}="{escape(v)}"')
83 return " ".join(params)
86class ListWidget:
87 """
88 Renders a list of fields as a `ul` or `ol` list.
90 This is used for fields which encapsulate many inner fields as subfields.
91 The widget will try to iterate the field to get access to the subfields and
92 call them to render them.
94 If `prefix_label` is set, the subfield's label is printed before the field,
95 otherwise afterwards. The latter is useful for iterating radios or
96 checkboxes.
97 """
99 def __init__(self, html_tag="ul", prefix_label=True):
100 assert html_tag in ("ol", "ul")
101 self.html_tag = html_tag
102 self.prefix_label = prefix_label
104 def __call__(self, field, **kwargs):
105 kwargs.setdefault("id", field.id)
106 html = [f"<{self.html_tag} {html_params(**kwargs)}>"]
107 for subfield in field:
108 if self.prefix_label:
109 html.append(f"<li>{subfield.label} {subfield()}</li>")
110 else:
111 html.append(f"<li>{subfield()} {subfield.label}</li>")
112 html.append("</%s>" % self.html_tag)
113 return Markup("".join(html))
116class TableWidget:
117 """
118 Renders a list of fields as a set of table rows with th/td pairs.
120 If `with_table_tag` is True, then an enclosing <table> is placed around the
121 rows.
123 Hidden fields will not be displayed with a row, instead the field will be
124 pushed into a subsequent table row to ensure XHTML validity. Hidden fields
125 at the end of the field list will appear outside the table.
126 """
128 def __init__(self, with_table_tag=True):
129 self.with_table_tag = with_table_tag
131 def __call__(self, field, **kwargs):
132 html = []
133 if self.with_table_tag:
134 kwargs.setdefault("id", field.id)
135 html.append("<table %s>" % html_params(**kwargs))
136 hidden = ""
137 for subfield in field:
138 if subfield.type in ("HiddenField", "CSRFTokenField"):
139 hidden += str(subfield)
140 else:
141 html.append(
142 "<tr><th>%s</th><td>%s%s</td></tr>"
143 % (str(subfield.label), hidden, str(subfield))
144 )
145 hidden = ""
146 if self.with_table_tag:
147 html.append("</table>")
148 if hidden:
149 html.append(hidden)
150 return Markup("".join(html))
153class Input:
154 """
155 Render a basic ``<input>`` field.
157 This is used as the basis for most of the other input fields.
159 By default, the `_value()` method will be called upon the associated field
160 to provide the ``value=`` HTML attribute.
161 """
163 html_params = staticmethod(html_params)
164 validation_attrs = ["required"]
166 def __init__(self, input_type=None):
167 if input_type is not None:
168 self.input_type = input_type
170 def __call__(self, field, **kwargs):
171 kwargs.setdefault("id", field.id)
172 kwargs.setdefault("type", self.input_type)
173 if "value" not in kwargs:
174 kwargs["value"] = field._value()
175 flags = getattr(field, "flags", {})
176 for k in dir(flags):
177 if k in self.validation_attrs and k not in kwargs:
178 kwargs[k] = getattr(flags, k)
179 return Markup("<input %s>" % self.html_params(name=field.name, **kwargs))
182class TextInput(Input):
183 """
184 Render a single-line text input.
185 """
187 input_type = "text"
188 validation_attrs = ["required", "maxlength", "minlength", "pattern"]
191class PasswordInput(Input):
192 """
193 Render a password input.
195 For security purposes, this field will not reproduce the value on a form
196 submit by default. To have the value filled in, set `hide_value` to
197 `False`.
198 """
200 input_type = "password"
201 validation_attrs = ["required", "maxlength", "minlength", "pattern"]
203 def __init__(self, hide_value=True):
204 self.hide_value = hide_value
206 def __call__(self, field, **kwargs):
207 if self.hide_value:
208 kwargs["value"] = ""
209 return super().__call__(field, **kwargs)
212class HiddenInput(Input):
213 """
214 Render a hidden input.
215 """
217 input_type = "hidden"
219 def __init__(self, *args, **kwargs):
220 super().__init__(*args, **kwargs)
221 self.field_flags = {"hidden": True}
224class CheckboxInput(Input):
225 """
226 Render a checkbox.
228 The ``checked`` HTML attribute is set if the field's data is a non-false value.
229 """
231 input_type = "checkbox"
233 def __call__(self, field, **kwargs):
234 if getattr(field, "checked", field.data):
235 kwargs["checked"] = True
236 return super().__call__(field, **kwargs)
239class RadioInput(Input):
240 """
241 Render a single radio button.
243 This widget is most commonly used in conjunction with ListWidget or some
244 other listing, as singular radio buttons are not very useful.
245 """
247 input_type = "radio"
249 def __call__(self, field, **kwargs):
250 if field.checked:
251 kwargs["checked"] = True
252 return super().__call__(field, **kwargs)
255class FileInput(Input):
256 """Render a file chooser input.
258 :param multiple: allow choosing multiple files
259 """
261 input_type = "file"
262 validation_attrs = ["required", "accept"]
264 def __init__(self, multiple=False):
265 super().__init__()
266 self.multiple = multiple
268 def __call__(self, field, **kwargs):
269 # browser ignores value of file input for security
270 kwargs["value"] = False
272 if self.multiple:
273 kwargs["multiple"] = True
275 return super().__call__(field, **kwargs)
278class SubmitInput(Input):
279 """
280 Renders a submit button.
282 The field's label is used as the text of the submit button instead of the
283 data on the field.
284 """
286 input_type = "submit"
288 def __call__(self, field, **kwargs):
289 kwargs.setdefault("value", field.label.text)
290 return super().__call__(field, **kwargs)
293class TextArea:
294 """
295 Renders a multi-line text area.
297 `rows` and `cols` ought to be passed as keyword args when rendering.
298 """
300 validation_attrs = ["required", "maxlength", "minlength"]
302 def __call__(self, field, **kwargs):
303 kwargs.setdefault("id", field.id)
304 flags = getattr(field, "flags", {})
305 for k in dir(flags):
306 if k in self.validation_attrs and k not in kwargs:
307 kwargs[k] = getattr(flags, k)
308 return Markup(
309 "<textarea %s>\r\n%s</textarea>"
310 % (html_params(name=field.name, **kwargs), escape(field._value()))
311 )
314class Select:
315 """
316 Renders a select field.
318 If `multiple` is True, then the `size` property should be specified on
319 rendering to make the field useful.
321 The field must provide an `iter_choices()` method which the widget will
322 call on rendering; this method must yield tuples of
323 `(value, label, selected)`.
324 It also must provide a `has_groups()` method which tells whether choices
325 are divided into groups, and if they do, the field must have an
326 `iter_groups()` method that yields tuples of `(label, choices)`, where
327 `choices` is a iterable of `(value, label, selected)` tuples.
328 """
330 validation_attrs = ["required"]
332 def __init__(self, multiple=False):
333 self.multiple = multiple
335 def __call__(self, field, **kwargs):
336 kwargs.setdefault("id", field.id)
337 if self.multiple:
338 kwargs["multiple"] = True
339 flags = getattr(field, "flags", {})
340 for k in dir(flags):
341 if k in self.validation_attrs and k not in kwargs:
342 kwargs[k] = getattr(flags, k)
343 html = ["<select %s>" % html_params(name=field.name, **kwargs)]
344 if field.has_groups():
345 for group, choices in field.iter_groups():
346 html.append("<optgroup %s>" % html_params(label=group))
347 for val, label, selected in choices:
348 html.append(self.render_option(val, label, selected))
349 html.append("</optgroup>")
350 else:
351 for val, label, selected in field.iter_choices():
352 html.append(self.render_option(val, label, selected))
353 html.append("</select>")
354 return Markup("".join(html))
356 @classmethod
357 def render_option(cls, value, label, selected, **kwargs):
358 if value is True:
359 # Handle the special case of a 'True' value.
360 value = str(value)
362 options = dict(kwargs, value=value)
363 if selected:
364 options["selected"] = True
365 return Markup(f"<option {html_params(**options)}>{escape(label)}</option>")
368class Option:
369 """
370 Renders the individual option from a select field.
372 This is just a convenience for various custom rendering situations, and an
373 option by itself does not constitute an entire field.
374 """
376 def __call__(self, field, **kwargs):
377 return Select.render_option(
378 field._value(), field.label.text, field.checked, **kwargs
379 )
382class SearchInput(Input):
383 """
384 Renders an input with type "search".
385 """
387 input_type = "search"
388 validation_attrs = ["required", "maxlength", "minlength", "pattern"]
391class TelInput(Input):
392 """
393 Renders an input with type "tel".
394 """
396 input_type = "tel"
397 validation_attrs = ["required", "maxlength", "minlength", "pattern"]
400class URLInput(Input):
401 """
402 Renders an input with type "url".
403 """
405 input_type = "url"
406 validation_attrs = ["required", "maxlength", "minlength", "pattern"]
409class EmailInput(Input):
410 """
411 Renders an input with type "email".
412 """
414 input_type = "email"
415 validation_attrs = ["required", "maxlength", "minlength", "pattern"]
418class DateTimeInput(Input):
419 """
420 Renders an input with type "datetime".
421 """
423 input_type = "datetime"
424 validation_attrs = ["required", "max", "min", "step"]
427class DateInput(Input):
428 """
429 Renders an input with type "date".
430 """
432 input_type = "date"
433 validation_attrs = ["required", "max", "min", "step"]
436class MonthInput(Input):
437 """
438 Renders an input with type "month".
439 """
441 input_type = "month"
442 validation_attrs = ["required", "max", "min", "step"]
445class WeekInput(Input):
446 """
447 Renders an input with type "week".
448 """
450 input_type = "week"
451 validation_attrs = ["required", "max", "min", "step"]
454class TimeInput(Input):
455 """
456 Renders an input with type "time".
457 """
459 input_type = "time"
460 validation_attrs = ["required", "max", "min", "step"]
463class DateTimeLocalInput(Input):
464 """
465 Renders an input with type "datetime-local".
466 """
468 input_type = "datetime-local"
469 validation_attrs = ["required", "max", "min", "step"]
472class NumberInput(Input):
473 """
474 Renders an input with type "number".
475 """
477 input_type = "number"
478 validation_attrs = ["required", "max", "min", "step"]
480 def __init__(self, step=None, min=None, max=None):
481 self.step = step
482 self.min = min
483 self.max = max
485 def __call__(self, field, **kwargs):
486 if self.step is not None:
487 kwargs.setdefault("step", self.step)
488 if self.min is not None:
489 kwargs.setdefault("min", self.min)
490 if self.max is not None:
491 kwargs.setdefault("max", self.max)
492 return super().__call__(field, **kwargs)
495class RangeInput(Input):
496 """
497 Renders an input with type "range".
498 """
500 input_type = "range"
501 validation_attrs = ["required", "max", "min", "step"]
503 def __init__(self, step=None):
504 self.step = step
506 def __call__(self, field, **kwargs):
507 if self.step is not None:
508 kwargs.setdefault("step", self.step)
509 return super().__call__(field, **kwargs)
512class ColorInput(Input):
513 """
514 Renders an input with type "color".
515 """
517 input_type = "color"