Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/wtforms/datalist.py: 39%
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 warnings
2from dataclasses import dataclass
3from dataclasses import field
4from dataclasses import replace
6from wtforms import widgets
7from wtforms.fields.choices import _enum_options
8from wtforms.fields.choices import _invoke_choices_callable
9from wtforms.fields.choices import Choice
11__all__ = ("DataList", "DataListChoice", "enum_datalist")
14@dataclass
15class DataListChoice:
16 """
17 An option declared via :class:`~wtforms.DataList`'s ``choices=``
18 parameter.
20 :param value:
21 The value rendered as the ``<option>``'s ``value`` attribute.
22 :param label:
23 The label of the option. Defaults to ``value`` when omitted.
24 :param render_kw:
25 A dict containing HTML attributes that will be rendered
26 with the option. Defaults to an empty dict when omitted.
27 """
29 value: str
30 label: str | None = None
31 render_kw: dict = field(default_factory=dict)
33 def __post_init__(self):
34 if self.label is None:
35 self.label = self.value
37 def __iter__(self):
38 return iter((self.value, self.label, self.render_kw))
40 @classmethod
41 def from_input(cls, input):
42 """Coerce a value passed by the user into a :class:`DataListChoice`."""
43 if isinstance(input, DataListChoice):
44 return input
46 if isinstance(input, Choice):
47 warnings.warn(
48 "Passing Choice to a DataList is deprecated; Choice is the "
49 "output type returned by iter_choices(). Use DataListChoice "
50 "instead. Support for Choice as input will be removed in "
51 "WTForms 4.0.",
52 DeprecationWarning,
53 stacklevel=4,
54 )
55 return cls(
56 value=input.value,
57 label=input.label,
58 render_kw=input.render_kw,
59 )
61 if isinstance(input, str):
62 return cls(value=input)
64 if isinstance(input, tuple):
65 return cls(*input)
68def enum_datalist(enum_cls, *, by="value", label=None):
69 """Build a list of :class:`DataListChoice` from an :class:`enum.Enum` class.
71 Same semantics as :func:`~wtforms.fields.enum_choices`: ``by`` selects
72 which member attribute becomes the ``<option>`` value (``"value"`` by
73 default, ``"name"`` otherwise) and ``label`` defaults to ``str(member)``
74 when the Enum defines its own ``__str__`` or is a :class:`enum.StrEnum`,
75 else ``member.name``.
77 A ``<datalist>`` only suggests values for a free-text field, so there is
78 no coercion counterpart — the field stores whatever string is submitted.
79 """
80 return _enum_options(enum_cls, by, label, DataListChoice)
83class DataList:
84 """A ``<datalist>`` of suggestions attached to a single field.
86 Passed to a :class:`~wtforms.Field` via its ``datalist=`` parameter
87 to add an autocomplete-style list of choices. See the WTForms
88 fields documentation for usage.
89 """
91 widget = widgets.DataListWidget()
93 def __init__(self, choices=None, *, render_kw=None, widget=None):
94 self._raw_choices = choices
95 self._choices = None if callable(choices) else choices
96 self._field = None
97 self.render_kw = render_kw or {}
98 if widget is not None:
99 self.widget = widget
100 self.id = None
102 def _clone(self, id):
103 clone = DataList.__new__(DataList)
104 clone._raw_choices = self._raw_choices
105 clone._choices = None if callable(self._raw_choices) else self._raw_choices
106 clone._field = None
107 clone.render_kw = dict(self.render_kw)
108 clone.widget = self.widget
109 clone.id = id
110 return clone
112 def _invalidate(self, field):
113 """Mark the choices for a lazy refresh on the next :meth:`iter_choices`."""
114 if callable(self._raw_choices):
115 self._field = field
117 def iter_choices(self, field=None):
118 if (bound := self._field) is not None:
119 self._field = None
120 self._choices = _invoke_choices_callable(
121 self._raw_choices, bound._form, bound
122 )
123 raw = self._choices
124 if raw is None:
125 return []
126 if isinstance(raw, dict):
127 return [
128 DataListChoice(value=value, label=label) for value, label in raw.items()
129 ]
130 return [
131 replace(choice, render_kw=dict(choice.render_kw or {}))
132 for choice in map(DataListChoice.from_input, raw)
133 ]
135 def __call__(self, field=None, **kwargs):
136 return self.widget(self, field=field, **kwargs)