From 961f9754c19d650dd4db28c2bfcf49052d97669f Mon Sep 17 00:00:00 2001 From: Ondrej Tuma Date: Fri, 1 May 2026 10:07:31 +0200 Subject: [PATCH] Update pre-commit hooks and fix resulting linter warnings --- .github/workflows/python-package.yml | 3 +-- .pre-commit-config.yaml | 8 ++++---- doc/ChangeLog | 9 +++++++++ examples/openapi3.py | 23 ++++++++++++++--------- examples/simple.py | 4 ++-- poorwsgi/state.py | 4 ++-- setup.py | 2 +- tests_integrity/openapi.py | 4 ++-- tests_integrity/support.py | 7 +++---- tests_integrity/test_response_closed.py | 2 ++ 10 files changed, 40 insertions(+), 26 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index c375f45..9b4145b 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -13,11 +13,10 @@ jobs: fail-fast: false matrix: python-version: - - "3.9" - - "3.10" - "3.11" - "3.12" - "3.13" + - "3.14" steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c7b8d56..bc573df 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,17 +1,17 @@ --- repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.6.0 + rev: v6.0.0 hooks: - id: check-yaml - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/astral-sh/ruff-pre-commit - rev: 'v0.5.0' + rev: 'v0.15.12' hooks: - id: ruff - repo: https://github.com/pycqa/flake8 - rev: 7.1.0 + rev: 7.3.0 hooks: - id: flake8 - repo: https://github.com/Lucas-C/pre-commit-hooks-markup @@ -29,7 +29,7 @@ repos: - id: pylint language: system - repo: https://github.com/pre-commit/mirrors-mypy - rev: 'v1.10.1' # Use the sha / tag you want to point at + rev: 'v1.20.2' # Use the sha / tag you want to point at hooks: - id: mypy args: [--explicit-package-bases] diff --git a/doc/ChangeLog b/doc/ChangeLog index 0b4b689..903d2cb 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,3 +1,12 @@ +==== 2.8.0.dev ==== + * Fix I/O operation on closed file/buffer error in Response classes (#21) + * Validate route filter definitions to reject spaces with clear error + messages (#25) + * Bad PATH_INFO encoding exception handling (#37, #38) + * Updated openapi3.py example to openapi-core 0.23+ API (Spec replaced + by OpenAPI) + * Drop Python 3.9 and 3.10 support, require Python >= 3.11 + ==== 2.7.0 ==== * Reserved Request.db attribute for usage * Right HTTPException type annotation diff --git a/examples/openapi3.py b/examples/openapi3.py index cf80517..b2e7723 100644 --- a/examples/openapi3.py +++ b/examples/openapi3.py @@ -14,10 +14,7 @@ import logging as log import json -from openapi_core import ( - Spec, - unmarshal_request, unmarshal_response, - ) +from openapi_core import OpenAPI from openapi_core.exceptions import OpenAPIError from openapi_core.templating.paths.exceptions import PathNotFound, \ @@ -56,7 +53,7 @@ with open(path.join(path.dirname(__file__), "openapi.json"), "r", encoding="utf-8") as openapi: - app.openapi_spec = Spec.from_dict(json.load(openapi)) # type: ignore + app.openapi_spec = OpenAPI.from_dict(json.load(openapi)) # type: ignore @app.before_response() @@ -86,7 +83,8 @@ def before_each_response(req): """Checks the API before processing each response.""" req.api = OpenAPIRequest(req) try: - unmarshal_request(req.api, app.openapi_spec) + result = app.openapi_spec.unmarshal_request(req.api) + result.raise_for_errors() except (OperationNotFound, PathNotFound) as error: log.debug("%s", error) return # not found @@ -105,10 +103,10 @@ def after_each_response(req, res): if not hasattr(req, "api"): req.api = OpenAPIRequest(req) try: - unmarshal_response( + result = app.openapi_spec.unmarshal_response( req.api, - OpenAPIResponse(res), - app.openapi_spec) + OpenAPIResponse(res)) + result.raise_for_errors() except (OperationNotFound, PathNotFound): return res except OpenAPIError as error: @@ -170,6 +168,13 @@ def ajax_uuid(req, arg): return json.dumps({"arg": str(arg)}), "application/json" +@app.route("/arg/") +def ajax_arg_fallback(req, arg): + """Catch-all for /arg/{arg} - OpenAPI validation handles invalid types.""" + assert req + return json.dumps({"arg": arg}), "application/json" + + @app.route('/internal-server-error') def method_raises_errror(req): """Internal server error test.""" diff --git a/examples/simple.py b/examples/simple.py index 9c6db74..37ce555 100644 --- a/examples/simple.py +++ b/examples/simple.py @@ -38,7 +38,7 @@ import uwsgi # type: ignore except ModuleNotFoundError: - uwsgi = None # pylint: disable=invalid-name + uwsgi = None # type: ignore[assignment] # pylint: disable=invalid-name logger = log.getLogger() logger.setLevel("DEBUG") @@ -275,7 +275,7 @@ def style(_): @app.route('/test/') @app.route('/test/') @app.route('/test/static') -def test_dynamic(req, variable=None): +def test_dynamic(req, variable=None): # noqa: PT028 """Tests dynamic values.""" if not variable and req.headers.get('ETag') == 'W/"0123"': return not_modified(req) diff --git a/poorwsgi/state.py b/poorwsgi/state.py index ce378ea..8cd9fdf 100644 --- a/poorwsgi/state.py +++ b/poorwsgi/state.py @@ -8,9 +8,9 @@ import warnings __author__ = "Ondrej Tuma (McBig) " -__date__ = "14 Oct 2024" +__date__ = "1 May 2026" # PEP 0386 -- Version Identification and Dependency Specification -__version__ = "2.7.0" +__version__ = "2.8.0.dev" DECLINED = 0 diff --git a/setup.py b/setup.py index 706141f..9197843 100644 --- a/setup.py +++ b/setup.py @@ -195,7 +195,7 @@ def doc(): "Topic :: Internet :: WWW/HTTP :: WSGI :: Middleware", "Topic :: Software Development :: Libraries :: Python Modules" ], - python_requires=">=3.8", + python_requires=">=3.11", cmdclass={ 'build_doc': BuildDoc, 'clean_doc': CleanDoc, diff --git a/tests_integrity/openapi.py b/tests_integrity/openapi.py index 64d8bf5..1a6a39c 100644 --- a/tests_integrity/openapi.py +++ b/tests_integrity/openapi.py @@ -4,7 +4,7 @@ from email.message import Message from urllib.parse import parse_qs, urlparse -from openapi_core import Spec # type: ignore +from openapi_core import OpenAPI # type: ignore from openapi_core.validation.request.datatypes import ( # type: ignore RequestParameters) @@ -83,7 +83,7 @@ def headers(self): def response_spec_json(filename): """Initializes a response_validator for openapi.json.""" with open(filename, "r", encoding="utf-8") as openapi: - return Spec.from_dict(json.load(openapi)) + return OpenAPI.from_dict(json.load(openapi)) __all__ = ["OpenAPIRequest", "OpenAPIResponse", "response_spec_json"] diff --git a/tests_integrity/support.py b/tests_integrity/support.py index 8cd451d..dc8c80b 100644 --- a/tests_integrity/support.py +++ b/tests_integrity/support.py @@ -8,7 +8,6 @@ from requests import Request, Session from requests.exceptions import RequestException -from openapi_core import unmarshal_response from openapi_core.contrib.requests import (RequestsOpenAPIRequest, RequestsOpenAPIResponse) from openapi_core.exceptions import OpenAPIError @@ -95,10 +94,10 @@ def check_api(url, method="GET", status_code=200, status_code = [status_code] assert response.status_code in status_code try: - unmarshal_response( + result = response_spec.unmarshal_response( RequestsOpenAPIRequest(request), - RequestsOpenAPIResponse(response), - response_spec) + RequestsOpenAPIResponse(response)) + result.raise_for_errors() except PathNotFound: if response.status_code == 404: return response diff --git a/tests_integrity/test_response_closed.py b/tests_integrity/test_response_closed.py index 6fa9ed8..b833548 100644 --- a/tests_integrity/test_response_closed.py +++ b/tests_integrity/test_response_closed.py @@ -24,6 +24,8 @@ def url(request): class TestResponseClosed: """Test for Response with closed buffer handling.""" + # pylint: disable=no-self-use + def test_simple_response(self, url): """Test that a simple response works normally.""" check_url(url+"/test")