WTForms 3.3.0b1__tar.gz → 3.3.0b3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (185) hide show
  1. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/CHANGES.rst +66 -25
  2. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/PKG-INFO +1 -1
  3. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/docs/fields.rst +66 -31
  4. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/docs/widgets.rst +2 -3
  5. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/__init__.py +5 -1
  6. wtforms-3.3.0b3/src/wtforms/_compat.py +15 -0
  7. wtforms-3.3.0b3/src/wtforms/datalist.py +127 -0
  8. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/fields/__init__.py +4 -0
  9. wtforms-3.3.0b3/src/wtforms/fields/choices.py +576 -0
  10. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/fields/core.py +16 -0
  11. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/fields/form.py +17 -2
  12. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/fields/list.py +5 -1
  13. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/form.py +29 -0
  14. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/widgets/core.py +55 -16
  15. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/conftest.py +14 -6
  16. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/fields/test_form.py +98 -0
  17. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/fields/test_radio.py +11 -7
  18. wtforms-3.3.0b3/tests/fields/test_select.py +810 -0
  19. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/fields/test_selectmultiple.py +81 -27
  20. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/test_datalist.py +36 -48
  21. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/test_widgets.py +68 -16
  22. wtforms-3.3.0b1/.hgignore +0 -1
  23. wtforms-3.3.0b1/src/wtforms/datalist.py +0 -52
  24. wtforms-3.3.0b1/src/wtforms/fields/choices.py +0 -338
  25. wtforms-3.3.0b1/tests/fields/test_select.py +0 -478
  26. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/.gitignore +0 -0
  27. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/LICENSE.rst +0 -0
  28. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/README.rst +0 -0
  29. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/docs/Makefile +0 -0
  30. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/docs/_static/logo_joined.svg +0 -0
  31. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/docs/_static/logo_split.svg +0 -0
  32. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/docs/_static/logo_square.svg +0 -0
  33. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/docs/changes.rst +0 -0
  34. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/docs/conf.py +0 -0
  35. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/docs/contributing.rst +0 -0
  36. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/docs/crash_course.rst +0 -0
  37. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/docs/csrf.rst +0 -0
  38. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/docs/ext.rst +0 -0
  39. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/docs/faq.rst +0 -0
  40. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/docs/forms.rst +0 -0
  41. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/docs/i18n.rst +0 -0
  42. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/docs/index.rst +0 -0
  43. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/docs/license.rst +0 -0
  44. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/docs/make.bat +0 -0
  45. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/docs/meta.rst +0 -0
  46. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/docs/specific_problems.rst +0 -0
  47. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/docs/validators.rst +0 -0
  48. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/docs/whats_new.rst +0 -0
  49. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/hatch_build.py +0 -0
  50. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/pyproject.toml +0 -0
  51. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/csrf/__init__.py +0 -0
  52. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/csrf/core.py +0 -0
  53. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/csrf/session.py +0 -0
  54. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/fields/datetime.py +0 -0
  55. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/fields/numeric.py +0 -0
  56. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/fields/simple.py +0 -0
  57. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/i18n.py +0 -0
  58. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/README.md +0 -0
  59. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/ar/LC_MESSAGES/wtforms.mo +0 -0
  60. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/ar/LC_MESSAGES/wtforms.po +0 -0
  61. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/bg/LC_MESSAGES/wtforms.mo +0 -0
  62. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/bg/LC_MESSAGES/wtforms.po +0 -0
  63. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/ca/LC_MESSAGES/wtforms.mo +0 -0
  64. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/ca/LC_MESSAGES/wtforms.po +0 -0
  65. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/cs_CZ/LC_MESSAGES/wtforms.mo +0 -0
  66. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/cs_CZ/LC_MESSAGES/wtforms.po +0 -0
  67. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/cy/LC_MESSAGES/wtforms.mo +0 -0
  68. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/cy/LC_MESSAGES/wtforms.po +0 -0
  69. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/de/LC_MESSAGES/wtforms.mo +0 -0
  70. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/de/LC_MESSAGES/wtforms.po +0 -0
  71. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/de_CH/LC_MESSAGES/wtforms.mo +0 -0
  72. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/de_CH/LC_MESSAGES/wtforms.po +0 -0
  73. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/el/LC_MESSAGES/wtforms.mo +0 -0
  74. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/el/LC_MESSAGES/wtforms.po +0 -0
  75. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/en/LC_MESSAGES/wtforms.mo +0 -0
  76. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/en/LC_MESSAGES/wtforms.po +0 -0
  77. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/es/LC_MESSAGES/wtforms.mo +0 -0
  78. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/es/LC_MESSAGES/wtforms.po +0 -0
  79. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/et/LC_MESSAGES/wtforms.mo +0 -0
  80. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/et/LC_MESSAGES/wtforms.po +0 -0
  81. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/fa/LC_MESSAGES/wtforms.mo +0 -0
  82. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/fa/LC_MESSAGES/wtforms.po +0 -0
  83. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/fi/LC_MESSAGES/wtforms.mo +0 -0
  84. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/fi/LC_MESSAGES/wtforms.po +0 -0
  85. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/fr/LC_MESSAGES/wtforms.mo +0 -0
  86. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/fr/LC_MESSAGES/wtforms.po +0 -0
  87. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/he/LC_MESSAGES/wtforms.mo +0 -0
  88. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/he/LC_MESSAGES/wtforms.po +0 -0
  89. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/hu/LC_MESSAGES/wtforms.mo +0 -0
  90. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/hu/LC_MESSAGES/wtforms.po +0 -0
  91. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/it/LC_MESSAGES/wtforms.mo +0 -0
  92. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/it/LC_MESSAGES/wtforms.po +0 -0
  93. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/ja/LC_MESSAGES/wtforms.mo +0 -0
  94. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/ja/LC_MESSAGES/wtforms.po +0 -0
  95. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/kk/LC_MESSAGES/wtforms.mo +0 -0
  96. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/kk/LC_MESSAGES/wtforms.po +0 -0
  97. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/ko/LC_MESSAGES/wtforms.mo +0 -0
  98. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/ko/LC_MESSAGES/wtforms.po +0 -0
  99. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/nb/LC_MESSAGES/wtforms.mo +0 -0
  100. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/nb/LC_MESSAGES/wtforms.po +0 -0
  101. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/nl/LC_MESSAGES/wtforms.mo +0 -0
  102. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/nl/LC_MESSAGES/wtforms.po +0 -0
  103. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/pl/LC_MESSAGES/wtforms.mo +0 -0
  104. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/pl/LC_MESSAGES/wtforms.po +0 -0
  105. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/pt/LC_MESSAGES/wtforms.mo +0 -0
  106. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/pt/LC_MESSAGES/wtforms.po +0 -0
  107. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/ro/LC_MESSAGES/wtforms.mo +0 -0
  108. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/ro/LC_MESSAGES/wtforms.po +0 -0
  109. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/ru/LC_MESSAGES/wtforms.mo +0 -0
  110. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/ru/LC_MESSAGES/wtforms.po +0 -0
  111. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/sk/LC_MESSAGES/wtforms.mo +0 -0
  112. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/sk/LC_MESSAGES/wtforms.po +0 -0
  113. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/sr/LC_MESSAGES/wtforms.mo +0 -0
  114. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/sr/LC_MESSAGES/wtforms.po +0 -0
  115. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/sv/LC_MESSAGES/wtforms.mo +0 -0
  116. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/sv/LC_MESSAGES/wtforms.po +0 -0
  117. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/ta/LC_MESSAGES/wtforms.mo +0 -0
  118. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/ta/LC_MESSAGES/wtforms.po +0 -0
  119. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/tr/LC_MESSAGES/wtforms.mo +0 -0
  120. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/tr/LC_MESSAGES/wtforms.po +0 -0
  121. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/uk/LC_MESSAGES/wtforms.mo +0 -0
  122. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/uk/LC_MESSAGES/wtforms.po +0 -0
  123. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/wtforms.pot +0 -0
  124. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/zh/LC_MESSAGES/wtforms.mo +0 -0
  125. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/zh/LC_MESSAGES/wtforms.po +0 -0
  126. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/zh_TW/LC_MESSAGES/wtforms.mo +0 -0
  127. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/locale/zh_TW/LC_MESSAGES/wtforms.po +0 -0
  128. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/meta.py +0 -0
  129. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/utils.py +0 -0
  130. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/validators.py +0 -0
  131. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/src/wtforms/widgets/__init__.py +0 -0
  132. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/__init__.py +0 -0
  133. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/common.py +0 -0
  134. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/fields/__init__.py +0 -0
  135. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/fields/test_boolean.py +0 -0
  136. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/fields/test_button.py +0 -0
  137. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/fields/test_color.py +0 -0
  138. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/fields/test_custom.py +0 -0
  139. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/fields/test_date.py +0 -0
  140. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/fields/test_datetime.py +0 -0
  141. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/fields/test_datetimelocal.py +0 -0
  142. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/fields/test_decimal.py +0 -0
  143. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/fields/test_defaults.py +0 -0
  144. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/fields/test_field.py +0 -0
  145. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/fields/test_file.py +0 -0
  146. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/fields/test_filters.py +0 -0
  147. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/fields/test_flags.py +0 -0
  148. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/fields/test_float.py +0 -0
  149. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/fields/test_hidden.py +0 -0
  150. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/fields/test_html5.py +0 -0
  151. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/fields/test_integer.py +0 -0
  152. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/fields/test_label.py +0 -0
  153. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/fields/test_list.py +0 -0
  154. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/fields/test_month.py +0 -0
  155. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/fields/test_password.py +0 -0
  156. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/fields/test_string.py +0 -0
  157. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/fields/test_submit.py +0 -0
  158. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/fields/test_textarea.py +0 -0
  159. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/fields/test_time.py +0 -0
  160. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/fields/test_week.py +0 -0
  161. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/test_csrf.py +0 -0
  162. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/test_form.py +0 -0
  163. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/test_i18n.py +0 -0
  164. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/test_locale_babel.py +0 -0
  165. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/test_webob.py +0 -0
  166. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/validators/__init__.py +0 -0
  167. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/validators/test_anyof.py +0 -0
  168. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/validators/test_data_required.py +0 -0
  169. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/validators/test_date_range.py +0 -0
  170. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/validators/test_disabled.py +0 -0
  171. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/validators/test_email.py +0 -0
  172. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/validators/test_equal_to.py +0 -0
  173. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/validators/test_input_required.py +0 -0
  174. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/validators/test_ipaddress.py +0 -0
  175. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/validators/test_lazy_proxy.py +0 -0
  176. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/validators/test_length.py +0 -0
  177. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/validators/test_mac_address.py +0 -0
  178. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/validators/test_noneof.py +0 -0
  179. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/validators/test_number_range.py +0 -0
  180. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/validators/test_optional.py +0 -0
  181. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/validators/test_readonly.py +0 -0
  182. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/validators/test_regexp.py +0 -0
  183. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/validators/test_url.py +0 -0
  184. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/validators/test_uuid.py +0 -0
  185. {wtforms-3.3.0b1 → wtforms-3.3.0b3}/tests/validators/test_validators.py +0 -0
@@ -1,5 +1,48 @@
1
1
  .. currentmodule:: wtforms
2
2
 
3
+ Version 3.3.0b3
4
+ ---------------
5
+
6
+ Released 2026-06-12
7
+
8
+ - Add :func:`fields.enum_choices`, :func:`fields.enum_coerce` and
9
+ :func:`~wtforms.datalist.enum_datalist` for Enum-backed choices,
10
+ replacing ``SelectChoice.from_enum`` and friends. :issue:`922`
11
+ - An Enum ``coerce`` is no longer wrapped, and options default to
12
+ ``member.value``. :issue:`922`
13
+
14
+ Version 3.3.0b2
15
+ ---------------
16
+
17
+ Released 2026-05-24
18
+
19
+ - :class:`~fields.SelectField` ``choices`` accepts a shorthand dict
20
+ syntax: ``{value: label}`` for flat options and
21
+ ``{label: {value: label}}`` for optgroups. :issue:`886`
22
+ - Split choice types by role: :class:`~fields.SelectChoice` and
23
+ :class:`~fields.DataListChoice` to declare options via ``choices=``,
24
+ :class:`~fields.Choice` for what ``iter_choices`` / ``iter_groups``
25
+ yield. :issue:`922`
26
+ - ``choices`` callables on :class:`~fields.SelectField` and
27
+ :class:`~datalist.DataList` may accept ``(form, field)``. Resolved
28
+ once per processing cycle. :issue:`922`
29
+ - Add :meth:`fields.Field.post_process` and
30
+ :meth:`form.BaseForm.post_process` hooks, propagated through
31
+ :class:`~fields.FormField` and :class:`~fields.FieldList` for
32
+ cross-field finalization. :issue:`922`
33
+ - Add ``Field._form`` and ``BaseForm._parent_form`` so fields and nested
34
+ forms can reach their enclosing form. Fix :class:`~fields.FieldList`
35
+ entries and :class:`~fields.SelectField` option subfields which
36
+ previously got ``form=None``. :issue:`922`
37
+ - Restore 3.2 compatibility for :class:`~fields.SelectField` subclasses:
38
+ tuple yields from :meth:`~fields.SelectFieldBase.iter_choices` /
39
+ :meth:`~fields.SelectFieldBase.iter_groups`,
40
+ 3-positional :meth:`widgets.Select.render_option` overrides,
41
+ :meth:`~fields.SelectFieldBase.has_groups` /
42
+ :meth:`~fields.SelectFieldBase.iter_groups`, and ``self.choices``
43
+ keeping the user-supplied shape. Each emits a ``DeprecationWarning``;
44
+ will be removed in WTForms 4.0. :issue:`922`
45
+
3
46
  Version 3.3.0b1
4
47
  ---------------
5
48
 
@@ -12,66 +55,64 @@ Released 2026-05-10
12
55
  ``step="any"``. :issue:`677` :pr:`679`
13
56
  - :meth:`~fields.FieldList._add_entry` now routes through :meth:`~meta.DefaultMeta.bind_field`,
14
57
  consistent with how the form constructor binds top-level fields. Custom ``Meta.bind_field``
15
- overrides were silently bypassed for all ``FieldList`` entries.
16
- - Add `test_environment` support to :class:`~validators.Email`. :issue:`869`
17
- - Reset :class:`~fields.FieldList` index state when reprocessing entries. :issue:`874`
18
- - Let :class:`~widgets.RangeInput` accept `min` and `max` at construction time. :issue:`693`
58
+ overrides were silently bypassed for all ``FieldList`` entries. :pr:`891`
59
+ - Add `test_environment` support to :class:`~validators.Email`. :issue:`869` :pr:`894`
60
+ - Reset :class:`~fields.FieldList` index state when reprocessing entries. :issue:`874` :pr:`895`
61
+ - Let :class:`~widgets.RangeInput` accept `min` and `max` at construction time. :issue:`693` :pr:`898`
19
62
  - Do not render a ``for`` attribute on the main label of :class:`~fields.RadioField`.
20
- :issue:`775`
21
- - Deprecate :class:`~fields.DateTimeField`, which will be removed in WTForms 3.4. :issue:`831`
63
+ :issue:`775` :pr:`900`
64
+ - Deprecate :class:`~fields.DateTimeField`, which will be removed in WTForms 3.4. :issue:`831` :pr:`897`
22
65
  - Clarify that :class:`~fields.SelectField` ``choices`` is re-evaluated at
23
- every form instantiation, including callables, in the documentation. :issue:`875`
66
+ every form instantiation, including callables, in the documentation. :issue:`875` :pr:`909`
24
67
  - Render ``False`` as ``value="False"`` in :class:`~fields.SelectField` options
25
- instead of omitting the attribute. :issue:`878`
68
+ instead of omitting the attribute. :issue:`878` :pr:`908`
26
69
  - Accept :class:`uuid.UUID` instances in the :class:`~validators.UUID` validator,
27
70
  and translate unexpected types into :class:`~validators.ValidationError`
28
71
  instead of letting :class:`TypeError` escape. :issue:`549` :pr:`769`
29
72
  - Escape double quotes in :class:`~markupsafe.Markup` attribute values
30
73
  passed to :func:`~widgets.html_params`, and return a
31
- :class:`~markupsafe.Markup`. :issue:`801`
32
- - Clarify documentation about filters. :issue:`859`
74
+ :class:`~markupsafe.Markup`. :issue:`801` :pr:`906` :pr:`907`
75
+ - Clarify documentation about filters. :issue:`859` :pr:`896`
33
76
  - Let render-time ``checked`` kwargs take precedence over the field's value
34
77
  in :class:`~widgets.CheckboxInput` and :class:`~widgets.RadioInput`.
35
- :issue:`706`
78
+ :issue:`706` :pr:`910`
36
79
  - Add a ``tz`` parameter to :class:`~fields.DateTimeLocalField` to associate a
37
- timezone with the input. :issue:`833`
80
+ timezone with the input. :issue:`833` :pr:`905`
38
81
  - Add :class:`~fields.FieldList` ``insert_entry`` and indexed ``pop_entry``.
39
- :issue:`256`
40
- - :class:`~fields.FieldList` builds entries in the order keys appear in
41
- ``formdata``, instead of sorting them by numeric index. :issue:`256`
82
+ :issue:`256` :pr:`901` :pr:`912`
42
83
  - Add :class:`~fields.ButtonField` to render ``<button type="submit">``
43
- controls with string values. :issue:`784`
84
+ controls with string values. :issue:`784` :pr:`892`
44
85
  - :class:`~fields.SelectField` refactor. Choices tuples and dicts are
45
- deprecated in favor of :class:`~fields.Choice`. :pr:`739`
86
+ deprecated in favor of :class:`~fields.Choice`. :pr:`739` :pr:`889`
46
87
  - ``<option>`` HTML attributes can be passed using
47
- :class:`~fields.Choice`. :issue:`692` :pr:`739`
88
+ :class:`~fields.Choice`. :issue:`692` :pr:`739` :pr:`889`
48
89
  - Add :meth:`fields.Choice.from_enum` to build choices from an
49
90
  :class:`enum.Enum` class, and let :class:`~fields.SelectField` accept an
50
- Enum class as ``coerce``. :issue:`338`
91
+ Enum class as ``coerce``. :issue:`338` :pr:`916`
51
92
  - Defer to ``email_validator``'s module-level defaults for the
52
93
  :class:`~validators.Email` ``test_environment``, ``allow_smtputf8`` and
53
- ``allow_empty_local`` options. :issue:`915`
94
+ ``allow_empty_local`` options. :issue:`915` :pr:`917`
54
95
  - Add ``invalid_value_message`` and ``invalid_choice_message`` keyword
55
96
  arguments for customizing built-in field error messages. On
56
97
  ``SelectMultipleField``, ``invalid_choice_message`` may also be a callable
57
- for custom pluralization. :issue:`39` :issue:`832`
98
+ for custom pluralization. :issue:`39` :issue:`832` :pr:`902`
58
99
  - :class:`~fields.SelectField` and :class:`~fields.SelectMultipleField`
59
100
  ``pre_validate`` now short-circuits when ``process_formdata`` already
60
101
  produced an error, so a single coercion failure no longer surfaces as both
61
- an "invalid value" and an "invalid choice" error.
102
+ an "invalid value" and an "invalid choice" error. :pr:`902`
62
103
  - Add a ``matcher`` keyword argument to :class:`~validators.Regexp` to
63
104
  override the matching function. Defaults to :func:`re.match`; pass
64
105
  :func:`re.search` or :func:`re.fullmatch` to change the anchoring
65
- behaviour. :issue:`867`
106
+ behaviour. :issue:`867` :pr:`918`
66
107
  - Add an ``html_pattern`` keyword argument to :class:`~validators.Regexp`
67
108
  to control the HTML ``pattern`` attribute emitted on supporting widgets.
68
- :issue:`759`
109
+ :pr:`759`
69
110
  - :class:`~validators.URL` validator is based on ``urllib.parse.urlparse``
70
111
  instead of a regex. New keyword arguments: ``allow_userinfo`` for
71
112
  accepting ``user:password@host`` URLs; ``schemes``
72
113
  (default ``("http", "https")``) is the allowlist of accepted schemes
73
114
  :pr:`847`
74
- - Add native :class:`DataList` support. :issue:`653`
115
+ - Add native :class:`DataList` support. :issue:`653` :pr:`921`
75
116
 
76
117
  Version 3.2.2
77
118
  -------------
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: WTForms
3
- Version: 3.3.0b1
3
+ Version: 3.3.0b3
4
4
  Summary: Form validation and rendering for Python web development.
5
5
  Project-URL: Documentation, https://wtforms.readthedocs.io
6
6
  Project-URL: Changes, https://wtforms.readthedocs.io/changes
@@ -287,10 +287,10 @@ refer to a single input from the form.
287
287
  Choice Fields
288
288
  -------------
289
289
 
290
- .. autoclass:: Choice
291
-
292
290
  .. autoclass:: SelectChoice
293
291
 
292
+ .. autoclass:: Choice
293
+
294
294
  .. autoclass:: RadioField(default field arguments, choices=None, coerce=str)
295
295
 
296
296
  .. code-block:: jinja
@@ -310,23 +310,23 @@ Choice Fields
310
310
 
311
311
  Select fields take a ``choices`` parameter which is either:
312
312
 
313
- * a list of :class:`Choice`.
313
+ * a list of :class:`SelectChoice`.
314
314
  It can also be a list of only values, in which case the value is used
315
- as the label. The :class:`Choice` ``render_kw`` mapping is rendered as
315
+ as the label. The :class:`SelectChoice` ``render_kw`` mapping is rendered as
316
316
  HTML :mdn-tag:`option` parameters. The value can be of any
317
317
  type, but because form data is sent to the browser as strings, you
318
318
  will need to provide a ``coerce`` function that converts a string
319
319
  back to the expected type.
320
- * a function taking no argument, and returning a list of :class:`Choice`.
320
+ * a function taking no argument, and returning a list of :class:`SelectChoice`.
321
321
 
322
322
 
323
323
  **Select fields with static choice values**::
324
324
 
325
325
  class PastebinEntry(Form):
326
326
  language = SelectField('Programming Language', choices=[
327
- Choice('cpp', 'C++'),
328
- Choice('py', 'Python'),
329
- Choice('text', 'Plain Text'),
327
+ SelectChoice('cpp', 'C++'),
328
+ SelectChoice('py', 'Python'),
329
+ SelectChoice('text', 'Plain Text'),
330
330
  ])
331
331
 
332
332
  Note that the `choices` keyword is evaluated each time the form is
@@ -337,9 +337,7 @@ Choice Fields
337
337
  option cannot be applied to your problem you may wish to skip choice
338
338
  validation (see below).
339
339
 
340
- **Select fields with ``<optgroup>``**::
341
-
342
- Use :class:`SelectChoice` to assign an option to an ``<optgroup>``.
340
+ **Select fields with optgroup**::
343
341
 
344
342
  class PastebinEntry(Form):
345
343
  language = SelectField('Programming Language', choices=[
@@ -349,10 +347,12 @@ Choice Fields
349
347
  SelectChoice('text', 'Plain Text'),
350
348
  ])
351
349
 
350
+ Use :class:`SelectChoice` to assign an option to an ``<optgroup>``.
351
+
352
352
  **Select fields with dynamic choice values**::
353
353
 
354
354
  def available_groups():
355
- return [Choice(g.id, g.name) for g in Group.query.order_by('name')]
355
+ return [SelectChoice(g.id, g.name) for g in Group.query.order_by('name')]
356
356
 
357
357
  class UserDetails(Form):
358
358
  group_id = SelectField('Group', coerce=int, choices=available_groups)
@@ -368,6 +368,18 @@ Choice Fields
368
368
  keyword arg to :class:`~wtforms.fields.SelectField` says that we use
369
369
  :func:`int()` to coerce form data. The default coerce is :func:`str()`.
370
370
 
371
+ The callable may optionally accept ``(form, field)`` as positional
372
+ arguments. When this signature is used, the callable is invoked after the
373
+ whole form has been processed, so it can read ``field.data`` and the data
374
+ of any other field on the form::
375
+
376
+ def available_groups(form, field):
377
+ return [SelectChoice(g.id, g.name) for g in form.tenant.data.groups]
378
+
379
+ class UserDetails(Form):
380
+ tenant = QuerySelectField('Tenant', ...)
381
+ group_id = SelectField('Group', coerce=int, choices=available_groups)
382
+
371
383
  **Coerce function example**::
372
384
 
373
385
  def coerce_none(value):
@@ -377,9 +389,9 @@ Choice Fields
377
389
 
378
390
  class NonePossible(Form):
379
391
  my_select_field = SelectField('Select an option', choices=[
380
- Choice('1', 'Option 1'),
381
- Choice('2', 'Option 2'),
382
- Choice('None', 'No option'),
392
+ SelectChoice('1', 'Option 1'),
393
+ SelectChoice('2', 'Option 2'),
394
+ SelectChoice('None', 'No option'),
383
395
  ], coerce=coerce_none)
384
396
 
385
397
  Note when the option None is selected a 'None' str will be passed. By using a coerce
@@ -388,28 +400,44 @@ Choice Fields
388
400
  **Select fields backed by an Enum**::
389
401
 
390
402
  from enum import Enum
403
+ from wtforms.fields import enum_choices, enum_coerce
391
404
 
392
405
  class Color(Enum):
393
- RED = 1
394
- GREEN = 2
395
- BLUE = 3
406
+ RED = "red"
407
+ GREEN = "green"
408
+ BLUE = "blue"
396
409
 
397
410
  class PaintForm(Form):
398
- color = SelectField(choices=Choice.from_enum(Color), coerce=Color)
411
+ color = SelectField(
412
+ choices=enum_choices(Color),
413
+ coerce=enum_coerce(Color),
414
+ )
415
+
416
+ :func:`~wtforms.fields.enum_choices` builds the option list from
417
+ the Enum members; by default the HTML ``value`` of each option is the
418
+ member's ``value`` (``"red"``, ``"green"``, ...).
419
+ :func:`~wtforms.fields.enum_coerce` returns the matching ``coerce``
420
+ callable that resolves the submitted string back into a member, so
421
+ ``form.color.data`` is a ``Color`` member after submit. Pre-selecting
422
+ works the usual way, with an Enum member: ``PaintForm(color=Color.RED)``.
423
+
424
+ Use ``by="name"`` on **both** helpers when the Enum value is not a good
425
+ transport identifier — non-unique, non-serialisable, or when you simply
426
+ want ``member.name`` on the wire::
399
427
 
400
- :meth:`Choice.from_enum` builds the option list from the Enum items;
401
- the HTML ``value`` of each option is the item's ``name``. Passing the
402
- Enum class itself as ``coerce`` installs the matching coercion, so
403
- ``form.color.data`` is a ``Color`` item after submit. Pre-selecting
404
- works the usual way, with an Enum item: ``PaintForm(color=Color.RED)``.
428
+ class PaintForm(Form):
429
+ color = SelectField(
430
+ choices=enum_choices(Color, by="name"),
431
+ coerce=enum_coerce(Color, by="name"),
432
+ )
405
433
 
406
- By default the option label is ``str(item)`` if the Enum defines its
434
+ By default the option label is ``str(member)`` if the Enum defines its
407
435
  own ``__str__`` (also the case for :class:`enum.StrEnum`), otherwise
408
- ``item.name``. To customise, pass a ``label`` callable taking an Enum
409
- item and returning the label string::
436
+ ``member.name``. To customise, pass a ``label`` callable taking an Enum
437
+ member and returning the label string::
410
438
 
411
- Choice.from_enum(Color, label=lambda item: item.name.title())
412
- # → [Choice('RED', 'Red'), Choice('GREEN', 'Green'), Choice('BLUE', 'Blue')]
439
+ enum_choices(Color, label=lambda member: member.name.title())
440
+ # → [SelectChoice('red', 'Red'), SelectChoice('green', 'Green'), SelectChoice('blue', 'Blue')]
413
441
 
414
442
  **Skipping choice validation**::
415
443
 
@@ -442,6 +470,10 @@ Choice Fields
442
470
  which are not in the given choices list will cause validation on the field
443
471
  to fail.
444
472
 
473
+ .. autofunction:: wtforms.fields.enum_choices
474
+
475
+ .. autofunction:: wtforms.fields.enum_coerce
476
+
445
477
  Submit fields
446
478
  -------------
447
479
 
@@ -554,7 +586,7 @@ complex data structures such as lists and nested objects can be represented.
554
586
  FormField::
555
587
 
556
588
  class IMForm(Form):
557
- protocol = SelectField(choices=[Choice('aim', 'AIM'), Choice('msn', 'MSN')])
589
+ protocol = SelectField(choices=[SelectChoice('aim', 'AIM'), SelectChoice('msn', 'MSN')])
558
590
  username = StringField()
559
591
 
560
592
  class ContactForm(Form):
@@ -576,7 +608,7 @@ Data Lists
576
608
  suggestions but the user may type any value. Combine with
577
609
  :class:`~wtforms.validators.AnyOf` if you need a closed set.
578
610
 
579
- ``choices`` is either a list of :class:`~wtforms.fields.Choice`
611
+ ``choices`` is either a list of :class:`~wtforms.DataListChoice`
580
612
  (or plain strings, in which case the string is used as both value
581
613
  and label), or a callable invoked at render time. The callable
582
614
  may take no argument (``fn()``) for a static list, or ``(field)``
@@ -644,6 +676,9 @@ Data Lists
644
676
  :class:`~wtforms.fields.TextAreaField` is silently ignored by the
645
677
  browser.
646
678
 
679
+ .. autoclass:: DataListChoice
680
+
681
+ .. autofunction:: wtforms.datalist.enum_datalist
647
682
 
648
683
  .. currentmodule:: wtforms.fields
649
684
 
@@ -85,9 +85,8 @@ Inside the widget, the following ``field`` attributes are commonly used:
85
85
  - ``field._value()`` — the string representation of the current value, used
86
86
  for the ``value=`` attribute of inputs.
87
87
  - ``field.iter_choices()`` — for :class:`~wtforms.fields.SelectField` and
88
- similar; yields :class:`~wtforms.fields.SelectChoice` objects with
89
- ``value``, ``label``, ``render_kw`` and a ``_selected`` flag reflecting the
90
- current selection.
88
+ similar; yields :class:`~wtforms.fields.Choice` objects with
89
+ ``value``, ``label``, ``selected`` and ``render_kw``.
91
90
 
92
91
  To assemble the HTML, use :func:`~wtforms.widgets.html_params` for attribute
93
92
  strings, :class:`markupsafe.Markup` to mark the result as safe, and
@@ -1,6 +1,8 @@
1
1
  from wtforms import validators
2
2
  from wtforms import widgets
3
3
  from wtforms.datalist import DataList
4
+ from wtforms.datalist import DataListChoice
5
+ from wtforms.datalist import enum_datalist
4
6
  from wtforms.fields.choices import Choice
5
7
  from wtforms.fields.choices import RadioField
6
8
  from wtforms.fields.choices import SelectChoice
@@ -40,7 +42,7 @@ from wtforms.fields.simple import URLField
40
42
  from wtforms.form import Form
41
43
  from wtforms.validators import ValidationError
42
44
 
43
- __version__ = "3.3.0b1"
45
+ __version__ = "3.3.0b3"
44
46
 
45
47
  __all__ = [
46
48
  "validators",
@@ -84,4 +86,6 @@ __all__ = [
84
86
  "ColorField",
85
87
  "Choice",
86
88
  "SelectChoice",
89
+ "DataListChoice",
90
+ "enum_datalist",
87
91
  ]
@@ -0,0 +1,15 @@
1
+ """Compatibility helpers for different Python versions."""
2
+
3
+ import inspect
4
+ import sys
5
+
6
+ if sys.version_info >= (3, 14):
7
+ from annotationlib import Format
8
+
9
+ def get_signature(callable):
10
+ return inspect.signature(callable, annotation_format=Format.FORWARDREF)
11
+
12
+ else:
13
+
14
+ def get_signature(callable):
15
+ return inspect.signature(callable)
@@ -0,0 +1,127 @@
1
+ import warnings
2
+ from dataclasses import dataclass
3
+ from dataclasses import field
4
+
5
+ from wtforms import widgets
6
+ from wtforms._compat import get_signature
7
+ from wtforms.fields.choices import _enum_options
8
+ from wtforms.fields.choices import Choice
9
+
10
+ __all__ = ("DataList", "DataListChoice", "enum_datalist")
11
+
12
+
13
+ @dataclass
14
+ class 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
+
67
+ def 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
+
81
+ class 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
+ return [DataListChoice.from_input(item) for item in raw]
125
+
126
+ def __call__(self, field=None, **kwargs):
127
+ return self.widget(self, field=field, **kwargs)
@@ -1,4 +1,6 @@
1
1
  from wtforms.fields.choices import Choice
2
+ from wtforms.fields.choices import enum_choices
3
+ from wtforms.fields.choices import enum_coerce
2
4
  from wtforms.fields.choices import RadioField
3
5
  from wtforms.fields.choices import SelectChoice
4
6
  from wtforms.fields.choices import SelectField
@@ -46,6 +48,8 @@ __all__ = [
46
48
  "SelectMultipleField",
47
49
  "SelectFieldBase",
48
50
  "RadioField",
51
+ "enum_choices",
52
+ "enum_coerce",
49
53
  "DateTimeField",
50
54
  "DateField",
51
55
  "TimeField",