WTForms 3.3.0b2__tar.gz → 3.3.0b4__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.
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/CHANGES.rst +39 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/PKG-INFO +3 -3
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/docs/fields.rst +66 -36
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/docs/forms.rst +2 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/pyproject.toml +4 -4
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/__init__.py +3 -1
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/datalist.py +33 -23
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/fields/__init__.py +4 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/fields/choices.py +132 -57
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/fields/core.py +9 -3
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/fields/datetime.py +22 -25
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/fields/form.py +2 -2
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/fields/list.py +15 -3
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/fields/numeric.py +18 -16
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/form.py +24 -13
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/fields/test_date.py +13 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/fields/test_datetime.py +13 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/fields/test_datetimelocal.py +4 -4
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/fields/test_decimal.py +10 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/fields/test_float.py +13 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/fields/test_form.py +18 -17
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/fields/test_integer.py +13 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/fields/test_list.py +77 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/fields/test_radio.py +17 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/fields/test_select.py +276 -44
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/fields/test_selectmultiple.py +72 -6
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/fields/test_time.py +13 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/fields/test_week.py +13 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/test_datalist.py +76 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/test_form.py +22 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/.gitignore +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/LICENSE.rst +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/README.rst +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/docs/Makefile +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/docs/_static/logo_joined.svg +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/docs/_static/logo_split.svg +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/docs/_static/logo_square.svg +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/docs/changes.rst +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/docs/conf.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/docs/contributing.rst +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/docs/crash_course.rst +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/docs/csrf.rst +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/docs/ext.rst +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/docs/faq.rst +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/docs/i18n.rst +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/docs/index.rst +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/docs/license.rst +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/docs/make.bat +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/docs/meta.rst +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/docs/specific_problems.rst +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/docs/validators.rst +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/docs/whats_new.rst +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/docs/widgets.rst +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/hatch_build.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/_compat.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/csrf/__init__.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/csrf/core.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/csrf/session.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/fields/simple.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/i18n.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/README.md +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/ar/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/ar/LC_MESSAGES/wtforms.po +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/bg/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/bg/LC_MESSAGES/wtforms.po +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/ca/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/ca/LC_MESSAGES/wtforms.po +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/cs_CZ/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/cs_CZ/LC_MESSAGES/wtforms.po +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/cy/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/cy/LC_MESSAGES/wtforms.po +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/de/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/de/LC_MESSAGES/wtforms.po +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/de_CH/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/de_CH/LC_MESSAGES/wtforms.po +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/el/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/el/LC_MESSAGES/wtforms.po +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/en/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/en/LC_MESSAGES/wtforms.po +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/es/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/es/LC_MESSAGES/wtforms.po +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/et/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/et/LC_MESSAGES/wtforms.po +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/fa/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/fa/LC_MESSAGES/wtforms.po +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/fi/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/fi/LC_MESSAGES/wtforms.po +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/fr/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/fr/LC_MESSAGES/wtforms.po +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/he/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/he/LC_MESSAGES/wtforms.po +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/hu/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/hu/LC_MESSAGES/wtforms.po +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/it/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/it/LC_MESSAGES/wtforms.po +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/ja/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/ja/LC_MESSAGES/wtforms.po +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/kk/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/kk/LC_MESSAGES/wtforms.po +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/ko/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/ko/LC_MESSAGES/wtforms.po +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/nb/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/nb/LC_MESSAGES/wtforms.po +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/nl/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/nl/LC_MESSAGES/wtforms.po +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/pl/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/pl/LC_MESSAGES/wtforms.po +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/pt/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/pt/LC_MESSAGES/wtforms.po +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/ro/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/ro/LC_MESSAGES/wtforms.po +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/ru/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/ru/LC_MESSAGES/wtforms.po +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/sk/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/sk/LC_MESSAGES/wtforms.po +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/sr/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/sr/LC_MESSAGES/wtforms.po +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/sv/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/sv/LC_MESSAGES/wtforms.po +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/ta/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/ta/LC_MESSAGES/wtforms.po +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/tr/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/tr/LC_MESSAGES/wtforms.po +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/uk/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/uk/LC_MESSAGES/wtforms.po +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/wtforms.pot +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/zh/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/zh/LC_MESSAGES/wtforms.po +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/zh_TW/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/locale/zh_TW/LC_MESSAGES/wtforms.po +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/meta.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/utils.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/validators.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/widgets/__init__.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/src/wtforms/widgets/core.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/__init__.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/common.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/conftest.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/fields/__init__.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/fields/test_boolean.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/fields/test_button.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/fields/test_color.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/fields/test_custom.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/fields/test_defaults.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/fields/test_field.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/fields/test_file.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/fields/test_filters.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/fields/test_flags.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/fields/test_hidden.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/fields/test_html5.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/fields/test_label.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/fields/test_month.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/fields/test_password.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/fields/test_string.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/fields/test_submit.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/fields/test_textarea.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/test_csrf.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/test_i18n.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/test_locale_babel.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/test_webob.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/test_widgets.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/validators/__init__.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/validators/test_anyof.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/validators/test_data_required.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/validators/test_date_range.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/validators/test_disabled.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/validators/test_email.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/validators/test_equal_to.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/validators/test_input_required.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/validators/test_ipaddress.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/validators/test_lazy_proxy.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/validators/test_length.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/validators/test_mac_address.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/validators/test_noneof.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/validators/test_number_range.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/validators/test_optional.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/validators/test_readonly.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/validators/test_regexp.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/validators/test_url.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/validators/test_uuid.py +0 -0
- {wtforms-3.3.0b2 → wtforms-3.3.0b4}/tests/validators/test_validators.py +0 -0
|
@@ -1,5 +1,44 @@
|
|
|
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
|
+
---------------
|
|
33
|
+
|
|
34
|
+
Released 2026-06-12
|
|
35
|
+
|
|
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`
|
|
41
|
+
|
|
3
42
|
Version 3.3.0b2
|
|
4
43
|
---------------
|
|
5
44
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: WTForms
|
|
3
|
-
Version: 3.3.
|
|
3
|
+
Version: 3.3.0b4
|
|
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
|
|
@@ -44,7 +44,7 @@ Classifier: License :: OSI Approved :: BSD License
|
|
|
44
44
|
Classifier: Operating System :: OS Independent
|
|
45
45
|
Classifier: Programming Language :: Python
|
|
46
46
|
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
|
47
|
-
Requires-Python: >=3.
|
|
47
|
+
Requires-Python: >=3.11
|
|
48
48
|
Requires-Dist: markupsafe>=1.1.1
|
|
49
49
|
Provides-Extra: email
|
|
50
50
|
Requires-Dist: email-validator>=2.0; extra == 'email'
|
|
@@ -67,6 +67,7 @@ The Field base class
|
|
|
67
67
|
.. automethod:: process(formdata [, data])
|
|
68
68
|
.. automethod:: process_data
|
|
69
69
|
.. automethod:: process_formdata
|
|
70
|
+
.. automethod:: post_process
|
|
70
71
|
.. attribute:: data
|
|
71
72
|
|
|
72
73
|
Contains the resulting (sanitized) value of calling either of the
|
|
@@ -329,17 +330,15 @@ Choice Fields
|
|
|
329
330
|
SelectChoice('text', 'Plain Text'),
|
|
330
331
|
])
|
|
331
332
|
|
|
332
|
-
|
|
333
|
-
|
|
333
|
+
A callable passed as `choices` is resolved separately for each form
|
|
334
|
+
instance, the first time its choices are read.
|
|
334
335
|
For dynamic options, either pass a callable to `choices` or assign
|
|
335
336
|
`field.choices` after instantiation. Any submitted choices which are not in
|
|
336
337
|
the given choices list will cause validation on the field to fail. If this
|
|
337
338
|
option cannot be applied to your problem you may wish to skip choice
|
|
338
339
|
validation (see below).
|
|
339
340
|
|
|
340
|
-
**Select fields with
|
|
341
|
-
|
|
342
|
-
Use :class:`SelectChoice` to assign an option to an ``<optgroup>``.
|
|
341
|
+
**Select fields with optgroup**::
|
|
343
342
|
|
|
344
343
|
class PastebinEntry(Form):
|
|
345
344
|
language = SelectField('Programming Language', choices=[
|
|
@@ -349,6 +348,8 @@ Choice Fields
|
|
|
349
348
|
SelectChoice('text', 'Plain Text'),
|
|
350
349
|
])
|
|
351
350
|
|
|
351
|
+
Use :class:`SelectChoice` to assign an option to an ``<optgroup>``.
|
|
352
|
+
|
|
352
353
|
**Select fields with dynamic choice values**::
|
|
353
354
|
|
|
354
355
|
def available_groups():
|
|
@@ -361,17 +362,22 @@ Choice Fields
|
|
|
361
362
|
user = User.query.get(id)
|
|
362
363
|
form = UserDetails(request.POST, obj=user)
|
|
363
364
|
|
|
364
|
-
Note we passed a callable to `choices` rather than a static list; it
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
365
|
+
Note we passed a callable to `choices` rather than a static list; it
|
|
366
|
+
produces fresh options per request. Its result is cached. Calling
|
|
367
|
+
:meth:`~wtforms.form.BaseForm.post_process` again refreshes it.
|
|
368
|
+
|
|
369
|
+
If the choices depend on data only available in the view, you can instead
|
|
370
|
+
assign `form.group_id.choices` after instantiation. A list assigned this
|
|
371
|
+
way replaces the callable.
|
|
372
|
+
|
|
373
|
+
The `coerce` keyword arg to :class:`~wtforms.fields.SelectField` says
|
|
374
|
+
that we use :func:`int()` to coerce form data. The default coerce is
|
|
375
|
+
:func:`str()`.
|
|
370
376
|
|
|
371
377
|
The callable may optionally accept ``(form, field)`` as positional
|
|
372
|
-
arguments.
|
|
373
|
-
whole form has been processed, so it can read ``field.data`` and
|
|
374
|
-
of any other field on the form::
|
|
378
|
+
arguments. The callable is invoked when the choices are read, normally
|
|
379
|
+
after the whole form has been processed, so it can read ``field.data`` and
|
|
380
|
+
the data of any other field on the form::
|
|
375
381
|
|
|
376
382
|
def available_groups(form, field):
|
|
377
383
|
return [SelectChoice(g.id, g.name) for g in form.tenant.data.groups]
|
|
@@ -400,28 +406,44 @@ Choice Fields
|
|
|
400
406
|
**Select fields backed by an Enum**::
|
|
401
407
|
|
|
402
408
|
from enum import Enum
|
|
409
|
+
from wtforms.fields import enum_choices, enum_coerce
|
|
403
410
|
|
|
404
411
|
class Color(Enum):
|
|
405
|
-
RED =
|
|
406
|
-
GREEN =
|
|
407
|
-
BLUE =
|
|
412
|
+
RED = "red"
|
|
413
|
+
GREEN = "green"
|
|
414
|
+
BLUE = "blue"
|
|
408
415
|
|
|
409
416
|
class PaintForm(Form):
|
|
410
|
-
color = SelectField(
|
|
417
|
+
color = SelectField(
|
|
418
|
+
choices=enum_choices(Color),
|
|
419
|
+
coerce=enum_coerce(Color),
|
|
420
|
+
)
|
|
421
|
+
|
|
422
|
+
:func:`~wtforms.fields.enum_choices` builds the option list from
|
|
423
|
+
the Enum members; by default the HTML ``value`` of each option is the
|
|
424
|
+
member's ``value`` (``"red"``, ``"green"``, ...).
|
|
425
|
+
:func:`~wtforms.fields.enum_coerce` returns the matching ``coerce``
|
|
426
|
+
callable that resolves the submitted string back into a member, so
|
|
427
|
+
``form.color.data`` is a ``Color`` member after submit. Pre-selecting
|
|
428
|
+
works the usual way, with an Enum member: ``PaintForm(color=Color.RED)``.
|
|
411
429
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
``form.color.data`` is a ``Color`` item after submit. Pre-selecting
|
|
416
|
-
works the usual way, with an Enum item: ``PaintForm(color=Color.RED)``.
|
|
430
|
+
Use ``by="name"`` on **both** helpers when the Enum value is not a good
|
|
431
|
+
transport identifier — non-unique, non-serialisable, or when you simply
|
|
432
|
+
want ``member.name`` on the wire::
|
|
417
433
|
|
|
418
|
-
|
|
434
|
+
class PaintForm(Form):
|
|
435
|
+
color = SelectField(
|
|
436
|
+
choices=enum_choices(Color, by="name"),
|
|
437
|
+
coerce=enum_coerce(Color, by="name"),
|
|
438
|
+
)
|
|
439
|
+
|
|
440
|
+
By default the option label is ``str(member)`` if the Enum defines its
|
|
419
441
|
own ``__str__`` (also the case for :class:`enum.StrEnum`), otherwise
|
|
420
|
-
``
|
|
421
|
-
|
|
442
|
+
``member.name``. To customise, pass a ``label`` callable taking an Enum
|
|
443
|
+
member and returning the label string::
|
|
422
444
|
|
|
423
|
-
|
|
424
|
-
# → [SelectChoice('
|
|
445
|
+
enum_choices(Color, label=lambda member: member.name.title())
|
|
446
|
+
# → [SelectChoice('red', 'Red'), SelectChoice('green', 'Green'), SelectChoice('blue', 'Blue')]
|
|
425
447
|
|
|
426
448
|
**Skipping choice validation**::
|
|
427
449
|
|
|
@@ -454,6 +476,10 @@ Choice Fields
|
|
|
454
476
|
which are not in the given choices list will cause validation on the field
|
|
455
477
|
to fail.
|
|
456
478
|
|
|
479
|
+
.. autofunction:: wtforms.fields.enum_choices
|
|
480
|
+
|
|
481
|
+
.. autofunction:: wtforms.fields.enum_coerce
|
|
482
|
+
|
|
457
483
|
Submit fields
|
|
458
484
|
-------------
|
|
459
485
|
|
|
@@ -580,8 +606,6 @@ Data Lists
|
|
|
580
606
|
|
|
581
607
|
.. currentmodule:: wtforms
|
|
582
608
|
|
|
583
|
-
.. autoclass:: DataListChoice
|
|
584
|
-
|
|
585
609
|
.. class:: DataList(choices=None, *, render_kw=None, widget=None)
|
|
586
610
|
|
|
587
611
|
A :mdn-tag:`datalist` of suggestions. Unlike
|
|
@@ -592,12 +616,15 @@ Data Lists
|
|
|
592
616
|
|
|
593
617
|
``choices`` is either a list of :class:`~wtforms.DataListChoice`
|
|
594
618
|
(or plain strings, in which case the string is used as both value
|
|
595
|
-
and label),
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
619
|
+
and label), a shorthand ``{value: label}`` dict, or a callable. The
|
|
620
|
+
callable runs when the datalist is rendered, after the whole form has been
|
|
621
|
+
processed. Its result is cached until the next
|
|
622
|
+
:meth:`~wtforms.form.BaseForm.post_process`. The callable may
|
|
623
|
+
take no argument (``fn()``) for a static list, or ``(form, field)``
|
|
624
|
+
to adapt the suggestions to the form data, for example the current
|
|
625
|
+
field value. This is convenient for server-side filtering::
|
|
599
626
|
|
|
600
|
-
DataList(lambda field: search(field.data))
|
|
627
|
+
DataList(lambda form, field: search(field.data))
|
|
601
628
|
|
|
602
629
|
**Inline DataList owned by a field**
|
|
603
630
|
|
|
@@ -624,7 +651,7 @@ Data Lists
|
|
|
624
651
|
class F(Form):
|
|
625
652
|
items = FieldList(
|
|
626
653
|
StringField(
|
|
627
|
-
datalist=DataList(lambda field: suggest(field.data))
|
|
654
|
+
datalist=DataList(lambda form, field: suggest(field.data))
|
|
628
655
|
),
|
|
629
656
|
min_entries=3,
|
|
630
657
|
)
|
|
@@ -658,6 +685,9 @@ Data Lists
|
|
|
658
685
|
:class:`~wtforms.fields.TextAreaField` is silently ignored by the
|
|
659
686
|
browser.
|
|
660
687
|
|
|
688
|
+
.. autoclass:: DataListChoice
|
|
689
|
+
|
|
690
|
+
.. autofunction:: wtforms.datalist.enum_datalist
|
|
661
691
|
|
|
662
692
|
.. currentmodule:: wtforms.fields
|
|
663
693
|
|
|
@@ -13,7 +13,7 @@ classifiers = [
|
|
|
13
13
|
"Programming Language :: Python",
|
|
14
14
|
"Topic :: Internet :: WWW/HTTP :: Dynamic Content",
|
|
15
15
|
]
|
|
16
|
-
requires-python = ">=3.
|
|
16
|
+
requires-python = ">=3.11"
|
|
17
17
|
dependencies = [
|
|
18
18
|
"MarkupSafe>=1.1.1",
|
|
19
19
|
]
|
|
@@ -49,12 +49,12 @@ dev = [
|
|
|
49
49
|
env_list = [
|
|
50
50
|
"style",
|
|
51
51
|
"minversions",
|
|
52
|
+
"py315",
|
|
52
53
|
"py314",
|
|
53
54
|
"py313",
|
|
54
55
|
"py312",
|
|
55
56
|
"py311",
|
|
56
|
-
"
|
|
57
|
-
"pypy310",
|
|
57
|
+
"pypy311",
|
|
58
58
|
"docs",
|
|
59
59
|
]
|
|
60
60
|
skip_missing_interpreters = true
|
|
@@ -86,7 +86,7 @@ dependency_groups = ["docs"]
|
|
|
86
86
|
commands = [["sphinx-build", "-W", "-b", "html", "-d", "{envtmpdir}/doctrees", "docs", "{envtmpdir}/html"]]
|
|
87
87
|
|
|
88
88
|
[tool.tox.env.minversions]
|
|
89
|
-
base_python = ["py3.
|
|
89
|
+
base_python = ["py3.11"]
|
|
90
90
|
uv_resolution = "lowest-direct"
|
|
91
91
|
dependency_groups = ["dev"]
|
|
92
92
|
commands = [
|
|
@@ -2,6 +2,7 @@ from wtforms import validators
|
|
|
2
2
|
from wtforms import widgets
|
|
3
3
|
from wtforms.datalist import DataList
|
|
4
4
|
from wtforms.datalist import DataListChoice
|
|
5
|
+
from wtforms.datalist import enum_datalist
|
|
5
6
|
from wtforms.fields.choices import Choice
|
|
6
7
|
from wtforms.fields.choices import RadioField
|
|
7
8
|
from wtforms.fields.choices import SelectChoice
|
|
@@ -41,7 +42,7 @@ from wtforms.fields.simple import URLField
|
|
|
41
42
|
from wtforms.form import Form
|
|
42
43
|
from wtforms.validators import ValidationError
|
|
43
44
|
|
|
44
|
-
__version__ = "3.3.
|
|
45
|
+
__version__ = "3.3.0b4"
|
|
45
46
|
|
|
46
47
|
__all__ = [
|
|
47
48
|
"validators",
|
|
@@ -86,4 +87,5 @@ __all__ = [
|
|
|
86
87
|
"Choice",
|
|
87
88
|
"SelectChoice",
|
|
88
89
|
"DataListChoice",
|
|
90
|
+
"enum_datalist",
|
|
89
91
|
]
|
|
@@ -3,10 +3,11 @@ from dataclasses import dataclass
|
|
|
3
3
|
from dataclasses import field
|
|
4
4
|
|
|
5
5
|
from wtforms import widgets
|
|
6
|
-
from wtforms.
|
|
6
|
+
from wtforms.fields.choices import _enum_options
|
|
7
|
+
from wtforms.fields.choices import _invoke_choices_callable
|
|
7
8
|
from wtforms.fields.choices import Choice
|
|
8
9
|
|
|
9
|
-
__all__ = ("DataList", "DataListChoice")
|
|
10
|
+
__all__ = ("DataList", "DataListChoice", "enum_datalist")
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
@dataclass
|
|
@@ -35,16 +36,6 @@ class DataListChoice:
|
|
|
35
36
|
def __iter__(self):
|
|
36
37
|
return iter((self.value, self.label, self.render_kw))
|
|
37
38
|
|
|
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
39
|
@classmethod
|
|
49
40
|
def from_input(cls, input):
|
|
50
41
|
"""Coerce a value passed by the user into a :class:`DataListChoice`."""
|
|
@@ -73,6 +64,21 @@ class DataListChoice:
|
|
|
73
64
|
return cls(*input)
|
|
74
65
|
|
|
75
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__`` or is a :class:`enum.StrEnum`,
|
|
74
|
+
else ``member.name``.
|
|
75
|
+
|
|
76
|
+
A ``<datalist>`` only suggests values for a free-text field, so there is
|
|
77
|
+
no coercion counterpart — the field stores whatever string is submitted.
|
|
78
|
+
"""
|
|
79
|
+
return _enum_options(enum_cls, by, label, DataListChoice)
|
|
80
|
+
|
|
81
|
+
|
|
76
82
|
class DataList:
|
|
77
83
|
"""A ``<datalist>`` of suggestions attached to a single field.
|
|
78
84
|
|
|
@@ -86,6 +92,7 @@ class DataList:
|
|
|
86
92
|
def __init__(self, choices=None, *, render_kw=None, widget=None):
|
|
87
93
|
self._raw_choices = choices
|
|
88
94
|
self._choices = None if callable(choices) else choices
|
|
95
|
+
self._field = None
|
|
89
96
|
self.render_kw = render_kw or {}
|
|
90
97
|
if widget is not None:
|
|
91
98
|
self.widget = widget
|
|
@@ -95,27 +102,30 @@ class DataList:
|
|
|
95
102
|
clone = DataList.__new__(DataList)
|
|
96
103
|
clone._raw_choices = self._raw_choices
|
|
97
104
|
clone._choices = None if callable(self._raw_choices) else self._raw_choices
|
|
105
|
+
clone._field = None
|
|
98
106
|
clone.render_kw = self.render_kw
|
|
99
107
|
clone.widget = self.widget
|
|
100
108
|
clone.id = id
|
|
101
109
|
return clone
|
|
102
110
|
|
|
103
|
-
def
|
|
104
|
-
|
|
105
|
-
if
|
|
106
|
-
|
|
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)
|
|
111
|
+
def _invalidate(self, field):
|
|
112
|
+
"""Mark the choices for a lazy refresh on the next :meth:`iter_choices`."""
|
|
113
|
+
if callable(self._raw_choices):
|
|
114
|
+
self._field = field
|
|
114
115
|
|
|
115
116
|
def iter_choices(self, field=None):
|
|
117
|
+
if (bound := self._field) is not None:
|
|
118
|
+
self._field = None
|
|
119
|
+
self._choices = _invoke_choices_callable(
|
|
120
|
+
self._raw_choices, bound._form, bound
|
|
121
|
+
)
|
|
116
122
|
raw = self._choices
|
|
117
123
|
if raw is None:
|
|
118
124
|
return []
|
|
125
|
+
if isinstance(raw, dict):
|
|
126
|
+
return [
|
|
127
|
+
DataListChoice(value=value, label=label) for value, label in raw.items()
|
|
128
|
+
]
|
|
119
129
|
return [DataListChoice.from_input(item) for item in raw]
|
|
120
130
|
|
|
121
131
|
def __call__(self, field=None, **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",
|