WTForms 3.3.0b1__tar.gz → 3.3.0b2__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.0b2}/CHANGES.rst +55 -25
  2. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/PKG-INFO +1 -1
  3. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/docs/fields.rst +32 -18
  4. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/docs/widgets.rst +2 -3
  5. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/__init__.py +3 -1
  6. wtforms-3.3.0b2/src/wtforms/_compat.py +15 -0
  7. wtforms-3.3.0b2/src/wtforms/datalist.py +122 -0
  8. wtforms-3.3.0b2/src/wtforms/fields/choices.py +550 -0
  9. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/fields/core.py +13 -0
  10. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/fields/form.py +17 -2
  11. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/fields/list.py +5 -1
  12. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/form.py +24 -0
  13. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/widgets/core.py +55 -16
  14. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/conftest.py +14 -6
  15. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/fields/test_form.py +98 -0
  16. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/fields/test_radio.py +11 -7
  17. wtforms-3.3.0b2/tests/fields/test_select.py +718 -0
  18. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/fields/test_selectmultiple.py +39 -22
  19. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/test_datalist.py +16 -48
  20. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/test_widgets.py +68 -16
  21. wtforms-3.3.0b1/.hgignore +0 -1
  22. wtforms-3.3.0b1/src/wtforms/datalist.py +0 -52
  23. wtforms-3.3.0b1/src/wtforms/fields/choices.py +0 -338
  24. wtforms-3.3.0b1/tests/fields/test_select.py +0 -478
  25. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/.gitignore +0 -0
  26. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/LICENSE.rst +0 -0
  27. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/README.rst +0 -0
  28. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/docs/Makefile +0 -0
  29. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/docs/_static/logo_joined.svg +0 -0
  30. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/docs/_static/logo_split.svg +0 -0
  31. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/docs/_static/logo_square.svg +0 -0
  32. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/docs/changes.rst +0 -0
  33. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/docs/conf.py +0 -0
  34. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/docs/contributing.rst +0 -0
  35. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/docs/crash_course.rst +0 -0
  36. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/docs/csrf.rst +0 -0
  37. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/docs/ext.rst +0 -0
  38. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/docs/faq.rst +0 -0
  39. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/docs/forms.rst +0 -0
  40. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/docs/i18n.rst +0 -0
  41. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/docs/index.rst +0 -0
  42. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/docs/license.rst +0 -0
  43. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/docs/make.bat +0 -0
  44. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/docs/meta.rst +0 -0
  45. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/docs/specific_problems.rst +0 -0
  46. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/docs/validators.rst +0 -0
  47. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/docs/whats_new.rst +0 -0
  48. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/hatch_build.py +0 -0
  49. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/pyproject.toml +0 -0
  50. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/csrf/__init__.py +0 -0
  51. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/csrf/core.py +0 -0
  52. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/csrf/session.py +0 -0
  53. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/fields/__init__.py +0 -0
  54. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/fields/datetime.py +0 -0
  55. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/fields/numeric.py +0 -0
  56. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/fields/simple.py +0 -0
  57. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/i18n.py +0 -0
  58. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/README.md +0 -0
  59. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/ar/LC_MESSAGES/wtforms.mo +0 -0
  60. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/ar/LC_MESSAGES/wtforms.po +0 -0
  61. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/bg/LC_MESSAGES/wtforms.mo +0 -0
  62. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/bg/LC_MESSAGES/wtforms.po +0 -0
  63. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/ca/LC_MESSAGES/wtforms.mo +0 -0
  64. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/ca/LC_MESSAGES/wtforms.po +0 -0
  65. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/cs_CZ/LC_MESSAGES/wtforms.mo +0 -0
  66. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/cs_CZ/LC_MESSAGES/wtforms.po +0 -0
  67. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/cy/LC_MESSAGES/wtforms.mo +0 -0
  68. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/cy/LC_MESSAGES/wtforms.po +0 -0
  69. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/de/LC_MESSAGES/wtforms.mo +0 -0
  70. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/de/LC_MESSAGES/wtforms.po +0 -0
  71. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/de_CH/LC_MESSAGES/wtforms.mo +0 -0
  72. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/de_CH/LC_MESSAGES/wtforms.po +0 -0
  73. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/el/LC_MESSAGES/wtforms.mo +0 -0
  74. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/el/LC_MESSAGES/wtforms.po +0 -0
  75. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/en/LC_MESSAGES/wtforms.mo +0 -0
  76. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/en/LC_MESSAGES/wtforms.po +0 -0
  77. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/es/LC_MESSAGES/wtforms.mo +0 -0
  78. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/es/LC_MESSAGES/wtforms.po +0 -0
  79. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/et/LC_MESSAGES/wtforms.mo +0 -0
  80. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/et/LC_MESSAGES/wtforms.po +0 -0
  81. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/fa/LC_MESSAGES/wtforms.mo +0 -0
  82. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/fa/LC_MESSAGES/wtforms.po +0 -0
  83. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/fi/LC_MESSAGES/wtforms.mo +0 -0
  84. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/fi/LC_MESSAGES/wtforms.po +0 -0
  85. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/fr/LC_MESSAGES/wtforms.mo +0 -0
  86. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/fr/LC_MESSAGES/wtforms.po +0 -0
  87. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/he/LC_MESSAGES/wtforms.mo +0 -0
  88. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/he/LC_MESSAGES/wtforms.po +0 -0
  89. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/hu/LC_MESSAGES/wtforms.mo +0 -0
  90. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/hu/LC_MESSAGES/wtforms.po +0 -0
  91. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/it/LC_MESSAGES/wtforms.mo +0 -0
  92. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/it/LC_MESSAGES/wtforms.po +0 -0
  93. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/ja/LC_MESSAGES/wtforms.mo +0 -0
  94. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/ja/LC_MESSAGES/wtforms.po +0 -0
  95. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/kk/LC_MESSAGES/wtforms.mo +0 -0
  96. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/kk/LC_MESSAGES/wtforms.po +0 -0
  97. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/ko/LC_MESSAGES/wtforms.mo +0 -0
  98. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/ko/LC_MESSAGES/wtforms.po +0 -0
  99. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/nb/LC_MESSAGES/wtforms.mo +0 -0
  100. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/nb/LC_MESSAGES/wtforms.po +0 -0
  101. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/nl/LC_MESSAGES/wtforms.mo +0 -0
  102. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/nl/LC_MESSAGES/wtforms.po +0 -0
  103. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/pl/LC_MESSAGES/wtforms.mo +0 -0
  104. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/pl/LC_MESSAGES/wtforms.po +0 -0
  105. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/pt/LC_MESSAGES/wtforms.mo +0 -0
  106. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/pt/LC_MESSAGES/wtforms.po +0 -0
  107. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/ro/LC_MESSAGES/wtforms.mo +0 -0
  108. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/ro/LC_MESSAGES/wtforms.po +0 -0
  109. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/ru/LC_MESSAGES/wtforms.mo +0 -0
  110. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/ru/LC_MESSAGES/wtforms.po +0 -0
  111. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/sk/LC_MESSAGES/wtforms.mo +0 -0
  112. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/sk/LC_MESSAGES/wtforms.po +0 -0
  113. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/sr/LC_MESSAGES/wtforms.mo +0 -0
  114. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/sr/LC_MESSAGES/wtforms.po +0 -0
  115. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/sv/LC_MESSAGES/wtforms.mo +0 -0
  116. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/sv/LC_MESSAGES/wtforms.po +0 -0
  117. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/ta/LC_MESSAGES/wtforms.mo +0 -0
  118. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/ta/LC_MESSAGES/wtforms.po +0 -0
  119. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/tr/LC_MESSAGES/wtforms.mo +0 -0
  120. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/tr/LC_MESSAGES/wtforms.po +0 -0
  121. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/uk/LC_MESSAGES/wtforms.mo +0 -0
  122. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/uk/LC_MESSAGES/wtforms.po +0 -0
  123. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/wtforms.pot +0 -0
  124. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/zh/LC_MESSAGES/wtforms.mo +0 -0
  125. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/zh/LC_MESSAGES/wtforms.po +0 -0
  126. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/zh_TW/LC_MESSAGES/wtforms.mo +0 -0
  127. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/locale/zh_TW/LC_MESSAGES/wtforms.po +0 -0
  128. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/meta.py +0 -0
  129. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/utils.py +0 -0
  130. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/validators.py +0 -0
  131. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/src/wtforms/widgets/__init__.py +0 -0
  132. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/__init__.py +0 -0
  133. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/common.py +0 -0
  134. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/fields/__init__.py +0 -0
  135. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/fields/test_boolean.py +0 -0
  136. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/fields/test_button.py +0 -0
  137. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/fields/test_color.py +0 -0
  138. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/fields/test_custom.py +0 -0
  139. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/fields/test_date.py +0 -0
  140. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/fields/test_datetime.py +0 -0
  141. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/fields/test_datetimelocal.py +0 -0
  142. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/fields/test_decimal.py +0 -0
  143. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/fields/test_defaults.py +0 -0
  144. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/fields/test_field.py +0 -0
  145. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/fields/test_file.py +0 -0
  146. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/fields/test_filters.py +0 -0
  147. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/fields/test_flags.py +0 -0
  148. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/fields/test_float.py +0 -0
  149. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/fields/test_hidden.py +0 -0
  150. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/fields/test_html5.py +0 -0
  151. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/fields/test_integer.py +0 -0
  152. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/fields/test_label.py +0 -0
  153. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/fields/test_list.py +0 -0
  154. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/fields/test_month.py +0 -0
  155. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/fields/test_password.py +0 -0
  156. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/fields/test_string.py +0 -0
  157. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/fields/test_submit.py +0 -0
  158. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/fields/test_textarea.py +0 -0
  159. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/fields/test_time.py +0 -0
  160. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/fields/test_week.py +0 -0
  161. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/test_csrf.py +0 -0
  162. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/test_form.py +0 -0
  163. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/test_i18n.py +0 -0
  164. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/test_locale_babel.py +0 -0
  165. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/test_webob.py +0 -0
  166. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/validators/__init__.py +0 -0
  167. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/validators/test_anyof.py +0 -0
  168. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/validators/test_data_required.py +0 -0
  169. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/validators/test_date_range.py +0 -0
  170. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/validators/test_disabled.py +0 -0
  171. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/validators/test_email.py +0 -0
  172. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/validators/test_equal_to.py +0 -0
  173. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/validators/test_input_required.py +0 -0
  174. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/validators/test_ipaddress.py +0 -0
  175. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/validators/test_lazy_proxy.py +0 -0
  176. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/validators/test_length.py +0 -0
  177. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/validators/test_mac_address.py +0 -0
  178. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/validators/test_noneof.py +0 -0
  179. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/validators/test_number_range.py +0 -0
  180. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/validators/test_optional.py +0 -0
  181. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/validators/test_readonly.py +0 -0
  182. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/validators/test_regexp.py +0 -0
  183. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/validators/test_url.py +0 -0
  184. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/validators/test_uuid.py +0 -0
  185. {wtforms-3.3.0b1 → wtforms-3.3.0b2}/tests/validators/test_validators.py +0 -0
@@ -1,5 +1,37 @@
1
1
  .. currentmodule:: wtforms
2
2
 
3
+ Version 3.3.0b2
4
+ ---------------
5
+
6
+ Released 2026-05-24
7
+
8
+ - :class:`~fields.SelectField` ``choices`` accepts a shorthand dict
9
+ syntax: ``{value: label}`` for flat options and
10
+ ``{label: {value: label}}`` for optgroups. :issue:`886`
11
+ - Split choice types by role: :class:`~fields.SelectChoice` and
12
+ :class:`~fields.DataListChoice` to declare options via ``choices=``,
13
+ :class:`~fields.Choice` for what ``iter_choices`` / ``iter_groups``
14
+ yield. :issue:`922`
15
+ - ``choices`` callables on :class:`~fields.SelectField` and
16
+ :class:`~datalist.DataList` may accept ``(form, field)``. Resolved
17
+ once per processing cycle. :issue:`922`
18
+ - Add :meth:`fields.Field.post_process` and
19
+ :meth:`form.BaseForm.post_process` hooks, propagated through
20
+ :class:`~fields.FormField` and :class:`~fields.FieldList` for
21
+ cross-field finalization. :issue:`922`
22
+ - Add ``Field._form`` and ``BaseForm._parent_form`` so fields and nested
23
+ forms can reach their enclosing form. Fix :class:`~fields.FieldList`
24
+ entries and :class:`~fields.SelectField` option subfields which
25
+ previously got ``form=None``. :issue:`922`
26
+ - Restore 3.2 compatibility for :class:`~fields.SelectField` subclasses:
27
+ tuple yields from :meth:`~fields.SelectFieldBase.iter_choices` /
28
+ :meth:`~fields.SelectFieldBase.iter_groups`,
29
+ 3-positional :meth:`widgets.Select.render_option` overrides,
30
+ :meth:`~fields.SelectFieldBase.has_groups` /
31
+ :meth:`~fields.SelectFieldBase.iter_groups`, and ``self.choices``
32
+ keeping the user-supplied shape. Each emits a ``DeprecationWarning``;
33
+ will be removed in WTForms 4.0. :issue:`922`
34
+
3
35
  Version 3.3.0b1
4
36
  ---------------
5
37
 
@@ -12,66 +44,64 @@ Released 2026-05-10
12
44
  ``step="any"``. :issue:`677` :pr:`679`
13
45
  - :meth:`~fields.FieldList._add_entry` now routes through :meth:`~meta.DefaultMeta.bind_field`,
14
46
  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`
47
+ overrides were silently bypassed for all ``FieldList`` entries. :pr:`891`
48
+ - Add `test_environment` support to :class:`~validators.Email`. :issue:`869` :pr:`894`
49
+ - Reset :class:`~fields.FieldList` index state when reprocessing entries. :issue:`874` :pr:`895`
50
+ - Let :class:`~widgets.RangeInput` accept `min` and `max` at construction time. :issue:`693` :pr:`898`
19
51
  - 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`
52
+ :issue:`775` :pr:`900`
53
+ - Deprecate :class:`~fields.DateTimeField`, which will be removed in WTForms 3.4. :issue:`831` :pr:`897`
22
54
  - Clarify that :class:`~fields.SelectField` ``choices`` is re-evaluated at
23
- every form instantiation, including callables, in the documentation. :issue:`875`
55
+ every form instantiation, including callables, in the documentation. :issue:`875` :pr:`909`
24
56
  - Render ``False`` as ``value="False"`` in :class:`~fields.SelectField` options
25
- instead of omitting the attribute. :issue:`878`
57
+ instead of omitting the attribute. :issue:`878` :pr:`908`
26
58
  - Accept :class:`uuid.UUID` instances in the :class:`~validators.UUID` validator,
27
59
  and translate unexpected types into :class:`~validators.ValidationError`
28
60
  instead of letting :class:`TypeError` escape. :issue:`549` :pr:`769`
29
61
  - Escape double quotes in :class:`~markupsafe.Markup` attribute values
30
62
  passed to :func:`~widgets.html_params`, and return a
31
- :class:`~markupsafe.Markup`. :issue:`801`
32
- - Clarify documentation about filters. :issue:`859`
63
+ :class:`~markupsafe.Markup`. :issue:`801` :pr:`906` :pr:`907`
64
+ - Clarify documentation about filters. :issue:`859` :pr:`896`
33
65
  - Let render-time ``checked`` kwargs take precedence over the field's value
34
66
  in :class:`~widgets.CheckboxInput` and :class:`~widgets.RadioInput`.
35
- :issue:`706`
67
+ :issue:`706` :pr:`910`
36
68
  - Add a ``tz`` parameter to :class:`~fields.DateTimeLocalField` to associate a
37
- timezone with the input. :issue:`833`
69
+ timezone with the input. :issue:`833` :pr:`905`
38
70
  - 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`
71
+ :issue:`256` :pr:`901` :pr:`912`
42
72
  - Add :class:`~fields.ButtonField` to render ``<button type="submit">``
43
- controls with string values. :issue:`784`
73
+ controls with string values. :issue:`784` :pr:`892`
44
74
  - :class:`~fields.SelectField` refactor. Choices tuples and dicts are
45
- deprecated in favor of :class:`~fields.Choice`. :pr:`739`
75
+ deprecated in favor of :class:`~fields.Choice`. :pr:`739` :pr:`889`
46
76
  - ``<option>`` HTML attributes can be passed using
47
- :class:`~fields.Choice`. :issue:`692` :pr:`739`
77
+ :class:`~fields.Choice`. :issue:`692` :pr:`739` :pr:`889`
48
78
  - Add :meth:`fields.Choice.from_enum` to build choices from an
49
79
  :class:`enum.Enum` class, and let :class:`~fields.SelectField` accept an
50
- Enum class as ``coerce``. :issue:`338`
80
+ Enum class as ``coerce``. :issue:`338` :pr:`916`
51
81
  - Defer to ``email_validator``'s module-level defaults for the
52
82
  :class:`~validators.Email` ``test_environment``, ``allow_smtputf8`` and
53
- ``allow_empty_local`` options. :issue:`915`
83
+ ``allow_empty_local`` options. :issue:`915` :pr:`917`
54
84
  - Add ``invalid_value_message`` and ``invalid_choice_message`` keyword
55
85
  arguments for customizing built-in field error messages. On
56
86
  ``SelectMultipleField``, ``invalid_choice_message`` may also be a callable
57
- for custom pluralization. :issue:`39` :issue:`832`
87
+ for custom pluralization. :issue:`39` :issue:`832` :pr:`902`
58
88
  - :class:`~fields.SelectField` and :class:`~fields.SelectMultipleField`
59
89
  ``pre_validate`` now short-circuits when ``process_formdata`` already
60
90
  produced an error, so a single coercion failure no longer surfaces as both
61
- an "invalid value" and an "invalid choice" error.
91
+ an "invalid value" and an "invalid choice" error. :pr:`902`
62
92
  - Add a ``matcher`` keyword argument to :class:`~validators.Regexp` to
63
93
  override the matching function. Defaults to :func:`re.match`; pass
64
94
  :func:`re.search` or :func:`re.fullmatch` to change the anchoring
65
- behaviour. :issue:`867`
95
+ behaviour. :issue:`867` :pr:`918`
66
96
  - Add an ``html_pattern`` keyword argument to :class:`~validators.Regexp`
67
97
  to control the HTML ``pattern`` attribute emitted on supporting widgets.
68
- :issue:`759`
98
+ :pr:`759`
69
99
  - :class:`~validators.URL` validator is based on ``urllib.parse.urlparse``
70
100
  instead of a regex. New keyword arguments: ``allow_userinfo`` for
71
101
  accepting ``user:password@host`` URLs; ``schemes``
72
102
  (default ``("http", "https")``) is the allowlist of accepted schemes
73
103
  :pr:`847`
74
- - Add native :class:`DataList` support. :issue:`653`
104
+ - Add native :class:`DataList` support. :issue:`653` :pr:`921`
75
105
 
76
106
  Version 3.2.2
77
107
  -------------
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: WTForms
3
- Version: 3.3.0b1
3
+ Version: 3.3.0b2
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
@@ -352,7 +352,7 @@ Choice Fields
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
@@ -395,9 +407,9 @@ Choice Fields
395
407
  BLUE = 3
396
408
 
397
409
  class PaintForm(Form):
398
- color = SelectField(choices=Choice.from_enum(Color), coerce=Color)
410
+ color = SelectField(choices=SelectChoice.from_enum(Color), coerce=Color)
399
411
 
400
- :meth:`Choice.from_enum` builds the option list from the Enum items;
412
+ :meth:`SelectChoice.from_enum` builds the option list from the Enum items;
401
413
  the HTML ``value`` of each option is the item's ``name``. Passing the
402
414
  Enum class itself as ``coerce`` installs the matching coercion, so
403
415
  ``form.color.data`` is a ``Color`` item after submit. Pre-selecting
@@ -408,8 +420,8 @@ Choice Fields
408
420
  ``item.name``. To customise, pass a ``label`` callable taking an Enum
409
421
  item and returning the label string::
410
422
 
411
- Choice.from_enum(Color, label=lambda item: item.name.title())
412
- # → [Choice('RED', 'Red'), Choice('GREEN', 'Green'), Choice('BLUE', 'Blue')]
423
+ SelectChoice.from_enum(Color, label=lambda item: item.name.title())
424
+ # → [SelectChoice('RED', 'Red'), SelectChoice('GREEN', 'Green'), SelectChoice('BLUE', 'Blue')]
413
425
 
414
426
  **Skipping choice validation**::
415
427
 
@@ -554,7 +566,7 @@ complex data structures such as lists and nested objects can be represented.
554
566
  FormField::
555
567
 
556
568
  class IMForm(Form):
557
- protocol = SelectField(choices=[Choice('aim', 'AIM'), Choice('msn', 'MSN')])
569
+ protocol = SelectField(choices=[SelectChoice('aim', 'AIM'), SelectChoice('msn', 'MSN')])
558
570
  username = StringField()
559
571
 
560
572
  class ContactForm(Form):
@@ -568,6 +580,8 @@ Data Lists
568
580
 
569
581
  .. currentmodule:: wtforms
570
582
 
583
+ .. autoclass:: DataListChoice
584
+
571
585
  .. class:: DataList(choices=None, *, render_kw=None, widget=None)
572
586
 
573
587
  A :mdn-tag:`datalist` of suggestions. Unlike
@@ -576,7 +590,7 @@ Data Lists
576
590
  suggestions but the user may type any value. Combine with
577
591
  :class:`~wtforms.validators.AnyOf` if you need a closed set.
578
592
 
579
- ``choices`` is either a list of :class:`~wtforms.fields.Choice`
593
+ ``choices`` is either a list of :class:`~wtforms.DataListChoice`
580
594
  (or plain strings, in which case the string is used as both value
581
595
  and label), or a callable invoked at render time. The callable
582
596
  may take no argument (``fn()``) for a static list, or ``(field)``
@@ -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,7 @@
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
4
5
  from wtforms.fields.choices import Choice
5
6
  from wtforms.fields.choices import RadioField
6
7
  from wtforms.fields.choices import SelectChoice
@@ -40,7 +41,7 @@ from wtforms.fields.simple import URLField
40
41
  from wtforms.form import Form
41
42
  from wtforms.validators import ValidationError
42
43
 
43
- __version__ = "3.3.0b1"
44
+ __version__ = "3.3.0b2"
44
45
 
45
46
  __all__ = [
46
47
  "validators",
@@ -84,4 +85,5 @@ __all__ = [
84
85
  "ColorField",
85
86
  "Choice",
86
87
  "SelectChoice",
88
+ "DataListChoice",
87
89
  ]
@@ -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,122 @@
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 Choice
8
+
9
+ __all__ = ("DataList", "DataListChoice")
10
+
11
+
12
+ @dataclass
13
+ class DataListChoice:
14
+ """
15
+ An option declared via :class:`~wtforms.DataList`'s ``choices=``
16
+ parameter.
17
+
18
+ :param value:
19
+ The value rendered as the ``<option>``'s ``value`` attribute.
20
+ :param label:
21
+ The label of the option. Defaults to ``value`` when omitted.
22
+ :param render_kw:
23
+ A dict containing HTML attributes that will be rendered
24
+ with the option. Defaults to an empty dict when omitted.
25
+ """
26
+
27
+ value: str
28
+ label: str | None = None
29
+ render_kw: dict = field(default_factory=dict)
30
+
31
+ def __post_init__(self):
32
+ if self.label is None:
33
+ self.label = self.value
34
+
35
+ def __iter__(self):
36
+ return iter((self.value, self.label, self.render_kw))
37
+
38
+ @classmethod
39
+ def from_enum(cls, enum_cls, *, label=None):
40
+ """Build a list of choices from an :class:`enum.Enum` class.
41
+
42
+ See :meth:`SelectChoice.from_enum` for details.
43
+ """
44
+ if label is None:
45
+ label = str if "__str__" in enum_cls.__dict__ else lambda m: m.name
46
+ return [cls(value=m.name, label=label(m)) for m in enum_cls]
47
+
48
+ @classmethod
49
+ def from_input(cls, input):
50
+ """Coerce a value passed by the user into a :class:`DataListChoice`."""
51
+ if isinstance(input, DataListChoice):
52
+ return input
53
+
54
+ if isinstance(input, Choice):
55
+ warnings.warn(
56
+ "Passing Choice to a DataList is deprecated; Choice is the "
57
+ "output type returned by iter_choices(). Use DataListChoice "
58
+ "instead. Support for Choice as input will be removed in "
59
+ "WTForms 4.0.",
60
+ DeprecationWarning,
61
+ stacklevel=4,
62
+ )
63
+ return cls(
64
+ value=input.value,
65
+ label=input.label,
66
+ render_kw=input.render_kw,
67
+ )
68
+
69
+ if isinstance(input, str):
70
+ return cls(value=input)
71
+
72
+ if isinstance(input, tuple):
73
+ return cls(*input)
74
+
75
+
76
+ class DataList:
77
+ """A ``<datalist>`` of suggestions attached to a single field.
78
+
79
+ Passed to a :class:`~wtforms.Field` via its ``datalist=`` parameter
80
+ to add an autocomplete-style list of choices. See the WTForms
81
+ fields documentation for usage.
82
+ """
83
+
84
+ widget = widgets.DataListWidget()
85
+
86
+ def __init__(self, choices=None, *, render_kw=None, widget=None):
87
+ self._raw_choices = choices
88
+ self._choices = None if callable(choices) else choices
89
+ self.render_kw = render_kw or {}
90
+ if widget is not None:
91
+ self.widget = widget
92
+ self.id = None
93
+
94
+ def _clone(self, id):
95
+ clone = DataList.__new__(DataList)
96
+ clone._raw_choices = self._raw_choices
97
+ clone._choices = None if callable(self._raw_choices) else self._raw_choices
98
+ clone.render_kw = self.render_kw
99
+ clone.widget = self.widget
100
+ clone.id = id
101
+ return clone
102
+
103
+ def _resolve(self, field):
104
+ raw = self._raw_choices
105
+ if not callable(raw):
106
+ return
107
+ try:
108
+ sig = get_signature(raw)
109
+ sig.bind(field._form, field)
110
+ except TypeError:
111
+ self._choices = raw()
112
+ return
113
+ self._choices = raw(field._form, field)
114
+
115
+ def iter_choices(self, field=None):
116
+ raw = self._choices
117
+ if raw is None:
118
+ return []
119
+ return [DataListChoice.from_input(item) for item in raw]
120
+
121
+ def __call__(self, field=None, **kwargs):
122
+ return self.widget(self, field=field, **kwargs)