From 9fa3c7d9ddf5a3e36e994b30da5a33e237eb186a Mon Sep 17 00:00:00 2001 From: Lukas Juhrich Date: Fri, 3 Nov 2023 15:54:35 +0100 Subject: [PATCH 01/10] Make compatible with python 3.12 --- build/requirements/requirements.txt | 12 ++++++------ sipa/flatpages.py | 4 +++- sipa/initialization.py | 3 +-- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/build/requirements/requirements.txt b/build/requirements/requirements.txt index 44d334b8..43d52113 100644 --- a/build/requirements/requirements.txt +++ b/build/requirements/requirements.txt @@ -1,22 +1,22 @@ -flask~=2.2.2 +flask~=3.0.0 +flask-babel~=4.0.0 Flask-Login~=0.6.1 -Babel~=2.10.3 -Flask-Babel~=2.0.0 +flask-wtf~=1.2.1 +babel~=2.13.1 SQLAlchemy~=1.4.40 Markdown~=3.4.1 requests~=2.28.1 Flask-FlatPages~=0.8.1 pygal~=3.0.0 GitPython~=3.1.27 -Flask-WTF~=1.0.1 PyMySQL~=1.0.2 blinker~=1.5 factory-boy~=3.2.1 psycopg2~=2.9.3 Flask-QRcode~=3.1.0 email_validator~=1.2.1 -Werkzeug~=2.2.2 -sentry-sdk[flask]~=1.9.5 +Werkzeug~=3.0.1 +sentry-sdk[flask]~=1.34.0 # Pin to avoid incompatibility jinja2~=3.1.2 MarkupSafe~=2.1.1 diff --git a/sipa/flatpages.py b/sipa/flatpages.py index d0e1c42c..99db526d 100644 --- a/sipa/flatpages.py +++ b/sipa/flatpages.py @@ -8,6 +8,7 @@ from babel.core import Locale, UnknownLocaleError, negotiate_locale from flask import abort, request +from flask_babel import get_babel from flask_flatpages import FlatPages, Page from yaml.scanner import ScannerError @@ -323,10 +324,11 @@ def init_app(self, app): self.app = app app.cf_pages = self self.flat_pages.init_app(app) + babel = get_babel(app) self.root_category = Category( parent=None, id="", - default_locale=app.babel_instance.default_locale, + default_locale=babel.default_locale, ) self._init_categories() diff --git a/sipa/initialization.py b/sipa/initialization.py index afdcc553..38c877d4 100644 --- a/sipa/initialization.py +++ b/sipa/initialization.py @@ -54,8 +54,7 @@ def init_app(app, **kwargs): logger.debug('Initializing app') login_manager.init_app(app, add_context_processor=False) babel = Babel() - babel.init_app(app) - babel.localeselector(select_locale) + babel.init_app(app, locale_selector=select_locale) app.before_request(setup_request_locale_context) app.after_request(ensure_csp) app.session_interface = SeparateLocaleCookieSessionInterface() From 85a57d0943005686616dfb01113f04d3632e6a0e Mon Sep 17 00:00:00 2001 From: Lukas Juhrich Date: Fri, 3 Nov 2023 16:59:35 +0100 Subject: [PATCH 02/10] Use python 3.12 in docker file --- build/Dockerfile | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/build/Dockerfile b/build/Dockerfile index c9e6f7f2..99ddb8a2 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.11 +FROM python:3.12 MAINTAINER Lukas Juhrich der Große @@ -24,12 +24,11 @@ RUN addgroup --gid 9999 sipa && \ RUN mkdir /home/sipa/sipa WORKDIR /home/sipa/sipa -ADD ./build /home/sipa/sipa/build/ +COPY ./build /home/sipa/sipa/build/ ARG additional_requirements RUN ./build/install_requirements.py $additional_requirements -ADD . /home/sipa/sipa -RUN chown -R sipa:sipa /home/sipa/sipa +COPY --chown=sipa:sipa . /home/sipa/sipa EXPOSE 5000 From 05340da2ce3af675e8fec02872e7cd7e856f2666 Mon Sep 17 00:00:00 2001 From: Lukas Juhrich Date: Fri, 3 Nov 2023 17:01:37 +0100 Subject: [PATCH 03/10] Enforce use of f-string literals using pyupgrade ruff rules --- pyproject.toml | 1 + sipa/babel.py | 10 ++++------ sipa/backends/datasource.py | 5 +---- sipa/flatpages.py | 8 ++++---- sipa/model/pycroft/user.py | 6 +----- sipa/model/pycroft/userdb.py | 4 ++-- sipa/utils/link_patch.py | 5 +---- 7 files changed, 14 insertions(+), 25 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 84809bb1..01daa1f7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,6 +8,7 @@ select = [ "E", "F", "B", # flake8-bugbear + "UP", # pyupgrade ] ignore = [ "E741", # ambiguous variable names diff --git a/sipa/babel.py b/sipa/babel.py index d3eaa5ae..eb9add4c 100644 --- a/sipa/babel.py +++ b/sipa/babel.py @@ -27,15 +27,13 @@ def get_user_locale_setting() -> Locale | None: try: locale = Locale.parse(locale_identifier) except (UnknownLocaleError, ValueError): - logger.warning("Illegal locale {!r} stored in user session." - .format(locale_identifier)) - session.pop('locale') + logger.warning(f"Illegal locale {locale_identifier!r} stored in user session.") + session.pop("locale") return None if locale not in possible_locales(): - logger.warning("Unavailable locale {} stored in user session." - .format(locale)) - session.pop('locale', None) + logger.warning(f"Unavailable locale {locale} stored in user session.") + session.pop("locale", None) return None return locale diff --git a/sipa/backends/datasource.py b/sipa/backends/datasource.py index 59b31c06..28e3b54a 100644 --- a/sipa/backends/datasource.py +++ b/sipa/backends/datasource.py @@ -54,10 +54,7 @@ def __eq__(self, other): return compare_all_attributes(self, other, ['name']) def __repr__(self): - return "<{cls} {name!r}>".format( - cls=type(self).__name__, - name=self.name, - ) + return f"<{type(self).__name__} {self.name!r}>" def __hash__(self): return xor_hashes(self.name, self.user_class, self.support_mail, self.mail_server) diff --git a/sipa/flatpages.py b/sipa/flatpages.py index 99db526d..58725250 100644 --- a/sipa/flatpages.py +++ b/sipa/flatpages.py @@ -150,8 +150,8 @@ def __getattr__(self, attr: str) -> str: return self.localized_page.meta[attr] except KeyError as e: raise AttributeError( - "{!r} object has no attribute {!r}" - .format(type(self).__name__, attr)) from e + f"{type(self).__name__!r} object has no attribute {attr!r}" + ) from e @cached_property def available_locales(self) -> tuple[str]: @@ -232,8 +232,8 @@ def __getattr__(self, attr): index = self._articles['index'] except KeyError as e: raise AttributeError( - "{!r} object has no attribute {!r}" - .format(type(self).__name__, attr)) from e + f"{type(self).__name__!r} object has no attribute {attr!r}" + ) from e return getattr(index, attr) def add_child_category(self, id): diff --git a/sipa/model/pycroft/user.py b/sipa/model/pycroft/user.py index b7b35cbe..2ae6ae19 100644 --- a/sipa/model/pycroft/user.py +++ b/sipa/model/pycroft/user.py @@ -300,11 +300,7 @@ def payment_details(self) -> PaymentDetails: bank="Ostsächsische Sparkasse Dresden", iban="DE61 8505 0300 3120 2195 40", bic="OSDD DE 81 XXX", - purpose="{id}, {name}, {address}".format( - id=self.user_data.user_id, - name=self.user_data.name, - address=self.user_data.room, - ), + purpose=f"{self.user_data.user_id}, {self.user_data.name}, {self.user_data.room}", ) def has_property(self, property: str) -> bool: diff --git a/sipa/model/pycroft/userdb.py b/sipa/model/pycroft/userdb.py index 5ecf3d81..79c836ce 100644 --- a/sipa/model/pycroft/userdb.py +++ b/sipa/model/pycroft/userdb.py @@ -109,8 +109,8 @@ def change_password(self, password): self.sql_query( "GRANT SELECT, INSERT, UPDATE, DELETE, " "ALTER, CREATE, DROP, INDEX, LOCK TABLES " - "ON `{}`.* " - "TO %s@%s".format(self.db_name()), + f"ON `{self.db_name()}`.* " + "TO %s@%s", (self.db_name(), self.ip_mask), ) diff --git a/sipa/utils/link_patch.py b/sipa/utils/link_patch.py index bd86eb6b..f3c61b60 100644 --- a/sipa/utils/link_patch.py +++ b/sipa/utils/link_patch.py @@ -14,10 +14,7 @@ def absolute_path_replacer(match): if prefix.endswith("/"): prefix = prefix[:-1] - return "{key}=\"{path}\"".format( - key=match.group(1), - path=prefix + match.group(2) - ) + return f'{match.group(1)}="{prefix + match.group(2)}"' class LinkPostprocessor(Postprocessor): From c514c11b85959f7135b0abd85641d968b4d4343e Mon Sep 17 00:00:00 2001 From: Lukas Juhrich Date: Fri, 3 Nov 2023 17:07:47 +0100 Subject: [PATCH 04/10] Target py312 in ruff config as well --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 01daa1f7..56ccbcb7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.ruff] line-length = 100 -target-version = "py311" +target-version = "py312" exclude = [ ] # to look up the meaning of specific rule IDs, use `ruff rule $id` From 27c5ac1917e964561b2cebe20bc1c20bcd127a0b Mon Sep 17 00:00:00 2001 From: Lukas Juhrich Date: Fri, 3 Nov 2023 17:10:33 +0100 Subject: [PATCH 05/10] Use 3.12 in CI --- .github/workflows/sipa-ci .yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/sipa-ci .yml b/.github/workflows/sipa-ci .yml index 333aa8f2..427fc562 100644 --- a/.github/workflows/sipa-ci .yml +++ b/.github/workflows/sipa-ci .yml @@ -9,7 +9,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 with: - python-version: "3.11" + python-version: "3.12" - uses: pre-commit/action@v3.0.0 build: runs-on: ubuntu-latest @@ -41,7 +41,7 @@ jobs: submodules: recursive - uses: actions/setup-python@v4 with: - python-version: '3.11' + python-version: '3.12' cache: 'pip' - name: print information about pip cache run: pip cache info && pip cache list From 5b2cfbcde8444192a7be2f1140d206d698ebe399 Mon Sep 17 00:00:00 2001 From: Lukas Juhrich Date: Fri, 3 Nov 2023 17:13:13 +0100 Subject: [PATCH 06/10] Upgrade pre-commit-hooks --- .pre-commit-config.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6ca5c2b2..cee57f7f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,16 +1,16 @@ # see https://github.com/topics/pre-commit-hook for more repos: - repo: https://github.com/akaihola/darker - rev: 1.6.1 + rev: 1.7.2 hooks: - id: darker - repo: https://github.com/asottile/pyupgrade - rev: v2.29.0 + rev: v3.15.0 hooks: - id: pyupgrade - args: ["--py310-plus"] + args: ["--py312-plus"] - repo: https://github.com/charliermarsh/ruff-pre-commit # Ruff version. - rev: 'v0.0.241' + rev: 'v0.1.3' hooks: - id: ruff From fc2c159517aa5dd5e72af05f9978ea1126665adb Mon Sep 17 00:00:00 2001 From: Lukas Juhrich Date: Fri, 3 Nov 2023 17:30:37 +0100 Subject: [PATCH 07/10] Use timezone-aware datetimes --- .flaskenv | 1 + sipa/initialization.py | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.flaskenv b/.flaskenv index ed5772a7..836277f3 100644 --- a/.flaskenv +++ b/.flaskenv @@ -1 +1,2 @@ FLASK_APP=sipa:create_app +FLASK_TEMPLATES_AUTO_RELOAD=true diff --git a/sipa/initialization.py b/sipa/initialization.py index 38c877d4..4f8ffb05 100644 --- a/sipa/initialization.py +++ b/sipa/initialization.py @@ -2,7 +2,7 @@ import logging.config import os import os.path -from datetime import datetime +from datetime import datetime, UTC import sentry_sdk from flask import g @@ -96,7 +96,7 @@ def init_app(app, **kwargs): form_input_width_class=f"col-sm-{form_input_width}", form_input_offset_class=f"offset-sm-{form_label_width}", url_self=url_self, - now=datetime.utcnow() + now=datetime.now(UTC), ) app.add_template_filter(render_links) From 83c4ff8cd18962060044532286711db5024f9284 Mon Sep 17 00:00:00 2001 From: Lukas Juhrich Date: Fri, 3 Nov 2023 17:30:54 +0100 Subject: [PATCH 08/10] Upgrade pytest and remove unused pytest plugin `freezegun` is unmaintained anyway, time-machine should be used instead. --- build/requirements/requirements_testing.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/requirements/requirements_testing.txt b/build/requirements/requirements_testing.txt index c260e87d..287026ef 100644 --- a/build/requirements/requirements_testing.txt +++ b/build/requirements/requirements_testing.txt @@ -4,5 +4,5 @@ profilehooks~=1.9.0 pycodestyle~=2.4.0 mypy~=0.960 sphinx~=1.8.4 -pytest~=7.1.2 pytest-freezegun~=0.4.2 +pytest~=7.4.3 From 38a152d8ef5b56d236602f54104b9bbc05472146 Mon Sep 17 00:00:00 2001 From: Lukas Juhrich Date: Thu, 9 Nov 2023 14:54:50 +0100 Subject: [PATCH 09/10] Replace `freezegun` by `time_machine` The latter is better maintained, and iirc the creator of freezegun mentioned that he himself uses `time_machine` instead. --- build/requirements/requirements_testing.txt | 2 +- tests/test_calendar.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/requirements/requirements_testing.txt b/build/requirements/requirements_testing.txt index 287026ef..d8f233e9 100644 --- a/build/requirements/requirements_testing.txt +++ b/build/requirements/requirements_testing.txt @@ -4,5 +4,5 @@ profilehooks~=1.9.0 pycodestyle~=2.4.0 mypy~=0.960 sphinx~=1.8.4 -pytest-freezegun~=0.4.2 pytest~=7.4.3 +time_machine~=2.13.0 diff --git a/tests/test_calendar.py b/tests/test_calendar.py index 525ed9bb..4e191124 100644 --- a/tests/test_calendar.py +++ b/tests/test_calendar.py @@ -18,8 +18,8 @@ def calendar(ical_data: str) -> icalendar.Calendar: return icalendar.Calendar.from_ical(ical_data) -def test_ical_conversion(calendar: icalendar.Calendar, freezer): - freezer.move_to("2023-05-20") +def test_ical_conversion(calendar: icalendar.Calendar, time_machine): + time_machine.move_to("2023-05-20") events = events_from_calendar(calendar) assert len(events) == 1 [ev] = events From fed7685c27509616235217caa4b85fab5f2acc17 Mon Sep 17 00:00:00 2001 From: Lukas Juhrich Date: Thu, 9 Nov 2023 15:50:59 +0100 Subject: [PATCH 10/10] Close response in static file test --- tests/test_static_files.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/test_static_files.py b/tests/test_static_files.py index d36bdcbb..1a2926d6 100644 --- a/tests/test_static_files.py +++ b/tests/test_static_files.py @@ -33,6 +33,5 @@ def app(static_file, documents_dir): def test_static_view(app: Flask): - with app.test_client() as c: - resp = c.get("/documents/test.txt") - assert resp.text == "Test!" + with app.test_client() as c, c.get("/documents/test.txt") as resp: + assert resp.text == "Test!"