Drop Python 3.9, bump tests/builds to Python 3.10 (#19099)

Python 3.9 EOL is on 2025-10-31
This commit is contained in:
Andrew Ferrazzutti 2025-10-29 13:15:00 -04:00 committed by GitHub
parent 32998d07d2
commit e0838c2567
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 59 additions and 65 deletions

View File

@ -36,11 +36,11 @@ IS_PR = os.environ["GITHUB_REF"].startswith("refs/pull/")
# First calculate the various trial jobs. # First calculate the various trial jobs.
# #
# For PRs, we only run each type of test with the oldest Python version supported (which # For PRs, we only run each type of test with the oldest Python version supported (which
# is Python 3.9 right now) # is Python 3.10 right now)
trial_sqlite_tests = [ trial_sqlite_tests = [
{ {
"python-version": "3.9", "python-version": "3.10",
"database": "sqlite", "database": "sqlite",
"extras": "all", "extras": "all",
} }
@ -53,12 +53,12 @@ if not IS_PR:
"database": "sqlite", "database": "sqlite",
"extras": "all", "extras": "all",
} }
for version in ("3.10", "3.11", "3.12", "3.13") for version in ("3.11", "3.12", "3.13")
) )
trial_postgres_tests = [ trial_postgres_tests = [
{ {
"python-version": "3.9", "python-version": "3.10",
"database": "postgres", "database": "postgres",
"postgres-version": "13", "postgres-version": "13",
"extras": "all", "extras": "all",
@ -77,7 +77,7 @@ if not IS_PR:
trial_no_extra_tests = [ trial_no_extra_tests = [
{ {
"python-version": "3.9", "python-version": "3.10",
"database": "sqlite", "database": "sqlite",
"extras": "", "extras": "",
} }

View File

@ -145,7 +145,7 @@ jobs:
- name: Only build a single wheel on PR - name: Only build a single wheel on PR
if: startsWith(github.ref, 'refs/pull/') if: startsWith(github.ref, 'refs/pull/')
run: echo "CIBW_BUILD="cp39-manylinux_*"" >> $GITHUB_ENV run: echo "CIBW_BUILD="cp310-manylinux_*"" >> $GITHUB_ENV
- name: Build wheels - name: Build wheels
run: python -m cibuildwheel --output-dir wheelhouse run: python -m cibuildwheel --output-dir wheelhouse

View File

@ -470,7 +470,7 @@ jobs:
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
with: with:
python-version: '3.9' python-version: '3.10'
- name: Prepare old deps - name: Prepare old deps
if: steps.cache-poetry-old-deps.outputs.cache-hit != 'true' if: steps.cache-poetry-old-deps.outputs.cache-hit != 'true'
@ -514,7 +514,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
matrix: matrix:
python-version: ["pypy-3.9"] python-version: ["pypy-3.10"]
extras: ["all"] extras: ["all"]
steps: steps:
@ -638,7 +638,7 @@ jobs:
strategy: strategy:
matrix: matrix:
include: include:
- python-version: "3.9" - python-version: "3.10"
postgres-version: "13" postgres-version: "13"
- python-version: "3.13" - python-version: "3.13"

View File

@ -27,12 +27,12 @@ def build(setup_kwargs: dict[str, Any]) -> None:
setup_kwargs["zip_safe"] = False setup_kwargs["zip_safe"] = False
# We look up the minimum supported Python version with # We look up the minimum supported Python version with
# `python_requires` (e.g. ">=3.9.0,<4.0.0") and finding the first Python # `python_requires` (e.g. ">=3.10.0,<4.0.0") and finding the first Python
# version that matches. We then convert that into the `py_limited_api` form, # version that matches. We then convert that into the `py_limited_api` form,
# e.g. cp39 for Python 3.9. # e.g. cp310 for Python 3.10.
py_limited_api: str py_limited_api: str
python_bounds = SpecifierSet(setup_kwargs["python_requires"]) python_bounds = SpecifierSet(setup_kwargs["python_requires"])
for minor_version in itertools.count(start=8): for minor_version in itertools.count(start=10):
if f"3.{minor_version}.0" in python_bounds: if f"3.{minor_version}.0" in python_bounds:
py_limited_api = f"cp3{minor_version}" py_limited_api = f"cp3{minor_version}"
break break

View File

@ -0,0 +1 @@
Drop support for Python 3.9.

View File

@ -3,7 +3,7 @@
# #
# Used by `complement.sh`. Not suitable for production use. # Used by `complement.sh`. Not suitable for production use.
ARG PYTHON_VERSION=3.9 ARG PYTHON_VERSION=3.10
### ###
### Stage 0: generate requirements.txt ### Stage 0: generate requirements.txt

View File

@ -79,17 +79,17 @@ phonenumbers = [
We can see this pinned version inside the docker image for that release: We can see this pinned version inside the docker image for that release:
``` ```
$ docker pull vectorim/synapse:v1.97.0 $ docker pull matrixdotorg/synapse:latest
... ...
$ docker run --entrypoint pip vectorim/synapse:v1.97.0 show phonenumbers $ docker run --entrypoint pip matrixdotorg/synapse:latest show phonenumbers
Name: phonenumbers Name: phonenumbers
Version: 8.12.44 Version: 9.0.15
Summary: Python version of Google's common library for parsing, formatting, storing and validating international phone numbers. Summary: Python version of Google's common library for parsing, formatting, storing and validating international phone numbers.
Home-page: https://github.com/daviddrysdale/python-phonenumbers Home-page: https://github.com/daviddrysdale/python-phonenumbers
Author: David Drysdale Author: David Drysdale
Author-email: dmd@lurklurk.org Author-email: dmd@lurklurk.org
License: Apache License 2.0 License: Apache License 2.0
Location: /usr/local/lib/python3.9/site-packages Location: /usr/local/lib/python3.12/site-packages
Requires: Requires:
Required-by: matrix-synapse Required-by: matrix-synapse
``` ```

View File

@ -204,7 +204,7 @@ When following this route please make sure that the [Platform-specific prerequis
System requirements: System requirements:
- POSIX-compliant system (tested on Linux & OS X) - POSIX-compliant system (tested on Linux & OS X)
- Python 3.9 or later, up to Python 3.13. - Python 3.10 or later, up to Python 3.13.
- At least 1GB of free RAM if you want to join large public rooms like #matrix:matrix.org - At least 1GB of free RAM if you want to join large public rooms like #matrix:matrix.org
If building on an uncommon architecture for which pre-built wheels are If building on an uncommon architecture for which pre-built wheels are
@ -307,11 +307,16 @@ sudo dnf group install "Development Tools"
##### Red Hat Enterprise Linux / Rocky Linux / Oracle Linux ##### Red Hat Enterprise Linux / Rocky Linux / Oracle Linux
*Note: The term "RHEL" below refers to Red Hat Enterprise Linux, Oracle Linux and Rocky Linux. The distributions are 1:1 binary compatible.* *Note: The term "RHEL" below refers to Red Hat Enterprise Linux, Oracle Linux and Rocky Linux.
The distributions are 1:1 binary compatible.*
It's recommended to use the latest Python versions. It's recommended to use the latest Python versions.
RHEL 8 in particular ships with Python 3.6 by default which is EOL and therefore no longer supported by Synapse. RHEL 9 ships with Python 3.9 which is still supported by the Python core team as of this writing. However, newer Python versions provide significant performance improvements and they're available in official distributions' repositories. Therefore it's recommended to use them. RHEL 8 & 9 in particular ship with Python 3.6 & 3.9 respectively by default
which are EOL and therefore no longer supported by Synapse.
However, newer Python versions provide significant performance improvements
and they're available in official distributions' repositories.
Therefore it's recommended to use them.
Python 3.11 and 3.12 are available for both RHEL 8 and 9. Python 3.11 and 3.12 are available for both RHEL 8 and 9.

View File

@ -117,6 +117,18 @@ each upgrade are complete before moving on to the next upgrade, to avoid
stacking them up. You can monitor the currently running background updates with stacking them up. You can monitor the currently running background updates with
[the Admin API](usage/administration/admin_api/background_updates.html#status). [the Admin API](usage/administration/admin_api/background_updates.html#status).
# Upgrading to v1.142.0
## Minimum supported Python version
The minimum supported Python version has been increased from v3.9 to v3.10.
You will need Python 3.10+ to run Synapse v1.142.0.
If you use current versions of the
[matrixorg/synapse](setup/installation.html#docker-images-and-ansible-playbooks)
Docker images, no action is required.
# Upgrading to v1.141.0 # Upgrading to v1.141.0
## Docker images now based on Debian `trixie` with Python 3.13 ## Docker images now based on Debian `trixie` with Python 3.13

View File

@ -37,7 +37,7 @@ strict_equality = True
# Run mypy type checking with the minimum supported Python version to catch new usage # Run mypy type checking with the minimum supported Python version to catch new usage
# that isn't backwards-compatible (types, overloads, etc). # that isn't backwards-compatible (types, overloads, etc).
python_version = 3.9 python_version = 3.10
files = files =
docker/, docker/,

35
poetry.lock generated
View File

@ -60,9 +60,6 @@ files = [
{file = "automat-25.4.16.tar.gz", hash = "sha256:0017591a5477066e90d26b0e696ddc143baafd87b588cfac8100bc6be9634de0"}, {file = "automat-25.4.16.tar.gz", hash = "sha256:0017591a5477066e90d26b0e696ddc143baafd87b588cfac8100bc6be9634de0"},
] ]
[package.dependencies]
typing_extensions = {version = "*", markers = "python_version < \"3.10\""}
[package.extras] [package.extras]
visualize = ["Twisted (>=16.1.1)", "graphviz (>0.5.1)"] visualize = ["Twisted (>=16.1.1)", "graphviz (>0.5.1)"]
@ -510,7 +507,6 @@ files = [
[package.dependencies] [package.dependencies]
gitdb = ">=4.0.1,<5" gitdb = ">=4.0.1,<5"
typing-extensions = {version = ">=3.10.0.2", markers = "python_version < \"3.10\""}
[package.extras] [package.extras]
doc = ["sphinx (>=7.1.2,<7.2)", "sphinx-autodoc-typehints", "sphinx_rtd_theme"] doc = ["sphinx (>=7.1.2,<7.2)", "sphinx-autodoc-typehints", "sphinx_rtd_theme"]
@ -806,7 +802,7 @@ description = "Read metadata from Python packages"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
groups = ["dev"] groups = ["dev"]
markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and python_version < \"3.12\" or python_version < \"3.10\"" markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and python_version < \"3.12\""
files = [ files = [
{file = "importlib_metadata-6.7.0-py3-none-any.whl", hash = "sha256:cb52082e659e97afc5dac71e79de97d8681de3aa07ff18578330904a9d18e5b5"}, {file = "importlib_metadata-6.7.0-py3-none-any.whl", hash = "sha256:cb52082e659e97afc5dac71e79de97d8681de3aa07ff18578330904a9d18e5b5"},
{file = "importlib_metadata-6.7.0.tar.gz", hash = "sha256:1aaf550d4f73e5d6783e7acb77aec43d49da8017410afae93822cc9cca98c4d4"}, {file = "importlib_metadata-6.7.0.tar.gz", hash = "sha256:1aaf550d4f73e5d6783e7acb77aec43d49da8017410afae93822cc9cca98c4d4"},
@ -820,26 +816,6 @@ docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker
perf = ["ipython"] perf = ["ipython"]
testing = ["flufl.flake8", "importlib-resources (>=1.3) ; python_version < \"3.9\"", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7) ; platform_python_implementation != \"PyPy\"", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1) ; platform_python_implementation != \"PyPy\"", "pytest-perf (>=0.9.2)", "pytest-ruff"] testing = ["flufl.flake8", "importlib-resources (>=1.3) ; python_version < \"3.9\"", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7) ; platform_python_implementation != \"PyPy\"", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1) ; platform_python_implementation != \"PyPy\"", "pytest-perf (>=0.9.2)", "pytest-ruff"]
[[package]]
name = "importlib-resources"
version = "5.12.0"
description = "Read resources from Python packages"
optional = false
python-versions = ">=3.7"
groups = ["dev"]
markers = "python_version < \"3.10\""
files = [
{file = "importlib_resources-5.12.0-py3-none-any.whl", hash = "sha256:7b1deeebbf351c7578e09bf2f63fa2ce8b5ffec296e0d349139d43cca061a81a"},
{file = "importlib_resources-5.12.0.tar.gz", hash = "sha256:4be82589bf5c1d7999aedf2a45159d10cb3ca4f19b2271f8792bc8e6da7b22f6"},
]
[package.dependencies]
zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""}
[package.extras]
docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
testing = ["flake8 (<5)", "pytest (>=6)", "pytest-black (>=0.3.7) ; platform_python_implementation != \"PyPy\"", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8 ; python_version < \"3.12\"", "pytest-mypy (>=0.9.1) ; platform_python_implementation != \"PyPy\""]
[[package]] [[package]]
name = "incremental" name = "incremental"
version = "24.7.2" version = "24.7.2"
@ -2846,8 +2822,6 @@ files = [
[package.dependencies] [package.dependencies]
click = "*" click = "*"
importlib-metadata = {version = ">=4.6", markers = "python_version < \"3.10\""}
importlib-resources = {version = ">=5", markers = "python_version < \"3.10\""}
jinja2 = "*" jinja2 = "*"
tomli = {version = "*", markers = "python_version < \"3.11\""} tomli = {version = "*", markers = "python_version < \"3.11\""}
@ -2893,7 +2867,6 @@ files = [
[package.dependencies] [package.dependencies]
id = "*" id = "*"
importlib-metadata = {version = ">=3.6", markers = "python_version < \"3.10\""}
keyring = {version = ">=21.2.0", markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\""} keyring = {version = ">=21.2.0", markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\""}
packaging = ">=24.0" packaging = ">=24.0"
readme-renderer = ">=35.0" readme-renderer = ">=35.0"
@ -3220,7 +3193,7 @@ description = "Backport of pathlib-compatible object wrapper for zip files"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
groups = ["dev"] groups = ["dev"]
markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and python_version < \"3.12\" or python_version < \"3.10\"" markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and python_version < \"3.12\""
files = [ files = [
{file = "zipp-3.19.1-py3-none-any.whl", hash = "sha256:2828e64edb5386ea6a52e7ba7cdb17bb30a73a858f5eb6eb93d8d36f5ea26091"}, {file = "zipp-3.19.1-py3-none-any.whl", hash = "sha256:2828e64edb5386ea6a52e7ba7cdb17bb30a73a858f5eb6eb93d8d36f5ea26091"},
{file = "zipp-3.19.1.tar.gz", hash = "sha256:35427f6d5594f4acf82d25541438348c26736fa9b3afa2754bcd63cdb99d8e8f"}, {file = "zipp-3.19.1.tar.gz", hash = "sha256:35427f6d5594f4acf82d25541438348c26736fa9b3afa2754bcd63cdb99d8e8f"},
@ -3342,5 +3315,5 @@ url-preview = ["lxml"]
[metadata] [metadata]
lock-version = "2.1" lock-version = "2.1"
python-versions = "^3.9.0" python-versions = "^3.10.0"
content-hash = "5d71c862b924bc2af936cb6fef264a023213153543f738af31357deaf6de19b8" content-hash = "0122c5aa55099678f2ba5094ec393ebd814def15213388b33e5f1d7760392ffc"

View File

@ -36,7 +36,7 @@
[tool.ruff] [tool.ruff]
line-length = 88 line-length = 88
target-version = "py39" target-version = "py310"
[tool.ruff.lint] [tool.ruff.lint]
# See https://beta.ruff.rs/docs/rules/#error-e # See https://beta.ruff.rs/docs/rules/#error-e
@ -165,7 +165,7 @@ synapse_review_recent_signups = "synapse._scripts.review_recent_signups:main"
update_synapse_database = "synapse._scripts.update_synapse_database:main" update_synapse_database = "synapse._scripts.update_synapse_database:main"
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "^3.9.0" python = "^3.10.0"
# Mandatory Dependencies # Mandatory Dependencies
# ---------------------- # ----------------------
@ -201,7 +201,8 @@ bcrypt = ">=3.1.7"
# Packagers that already took care of libwebp can lower that down to 5.4.0. # Packagers that already took care of libwebp can lower that down to 5.4.0.
Pillow = ">=10.0.1" Pillow = ">=10.0.1"
# We use SortedDict.peekitem(), which was added in sortedcontainers 1.5.2. # We use SortedDict.peekitem(), which was added in sortedcontainers 1.5.2.
sortedcontainers = ">=1.5.2" # 2.0.5 updates collections.abc imports to avoid Python 3.10 incompatibility.
sortedcontainers = ">=2.0.5"
pymacaroons = ">=0.13.0" pymacaroons = ">=0.13.0"
msgpack = ">=0.5.2" msgpack = ">=0.5.2"
phonenumbers = ">=8.2.0" phonenumbers = ">=8.2.0"
@ -217,7 +218,8 @@ netaddr = ">=0.7.18"
# end up with a broken installation, with recent MarkupSafe but old Jinja, we # end up with a broken installation, with recent MarkupSafe but old Jinja, we
# add a lower bound to the Jinja2 dependency. # add a lower bound to the Jinja2 dependency.
Jinja2 = ">=3.0" Jinja2 = ">=3.0"
bleach = ">=1.4.3" # 3.2.0 updates collections.abc imports to avoid Python 3.10 incompatibility.
bleach = ">=3.2.0"
# We use `assert_never`, which were added in `typing-extensions` 4.1. # We use `assert_never`, which were added in `typing-extensions` 4.1.
typing-extensions = ">=4.1" typing-extensions = ">=4.1"
# We enforce that we have a `cryptography` version that bundles an `openssl` # We enforce that we have a `cryptography` version that bundles an `openssl`
@ -258,10 +260,12 @@ authlib = { version = ">=0.15.1", optional = true }
# `contrib/systemd/log_config.yaml`. # `contrib/systemd/log_config.yaml`.
# Note: systemd-python 231 appears to have been yanked from pypi # Note: systemd-python 231 appears to have been yanked from pypi
systemd-python = { version = ">=231", optional = true } systemd-python = { version = ">=231", optional = true }
lxml = { version = ">=4.5.2", optional = true } # 4.6.3 removes usage of _PyGen_Send which is unavailable in CPython as of Python 3.10.
lxml = { version = ">=4.6.3", optional = true }
sentry-sdk = { version = ">=0.7.2", optional = true } sentry-sdk = { version = ">=0.7.2", optional = true }
opentracing = { version = ">=2.2.0", optional = true } opentracing = { version = ">=2.2.0", optional = true }
jaeger-client = { version = ">=4.0.0", optional = true } # 4.2.0 updates collections.abc imports to avoid Python 3.10 incompatibility.
jaeger-client = { version = ">=4.2.0", optional = true }
txredisapi = { version = ">=1.4.7", optional = true } txredisapi = { version = ">=1.4.7", optional = true }
hiredis = { version = "*", optional = true } hiredis = { version = "*", optional = true }
Pympler = { version = "*", optional = true } Pympler = { version = "*", optional = true }

View File

@ -34,7 +34,7 @@ pyo3 = { version = "0.25.1", features = [
"macros", "macros",
"anyhow", "anyhow",
"abi3", "abi3",
"abi3-py39", "abi3-py310",
] } ] }
pyo3-log = "0.12.4" pyo3-log = "0.12.4"
pythonize = "0.25.0" pythonize = "0.25.0"

View File

@ -21,13 +21,12 @@ from types import FrameType
from typing import Collection, Optional, Sequence from typing import Collection, Optional, Sequence
# These are expanded inside the dockerfile to be a fully qualified image name. # These are expanded inside the dockerfile to be a fully qualified image name.
# e.g. docker.io/library/debian:bullseye # e.g. docker.io/library/debian:bookworm
# #
# If an EOL is forced by a Python version and we're dropping support for it, make sure # If an EOL is forced by a Python version and we're dropping support for it, make sure
# to remove references to the distibution across Synapse (search for "bullseye" for # to remove references to the distibution across Synapse (search for "bookworm" for
# example) # example)
DISTS = ( DISTS = (
"debian:bullseye", # (EOL ~2024-07) (our EOL forced by Python 3.9 is 2025-10-05)
"debian:bookworm", # (EOL 2026-06) (our EOL forced by Python 3.11 is 2027-10-24) "debian:bookworm", # (EOL 2026-06) (our EOL forced by Python 3.11 is 2027-10-24)
"debian:sid", # (rolling distro, no EOL) "debian:sid", # (rolling distro, no EOL)
"ubuntu:jammy", # 22.04 LTS (EOL 2027-04) (our EOL forced by Python 3.10 is 2026-10-04) "ubuntu:jammy", # 22.04 LTS (EOL 2027-04) (our EOL forced by Python 3.10 is 2026-10-04)

View File

@ -39,8 +39,8 @@ ImageFile.LOAD_TRUNCATED_IMAGES = True
# Note that we use an (unneeded) variable here so that pyupgrade doesn't nuke the # Note that we use an (unneeded) variable here so that pyupgrade doesn't nuke the
# if-statement completely. # if-statement completely.
py_version = sys.version_info py_version = sys.version_info
if py_version < (3, 9): if py_version < (3, 10):
print("Synapse requires Python 3.9 or above.") print("Synapse requires Python 3.10 or above.")
sys.exit(1) sys.exit(1)
# Allow using the asyncio reactor via env var. # Allow using the asyncio reactor via env var.

View File

@ -1,5 +1,5 @@
[tox] [tox]
envlist = py39, py310, py311, py312, py313 envlist = py310, py311, py312, py313
# we require tox>=2.3.2 for the fix to https://github.com/tox-dev/tox/issues/208 # we require tox>=2.3.2 for the fix to https://github.com/tox-dev/tox/issues/208
minversion = 2.3.2 minversion = 2.3.2