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