diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index b1f9216b8..3a138517f 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -21,11 +21,11 @@ jobs: fail-fast: false matrix: include: + - {python: '3.14'} - {python: '3.13'} - {python: '3.12'} - {python: '3.11'} - {python: '3.10'} - - {python: '3.9'} steps: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 diff --git a/CHANGES.rst b/CHANGES.rst index f8e865e6b..014f60acd 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -6,6 +6,7 @@ Unreleased Unreleased - Fix :class:`~validators.Disabled` validation with provided formdata. :pr:`880` +- End support for Python 3.9, start support for Python 3.14. :pr:`883` Version 3.2.1 ------------- diff --git a/pyproject.toml b/pyproject.toml index 3e9198dc6..51499a7e0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,7 +13,7 @@ classifiers = [ "Programming Language :: Python", "Topic :: Internet :: WWW/HTTP :: Dynamic Content", ] -requires-python = ">=3.9" +requires-python = ">=3.10" dependencies = [ "MarkupSafe", ] diff --git a/src/wtforms/fields/choices.py b/src/wtforms/fields/choices.py index 62ed49763..180927066 100644 --- a/src/wtforms/fields/choices.py +++ b/src/wtforms/fields/choices.py @@ -114,11 +114,11 @@ def _choices_generator(self, choices): if not choices: _choices = [] - elif isinstance(choices[0], (list, tuple)): + elif isinstance(choices[0], list | tuple): _choices = choices else: - _choices = zip(choices, choices) + _choices = zip(choices, choices, strict=False) for value, label, *other_args in _choices: selected = self.coerce(value) == self.data @@ -168,11 +168,11 @@ def _choices_generator(self, choices): if not choices: _choices = [] - elif isinstance(choices[0], (list, tuple)): + elif isinstance(choices[0], list | tuple): _choices = choices else: - _choices = zip(choices, choices) + _choices = zip(choices, choices, strict=False) for value, label, *other_args in _choices: selected = self.data is not None and self.coerce(value) in self.data diff --git a/src/wtforms/fields/list.py b/src/wtforms/fields/list.py index dabd8b404..e80beefa0 100644 --- a/src/wtforms/fields/list.py +++ b/src/wtforms/fields/list.py @@ -144,7 +144,7 @@ def populate_obj(self, obj, name): candidates = itertools.chain(ivalues, itertools.repeat(None)) _fake = type("_fake", (object,), {}) output = [] - for field, data in zip(self.entries, candidates): + for field, data in zip(self.entries, candidates, strict=False): fake_obj = _fake() fake_obj.data = data field.populate_obj(fake_obj, "data") diff --git a/tests/common.py b/tests/common.py index c8d28a0f3..22ca613ac 100644 --- a/tests/common.py +++ b/tests/common.py @@ -1,6 +1,6 @@ class DummyPostData(dict): def getlist(self, key): v = self[key] - if not isinstance(v, (list, tuple)): + if not isinstance(v, list | tuple): v = [v] return v diff --git a/tox.ini b/tox.ini index 58ce365df..31533a7b4 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,7 @@ [tox] envlist = style - py{313,312,311,310,39,py3} + py{314,313,312,311,310,py310} docs skip_missing_interpreters = true