From 8c71aa8047b813d308fb777322ea1fe2c7101bda Mon Sep 17 00:00:00 2001 From: Lukas Juhrich Date: Wed, 2 Aug 2023 15:36:49 +0200 Subject: [PATCH 01/18] [web] upgrade to flask 2.3 for fixed type hints See https://github.com/python/mypy/issues/15803 --- requirements.txt | 4 ++-- web/blueprints/__init__.py | 4 +--- web/blueprints/access.py | 3 +-- web/type_utils.py | 13 ------------- 4 files changed, 4 insertions(+), 20 deletions(-) delete mode 100644 web/type_utils.py diff --git a/requirements.txt b/requirements.txt index 873471270..cf87ba3ba 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,12 +6,12 @@ Flask-Login~=0.6.2 Flask-Testing~=0.8.1 Flask-WTF~=1.0.0 Flask-RESTful~=0.3.7 -Flask~=2.0.0 +Flask~=2.3.2 Jinja2~=3.0 MarkupSafe~=2.0 SQLAlchemy>=2.0.1 WTForms~=2.2.1 -Werkzeug~=2.0.0 +Werkzeug~=2.3.6 blinker~=1.4 ipaddr~=2.2.0 jsonschema~=3.2.0 diff --git a/web/blueprints/__init__.py b/web/blueprints/__init__.py index c2a6ba265..87dab2ca4 100644 --- a/web/blueprints/__init__.py +++ b/web/blueprints/__init__.py @@ -4,12 +4,10 @@ import typing as t from typing import NoReturn -from flask import Blueprint +from flask import Blueprint, abort from werkzeug import Response from werkzeug.utils import redirect -from ..type_utils import abort - def bake_endpoint(blueprint: Blueprint, fn: t.Callable) -> str: return f"{blueprint.name}.{fn.__name__}" diff --git a/web/blueprints/access.py b/web/blueprints/access.py index c3c08b150..b419bea60 100644 --- a/web/blueprints/access.py +++ b/web/blueprints/access.py @@ -4,12 +4,11 @@ import typing as t from itertools import chain from flask.globals import current_app -from flask import request, Blueprint +from flask import request, Blueprint, abort from flask_login import current_user from werkzeug.wrappers import BaseResponse from web.blueprints import bake_endpoint -from ..type_utils import abort TFun = t.TypeVar("TFun", bound=t.Callable) diff --git a/web/type_utils.py b/web/type_utils.py deleted file mode 100644 index 17da29254..000000000 --- a/web/type_utils.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2023. The Pycroft Authors. See the AUTHORS file. -# This file is part of the Pycroft project and licensed under the terms of -# the Apache License, Version 2.0. See the LICENSE file for details -import typing as t - -from werkzeug.exceptions import abort as abort_ -from werkzeug.wrappers import BaseResponse - - -def abort(code: int | BaseResponse, *args: t.Any, **kwargs: t.Any) -> t.NoReturn: - """Typed wrapper for werkzeug/flask's `abort` function.""" - # TODO remove once on flask v2 - return abort_(code, *args, **kwargs) From a2f2062fe629157d176dc4b21c91e78d8746d246 Mon Sep 17 00:00:00 2001 From: Lukas Juhrich Date: Fri, 11 Aug 2023 11:54:03 +0200 Subject: [PATCH 02/18] Upgrade some flask dependencies to fix issues with 2.0 update --- requirements.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/requirements.txt b/requirements.txt index cf87ba3ba..0fbda6ea9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,10 +1,10 @@ babel>=2.12.1 Click~=8.0 #MySQL-python==1.2.5 -Flask-Babel~=2.0.0 +Flask-Babel~=3.1.0 Flask-Login~=0.6.2 Flask-Testing~=0.8.1 -Flask-WTF~=1.0.0 +Flask-WTF~=1.1.1 Flask-RESTful~=0.3.7 Flask~=2.3.2 Jinja2~=3.0 @@ -35,7 +35,7 @@ GitPython~=2.1.7 mac-vendor-lookup~=0.1.11 marshmallow~=3.11.1 email-validator~=1.1.1 -sentry-sdk[Flask]~=1.0.0 +sentry-sdk[Flask]~=1.29.2 -e ./deps/wtforms-widgets/ python-dotenv~=0.21.0 pydantic~=2.0.0 From 01066aea610081341d5957bf9a41480218db3429 Mon Sep 17 00:00:00 2001 From: Lukas Juhrich Date: Fri, 11 Aug 2023 13:58:49 +0200 Subject: [PATCH 03/18] suppress `flask-wtf` warnings --- pyproject.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index c47d602d1..868efc347 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -79,6 +79,9 @@ filterwarnings = [ "ignore::DeprecationWarning:kombu.utils.compat", # actually caused by reportlab. "ignore:.*load_module.*:DeprecationWarning:importlib._bootstrap", + # fixed in v1.1.2 (unreleased): + # https://github.com/wtforms/flask-wtf/blob/main/docs/changes.rst#version-112 + "ignore::DeprecationWarning:flask_wtf.recaptcha.widgets" ] timeout = "10" From aa6b571c8c42d567e92ff578a36f2dfe4249480c Mon Sep 17 00:00:00 2001 From: Lukas Juhrich Date: Fri, 11 Aug 2023 18:42:21 +0200 Subject: [PATCH 04/18] [model] fix `set_scoped_session` localproxy access In Werkzeug 2.3, the LocalProxy implementation has been reworked slightly; `_get_current_object` is now closure directly binding the `local`, so setting the attribute does not do anything. --- pycroft/model/session.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pycroft/model/session.py b/pycroft/model/session.py index 8b9868d94..00eb3ccee 100644 --- a/pycroft/model/session.py +++ b/pycroft/model/session.py @@ -47,8 +47,8 @@ def session(): def set_scoped_session(scoped_session: orm.Session) -> None: Session.remove() - # noinspection PyCallByClass - object.__setattr__(Session, '_LocalProxy__local', lambda: scoped_session) + object.__setattr__(Session, "_LocalProxy__wrapped", lambda: scoped_session) + object.__setattr__(Session, "_get_current_object", lambda: scoped_session) F = TypeVar('F', bound=Callable[..., Any]) From 6bbc28294f0247190424af5e4434d9bc50d559d5 Mon Sep 17 00:00:00 2001 From: Lukas Juhrich Date: Fri, 11 Aug 2023 18:44:28 +0200 Subject: [PATCH 05/18] [model] fix type hints for `session` and `Session` proxies Since we call `.remove()`, we actually expect a `scoped_session` rather than just a `Session`. Furthermore, we make use of the newer generic typing features of said types. --- ldap_sync/sources/db.py | 2 +- pycroft/model/session.py | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/ldap_sync/sources/db.py b/ldap_sync/sources/db.py index 752be4ded..83f4a7819 100644 --- a/ldap_sync/sources/db.py +++ b/ldap_sync/sources/db.py @@ -31,7 +31,7 @@ def establish_and_return_session(connection_string: str) -> Session: engine = create_engine(connection_string) - set_scoped_session(typing.cast(Session, scoped_session(sessionmaker(bind=engine)))) + set_scoped_session(scoped_session(sessionmaker(bind=engine))) return typing.cast(Session, global_session) # from pycroft.model.session diff --git a/pycroft/model/session.py b/pycroft/model/session.py index 00eb3ccee..9f42928ba 100644 --- a/pycroft/model/session.py +++ b/pycroft/model/session.py @@ -9,8 +9,10 @@ :copyright: (c) 2011 by AG DSN. """ -from typing import overload, TypeVar, Callable, Any, TYPE_CHECKING, cast +import typing as t +from typing import overload, TypeVar, Callable, Any, TYPE_CHECKING +from sqlalchemy.orm import scoped_session from werkzeug.local import LocalProxy import wrapt @@ -30,8 +32,10 @@ def remove(self): pass -Session = LocalProxy(lambda: NullScopedSession()) -session: orm.Session = cast(orm.Session, LocalProxy(lambda: Session())) +Session: scoped_session[orm.Session] = t.cast( + scoped_session[orm.Session], LocalProxy(lambda: NullScopedSession()) +) +session: orm.Session = t.cast(orm.Session, LocalProxy(lambda: Session())) if TYPE_CHECKING: def session(): @@ -45,7 +49,7 @@ def session(): ) -def set_scoped_session(scoped_session: orm.Session) -> None: +def set_scoped_session(scoped_session: scoped_session[orm.Session]) -> None: Session.remove() object.__setattr__(Session, "_LocalProxy__wrapped", lambda: scoped_session) object.__setattr__(Session, "_get_current_object", lambda: scoped_session) From 067d97b5b394c4b8901c53cda337ea131f9e63a9 Mon Sep 17 00:00:00 2001 From: Lukas Juhrich Date: Fri, 11 Aug 2023 18:47:53 +0200 Subject: [PATCH 06/18] [web] remove deprecated usages of `_request_ctx_stack` --- scripts/server_run.py | 13 +++++++++---- tests/frontend/conftest.py | 4 ++-- web/templates/__init__.py | 4 ++-- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/scripts/server_run.py b/scripts/server_run.py index 61c9502aa..40c8ccf74 100755 --- a/scripts/server_run.py +++ b/scripts/server_run.py @@ -11,7 +11,8 @@ from typing import Callable from babel.support import Translations -from flask import _request_ctx_stack, g, request +from flask import g, request +from flask.globals import request_ctx from sqlalchemy.orm import scoped_session, sessionmaker from werkzeug.middleware.profiler import ProfilerMiddleware @@ -71,11 +72,15 @@ def time_response(exception=None): connection, engine = try_create_connection(connection_string, wait_for_db, app.logger, args.profile) - set_scoped_session(scoped_session(sessionmaker(bind=engine), - scopefunc=lambda: _request_ctx_stack.top)) + set_scoped_session( + scoped_session( + sessionmaker(bind=engine), + scopefunc=lambda: request_ctx._get_current_object(), + ) + ) def lookup_translation(): - ctx = _request_ctx_stack.top + ctx = request_ctx if ctx is None: return None translations = getattr(ctx, 'pycroft_translations', None) diff --git a/tests/frontend/conftest.py b/tests/frontend/conftest.py index 40b539cea..8bb29cfbf 100644 --- a/tests/frontend/conftest.py +++ b/tests/frontend/conftest.py @@ -2,7 +2,7 @@ import typing as t import pytest -from flask import _request_ctx_stack +from flask.globals import request_ctx from sqlalchemy.orm import Session from pycroft import Config @@ -74,7 +74,7 @@ def config(module_session: Session) -> Config: def blueprint_urls(app: PycroftFlask) -> BlueprintUrls: def _blueprint_urls(blueprint_name: str) -> list[str]: return [ - _build_rule(_request_ctx_stack.top.url_adapter, rule) + _build_rule(request_ctx.url_adapter, rule) for rule in app.url_map.iter_rules() if rule.endpoint.startswith(f"{blueprint_name}.") ] diff --git a/web/templates/__init__.py b/web/templates/__init__.py index bb82b5516..a1dc59451 100644 --- a/web/templates/__init__.py +++ b/web/templates/__init__.py @@ -3,7 +3,7 @@ # the Apache License, Version 2.0. See the LICENSE file for details. from collections import OrderedDict, namedtuple -from flask import _request_ctx_stack +from flask.globals import request_ctx LinkedScript = namedtuple("LinkedScript", ("url", "mime_type")) PageResources = namedtuple("PageResources", ("script_files", "ready_scripts", @@ -16,7 +16,7 @@ class PageResourceRegistry: @property def page_resources(self): """Page resources are attached to Flask's current request context.""" - ctx = _request_ctx_stack.top + ctx = request_ctx if not hasattr(ctx, "page_resources"): ctx.page_resources = PageResources(OrderedDict(), list(), OrderedDict()) From d5963d26fcb99376240f633fb36e4d22c24f141a Mon Sep 17 00:00:00 2001 From: Lukas Juhrich Date: Fri, 11 Aug 2023 18:53:03 +0200 Subject: [PATCH 07/18] [web] replace BaseResponse by Response --- web/blueprints/access.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/blueprints/access.py b/web/blueprints/access.py index b419bea60..3840bb22d 100644 --- a/web/blueprints/access.py +++ b/web/blueprints/access.py @@ -6,7 +6,7 @@ from flask.globals import current_app from flask import request, Blueprint, abort from flask_login import current_user -from werkzeug.wrappers import BaseResponse +from werkzeug import Response from web.blueprints import bake_endpoint @@ -83,9 +83,9 @@ def decorator(f: TFun) -> TFun: return f return decorator - def _check_access(self) -> BaseResponse | None: + def _check_access(self) -> Response | None: if not current_user.is_authenticated: - return t.cast(BaseResponse, current_app.login_manager.unauthorized()) # type: ignore[attr-defined] + return t.cast(Response, current_app.login_manager.unauthorized()) # type: ignore[attr-defined] endpoint = request.endpoint properties = chain(self.required_properties, self.endpoint_properties_map.get(endpoint, ())) From 334354f6b7206d36999b14f791bd447f45f7ef98 Mon Sep 17 00:00:00 2001 From: Lukas Juhrich Date: Fri, 11 Aug 2023 19:11:15 +0200 Subject: [PATCH 08/18] update marshmallow to >3.13.0 This is necessary so that `load_default` works. That's what triggered that annoying deprecation warning about `metadata`. See https://marshmallow.readthedocs.io/en/latest/changelog.html#id11 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 0fbda6ea9..dd43620c7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -33,7 +33,7 @@ uwsgi~=2.0.21 uwsgitop~=0.11 GitPython~=2.1.7 mac-vendor-lookup~=0.1.11 -marshmallow~=3.11.1 +marshmallow~=3.20.1 email-validator~=1.1.1 sentry-sdk[Flask]~=1.29.2 -e ./deps/wtforms-widgets/ From ea1039706226c8d554d4ac0f986f99400d0e288a Mon Sep 17 00:00:00 2001 From: Lukas Juhrich Date: Fri, 11 Aug 2023 19:12:16 +0200 Subject: [PATCH 09/18] Upgrade to wtforms~=2.3.3 --- deps/wtforms-widgets | 2 +- requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/deps/wtforms-widgets b/deps/wtforms-widgets index 7e058f0fa..0c2d336ae 160000 --- a/deps/wtforms-widgets +++ b/deps/wtforms-widgets @@ -1 +1 @@ -Subproject commit 7e058f0faf2dfe635c5a06aa08aa0b4124276f72 +Subproject commit 0c2d336aeb167328a92fb3b6a88c6775f65a11c3 diff --git a/requirements.txt b/requirements.txt index dd43620c7..7ea636e79 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,7 @@ Flask~=2.3.2 Jinja2~=3.0 MarkupSafe~=2.0 SQLAlchemy>=2.0.1 -WTForms~=2.2.1 +WTForms~=2.3.3 Werkzeug~=2.3.6 blinker~=1.4 ipaddr~=2.2.0 From dada633b4abc5790287171c82189162ead05ef8e Mon Sep 17 00:00:00 2001 From: Lukas Juhrich Date: Fri, 11 Aug 2023 19:17:36 +0200 Subject: [PATCH 10/18] ignore flask_babel deprecation warnings --- pyproject.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 868efc347..f69dcd7b1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -81,7 +81,9 @@ filterwarnings = [ "ignore:.*load_module.*:DeprecationWarning:importlib._bootstrap", # fixed in v1.1.2 (unreleased): # https://github.com/wtforms/flask-wtf/blob/main/docs/changes.rst#version-112 - "ignore::DeprecationWarning:flask_wtf.recaptcha.widgets" + "ignore::DeprecationWarning:flask_wtf.recaptcha.widgets", + # fixed once this PR goes through: https://github.com/python-babel/flask-babel/pull/230 + "ignore:'locked_cached_property' is deprecated:DeprecationWarning:flask_babel", ] timeout = "10" From beecb3082c6c408b065b6b7b7d141aed7a6d1205 Mon Sep 17 00:00:00 2001 From: Lukas Juhrich Date: Fri, 11 Aug 2023 19:31:00 +0200 Subject: [PATCH 11/18] ignore passlib pkg_resources warning This is reported: https://foss.heptapod.net/python-libs/passlib/-/issues/185 Given that passlib 1.8 will be dropping old versions[1], the maintainer will most likely fix this in 1.8. [1] https://foss.heptapod.net/python-libs/passlib/-/issues/119 --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index f69dcd7b1..1ac43e5bf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -84,6 +84,7 @@ filterwarnings = [ "ignore::DeprecationWarning:flask_wtf.recaptcha.widgets", # fixed once this PR goes through: https://github.com/python-babel/flask-babel/pull/230 "ignore:'locked_cached_property' is deprecated:DeprecationWarning:flask_babel", + "ignore:pkg_resources is deprecated:DeprecationWarning:passlib", ] timeout = "10" From 06bfa02017fc079df9182e0b632935b886a637e0 Mon Sep 17 00:00:00 2001 From: Lukas Juhrich Date: Fri, 11 Aug 2023 20:01:02 +0200 Subject: [PATCH 12/18] Remove `fully_qualify_location` in frontend assertions This appearently is not necessary anymore. --- tests/frontend/assertions.py | 13 ++----------- tests/frontend/test_properties.py | 5 ++--- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/tests/frontend/assertions.py b/tests/frontend/assertions.py index 5ba82b3df..b197fe500 100644 --- a/tests/frontend/assertions.py +++ b/tests/frontend/assertions.py @@ -4,7 +4,6 @@ import contextlib import re import typing as t -from urllib.parse import urlparse, urljoin import flask.testing import jinja2 as j @@ -60,14 +59,6 @@ def assert_ok(self, endpoint: str, **kw) -> Response: __tracebackhide__ = True return self.assert_response_code(endpoint, code=200, **kw) - def fully_qualify_location(self, location: str) -> str: - # inspired by `flask_testing.utils` - if urlparse(location).netloc: - return location - - server_name = self.application.config.get('SERVER_NAME') or 'localhost' - return urljoin(f"http://{server_name}", location) - def assert_url_redirects( self, url: str, expected_location: str | None = None, method: str = "GET", **kw ) -> Response: @@ -76,7 +67,7 @@ def assert_url_redirects( assert 300 <= resp.status_code < 400, \ f"Expected {url!r} to redirect, got status {resp.status}" if expected_location is not None: - assert resp.location == self.fully_qualify_location(expected_location) + assert resp.location == expected_location return resp def assert_redirects( @@ -91,7 +82,7 @@ def assert_redirects( assert 300 <= resp.status_code < 400, \ f"Expected endpoint {endpoint} to redirect, got status {resp.status}" if expected_location is not None: - assert resp.location == self.fully_qualify_location(expected_location) + assert resp.location == expected_location return resp def assert_url_forbidden(self, url: str, method: str = "HEAD", **kw) -> Response: diff --git a/tests/frontend/test_properties.py b/tests/frontend/test_properties.py index 5c825c422..ee6128d2d 100644 --- a/tests/frontend/test_properties.py +++ b/tests/frontend/test_properties.py @@ -15,13 +15,12 @@ class TestPropertiesFrontend: def test_property_gets_added(self, client: TestClient): group_name = "This is my first property group" # first time: a redirect - response = client.assert_response_code( + response = client.assert_redirects( "properties.property_group_create", - code=302, method="post", data={"name": group_name}, + expected_location=url_for("properties.property_groups"), ) - assert response.location == url_for('properties.property_groups', _external=True) response = client.assert_url_response_code(response.location, code=200) content = response.data.decode('utf-8') From 63994cad92f9fe5b0f36310a59bfa8c1e87a56c9 Mon Sep 17 00:00:00 2001 From: Lukas Juhrich Date: Fri, 11 Aug 2023 20:03:12 +0200 Subject: [PATCH 13/18] fix config proxy the second attribute to `LocalProxy` now serves as an attribute name to be looked up. --- pycroft/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pycroft/__init__.py b/pycroft/__init__.py index 20f0163d3..d6179a00d 100644 --- a/pycroft/__init__.py +++ b/pycroft/__init__.py @@ -29,4 +29,4 @@ def _get_config(): return config -config: Config = typing.cast(Config, LocalProxy(_get_config, "config")) +config: Config = typing.cast(Config, LocalProxy(_get_config)) From 59e13cc55e40466fd8c8f357caa1a1144f61698d Mon Sep 17 00:00:00 2001 From: Lukas Juhrich Date: Fri, 11 Aug 2023 20:03:59 +0200 Subject: [PATCH 14/18] Add comment about sphinxcontrib warnings --- pyproject.toml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 1ac43e5bf..74727e5f4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -85,6 +85,11 @@ filterwarnings = [ # fixed once this PR goes through: https://github.com/python-babel/flask-babel/pull/230 "ignore:'locked_cached_property' is deprecated:DeprecationWarning:flask_babel", "ignore:pkg_resources is deprecated:DeprecationWarning:passlib", + + # TODO this probably comes from the `sphinxcontrib` packages. + # however, e.g. `sphinxcontrib-fulltoc` is unmaintained (last commit 2017). + # we probably need to start forking before updating to py3.12. + # "error:.*Deprecated call.*:DeprecationWarning:", ] timeout = "10" From f9e3bcf0f3389c425328810222633b3a0d8a1e74 Mon Sep 17 00:00:00 2001 From: Lukas Juhrich Date: Mon, 14 Aug 2023 18:19:20 +0200 Subject: [PATCH 15/18] Fix some mypy errors The `type: ignore` is probably obsolete due to the sentry-sdk update, and the url_for false positive seems like a mypy quirk. --- ldap_sync/__main__.py | 2 +- web/blueprints/finance/__init__.py | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/ldap_sync/__main__.py b/ldap_sync/__main__.py index d6f899b2c..12519b78d 100644 --- a/ldap_sync/__main__.py +++ b/ldap_sync/__main__.py @@ -151,7 +151,7 @@ def try_setup_sentry() -> None: dsn=dsn, integrations=[logging_integration], traces_sample_rate=1.0, - ) # type: ignore + ) def trigger_sentry() -> typing.NoReturn: diff --git a/web/blueprints/finance/__init__.py b/web/blueprints/finance/__init__.py index 09b179d6c..b39effb78 100644 --- a/web/blueprints/finance/__init__.py +++ b/web/blueprints/finance/__init__.py @@ -1055,9 +1055,8 @@ def transaction_delete(transaction_id): @access.require('finance_show') @bp.route('/transactions') def transactions_all(): - return render_template('finance/transactions_overview.html', - api_endpoint=url_for(".transactions_all_json", - **request.args)) + url = url_for(".transactions_all_json", **request.args) # type: ignore[arg-type] + return render_template("finance/transactions_overview.html", api_endpoint=url) @access.require('finance_show') From 6840ee01aefbc1a880718944495bff7db035df1e Mon Sep 17 00:00:00 2001 From: Lukas Juhrich Date: Mon, 14 Aug 2023 18:21:39 +0200 Subject: [PATCH 16/18] Update sphinx-paramlinks to 0.6.0 This contains the fix for sqlalchemyorg/sphinx-paramlinks#20, which adresses some deprecation warnings. --- requirements.dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.dev.txt b/requirements.dev.txt index 9122242a5..968ae2687 100644 --- a/requirements.dev.txt +++ b/requirements.dev.txt @@ -14,7 +14,7 @@ mypy>=0.961 celery-types~=0.9.3 types-jsonschema~=4.3.0 types-passlib~=1.7.7 -sphinx-paramlinks~=0.5.4 +sphinx-paramlinks~=0.6.0 sphinxcontrib-fulltoc~=1.2.0 sphinxcontrib-httpdomain~=1.8.0 ruff~=0.0.241 From 534c0b86b7d4af5c892ceffa0e0381c7317ec0d2 Mon Sep 17 00:00:00 2001 From: Lukas Juhrich Date: Mon, 14 Aug 2023 18:29:59 +0200 Subject: [PATCH 17/18] Fix `json` detection in one blueprint in 2.1, calling `.json` on a request without the proper media type aborts with 415 WRONG MEDIA TYPE. `is_json` is what we want here. --- web/blueprints/finance/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/blueprints/finance/__init__.py b/web/blueprints/finance/__init__.py index b39effb78..963517e60 100644 --- a/web/blueprints/finance/__init__.py +++ b/web/blueprints/finance/__init__.py @@ -967,7 +967,7 @@ def transactions_confirm_selected(): Confirms the unconfirmed transactions that where selected by the user in the frontend Javascript is used to post """ - if not request.json: + if not request.is_json: return redirect(url_for(".transactions_unconfirmed")) ids = request.json.get("ids", []) From 2cf549f2b9555d7001bf977c67a91f2cdb7dc02b Mon Sep 17 00:00:00 2001 From: Lukas Juhrich Date: Mon, 14 Aug 2023 18:38:59 +0200 Subject: [PATCH 18/18] Remove obsolete ruff excludes and fix errors Since v0.251, ruff supports `match` statements. --- ldap_sync/record_diff.py | 2 +- pyproject.toml | 5 ----- web/blueprints/helpers/exception.py | 3 +-- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/ldap_sync/record_diff.py b/ldap_sync/record_diff.py index 0ccd20fa0..b0b4a002b 100644 --- a/ldap_sync/record_diff.py +++ b/ldap_sync/record_diff.py @@ -78,7 +78,7 @@ def diff_records(current: T | None, desired: T | None) -> action.Action: case (c, d): raise TypeError(f"Cannot diff {type(c).__name__} and {type(d).__name__}") # see https://github.com/python/mypy/issues/12534 - assert False # pragma: no cover + raise AssertionError # pragma: no cover TKey = typing.TypeVar("TKey") diff --git a/pyproject.toml b/pyproject.toml index 74727e5f4..cdaec14b6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -99,11 +99,6 @@ target-version = "py310" exclude = [ "deps/", "doc/", - # match statement cannot be parsed yet… :/ - # see https://github.com/charliermarsh/ruff/issues/282 - - "ldap_sync/record_diff.py", - "web/blueprints/helpers/exception.py", ] # to look up the meaning of specific rule IDs, use `ruff rule $id` select = [ diff --git a/web/blueprints/helpers/exception.py b/web/blueprints/helpers/exception.py index fb9318c38..66f1dc705 100644 --- a/web/blueprints/helpers/exception.py +++ b/web/blueprints/helpers/exception.py @@ -5,11 +5,10 @@ from flask import flash, abort, make_response from flask.typing import ResponseReturnValue -from sqlalchemy.orm import Session, SessionTransaction +from sqlalchemy.orm import SessionTransaction from pycroft.exc import PycroftException from pycroft.lib.net import MacExistsException, SubnetFullException -from pycroft.model import session from pycroft.model.host import MulticastFlagException from pycroft.model.types import InvalidMACAddressException