From 2abb298b53cee5ebeaa681427d1de6002da70cf9 Mon Sep 17 00:00:00 2001 From: p1c2u Date: Fri, 30 Jan 2026 12:51:35 +0000 Subject: [PATCH 1/2] Python 3.9 support drop --- .github/workflows/build-docs.yml | 4 +- .github/workflows/python-test.yml | 4 +- poetry.lock | 74 ++----------------------------- pyproject.toml | 3 +- 4 files changed, 8 insertions(+), 77 deletions(-) diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index 377c41cf..753a375b 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -11,10 +11,10 @@ jobs: steps: - uses: actions/checkout@v6 - - name: Set up Python 3.9 + - name: Set up Python uses: actions/setup-python@v6 with: - python-version: 3.9 + python-version: "3.10" - name: Get full Python version id: full-python-version diff --git a/.github/workflows/python-test.yml b/.github/workflows/python-test.yml index 56d6146c..3303787b 100644 --- a/.github/workflows/python-test.yml +++ b/.github/workflows/python-test.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] + python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] fail-fast: false steps: - uses: actions/checkout@v6 @@ -74,7 +74,7 @@ jobs: - name: "Setup Python" uses: actions/setup-python@v6 with: - python-version: 3.9 + python-version: "3.10" - name: Get full Python version id: full-python-version diff --git a/poetry.lock b/poetry.lock index c3e54c98..3bc762c3 100644 --- a/poetry.lock +++ b/poetry.lock @@ -168,9 +168,6 @@ files = [ {file = "aioitertools-0.13.0.tar.gz", hash = "sha256:620bd241acc0bbb9ec819f1ab215866871b4bbd1f73836a55f799200ee86950c"}, ] -[package.dependencies] -typing_extensions = {version = ">=4.0", markers = "python_version < \"3.10\""} - [[package]] name = "aiosignal" version = "1.4.0" @@ -871,19 +868,6 @@ all = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.8)", "httpx (> standard = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.8)", "httpx (>=0.23.0,<1.0.0)", "jinja2 (>=3.1.5)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.18)", "uvicorn[standard] (>=0.12.0)"] standard-no-fastapi-cloud-cli = ["email-validator (>=2.0.0)", "fastapi-cli[standard-no-fastapi-cloud-cli] (>=0.0.8)", "httpx (>=0.23.0,<1.0.0)", "jinja2 (>=3.1.5)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.18)", "uvicorn[standard] (>=0.12.0)"] -[[package]] -name = "filelock" -version = "3.19.1" -description = "A platform independent file lock." -optional = false -python-versions = ">=3.9" -groups = ["dev"] -markers = "python_version < \"3.10\"" -files = [ - {file = "filelock-3.19.1-py3-none-any.whl", hash = "sha256:d38e30481def20772f5baf097c122c3babc4fcdb7e14e57049eb9d88c6dc017d"}, - {file = "filelock-3.19.1.tar.gz", hash = "sha256:66eda1888b0171c998b35be2bcc0f6d75c388a7ce20c3f3f37aa8e96c2dddf58"}, -] - [[package]] name = "filelock" version = "3.20.3" @@ -891,7 +875,6 @@ description = "A platform independent file lock." optional = false python-versions = ">=3.10" groups = ["dev"] -markers = "python_version >= \"3.10\"" files = [ {file = "filelock-3.20.3-py3-none-any.whl", hash = "sha256:4b0dda527ee31078689fc205ec4f1c1bf7d56cf88b6dc9426c4f230e46c2dce1"}, {file = "filelock-3.20.3.tar.gz", hash = "sha256:18c57ee915c7ec61cff0ecf7f0f869936c7c30191bb0cf406f1341778d0834e1"}, @@ -929,7 +912,6 @@ files = [ [package.dependencies] blinker = ">=1.9.0" click = ">=8.1.3" -importlib-metadata = {version = ">=3.6.0", markers = "python_version < \"3.10\""} itsdangerous = ">=2.2.0" jinja2 = ">=3.1.2" markupsafe = ">=2.1.1" @@ -1188,27 +1170,6 @@ files = [ {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, ] -[[package]] -name = "importlib-metadata" -version = "6.8.0" -description = "Read metadata from Python packages" -optional = false -python-versions = ">=3.8" -groups = ["main", "dev", "docs"] -markers = "python_version < \"3.10\"" -files = [ - {file = "importlib_metadata-6.8.0-py3-none-any.whl", hash = "sha256:3ebb78df84a805d7698245025b975d9d67053cd94c79245ba4b3eb694abe68bb"}, - {file = "importlib_metadata-6.8.0.tar.gz", hash = "sha256:dbace7892d8c0c4ac1ad096662232f831d4e64f4c4545bd53016a3e9d4654743"}, -] - -[package.dependencies] -zipp = ">=0.5" - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -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 (>=2.2)", "pytest-mypy (>=0.9.1) ; platform_python_implementation != \"PyPy\"", "pytest-perf (>=0.9.2)", "pytest-ruff"] - [[package]] name = "iniconfig" version = "2.0.0" @@ -1245,9 +1206,6 @@ files = [ {file = "isort-6.1.0.tar.gz", hash = "sha256:9b8f96a14cfee0677e78e941ff62f03769a06d412aabb9e2a90487b3b7e8d481"}, ] -[package.dependencies] -importlib-metadata = {version = ">=4.6.0", markers = "python_version < \"3.10\""} - [package.extras] colors = ["colorama"] plugins = ["setuptools"] @@ -1495,9 +1453,6 @@ files = [ {file = "markdown-3.7.tar.gz", hash = "sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2"}, ] -[package.dependencies] -importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""} - [package.extras] docs = ["mdx-gh-links (>=0.2)", "mkdocs (>=1.5)", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-nature (>=0.6)", "mkdocs-section-index", "mkdocstrings[python]"] testing = ["coverage", "pyyaml"] @@ -1612,7 +1567,6 @@ files = [ click = ">=7.0" colorama = {version = ">=0.4", markers = "platform_system == \"Windows\""} ghp-import = ">=1.0" -importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""} jinja2 = ">=2.11.1" markdown = ">=3.3.6" markupsafe = ">=2.0.1" @@ -1658,7 +1612,6 @@ files = [ ] [package.dependencies] -importlib-metadata = {version = ">=4.3", markers = "python_version < \"3.10\""} mergedeep = ">=1.3.4" platformdirs = ">=2.2.0" pyyaml = ">=5.1" @@ -1718,7 +1671,6 @@ files = [ ] [package.dependencies] -importlib-metadata = {version = ">=4.6", markers = "python_version < \"3.10\""} Jinja2 = ">=2.11.1" Markdown = ">=3.6" MarkupSafe = ">=1.1" @@ -3196,10 +3148,7 @@ files = [ [package.dependencies] distlib = ">=0.3.7,<1" -filelock = [ - {version = ">=3.16.1,<4", markers = "python_version < \"3.10\""}, - {version = ">=3.20.1,<4", markers = "python_version >= \"3.10\""}, -] +filelock = {version = ">=3.20.1,<4", markers = "python_version >= \"3.10\""} platformdirs = ">=3.9.1,<5" typing-extensions = {version = ">=4.13.2", markers = "python_version < \"3.11\""} @@ -3389,23 +3338,6 @@ idna = ">=2.0" multidict = ">=4.0" propcache = ">=0.2.0" -[[package]] -name = "zipp" -version = "3.19.1" -description = "Backport of pathlib-compatible object wrapper for zip files" -optional = false -python-versions = ">=3.8" -groups = ["main", "dev", "docs"] -markers = "python_version < \"3.10\"" -files = [ - {file = "zipp-3.19.1-py3-none-any.whl", hash = "sha256:2828e64edb5386ea6a52e7ba7cdb17bb30a73a858f5eb6eb93d8d36f5ea26091"}, - {file = "zipp-3.19.1.tar.gz", hash = "sha256:35427f6d5594f4acf82d25541438348c26736fa9b3afa2754bcd63cdb99d8e8f"}, -] - -[package.extras] -doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -test = ["big-O", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"] - [extras] aiohttp = ["aiohttp", "multidict"] django = ["django"] @@ -3417,5 +3349,5 @@ starlette = ["aioitertools", "starlette"] [metadata] lock-version = "2.1" -python-versions = "^3.9.0" -content-hash = "46fec06aa2ade7139dcf379bf2c328f5b1c91ce7554eb90e4f52961f06f0481d" +python-versions = "^3.10.0" +content-hash = "9bd919ff2e6f35143277209c2fc0957150772e986518cfbdd02fc4e9370c73fa" diff --git a/pyproject.toml b/pyproject.toml index aa8f185c..971520aa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,7 +46,6 @@ classifiers = [ "Topic :: Software Development :: Libraries :: Python Modules", "Operating System :: OS Independent", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -60,7 +59,7 @@ include = [ ] [tool.poetry.dependencies] -python = "^3.9.0" +python = "^3.10.0" django = {version = ">=3.0", optional = true} falcon = {version = ">=3.0", optional = true} flask = {version = "*", optional = true} From 20bf8d50e09b8dc9175278de264184d4b8cf7b3c Mon Sep 17 00:00:00 2001 From: p1c2u Date: Fri, 30 Jan 2026 13:04:57 +0000 Subject: [PATCH 2/2] Remove future annotations --- openapi_core/contrib/aiohttp/requests.py | 2 -- openapi_core/datatypes.py | 2 -- openapi_core/templating/paths/parsers.py | 3 --- openapi_core/unmarshalling/request/datatypes.py | 2 -- .../contrib/aiohttp/test_aiohttp_validation.py | 10 +++++----- 5 files changed, 5 insertions(+), 14 deletions(-) diff --git a/openapi_core/contrib/aiohttp/requests.py b/openapi_core/contrib/aiohttp/requests.py index eac7965e..27c3d327 100644 --- a/openapi_core/contrib/aiohttp/requests.py +++ b/openapi_core/contrib/aiohttp/requests.py @@ -1,7 +1,5 @@ """OpenAPI core contrib aiohttp requests module""" -from __future__ import annotations - from aiohttp import web from openapi_core.datatypes import RequestParameters diff --git a/openapi_core/datatypes.py b/openapi_core/datatypes.py index 12ffcbba..07d4225f 100644 --- a/openapi_core/datatypes.py +++ b/openapi_core/datatypes.py @@ -1,7 +1,5 @@ """OpenAPI core validation request datatypes module""" -from __future__ import annotations - from dataclasses import dataclass from dataclasses import field from typing import Any diff --git a/openapi_core/templating/paths/parsers.py b/openapi_core/templating/paths/parsers.py index 97c2e182..27049ec6 100644 --- a/openapi_core/templating/paths/parsers.py +++ b/openapi_core/templating/paths/parsers.py @@ -1,6 +1,3 @@ -# Allow writing union types as X | Y in Python 3.9 -from __future__ import annotations - import re from dataclasses import dataclass diff --git a/openapi_core/unmarshalling/request/datatypes.py b/openapi_core/unmarshalling/request/datatypes.py index 47d520c3..02f70636 100644 --- a/openapi_core/unmarshalling/request/datatypes.py +++ b/openapi_core/unmarshalling/request/datatypes.py @@ -1,7 +1,5 @@ """OpenAPI core unmarshalling request datatypes module""" -from __future__ import annotations - from dataclasses import dataclass from dataclasses import field from typing import Any diff --git a/tests/integration/contrib/aiohttp/test_aiohttp_validation.py b/tests/integration/contrib/aiohttp/test_aiohttp_validation.py index 134e530d..90ce875c 100644 --- a/tests/integration/contrib/aiohttp/test_aiohttp_validation.py +++ b/tests/integration/contrib/aiohttp/test_aiohttp_validation.py @@ -1,5 +1,3 @@ -from __future__ import annotations - from typing import TYPE_CHECKING from unittest import mock @@ -9,7 +7,7 @@ from aiohttp.test_utils import TestClient -async def test_aiohttp_integration_valid_input(client: TestClient): +async def test_aiohttp_integration_valid_input(client: "TestClient"): # Given given_query_string = { "q": "string", @@ -34,7 +32,9 @@ async def test_aiohttp_integration_valid_input(client: TestClient): assert response_data == expected_response_data -async def test_aiohttp_integration_invalid_server(client: TestClient, request): +async def test_aiohttp_integration_invalid_server( + client: "TestClient", request +): if "no_validation" in request.node.name: pytest.skip("No validation for given handler.") # Given @@ -71,7 +71,7 @@ async def test_aiohttp_integration_invalid_server(client: TestClient, request): async def test_aiohttp_integration_invalid_input( - client: TestClient, response_getter, request + client: "TestClient", response_getter, request ): if "no_validation" in request.node.name: pytest.skip("No validation for given handler.")