From a7ef47ecfebf57dca559c72ff0a527fc31eb26b7 Mon Sep 17 00:00:00 2001 From: Lukas Juhrich Date: Thu, 25 Jan 2024 10:21:29 +0100 Subject: [PATCH 1/5] output mypy version in `run_mypy.sh` --- scripts/run_mypy.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/run_mypy.sh b/scripts/run_mypy.sh index 176ac653b..eec6411ae 100755 --- a/scripts/run_mypy.sh +++ b/scripts/run_mypy.sh @@ -2,6 +2,7 @@ # we don't want errexit set -uo pipefail +mypy --version mypy | tee mypy_results.log; export mypy_status=$? python ./scripts/render_mypy_results.py mypy_results.log From 1f653429224e9d616a93df52e5d8b84178cb5605 Mon Sep 17 00:00:00 2001 From: Lukas Juhrich Date: Thu, 25 Jan 2024 10:21:37 +0100 Subject: [PATCH 2/5] upgrade to mypy 1.8.0 --- requirements.dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.dev.txt b/requirements.dev.txt index 998fecc20..022542d22 100644 --- a/requirements.dev.txt +++ b/requirements.dev.txt @@ -10,7 +10,7 @@ git+https://github.com/lukasjuhrich/sqlalchemy_schemadisplay.git@master#egg=sqla sphinx~=5.1.1 sphinx-autobuild~=2021.3.14 git+https://github.com/agdsn/guzzle_sphinx_theme.git@977d49fcbdf2b3df9660d813d4b33369391923e1#egg=guzzle-sphinx-theme -mypy~=1.5.0 +mypy~=1.8.0 celery-types~=0.9.3 types-jsonschema~=4.3.0 types-passlib~=1.7.7 From 3a7eebcbd5ca60e1e4e3743cbfa30dfdc74edd4b Mon Sep 17 00:00:00 2001 From: Lukas Juhrich Date: Thu, 25 Jan 2024 10:34:26 +0100 Subject: [PATCH 3/5] [typing] Remove superfluous sqlalchemy-related typing suppressions The most important change was the type hinting of sqlalchemy's `functions` module[1], which allowed us to have properly type-hinted `over()` calls. [1] https://github.com/sqlalchemy/sqlalchemy/commit/045732a738a10891b85be8e286eab3e5b756a445 --- pycroft/lib/finance/membership_fee.py | 6 ++---- pycroft/lib/user_deletion.py | 22 +++++++++++++++------- web/blueprints/finance/__init__.py | 2 +- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/pycroft/lib/finance/membership_fee.py b/pycroft/lib/finance/membership_fee.py index 6956cf198..869dc9c3d 100644 --- a/pycroft/lib/finance/membership_fee.py +++ b/pycroft/lib/finance/membership_fee.py @@ -250,14 +250,12 @@ def post_transactions_for_membership_fee( ).fetchall() if not simulate: - # `over` not typed yet, - # see https://github.com/sqlalchemy/sqlalchemy/issues/6810 numbered_users = ( select( users.c.id, users.c.fee_account_id.label("fee_account_id"), users.c.account_id, - func.row_number().over().label("index"), # type: ignore[no-untyped-call] + func.row_number().over().label("index"), ) .select_from(users) .cte("membership_fee_numbered_users") @@ -289,7 +287,7 @@ def post_transactions_for_membership_fee( numbered_transactions = ( select( transactions.c.id, - func.row_number().over().label("index"), # type: ignore[no-untyped-call] + func.row_number().over().label("index"), ) .select_from(transactions) .cte("membership_fee_numbered_transactions") diff --git a/pycroft/lib/user_deletion.py b/pycroft/lib/user_deletion.py index bf831f9dd..d00eb257c 100644 --- a/pycroft/lib/user_deletion.py +++ b/pycroft/lib/user_deletion.py @@ -5,6 +5,7 @@ This module contains methods concerning user archival and deletion. """ from __future__ import annotations +import typing as t from datetime import timedelta, datetime from typing import Protocol, Sequence @@ -24,6 +25,16 @@ class ArchivableMemberInfo(Protocol): mem_end: datetime +TP = t.TypeVar("TP") +TO = t.TypeVar("TO") + + +# mrh, not available in py3.10… +class _WindowArgs(t.TypedDict, t.Generic[TP, TO]): + partition_by: TP + order_by: TO + + def get_archivable_members(session: Session) -> Sequence[ArchivableMemberInfo]: """Return all the users that qualify for being archived right now. @@ -33,28 +44,25 @@ def get_archivable_members(session: Session) -> Sequence[ArchivableMemberInfo]: """ # see FunctionElement.over mem_ends_at = func.upper(Membership.active_during) - window_args = { + window_args: _WindowArgs = { 'partition_by': User.id, 'order_by': nulls_last(mem_ends_at), } - # mypy: ignore[no-untyped-call] last_mem = ( select( User.id.label('user_id'), func.last_value(Membership.id) - .over(**window_args, rows=(None, None)) # type: ignore[no-untyped-call] + .over(**window_args, rows=(None, None)) .label("mem_id"), func.last_value(mem_ends_at) - .over(**window_args, rows=(None, None)) # type: ignore[no-untyped-call] + .over(**window_args, rows=(None, None)) .label("mem_end"), ) .select_from(User) .distinct() .join(Membership) .join(Config, Config.member_group_id == Membership.group_id) - ).cte( - "last_mem" - ) # mypy: ignore[no-untyped-call] + ).cte("last_mem") stmt = ( select( User, diff --git a/web/blueprints/finance/__init__.py b/web/blueprints/finance/__init__.py index c6a52b960..daff02296 100644 --- a/web/blueprints/finance/__init__.py +++ b/web/blueprints/finance/__init__.py @@ -736,7 +736,7 @@ def balance_json(account_id: int) -> ResponseReturnValue: sum_exp: ColumnElement[int] = t.cast( Over[int], - func.sum(Split.amount).over(order_by=Transaction.valid_on), # type: ignore[no-untyped-call] + func.sum(Split.amount).over(order_by=Transaction.valid_on), ) if invert: From 43521b1369d695b1db866fe4a9e652c3209da0ed Mon Sep 17 00:00:00 2001 From: Lukas Juhrich Date: Thu, 25 Jan 2024 10:35:39 +0100 Subject: [PATCH 4/5] [typing] remove more superfluous typing suppressions mypy appearently got a bit better at narrowing and inference. --- ldap_sync/concepts/record.py | 2 +- pycroft/lib/user_deletion.py | 2 +- web/table/lazy_join.py | 5 +---- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/ldap_sync/concepts/record.py b/ldap_sync/concepts/record.py index a88003712..a22211186 100644 --- a/ldap_sync/concepts/record.py +++ b/ldap_sync/concepts/record.py @@ -34,7 +34,7 @@ def _canonicalize_to_list( if isinstance(value, list): return list(value) if value == "" or value == b"" or value is None: - return [] # type: ignore + return [] # str, byte, int – or unknown. But good fallback. return [value] # type: ignore diff --git a/pycroft/lib/user_deletion.py b/pycroft/lib/user_deletion.py index d00eb257c..8ba292d9f 100644 --- a/pycroft/lib/user_deletion.py +++ b/pycroft/lib/user_deletion.py @@ -79,7 +79,7 @@ def get_archivable_members(session: Session) -> Sequence[ArchivableMemberInfo]: # …and use that to filter out the `do-not-archive` occurrences. .filter(CurrentProperty.property_name.is_(None)) .join(User, User.id == last_mem.c.user_id) - .filter(last_mem.c.mem_end < current_timestamp() - timedelta(days=14)) # type: ignore[no-untyped-call] + .filter(last_mem.c.mem_end < current_timestamp() - timedelta(days=14)) .order_by(last_mem.c.mem_end) .options(joinedload(User.hosts), # joinedload(User.current_memberships), joinedload(User.account, innerjoin=True), joinedload(User.room), diff --git a/web/table/lazy_join.py b/web/table/lazy_join.py index c95e6dcdf..cf7e05711 100644 --- a/web/table/lazy_join.py +++ b/web/table/lazy_join.py @@ -98,10 +98,7 @@ def __init__(self, glue: str = "") -> None: self.glue = glue def __call__(self, func: DecoratedInType) -> DecoratedOutType: - # error: Argument 1 to "__call__" of "IdentityFunction" has incompatible type - # "Callable[_P, LazilyJoined]"; expected "Callable[_P, LazilyJoined]" [arg-type] - # …go home mypy, you're drunk! - @wraps(func) # type: ignore[arg-type] + @wraps(func) def wrapped(*a: _P.args, **kw: _P.kwargs) -> LazilyJoined: return LazilyJoined(func(*a, **kw), glue=self.glue) From 114d9cac3b7ecc17c55210b024d3394719f8ecf7 Mon Sep 17 00:00:00 2001 From: Lukas Juhrich Date: Thu, 25 Jan 2024 11:23:40 +0100 Subject: [PATCH 5/5] Make sphinx quiet --- doc/conf.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/doc/conf.py b/doc/conf.py index 48a584df1..0e27c587d 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -125,7 +125,11 @@ } # see https://github.com/sphinx-doc/sphinx/issues/10480#issuecomment-1221396022 import sphinx.ext.autodoc -sphinx.ext.autodoc.NewTypeDataDocumenter.directivetype = 'class' + +try: + sphinx.ext.autodoc.NewTypeDataDocumenter.directivetype = "class" +except AttributeError: + pass todo_include_todos = True # -- Options for HTML output ---------------------------------------------------