Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/wtforms/datalist.py: 37%

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

68 statements  

1import warnings 

2from dataclasses import dataclass 

3from dataclasses import field 

4 

5from wtforms import widgets 

6from wtforms._compat import get_signature 

7from wtforms.fields.choices import _enum_options 

8from wtforms.fields.choices import Choice 

9 

10__all__ = ("DataList", "DataListChoice", "enum_datalist") 

11 

12 

13@dataclass 

14class DataListChoice: 

15 """ 

16 An option declared via :class:`~wtforms.DataList`'s ``choices=`` 

17 parameter. 

18 

19 :param value: 

20 The value rendered as the ``<option>``'s ``value`` attribute. 

21 :param label: 

22 The label of the option. Defaults to ``value`` when omitted. 

23 :param render_kw: 

24 A dict containing HTML attributes that will be rendered 

25 with the option. Defaults to an empty dict when omitted. 

26 """ 

27 

28 value: str 

29 label: str | None = None 

30 render_kw: dict = field(default_factory=dict) 

31 

32 def __post_init__(self): 

33 if self.label is None: 

34 self.label = self.value 

35 

36 def __iter__(self): 

37 return iter((self.value, self.label, self.render_kw)) 

38 

39 @classmethod 

40 def from_input(cls, input): 

41 """Coerce a value passed by the user into a :class:`DataListChoice`.""" 

42 if isinstance(input, DataListChoice): 

43 return input 

44 

45 if isinstance(input, Choice): 

46 warnings.warn( 

47 "Passing Choice to a DataList is deprecated; Choice is the " 

48 "output type returned by iter_choices(). Use DataListChoice " 

49 "instead. Support for Choice as input will be removed in " 

50 "WTForms 4.0.", 

51 DeprecationWarning, 

52 stacklevel=4, 

53 ) 

54 return cls( 

55 value=input.value, 

56 label=input.label, 

57 render_kw=input.render_kw, 

58 ) 

59 

60 if isinstance(input, str): 

61 return cls(value=input) 

62 

63 if isinstance(input, tuple): 

64 return cls(*input) 

65 

66 

67def enum_datalist(enum_cls, *, by="value", label=None): 

68 """Build a list of :class:`DataListChoice` from an :class:`enum.Enum` class. 

69 

70 Same semantics as :func:`~wtforms.fields.enum_choices`: ``by`` selects 

71 which member attribute becomes the ``<option>`` value (``"value"`` by 

72 default, ``"name"`` otherwise) and ``label`` defaults to ``str(member)`` 

73 when the Enum defines its own ``__str__``, else ``member.name``. 

74 

75 A ``<datalist>`` only suggests values for a free-text field, so there is 

76 no coercion counterpart — the field stores whatever string is submitted. 

77 """ 

78 return _enum_options(enum_cls, by, label, DataListChoice) 

79 

80 

81class DataList: 

82 """A ``<datalist>`` of suggestions attached to a single field. 

83 

84 Passed to a :class:`~wtforms.Field` via its ``datalist=`` parameter 

85 to add an autocomplete-style list of choices. See the WTForms 

86 fields documentation for usage. 

87 """ 

88 

89 widget = widgets.DataListWidget() 

90 

91 def __init__(self, choices=None, *, render_kw=None, widget=None): 

92 self._raw_choices = choices 

93 self._choices = None if callable(choices) else choices 

94 self.render_kw = render_kw or {} 

95 if widget is not None: 

96 self.widget = widget 

97 self.id = None 

98 

99 def _clone(self, id): 

100 clone = DataList.__new__(DataList) 

101 clone._raw_choices = self._raw_choices 

102 clone._choices = None if callable(self._raw_choices) else self._raw_choices 

103 clone.render_kw = self.render_kw 

104 clone.widget = self.widget 

105 clone.id = id 

106 return clone 

107 

108 def _resolve(self, field): 

109 raw = self._raw_choices 

110 if not callable(raw): 

111 return 

112 try: 

113 sig = get_signature(raw) 

114 sig.bind(field._form, field) 

115 except TypeError: 

116 self._choices = raw() 

117 return 

118 self._choices = raw(field._form, field) 

119 

120 def iter_choices(self, field=None): 

121 raw = self._choices 

122 if raw is None: 

123 return [] 

124 if isinstance(raw, dict): 

125 return [ 

126 DataListChoice(value=value, label=label) for value, label in raw.items() 

127 ] 

128 return [DataListChoice.from_input(item) for item in raw] 

129 

130 def __call__(self, field=None, **kwargs): 

131 return self.widget(self, field=field, **kwargs)