Skip to content

Commit

Permalink
drop support for Python 3.8 (#5623)
Browse files Browse the repository at this point in the history
  • Loading branch information
davidism authored Oct 31, 2024
2 parents e8b91cd + 1d610e4 commit 99ce7ed
Show file tree
Hide file tree
Showing 12 changed files with 25 additions and 42 deletions.
1 change: 0 additions & 1 deletion .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ jobs:
- {python: '3.11'}
- {python: '3.10'}
- {python: '3.9'}
- {python: '3.8'}
- {name: PyPy, python: 'pypy-3.10', tox: pypy310}
- {name: Minimum Versions, python: '3.12', tox: py-min}
- {name: Development Versions, python: '3.9', tox: py-dev}
Expand Down
1 change: 1 addition & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ Version 3.1.0

Unreleased

- Drop support for Python 3.8. :pr:`5623`
- Provide a configuration option to control automatic option
responses. :pr:`5496`
- ``Flask.open_resource``/``open_instance_resource`` and
Expand Down
6 changes: 0 additions & 6 deletions docs/async-await.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
: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

When using gevent or eventlet to serve an application or patch the
Expand Down
3 changes: 2 additions & 1 deletion docs/extensiondev.rst
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,8 @@ ecosystem remain consistent and compatible.
indicate minimum compatibility support. For example,
``sqlalchemy>=1.4``.
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, and this will update
over time.

.. _PyPI: https://pypi.org/search/?c=Framework+%3A%3A+Flask
.. _Discord Chat: https://discord.gg/pallets
Expand Down
2 changes: 1 addition & 1 deletion docs/installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Installation
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
Expand Down
6 changes: 3 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ classifiers = [
"Topic :: Software Development :: Libraries :: Application Frameworks",
"Typing :: Typed",
]
requires-python = ">=3.8"
requires-python = ">=3.9"
dependencies = [
"Werkzeug>=3.0.0",
"Jinja2>=3.1.2",
Expand Down Expand Up @@ -78,7 +78,7 @@ source = ["flask", "tests"]
source = ["src", "*/site-packages"]

[tool.mypy]
python_version = "3.8"
python_version = "3.9"
files = ["src/flask", "tests/typing"]
show_error_codes = true
pretty = true
Expand All @@ -94,7 +94,7 @@ module = [
ignore_missing_imports = true

[tool.pyright]
pythonVersion = "3.8"
pythonVersion = "3.9"
include = ["src/flask", "tests/typing"]
typeCheckingMode = "basic"

Expand Down
2 changes: 1 addition & 1 deletion src/flask/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -1245,7 +1245,7 @@ def make_response(self, rv: ft.ResponseReturnValue) -> Response:

# extend existing headers with provided headers
if headers:
rv.headers.update(headers) # type: ignore[arg-type]
rv.headers.update(headers)

return rv

Expand Down
5 changes: 1 addition & 4 deletions src/flask/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,23 +150,20 @@ def from_prefixed_env(
.. versionadded:: 2.1
"""
prefix = f"{prefix}_"
len_prefix = len(prefix)

for key in sorted(os.environ):
if not key.startswith(prefix):
continue

value = os.environ[key]
key = key.removeprefix(prefix)

try:
value = loads(value)
except Exception:
# Keep the value as a string if loading failed.
pass

# Change to key.removeprefix(prefix) on Python >= 3.9.
key = key[len_prefix:]

if "__" not in key:
# A non-nested key, set directly.
self[key] = value
Expand Down
4 changes: 2 additions & 2 deletions src/flask/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import sys
import typing as t
from datetime import datetime
from functools import lru_cache
from functools import cache
from functools import update_wrapper

import werkzeug.utils
Expand Down Expand Up @@ -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]


@lru_cache(maxsize=None)
@cache
def _split_blueprint_path(name: str) -> list[str]:
out: list[str] = [name]

Expand Down
13 changes: 2 additions & 11 deletions src/flask/sansio/scaffold.py
Original file line number Diff line number Diff line change
Expand Up @@ -706,15 +706,6 @@ def _endpoint_from_view_func(view_func: ft.RouteCallable) -> str:
return view_func.__name__


def _path_is_relative_to(path: pathlib.PurePath, base: str) -> bool:
# Path.is_relative_to doesn't exist until Python 3.9
try:
path.relative_to(base)
return True
except ValueError:
return False


def _find_package_path(import_name: str) -> str:
"""Find the path that contains the package or module."""
root_mod_name, _, _ = import_name.partition(".")
Expand Down Expand Up @@ -745,7 +736,7 @@ def _find_package_path(import_name: str) -> str:
search_location = next(
location
for location in root_spec.submodule_search_locations
if _path_is_relative_to(package_path, location)
if package_path.is_relative_to(location)
)
else:
# Pick the first path.
Expand Down Expand Up @@ -777,7 +768,7 @@ def find_package(import_name: str) -> tuple[str | None, str]:
py_prefix = os.path.abspath(sys.prefix)

# installed to the system
if _path_is_relative_to(pathlib.PurePath(package_path), py_prefix):
if pathlib.PurePath(package_path).is_relative_to(py_prefix):
return py_prefix, package_path

site_parent, site_folder = os.path.split(package_path)
Expand Down
22 changes: 11 additions & 11 deletions src/flask/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"Response",
str,
bytes,
t.List[t.Any],
list[t.Any],
# Only dict is actually accepted, but Mapping allows for TypedDict.
t.Mapping[str, t.Any],
t.Iterator[str],
Expand All @@ -21,21 +21,21 @@

# the possible types for an individual HTTP header
# 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
HeadersValue = t.Union[
"Headers",
t.Mapping[str, HeaderValue],
t.Sequence[t.Tuple[str, HeaderValue]],
t.Sequence[tuple[str, HeaderValue]],
]

# The possible types returned by a route function.
ResponseReturnValue = t.Union[
ResponseValue,
t.Tuple[ResponseValue, HeadersValue],
t.Tuple[ResponseValue, int],
t.Tuple[ResponseValue, int, HeadersValue],
tuple[ResponseValue, HeadersValue],
tuple[ResponseValue, int],
tuple[ResponseValue, int, HeadersValue],
"WSGIApplication",
]

Expand All @@ -56,21 +56,21 @@
t.Callable[[], 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[
t.Callable[[t.Optional[BaseException]], None],
t.Callable[[t.Optional[BaseException]], t.Awaitable[None]],
]
TemplateContextProcessorCallable = t.Union[
t.Callable[[], t.Dict[str, t.Any]],
t.Callable[[], t.Awaitable[t.Dict[str, t.Any]]],
t.Callable[[], dict[str, t.Any]],
t.Callable[[], t.Awaitable[dict[str, t.Any]]],
]
TemplateFilterCallable = t.Callable[..., t.Any]
TemplateGlobalCallable = t.Callable[..., t.Any]
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[
[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
Expand Down
2 changes: 1 addition & 1 deletion tox.ini
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tox]
envlist =
py3{13,12,11,10,9,8}
py3{13,12,11,10,9}
pypy310
py312-min
py39-dev
Expand Down

0 comments on commit 99ce7ed

Please sign in to comment.