WTForms 3.3.0b4__tar.gz → 3.3.1__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 (181) hide show
  1. {wtforms-3.3.0b4 → wtforms-3.3.1}/CHANGES.rst +101 -122
  2. {wtforms-3.3.0b4 → wtforms-3.3.1}/PKG-INFO +1 -1
  3. {wtforms-3.3.0b4 → wtforms-3.3.1}/docs/whats_new.rst +16 -1
  4. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/__init__.py +1 -1
  5. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/datalist.py +6 -2
  6. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/fields/core.py +1 -1
  7. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/fields/list.py +1 -1
  8. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/wtforms.pot +54 -39
  9. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/validators.py +1 -1
  10. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/fields/test_field.py +9 -0
  11. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/fields/test_list.py +28 -0
  12. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/test_datalist.py +24 -0
  13. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/validators/test_url.py +8 -0
  14. {wtforms-3.3.0b4 → wtforms-3.3.1}/.gitignore +0 -0
  15. {wtforms-3.3.0b4 → wtforms-3.3.1}/LICENSE.rst +0 -0
  16. {wtforms-3.3.0b4 → wtforms-3.3.1}/README.rst +0 -0
  17. {wtforms-3.3.0b4 → wtforms-3.3.1}/docs/Makefile +0 -0
  18. {wtforms-3.3.0b4 → wtforms-3.3.1}/docs/_static/logo_joined.svg +0 -0
  19. {wtforms-3.3.0b4 → wtforms-3.3.1}/docs/_static/logo_split.svg +0 -0
  20. {wtforms-3.3.0b4 → wtforms-3.3.1}/docs/_static/logo_square.svg +0 -0
  21. {wtforms-3.3.0b4 → wtforms-3.3.1}/docs/changes.rst +0 -0
  22. {wtforms-3.3.0b4 → wtforms-3.3.1}/docs/conf.py +0 -0
  23. {wtforms-3.3.0b4 → wtforms-3.3.1}/docs/contributing.rst +0 -0
  24. {wtforms-3.3.0b4 → wtforms-3.3.1}/docs/crash_course.rst +0 -0
  25. {wtforms-3.3.0b4 → wtforms-3.3.1}/docs/csrf.rst +0 -0
  26. {wtforms-3.3.0b4 → wtforms-3.3.1}/docs/ext.rst +0 -0
  27. {wtforms-3.3.0b4 → wtforms-3.3.1}/docs/faq.rst +0 -0
  28. {wtforms-3.3.0b4 → wtforms-3.3.1}/docs/fields.rst +0 -0
  29. {wtforms-3.3.0b4 → wtforms-3.3.1}/docs/forms.rst +0 -0
  30. {wtforms-3.3.0b4 → wtforms-3.3.1}/docs/i18n.rst +0 -0
  31. {wtforms-3.3.0b4 → wtforms-3.3.1}/docs/index.rst +0 -0
  32. {wtforms-3.3.0b4 → wtforms-3.3.1}/docs/license.rst +0 -0
  33. {wtforms-3.3.0b4 → wtforms-3.3.1}/docs/make.bat +0 -0
  34. {wtforms-3.3.0b4 → wtforms-3.3.1}/docs/meta.rst +0 -0
  35. {wtforms-3.3.0b4 → wtforms-3.3.1}/docs/specific_problems.rst +0 -0
  36. {wtforms-3.3.0b4 → wtforms-3.3.1}/docs/validators.rst +0 -0
  37. {wtforms-3.3.0b4 → wtforms-3.3.1}/docs/widgets.rst +0 -0
  38. {wtforms-3.3.0b4 → wtforms-3.3.1}/hatch_build.py +0 -0
  39. {wtforms-3.3.0b4 → wtforms-3.3.1}/pyproject.toml +0 -0
  40. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/_compat.py +0 -0
  41. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/csrf/__init__.py +0 -0
  42. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/csrf/core.py +0 -0
  43. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/csrf/session.py +0 -0
  44. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/fields/__init__.py +0 -0
  45. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/fields/choices.py +0 -0
  46. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/fields/datetime.py +0 -0
  47. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/fields/form.py +0 -0
  48. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/fields/numeric.py +0 -0
  49. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/fields/simple.py +0 -0
  50. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/form.py +0 -0
  51. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/i18n.py +0 -0
  52. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/README.md +0 -0
  53. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/ar/LC_MESSAGES/wtforms.mo +0 -0
  54. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/ar/LC_MESSAGES/wtforms.po +0 -0
  55. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/bg/LC_MESSAGES/wtforms.mo +0 -0
  56. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/bg/LC_MESSAGES/wtforms.po +0 -0
  57. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/ca/LC_MESSAGES/wtforms.mo +0 -0
  58. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/ca/LC_MESSAGES/wtforms.po +0 -0
  59. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/cs_CZ/LC_MESSAGES/wtforms.mo +0 -0
  60. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/cs_CZ/LC_MESSAGES/wtforms.po +0 -0
  61. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/cy/LC_MESSAGES/wtforms.mo +0 -0
  62. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/cy/LC_MESSAGES/wtforms.po +0 -0
  63. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/de/LC_MESSAGES/wtforms.mo +0 -0
  64. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/de/LC_MESSAGES/wtforms.po +0 -0
  65. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/de_CH/LC_MESSAGES/wtforms.mo +0 -0
  66. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/de_CH/LC_MESSAGES/wtforms.po +0 -0
  67. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/el/LC_MESSAGES/wtforms.mo +0 -0
  68. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/el/LC_MESSAGES/wtforms.po +0 -0
  69. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/en/LC_MESSAGES/wtforms.mo +0 -0
  70. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/en/LC_MESSAGES/wtforms.po +0 -0
  71. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/es/LC_MESSAGES/wtforms.mo +0 -0
  72. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/es/LC_MESSAGES/wtforms.po +0 -0
  73. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/et/LC_MESSAGES/wtforms.mo +0 -0
  74. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/et/LC_MESSAGES/wtforms.po +0 -0
  75. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/fa/LC_MESSAGES/wtforms.mo +0 -0
  76. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/fa/LC_MESSAGES/wtforms.po +0 -0
  77. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/fi/LC_MESSAGES/wtforms.mo +0 -0
  78. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/fi/LC_MESSAGES/wtforms.po +0 -0
  79. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/fr/LC_MESSAGES/wtforms.mo +0 -0
  80. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/fr/LC_MESSAGES/wtforms.po +0 -0
  81. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/he/LC_MESSAGES/wtforms.mo +0 -0
  82. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/he/LC_MESSAGES/wtforms.po +0 -0
  83. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/hu/LC_MESSAGES/wtforms.mo +0 -0
  84. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/hu/LC_MESSAGES/wtforms.po +0 -0
  85. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/it/LC_MESSAGES/wtforms.mo +0 -0
  86. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/it/LC_MESSAGES/wtforms.po +0 -0
  87. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/ja/LC_MESSAGES/wtforms.mo +0 -0
  88. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/ja/LC_MESSAGES/wtforms.po +0 -0
  89. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/kk/LC_MESSAGES/wtforms.mo +0 -0
  90. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/kk/LC_MESSAGES/wtforms.po +0 -0
  91. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/ko/LC_MESSAGES/wtforms.mo +0 -0
  92. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/ko/LC_MESSAGES/wtforms.po +0 -0
  93. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/nb/LC_MESSAGES/wtforms.mo +0 -0
  94. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/nb/LC_MESSAGES/wtforms.po +0 -0
  95. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/nl/LC_MESSAGES/wtforms.mo +0 -0
  96. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/nl/LC_MESSAGES/wtforms.po +0 -0
  97. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/pl/LC_MESSAGES/wtforms.mo +0 -0
  98. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/pl/LC_MESSAGES/wtforms.po +0 -0
  99. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/pt/LC_MESSAGES/wtforms.mo +0 -0
  100. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/pt/LC_MESSAGES/wtforms.po +0 -0
  101. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/ro/LC_MESSAGES/wtforms.mo +0 -0
  102. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/ro/LC_MESSAGES/wtforms.po +0 -0
  103. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/ru/LC_MESSAGES/wtforms.mo +0 -0
  104. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/ru/LC_MESSAGES/wtforms.po +0 -0
  105. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/sk/LC_MESSAGES/wtforms.mo +0 -0
  106. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/sk/LC_MESSAGES/wtforms.po +0 -0
  107. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/sr/LC_MESSAGES/wtforms.mo +0 -0
  108. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/sr/LC_MESSAGES/wtforms.po +0 -0
  109. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/sv/LC_MESSAGES/wtforms.mo +0 -0
  110. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/sv/LC_MESSAGES/wtforms.po +0 -0
  111. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/ta/LC_MESSAGES/wtforms.mo +0 -0
  112. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/ta/LC_MESSAGES/wtforms.po +0 -0
  113. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/tr/LC_MESSAGES/wtforms.mo +0 -0
  114. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/tr/LC_MESSAGES/wtforms.po +0 -0
  115. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/uk/LC_MESSAGES/wtforms.mo +0 -0
  116. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/uk/LC_MESSAGES/wtforms.po +0 -0
  117. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/zh/LC_MESSAGES/wtforms.mo +0 -0
  118. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/zh/LC_MESSAGES/wtforms.po +0 -0
  119. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/zh_TW/LC_MESSAGES/wtforms.mo +0 -0
  120. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/locale/zh_TW/LC_MESSAGES/wtforms.po +0 -0
  121. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/meta.py +0 -0
  122. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/utils.py +0 -0
  123. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/widgets/__init__.py +0 -0
  124. {wtforms-3.3.0b4 → wtforms-3.3.1}/src/wtforms/widgets/core.py +0 -0
  125. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/__init__.py +0 -0
  126. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/common.py +0 -0
  127. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/conftest.py +0 -0
  128. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/fields/__init__.py +0 -0
  129. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/fields/test_boolean.py +0 -0
  130. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/fields/test_button.py +0 -0
  131. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/fields/test_color.py +0 -0
  132. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/fields/test_custom.py +0 -0
  133. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/fields/test_date.py +0 -0
  134. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/fields/test_datetime.py +0 -0
  135. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/fields/test_datetimelocal.py +0 -0
  136. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/fields/test_decimal.py +0 -0
  137. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/fields/test_defaults.py +0 -0
  138. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/fields/test_file.py +0 -0
  139. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/fields/test_filters.py +0 -0
  140. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/fields/test_flags.py +0 -0
  141. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/fields/test_float.py +0 -0
  142. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/fields/test_form.py +0 -0
  143. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/fields/test_hidden.py +0 -0
  144. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/fields/test_html5.py +0 -0
  145. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/fields/test_integer.py +0 -0
  146. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/fields/test_label.py +0 -0
  147. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/fields/test_month.py +0 -0
  148. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/fields/test_password.py +0 -0
  149. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/fields/test_radio.py +0 -0
  150. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/fields/test_select.py +0 -0
  151. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/fields/test_selectmultiple.py +0 -0
  152. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/fields/test_string.py +0 -0
  153. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/fields/test_submit.py +0 -0
  154. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/fields/test_textarea.py +0 -0
  155. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/fields/test_time.py +0 -0
  156. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/fields/test_week.py +0 -0
  157. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/test_csrf.py +0 -0
  158. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/test_form.py +0 -0
  159. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/test_i18n.py +0 -0
  160. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/test_locale_babel.py +0 -0
  161. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/test_webob.py +0 -0
  162. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/test_widgets.py +0 -0
  163. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/validators/__init__.py +0 -0
  164. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/validators/test_anyof.py +0 -0
  165. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/validators/test_data_required.py +0 -0
  166. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/validators/test_date_range.py +0 -0
  167. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/validators/test_disabled.py +0 -0
  168. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/validators/test_email.py +0 -0
  169. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/validators/test_equal_to.py +0 -0
  170. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/validators/test_input_required.py +0 -0
  171. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/validators/test_ipaddress.py +0 -0
  172. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/validators/test_lazy_proxy.py +0 -0
  173. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/validators/test_length.py +0 -0
  174. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/validators/test_mac_address.py +0 -0
  175. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/validators/test_noneof.py +0 -0
  176. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/validators/test_number_range.py +0 -0
  177. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/validators/test_optional.py +0 -0
  178. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/validators/test_readonly.py +0 -0
  179. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/validators/test_regexp.py +0 -0
  180. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/validators/test_uuid.py +0 -0
  181. {wtforms-3.3.0b4 → wtforms-3.3.1}/tests/validators/test_validators.py +0 -0
@@ -1,146 +1,125 @@
1
1
  .. currentmodule:: wtforms
2
2
 
3
- Version 3.3.0b4
4
- ---------------
5
-
6
- Released 2026-10-23
7
-
8
- - Drop support for Python 3.10. Add support for Python 3.15.
9
- - :func:`fields.enum_choices` and :func:`~wtforms.datalist.enum_datalist`
10
- label :class:`enum.IntEnum` members with their name on PyPy too, instead of
11
- their value.
12
- - :class:`~datalist.DataList` ``choices`` accepts the shorthand
13
- ``{value: label}`` dict syntax. :issue:`886`
14
- - Default ``invalid_value_message`` and ``invalid_choice_message`` are
15
- translated when the error is raised, not when the field is built. Forms can
16
- be built again outside of a translation context.
17
- - ``choices`` callables on :class:`~fields.SelectField` and
18
- :class:`~datalist.DataList` run on the first read and their result is
19
- cached. Calling :meth:`~form.BaseForm.post_process` again refreshes them.
20
- This also applies to entries added with
21
- :meth:`~fields.FieldList.append_entry` and
22
- :meth:`~fields.FieldList.insert_entry`.
23
- - Passing a callable to ``SelectFieldBase._choices_from_input`` is
24
- deprecated.
25
- - Numeric, date, time and select fields report a validation error instead of
26
- raising a :class:`TypeError` when the input is not a string.
27
- - The labels of :class:`~fields.FieldList` entries point to the right field
28
- after :meth:`~fields.FieldList.insert_entry` or
29
- :meth:`~fields.FieldList.pop_entry` renumbers the entries.
30
-
31
- Version 3.3.0b3
32
- ---------------
3
+ Version 3.3.1
4
+ -------------
33
5
 
34
- Released 2026-06-12
6
+ Released 2026-10-24
35
7
 
36
- - Add :func:`fields.enum_choices`, :func:`fields.enum_coerce` and
37
- :func:`~wtforms.datalist.enum_datalist` for Enum-backed choices,
38
- replacing ``SelectChoice.from_enum`` and friends. :issue:`922`
39
- - An Enum ``coerce`` is no longer wrapped, and options default to
40
- ``member.value``. :issue:`922`
8
+ - Fix :class:`~fields.FieldList` entries losing the form prefix in their
9
+ ``name`` when the list renumbers them. :issue:`944`
10
+ - Speed up :class:`~validators.URL` on hostnames with many consecutive
11
+ dashes. :issue:`945`
41
12
 
42
- Version 3.3.0b2
43
- ---------------
13
+ Version 3.3.0
14
+ -------------
44
15
 
45
- Released 2026-05-24
16
+ Released 2026-10-23
46
17
 
47
- - :class:`~fields.SelectField` ``choices`` accepts a shorthand dict
48
- syntax: ``{value: label}`` for flat options and
49
- ``{label: {value: label}}`` for optgroups. :issue:`886`
50
- - Split choice types by role: :class:`~fields.SelectChoice` and
51
- :class:`~fields.DataListChoice` to declare options via ``choices=``,
52
- :class:`~fields.Choice` for what ``iter_choices`` / ``iter_groups``
53
- yield. :issue:`922`
18
+ - Drop support for Python 3.10. Add support for Python 3.15.
19
+ - Add native :class:`DataList` support. Options are declared with
20
+ :class:`DataListChoice`, or with the shorthand ``{value: label}``
21
+ dict syntax. :issue:`653` :issue:`886` :pr:`921`
22
+ - Add :class:`~fields.ButtonField` to render ``<button type="submit">``
23
+ controls with string values. :issue:`784` :pr:`892`
24
+ - Add :class:`~validators.DateRange`. ``min`` and ``max`` may be callables
25
+ to compute dynamic bounds; :class:`~validators.NumberRange` accepts
26
+ callables on the same terms. :pr:`787`
27
+ - Add :class:`~fields.FieldList` ``insert_entry`` and indexed ``pop_entry``.
28
+ The following entries are renumbered, with their ids and labels.
29
+ :issue:`256` :pr:`901` :pr:`912`
30
+ - :class:`~fields.SelectField` refactor. Options are declared with
31
+ :class:`~fields.SelectChoice`, which takes HTML attributes in
32
+ ``render_kw`` and an ``optgroup``. ``iter_choices``
33
+ and ``iter_groups`` yield
34
+ :class:`~fields.Choice` objects. ``choices`` also accepts the shorthand
35
+ dict syntax: ``{value: label}`` for flat options and
36
+ ``{label: {value: label}}`` for optgroups. :issue:`692` :issue:`886`
37
+ :issue:`922` :pr:`739` :pr:`889`
54
38
  - ``choices`` callables on :class:`~fields.SelectField` and
55
- :class:`~datalist.DataList` may accept ``(form, field)``. Resolved
56
- once per processing cycle. :issue:`922`
39
+ :class:`DataList` may accept ``(form, field)``. They run on the
40
+ first read and their result is cached. Calling
41
+ :meth:`~form.BaseForm.post_process` again refreshes them, also for the
42
+ entries added with :meth:`~fields.FieldList.append_entry` and
43
+ :meth:`~fields.FieldList.insert_entry`. :issue:`922`
44
+ - Add :func:`fields.enum_choices`, :func:`fields.enum_coerce` and
45
+ :func:`~datalist.enum_datalist` for Enum-backed choices. :issue:`338`
46
+ :issue:`922` :pr:`916`
57
47
  - Add :meth:`fields.Field.post_process` and
58
48
  :meth:`form.BaseForm.post_process` hooks, propagated through
59
- :class:`~fields.FormField` and :class:`~fields.FieldList` for
49
+ :class:`~fields.FormField` and :class:`~fields.FieldList`, for
60
50
  cross-field finalization. :issue:`922`
61
51
  - Add ``Field._form`` and ``BaseForm._parent_form`` so fields and nested
62
- forms can reach their enclosing form. Fix :class:`~fields.FieldList`
63
- entries and :class:`~fields.SelectField` option subfields which
64
- previously got ``form=None``. :issue:`922`
65
- - Restore 3.2 compatibility for :class:`~fields.SelectField` subclasses:
66
- tuple yields from :meth:`~fields.SelectFieldBase.iter_choices` /
67
- :meth:`~fields.SelectFieldBase.iter_groups`,
68
- 3-positional :meth:`widgets.Select.render_option` overrides,
69
- :meth:`~fields.SelectFieldBase.has_groups` /
70
- :meth:`~fields.SelectFieldBase.iter_groups`, and ``self.choices``
71
- keeping the user-supplied shape. Each emits a ``DeprecationWarning``;
72
- will be removed in WTForms 4.0. :issue:`922`
73
-
74
- Version 3.3.0b1
75
- ---------------
76
-
77
- Released 2026-05-10
78
-
79
- - Add :class:`~validators.DateRange`. ``min`` and ``max`` may be callables
80
- to compute dynamic bounds; :class:`~validators.NumberRange` accepts
81
- callables on the same terms. :pr:`787`
52
+ forms can reach their enclosing form. :class:`~fields.FieldList` entries
53
+ and :class:`~fields.SelectField` option subfields no longer get
54
+ ``form=None``. :issue:`922`
55
+ - Add ``invalid_value_message`` and ``invalid_choice_message`` keyword
56
+ arguments for customizing built-in field error messages. On
57
+ :class:`~fields.SelectMultipleField`, ``invalid_choice_message`` may also
58
+ be a callable for custom pluralization. :issue:`39` :issue:`832` :pr:`902`
59
+ - :class:`~fields.SelectField` and :class:`~fields.SelectMultipleField`
60
+ ``pre_validate`` short-circuits when ``process_formdata`` already produced
61
+ an error, so a single coercion failure no longer surfaces as both an
62
+ "invalid value" and an "invalid choice" error. :pr:`902`
63
+ - Numeric, date, time and select fields report a validation error instead of
64
+ raising a :class:`TypeError` when the input is not a string. :pr:`936`
65
+ - Each bound field gets its own copy of ``render_kw``, and so does its
66
+ :class:`DataList`. Changing one of them on a form instance no
67
+ longer changes it on the other ones. :issue:`942`
82
68
  - Make :class:`~fields.FloatField` render as ``<input type="number">`` with
83
69
  ``step="any"``. :issue:`677` :pr:`679`
84
- - :meth:`~fields.FieldList._add_entry` now routes through :meth:`~meta.DefaultMeta.bind_field`,
85
- consistent with how the form constructor binds top-level fields. Custom ``Meta.bind_field``
86
- overrides were silently bypassed for all ``FieldList`` entries. :pr:`891`
87
- - Add `test_environment` support to :class:`~validators.Email`. :issue:`869` :pr:`894`
88
- - Reset :class:`~fields.FieldList` index state when reprocessing entries. :issue:`874` :pr:`895`
89
- - Let :class:`~widgets.RangeInput` accept `min` and `max` at construction time. :issue:`693` :pr:`898`
90
- - Do not render a ``for`` attribute on the main label of :class:`~fields.RadioField`.
91
- :issue:`775` :pr:`900`
92
- - Deprecate :class:`~fields.DateTimeField`, which will be removed in WTForms 3.4. :issue:`831` :pr:`897`
93
- - Clarify that :class:`~fields.SelectField` ``choices`` is re-evaluated at
94
- every form instantiation, including callables, in the documentation. :issue:`875` :pr:`909`
95
- - Render ``False`` as ``value="False"`` in :class:`~fields.SelectField` options
96
- instead of omitting the attribute. :issue:`878` :pr:`908`
97
- - Accept :class:`uuid.UUID` instances in the :class:`~validators.UUID` validator,
98
- and translate unexpected types into :class:`~validators.ValidationError`
99
- instead of letting :class:`TypeError` escape. :issue:`549` :pr:`769`
100
- - Escape double quotes in :class:`~markupsafe.Markup` attribute values
101
- passed to :func:`~widgets.html_params`, and return a
102
- :class:`~markupsafe.Markup`. :issue:`801` :pr:`906` :pr:`907`
103
- - Clarify documentation about filters. :issue:`859` :pr:`896`
70
+ - Add a ``tz`` parameter to :class:`~fields.DateTimeLocalField` to associate a
71
+ timezone with the input. :issue:`833` :pr:`905`
72
+ - Let :class:`~widgets.RangeInput` accept ``min`` and ``max`` at construction
73
+ time. :issue:`693` :pr:`898`
74
+ - ``FieldList._add_entry`` routes through
75
+ :meth:`~meta.DefaultMeta.bind_field`, like the top-level fields. Custom
76
+ ``Meta.bind_field`` overrides were silently bypassed for
77
+ :class:`~fields.FieldList` entries. :pr:`891`
78
+ - Reset :class:`~fields.FieldList` index state when reprocessing entries.
79
+ :issue:`874` :pr:`895`
80
+ - Do not render a ``for`` attribute on the main label of
81
+ :class:`~fields.RadioField`. :issue:`775` :pr:`900`
82
+ - Render ``False`` as ``value="False"`` in :class:`~fields.SelectField`
83
+ options instead of omitting the attribute. :issue:`878` :pr:`908`
104
84
  - Let render-time ``checked`` kwargs take precedence over the field's value
105
85
  in :class:`~widgets.CheckboxInput` and :class:`~widgets.RadioInput`.
106
86
  :issue:`706` :pr:`910`
107
- - Add a ``tz`` parameter to :class:`~fields.DateTimeLocalField` to associate a
108
- timezone with the input. :issue:`833` :pr:`905`
109
- - Add :class:`~fields.FieldList` ``insert_entry`` and indexed ``pop_entry``.
110
- :issue:`256` :pr:`901` :pr:`912`
111
- - Add :class:`~fields.ButtonField` to render ``<button type="submit">``
112
- controls with string values. :issue:`784` :pr:`892`
113
- - :class:`~fields.SelectField` refactor. Choices tuples and dicts are
114
- deprecated in favor of :class:`~fields.Choice`. :pr:`739` :pr:`889`
115
- - ``<option>`` HTML attributes can be passed using
116
- :class:`~fields.Choice`. :issue:`692` :pr:`739` :pr:`889`
117
- - Add :meth:`fields.Choice.from_enum` to build choices from an
118
- :class:`enum.Enum` class, and let :class:`~fields.SelectField` accept an
119
- Enum class as ``coerce``. :issue:`338` :pr:`916`
120
- - Defer to ``email_validator``'s module-level defaults for the
121
- :class:`~validators.Email` ``test_environment``, ``allow_smtputf8`` and
122
- ``allow_empty_local`` options. :issue:`915` :pr:`917`
123
- - Add ``invalid_value_message`` and ``invalid_choice_message`` keyword
124
- arguments for customizing built-in field error messages. On
125
- ``SelectMultipleField``, ``invalid_choice_message`` may also be a callable
126
- for custom pluralization. :issue:`39` :issue:`832` :pr:`902`
127
- - :class:`~fields.SelectField` and :class:`~fields.SelectMultipleField`
128
- ``pre_validate`` now short-circuits when ``process_formdata`` already
129
- produced an error, so a single coercion failure no longer surfaces as both
130
- an "invalid value" and an "invalid choice" error. :pr:`902`
131
- - Add a ``matcher`` keyword argument to :class:`~validators.Regexp` to
132
- override the matching function. Defaults to :func:`re.match`; pass
133
- :func:`re.search` or :func:`re.fullmatch` to change the anchoring
134
- behaviour. :issue:`867` :pr:`918`
135
- - Add an ``html_pattern`` keyword argument to :class:`~validators.Regexp`
136
- to control the HTML ``pattern`` attribute emitted on supporting widgets.
137
- :pr:`759`
87
+ - Escape double quotes in ``Markup`` attribute values
88
+ passed to :func:`~widgets.html_params`, and return a
89
+ ``Markup``. :issue:`801` :pr:`906` :pr:`907`
90
+ - Add ``test_environment`` support to :class:`~validators.Email`, and defer
91
+ to ``email_validator``'s module-level defaults for its
92
+ ``test_environment``, ``allow_smtputf8`` and ``allow_empty_local``
93
+ options. :issue:`869` :issue:`915` :pr:`894` :pr:`917`
94
+ - Add ``matcher`` and ``html_pattern`` keyword arguments to
95
+ :class:`~validators.Regexp`. ``matcher`` overrides the matching function,
96
+ :func:`re.match` by default: pass :func:`re.search` or :func:`re.fullmatch`
97
+ to change the anchoring. ``html_pattern`` controls the HTML ``pattern``
98
+ attribute emitted on supporting widgets. :issue:`867` :pr:`759` :pr:`918`
138
99
  - :class:`~validators.URL` validator is based on ``urllib.parse.urlparse``
139
100
  instead of a regex. New keyword arguments: ``allow_userinfo`` for
140
101
  accepting ``user:password@host`` URLs; ``schemes``
141
- (default ``("http", "https")``) is the allowlist of accepted schemes
102
+ (default ``("http", "https")``) is the allowlist of accepted schemes.
142
103
  :pr:`847`
143
- - Add native :class:`DataList` support. :issue:`653` :pr:`921`
104
+ - Accept :class:`uuid.UUID` instances in the :class:`~validators.UUID`
105
+ validator, and translate unexpected types into
106
+ :class:`~validators.ValidationError` instead of letting
107
+ :class:`TypeError` escape. :issue:`549` :pr:`769`
108
+ - Clarify the documentation of filters, and of :class:`~fields.SelectField`
109
+ ``choices``. :issue:`859` :issue:`875` :pr:`896` :pr:`909`
110
+ - Update the Dutch and Serbian translations.
111
+ - Deprecate :class:`~fields.DateTimeField`, which will be removed in WTForms
112
+ 3.4. :issue:`831` :pr:`897`
113
+ - Passing :class:`~fields.SelectField` ``choices`` as tuples, or as a dict
114
+ that is not the shorthand syntax, is deprecated and will be removed in
115
+ WTForms 3.4. Use :class:`~fields.SelectChoice` instead. :issue:`922`
116
+ - Passing a callable to ``SelectFieldBase._choices_from_input`` is
117
+ deprecated and will be removed in WTForms 3.4.
118
+ - For :class:`~fields.SelectField` subclasses, yielding raw tuples from
119
+ ``iter_choices`` or
120
+ ``iter_groups``, and overriding
121
+ ``Select.render_option`` with the pre-3.3 signature, are
122
+ deprecated and will be removed in WTForms 4.0. :issue:`922`
144
123
 
145
124
  Version 3.2.2
146
125
  -------------
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: WTForms
3
- Version: 3.3.0b4
3
+ Version: 3.3.1
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
@@ -4,7 +4,22 @@ What's New in WTForms 3
4
4
  New Features
5
5
  ------------
6
6
 
7
- WTForms 3 is something something TODO
7
+ WTForms 3.3 adds:
8
+
9
+ - native :class:`~wtforms.DataList` support, to suggest values in a text
10
+ input;
11
+ - :class:`~wtforms.fields.ButtonField`, to render ``<button type="submit">``
12
+ controls;
13
+ - the :class:`~wtforms.validators.DateRange` validator;
14
+ - :meth:`~wtforms.fields.FieldList.insert_entry` and an indexed
15
+ :meth:`~wtforms.fields.FieldList.pop_entry`;
16
+ - a reworked :class:`~wtforms.fields.SelectField`: options are declared with
17
+ :class:`~wtforms.fields.SelectChoice`, which takes HTML attributes and an
18
+ optgroup, and ``choices`` callables run on the first read;
19
+ - :meth:`~wtforms.form.BaseForm.post_process` hooks for cross-field
20
+ finalization.
21
+
22
+ See :doc:`changes` for the full list, including the deprecations.
8
23
 
9
24
 
10
25
  Past Major Releases
@@ -42,7 +42,7 @@ from wtforms.fields.simple import URLField
42
42
  from wtforms.form import Form
43
43
  from wtforms.validators import ValidationError
44
44
 
45
- __version__ = "3.3.0b4"
45
+ __version__ = "3.3.1"
46
46
 
47
47
  __all__ = [
48
48
  "validators",
@@ -1,6 +1,7 @@
1
1
  import warnings
2
2
  from dataclasses import dataclass
3
3
  from dataclasses import field
4
+ from dataclasses import replace
4
5
 
5
6
  from wtforms import widgets
6
7
  from wtforms.fields.choices import _enum_options
@@ -103,7 +104,7 @@ class DataList:
103
104
  clone._raw_choices = self._raw_choices
104
105
  clone._choices = None if callable(self._raw_choices) else self._raw_choices
105
106
  clone._field = None
106
- clone.render_kw = self.render_kw
107
+ clone.render_kw = dict(self.render_kw)
107
108
  clone.widget = self.widget
108
109
  clone.id = id
109
110
  return clone
@@ -126,7 +127,10 @@ class DataList:
126
127
  return [
127
128
  DataListChoice(value=value, label=label) for value, label in raw.items()
128
129
  ]
129
- return [DataListChoice.from_input(item) for item in raw]
130
+ return [
131
+ replace(choice, render_kw=dict(choice.render_kw or {}))
132
+ for choice in map(DataListChoice.from_input, raw)
133
+ ]
130
134
 
131
135
  def __call__(self, field=None, **kwargs):
132
136
  return self.widget(self, field=field, **kwargs)
@@ -115,7 +115,7 @@ class Field:
115
115
  self.default = default
116
116
  self.description = description
117
117
  self.invalid_value_message = invalid_value_message
118
- self.render_kw = render_kw
118
+ self.render_kw = dict(render_kw) if render_kw is not None else None
119
119
  self.filters = filters
120
120
  self.flags = Flags()
121
121
  self.name = _prefix + name
@@ -195,7 +195,7 @@ class FieldList(Field):
195
195
  """Rename ``entry`` to ``new_index`` and propagate to its descendants."""
196
196
  old_name = entry.name
197
197
  old_id = entry.id
198
- new_name = f"{self.short_name}{self._separator}{new_index}"
198
+ new_name = f"{self.name}{self._separator}{new_index}"
199
199
  new_id = f"{self.id}{self._separator}{new_index}"
200
200
 
201
201
  if old_name == new_name and old_id == new_id and entry.index == new_index:
@@ -1,116 +1,131 @@
1
1
  # Translations template for WTForms.
2
- # Copyright (C) 2024 WTForms Team
2
+ # Copyright (C) 2026 WTForms Team
3
3
  # This file is distributed under the same license as the WTForms project.
4
- # FIRST AUTHOR <EMAIL@ADDRESS>, 2024.
4
+ # FIRST AUTHOR <EMAIL@ADDRESS>, 2026.
5
5
  #
6
6
  #, fuzzy
7
7
  msgid ""
8
8
  msgstr ""
9
9
  "Project-Id-Version: WTForms 3.0.0\n"
10
10
  "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
11
- "POT-Creation-Date: 2024-01-11 08:20+0100\n"
11
+ "POT-Creation-Date: 2026-10-23 16:37+0200\n"
12
12
  "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13
13
  "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14
14
  "Language-Team: LANGUAGE <LL@li.org>\n"
15
15
  "MIME-Version: 1.0\n"
16
16
  "Content-Type: text/plain; charset=utf-8\n"
17
17
  "Content-Transfer-Encoding: 8bit\n"
18
- "Generated-By: Babel 2.12.1\n"
18
+ "Generated-By: Babel 2.18.0\n"
19
19
 
20
- #: src/wtforms/validators.py:86
20
+ #: src/wtforms/validators.py:93
21
21
  #, python-format
22
22
  msgid "Invalid field name '%s'."
23
23
  msgstr ""
24
24
 
25
- #: src/wtforms/validators.py:99
25
+ #: src/wtforms/validators.py:106
26
26
  #, python-format
27
27
  msgid "Field must be equal to %(other_name)s."
28
28
  msgstr ""
29
29
 
30
- #: src/wtforms/validators.py:145
30
+ #: src/wtforms/validators.py:153
31
31
  #, python-format
32
32
  msgid "Field must be at least %(min)d character long."
33
33
  msgid_plural "Field must be at least %(min)d characters long."
34
34
  msgstr[0] ""
35
35
  msgstr[1] ""
36
36
 
37
- #: src/wtforms/validators.py:151
37
+ #: src/wtforms/validators.py:159
38
38
  #, python-format
39
39
  msgid "Field cannot be longer than %(max)d character."
40
40
  msgid_plural "Field cannot be longer than %(max)d characters."
41
41
  msgstr[0] ""
42
42
  msgstr[1] ""
43
43
 
44
- #: src/wtforms/validators.py:157
44
+ #: src/wtforms/validators.py:165
45
45
  #, python-format
46
46
  msgid "Field must be exactly %(max)d character long."
47
47
  msgid_plural "Field must be exactly %(max)d characters long."
48
48
  msgstr[0] ""
49
49
  msgstr[1] ""
50
50
 
51
- #: src/wtforms/validators.py:163
51
+ #: src/wtforms/validators.py:171
52
52
  #, python-format
53
53
  msgid "Field must be between %(min)d and %(max)d characters long."
54
54
  msgstr ""
55
55
 
56
- #: src/wtforms/validators.py:216
56
+ #: src/wtforms/validators.py:233
57
57
  #, python-format
58
58
  msgid "Number must be at least %(min)s."
59
59
  msgstr ""
60
60
 
61
- #: src/wtforms/validators.py:219
61
+ #: src/wtforms/validators.py:236
62
62
  #, python-format
63
63
  msgid "Number must be at most %(max)s."
64
64
  msgstr ""
65
65
 
66
- #: src/wtforms/validators.py:222
66
+ #: src/wtforms/validators.py:239
67
67
  #, python-format
68
68
  msgid "Number must be between %(min)s and %(max)s."
69
69
  msgstr ""
70
70
 
71
- #: src/wtforms/validators.py:293 src/wtforms/validators.py:323
71
+ #: src/wtforms/validators.py:328
72
+ #, python-format
73
+ msgid "Date must be at least %(min)s."
74
+ msgstr ""
75
+
76
+ #: src/wtforms/validators.py:331
77
+ #, python-format
78
+ msgid "Date must be at most %(max)s."
79
+ msgstr ""
80
+
81
+ #: src/wtforms/validators.py:334
82
+ #, python-format
83
+ msgid "Date must be between %(min)s and %(max)s."
84
+ msgstr ""
85
+
86
+ #: src/wtforms/validators.py:405 src/wtforms/validators.py:435
72
87
  msgid "This field is required."
73
88
  msgstr ""
74
89
 
75
- #: src/wtforms/validators.py:358
90
+ #: src/wtforms/validators.py:504
76
91
  msgid "Invalid input."
77
92
  msgstr ""
78
93
 
79
- #: src/wtforms/validators.py:422
94
+ #: src/wtforms/validators.py:581
80
95
  msgid "Invalid email address."
81
96
  msgstr ""
82
97
 
83
- #: src/wtforms/validators.py:460
98
+ #: src/wtforms/validators.py:619
84
99
  msgid "Invalid IP address."
85
100
  msgstr ""
86
101
 
87
- #: src/wtforms/validators.py:503
102
+ #: src/wtforms/validators.py:662
88
103
  msgid "Invalid Mac address."
89
104
  msgstr ""
90
105
 
91
- #: src/wtforms/validators.py:540
106
+ #: src/wtforms/validators.py:710
92
107
  msgid "Invalid URL."
93
108
  msgstr ""
94
109
 
95
- #: src/wtforms/validators.py:561
110
+ #: src/wtforms/validators.py:749
96
111
  msgid "Invalid UUID."
97
112
  msgstr ""
98
113
 
99
- #: src/wtforms/validators.py:594
114
+ #: src/wtforms/validators.py:787
100
115
  #, python-format
101
116
  msgid "Invalid value, must be one of: %(values)s."
102
117
  msgstr ""
103
118
 
104
- #: src/wtforms/validators.py:629
119
+ #: src/wtforms/validators.py:823
105
120
  #, python-format
106
121
  msgid "Invalid value, can't be any of: %(values)s."
107
122
  msgstr ""
108
123
 
109
- #: src/wtforms/validators.py:698
124
+ #: src/wtforms/validators.py:892
110
125
  msgid "This field cannot be edited."
111
126
  msgstr ""
112
127
 
113
- #: src/wtforms/validators.py:714
128
+ #: src/wtforms/validators.py:908
114
129
  msgid "This field is disabled and cannot have a value."
115
130
  msgstr ""
116
131
 
@@ -118,65 +133,65 @@ msgstr ""
118
133
  msgid "Invalid CSRF Token."
119
134
  msgstr ""
120
135
 
121
- #: src/wtforms/csrf/session.py:63
136
+ #: src/wtforms/csrf/session.py:64
122
137
  msgid "CSRF token missing."
123
138
  msgstr ""
124
139
 
125
- #: src/wtforms/csrf/session.py:71
140
+ #: src/wtforms/csrf/session.py:72
126
141
  msgid "CSRF failed."
127
142
  msgstr ""
128
143
 
129
- #: src/wtforms/csrf/session.py:76
144
+ #: src/wtforms/csrf/session.py:77
130
145
  msgid "CSRF token expired."
131
146
  msgstr ""
132
147
 
133
- #: src/wtforms/fields/choices.py:142
148
+ #: src/wtforms/fields/choices.py:480
134
149
  msgid "Invalid Choice: could not coerce."
135
150
  msgstr ""
136
151
 
137
- #: src/wtforms/fields/choices.py:149 src/wtforms/fields/choices.py:203
152
+ #: src/wtforms/fields/choices.py:491 src/wtforms/fields/choices.py:590
138
153
  msgid "Choices cannot be None."
139
154
  msgstr ""
140
155
 
141
- #: src/wtforms/fields/choices.py:155
156
+ #: src/wtforms/fields/choices.py:495
142
157
  msgid "Not a valid choice."
143
158
  msgstr ""
144
159
 
145
- #: src/wtforms/fields/choices.py:193
160
+ #: src/wtforms/fields/choices.py:578
146
161
  msgid "Invalid choice(s): one or more data inputs could not be coerced."
147
162
  msgstr ""
148
163
 
149
- #: src/wtforms/fields/choices.py:214
164
+ #: src/wtforms/fields/choices.py:605
150
165
  #, python-format
151
166
  msgid "'%(value)s' is not a valid choice for this field."
152
167
  msgid_plural "'%(value)s' are not valid choices for this field."
153
168
  msgstr[0] ""
154
169
  msgstr[1] ""
155
170
 
156
- #: src/wtforms/fields/datetime.py:51
171
+ #: src/wtforms/fields/datetime.py:74
157
172
  msgid "Not a valid datetime value."
158
173
  msgstr ""
159
174
 
160
- #: src/wtforms/fields/datetime.py:77
175
+ #: src/wtforms/fields/datetime.py:115
161
176
  msgid "Not a valid date value."
162
177
  msgstr ""
163
178
 
164
- #: src/wtforms/fields/datetime.py:103
179
+ #: src/wtforms/fields/datetime.py:156
165
180
  msgid "Not a valid time value."
166
181
  msgstr ""
167
182
 
168
- #: src/wtforms/fields/datetime.py:148
183
+ #: src/wtforms/fields/datetime.py:218
169
184
  msgid "Not a valid week value."
170
185
  msgstr ""
171
186
 
172
- #: src/wtforms/fields/numeric.py:82 src/wtforms/fields/numeric.py:92
187
+ #: src/wtforms/fields/numeric.py:86 src/wtforms/fields/numeric.py:98
173
188
  msgid "Not a valid integer value."
174
189
  msgstr ""
175
190
 
176
- #: src/wtforms/fields/numeric.py:168
191
+ #: src/wtforms/fields/numeric.py:183
177
192
  msgid "Not a valid decimal value."
178
193
  msgstr ""
179
194
 
180
- #: src/wtforms/fields/numeric.py:197
195
+ #: src/wtforms/fields/numeric.py:220
181
196
  msgid "Not a valid float value."
182
197
  msgstr ""
@@ -836,7 +836,7 @@ class HostnameValidation:
836
836
  This is not a validator in and of itself, and as such is not exported.
837
837
  """
838
838
 
839
- hostname_part = re.compile(r"^(xn-|[a-z0-9_]+)(-[a-z0-9_-]+)*$", re.IGNORECASE)
839
+ hostname_part = re.compile(r"^(xn-|[a-z0-9_]+)(-[a-z0-9_-]+)?$", re.IGNORECASE)
840
840
  tld_part = re.compile(r"^([a-z]{2,20}|xn--([a-z0-9]+-)*[a-z0-9]+)$", re.IGNORECASE)
841
841
 
842
842
  def __init__(self, require_tld=True, allow_ip=False):
@@ -77,6 +77,15 @@ def test_render_kw():
77
77
  )
78
78
 
79
79
 
80
+ def test_render_kw_is_not_shared_between_form_instances():
81
+ """Changing render_kw on one form instance does not change it on the others."""
82
+ first, second = F(), F()
83
+ first.a.render_kw["readonly"] = False
84
+ del first.a.render_kw["foo"]
85
+ assert second.a.render_kw == {"readonly": True, "foo": "bar"}
86
+ assert F.a.kwargs["render_kw"] == {"readonly": True, "foo": "bar"}
87
+
88
+
80
89
  def test_render_special():
81
90
  class F(Form):
82
91
  s = StringField(render_kw={"class_": "foo"})
@@ -236,6 +236,34 @@ def test_renumbered_entries_keep_their_labels_in_sync():
236
236
  assert [e.foo.label.field_id for e in form.b.entries] == ["b-0-foo"]
237
237
 
238
238
 
239
+ def test_renumbered_entries_keep_the_form_prefix():
240
+ """Renumbered entries keep the form prefix in their name."""
241
+ F = make_form(a=FieldList(t, min_entries=2))
242
+
243
+ form = F(DummyPostData({"p-a-0": "x", "p-a-2": "y"}), prefix="p")
244
+ assert [e.name for e in form.a.entries] == ["p-a-0", "p-a-1"]
245
+
246
+ form = F(prefix="p")
247
+ form.a.insert_entry(0, "x")
248
+ assert [e.name for e in form.a.entries] == ["p-a-0", "p-a-1", "p-a-2"]
249
+ form.a.pop_entry(0)
250
+ assert [e.name for e in form.a.entries] == ["p-a-0", "p-a-1"]
251
+
252
+
253
+ def test_renumbered_entries_in_a_subform_keep_the_subform_prefix():
254
+ """Renumbered entries of a list inside a :class:`FormField` keep the
255
+ subform prefix in their name."""
256
+
257
+ class Inside(Form):
258
+ a = FieldList(FormField(make_form(foo=StringField())), min_entries=2)
259
+
260
+ F = make_form(sub=FormField(Inside))
261
+ form = F()
262
+ form.sub.a.pop_entry(0)
263
+ assert form.sub.a[0].name == "sub-a-0"
264
+ assert form.sub.a[0].foo.name == "sub-a-0-foo"
265
+
266
+
239
267
  def test_gap_compacted_through_formdata_round_trip():
240
268
  """A formdata with gaps in indices is compacted on read: entries are
241
269
  rebuilt with consecutive indices, preserving formdata key order."""