Drop Python 3.8 support

This addresses a failure in the `py38-dev` tests
caused by markupsafe recently dropping Python 3.8 support.

Changed in this commit:

* Drop Python 3.8 support
* Update CI and tox testing
* Update docs references to Python 3.8
* Remove a Windows / Python 3.8 admonition in the docs
* Address type annotations
* Incorporate style fixes

Still to do:

* `src/flask/sessions.py` contains these comments:

  > TODO generic when Python > 3.8

  This TODO should now be unblocked.
This commit is contained in:
Kurt McKee 2024-10-16 18:12:00 -05:00
parent dffe303482
commit 5796f50315
No known key found for this signature in database
GPG key ID: 64713C0B5BA8E1C2
9 changed files with 22 additions and 29 deletions

View file

@ -28,10 +28,9 @@ jobs:
- {python: '3.11'} - {python: '3.11'}
- {python: '3.10'} - {python: '3.10'}
- {python: '3.9'} - {python: '3.9'}
- {python: '3.8'}
- {name: PyPy, python: 'pypy-3.10', tox: pypy310} - {name: PyPy, python: 'pypy-3.10', tox: pypy310}
- {name: Minimum Versions, python: '3.12', tox: py-min} - {name: Minimum Versions, python: '3.12', tox: py-min}
- {name: Development Versions, python: '3.8', tox: py-dev} - {name: Development Versions, python: '3.9', tox: py-dev}
steps: steps:
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
- uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 - uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0

View file

@ -23,12 +23,6 @@ method in views that inherit from the :class:`flask.views.View` class, as
well as all the HTTP method handlers in views that inherit from the well as all the HTTP method handlers in views that inherit from the
:class:`flask.views.MethodView` class. :class:`flask.views.MethodView` class.
.. admonition:: Using ``async`` on Windows on Python 3.8
Python 3.8 has a bug related to asyncio on Windows. If you encounter
something like ``ValueError: set_wakeup_fd only works in main thread``,
please upgrade to Python 3.9.
.. admonition:: Using ``async`` with greenlet .. admonition:: Using ``async`` with greenlet
When using gevent or eventlet to serve an application or patch the When using gevent or eventlet to serve an application or patch the

View file

@ -294,7 +294,7 @@ ecosystem remain consistent and compatible.
indicate minimum compatibility support. For example, indicate minimum compatibility support. For example,
``sqlalchemy>=1.4``. ``sqlalchemy>=1.4``.
9. Indicate the versions of Python supported using ``python_requires=">=version"``. 9. Indicate the versions of Python supported using ``python_requires=">=version"``.
Flask itself supports Python >=3.8 as of April 2023, but this will update over time. Flask itself supports Python >=3.9 as of October 2024, but this will update over time.
.. _PyPI: https://pypi.org/search/?c=Framework+%3A%3A+Flask .. _PyPI: https://pypi.org/search/?c=Framework+%3A%3A+Flask
.. _Discord Chat: https://discord.gg/pallets .. _Discord Chat: https://discord.gg/pallets

View file

@ -5,7 +5,7 @@ Installation
Python Version Python Version
-------------- --------------
We recommend using the latest version of Python. Flask supports Python 3.8 and newer. We recommend using the latest version of Python. Flask supports Python 3.9 and newer.
Dependencies Dependencies

View file

@ -3,7 +3,7 @@ name = "flask-example-celery"
version = "1.0.0" version = "1.0.0"
description = "Example Flask application with Celery background tasks." description = "Example Flask application with Celery background tasks."
readme = "README.md" readme = "README.md"
requires-python = ">=3.8" requires-python = ">=3.9"
dependencies = ["flask>=2.2.2", "celery[redis]>=5.2.7"] dependencies = ["flask>=2.2.2", "celery[redis]>=5.2.7"]
[build-system] [build-system]

View file

@ -19,7 +19,7 @@ classifiers = [
"Topic :: Software Development :: Libraries :: Application Frameworks", "Topic :: Software Development :: Libraries :: Application Frameworks",
"Typing :: Typed", "Typing :: Typed",
] ]
requires-python = ">=3.8" requires-python = ">=3.9"
dependencies = [ dependencies = [
"Werkzeug>=3.0.0", "Werkzeug>=3.0.0",
"Jinja2>=3.1.2", "Jinja2>=3.1.2",
@ -78,7 +78,7 @@ source = ["flask", "tests"]
source = ["src", "*/site-packages"] source = ["src", "*/site-packages"]
[tool.mypy] [tool.mypy]
python_version = "3.8" python_version = "3.9"
files = ["src/flask", "tests/typing"] files = ["src/flask", "tests/typing"]
show_error_codes = true show_error_codes = true
pretty = true pretty = true
@ -94,7 +94,7 @@ module = [
ignore_missing_imports = true ignore_missing_imports = true
[tool.pyright] [tool.pyright]
pythonVersion = "3.8" pythonVersion = "3.9"
include = ["src/flask", "tests"] include = ["src/flask", "tests"]
typeCheckingMode = "basic" typeCheckingMode = "basic"

View file

@ -5,7 +5,7 @@ import os
import sys import sys
import typing as t import typing as t
from datetime import datetime from datetime import datetime
from functools import lru_cache from functools import cache
from functools import update_wrapper from functools import update_wrapper
import werkzeug.utils import werkzeug.utils
@ -623,7 +623,7 @@ def get_root_path(import_name: str) -> str:
return os.path.dirname(os.path.abspath(filepath)) # type: ignore[no-any-return] return os.path.dirname(os.path.abspath(filepath)) # type: ignore[no-any-return]
@lru_cache(maxsize=None) @cache
def _split_blueprint_path(name: str) -> list[str]: def _split_blueprint_path(name: str) -> list[str]:
out: list[str] = [name] out: list[str] = [name]

View file

@ -12,7 +12,7 @@ ResponseValue = t.Union[
"Response", "Response",
str, str,
bytes, bytes,
t.List[t.Any], list[t.Any],
# Only dict is actually accepted, but Mapping allows for TypedDict. # Only dict is actually accepted, but Mapping allows for TypedDict.
t.Mapping[str, t.Any], t.Mapping[str, t.Any],
t.Iterator[str], t.Iterator[str],
@ -21,21 +21,21 @@ ResponseValue = t.Union[
# the possible types for an individual HTTP header # the possible types for an individual HTTP header
# This should be a Union, but mypy doesn't pass unless it's a TypeVar. # This should be a Union, but mypy doesn't pass unless it's a TypeVar.
HeaderValue = t.Union[str, t.List[str], t.Tuple[str, ...]] HeaderValue = t.Union[str, list[str], tuple[str, ...]]
# the possible types for HTTP headers # the possible types for HTTP headers
HeadersValue = t.Union[ HeadersValue = t.Union[
"Headers", "Headers",
t.Mapping[str, HeaderValue], t.Mapping[str, HeaderValue],
t.Sequence[t.Tuple[str, HeaderValue]], t.Sequence[tuple[str, HeaderValue]],
] ]
# The possible types returned by a route function. # The possible types returned by a route function.
ResponseReturnValue = t.Union[ ResponseReturnValue = t.Union[
ResponseValue, ResponseValue,
t.Tuple[ResponseValue, HeadersValue], tuple[ResponseValue, HeadersValue],
t.Tuple[ResponseValue, int], tuple[ResponseValue, int],
t.Tuple[ResponseValue, int, HeadersValue], tuple[ResponseValue, int, HeadersValue],
"WSGIApplication", "WSGIApplication",
] ]
@ -56,21 +56,21 @@ BeforeRequestCallable = t.Union[
t.Callable[[], t.Optional[ResponseReturnValue]], t.Callable[[], t.Optional[ResponseReturnValue]],
t.Callable[[], t.Awaitable[t.Optional[ResponseReturnValue]]], t.Callable[[], t.Awaitable[t.Optional[ResponseReturnValue]]],
] ]
ShellContextProcessorCallable = t.Callable[[], t.Dict[str, t.Any]] ShellContextProcessorCallable = t.Callable[[], dict[str, t.Any]]
TeardownCallable = t.Union[ TeardownCallable = t.Union[
t.Callable[[t.Optional[BaseException]], None], t.Callable[[t.Optional[BaseException]], None],
t.Callable[[t.Optional[BaseException]], t.Awaitable[None]], t.Callable[[t.Optional[BaseException]], t.Awaitable[None]],
] ]
TemplateContextProcessorCallable = t.Union[ TemplateContextProcessorCallable = t.Union[
t.Callable[[], t.Dict[str, t.Any]], t.Callable[[], dict[str, t.Any]],
t.Callable[[], t.Awaitable[t.Dict[str, t.Any]]], t.Callable[[], t.Awaitable[dict[str, t.Any]]],
] ]
TemplateFilterCallable = t.Callable[..., t.Any] TemplateFilterCallable = t.Callable[..., t.Any]
TemplateGlobalCallable = t.Callable[..., t.Any] TemplateGlobalCallable = t.Callable[..., t.Any]
TemplateTestCallable = t.Callable[..., bool] TemplateTestCallable = t.Callable[..., bool]
URLDefaultCallable = t.Callable[[str, t.Dict[str, t.Any]], None] URLDefaultCallable = t.Callable[[str, dict[str, t.Any]], None]
URLValuePreprocessorCallable = t.Callable[ URLValuePreprocessorCallable = t.Callable[
[t.Optional[str], t.Optional[t.Dict[str, t.Any]]], None [t.Optional[str], t.Optional[dict[str, t.Any]]], None
] ]
# This should take Exception, but that either breaks typing the argument # This should take Exception, but that either breaks typing the argument

View file

@ -1,9 +1,9 @@
[tox] [tox]
envlist = envlist =
py3{13,12,11,10,9,8} py3{13,12,11,10,9}
pypy310 pypy310
py312-min py312-min
py38-dev py39-dev
style style
typing typing
docs docs