From a761f9644610dc8e271d4cf0668f37cc9b33d2f6 Mon Sep 17 00:00:00 2001 From: Kiran Jonnalagadda Date: Sun, 7 Jan 2024 23:48:41 +0530 Subject: [PATCH] Run Mypy as a local hook as it's unreliable if isolated without all deps --- .pre-commit-config.yaml | 36 +++++++++++------------------- funnel/models/account.py | 6 +++-- funnel/models/auth_client.py | 2 +- funnel/models/contact_exchange.py | 2 +- funnel/models/email_address.py | 5 +---- funnel/models/label.py | 2 +- funnel/models/notification.py | 1 - funnel/models/phone_number.py | 5 +---- funnel/models/update.py | 2 +- tests/unit/models/proposal_test.py | 2 +- 10 files changed, 24 insertions(+), 39 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c5e3d7318..3bf2166de 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -12,6 +12,7 @@ ci: 'no-commit-to-branch', # 'hadolint-docker', 'docker-compose-check', + 'mypy', # Runs as a local hook now ] repos: - repo: https://github.com/pre-commit-ci/pre-commit-ci-config @@ -100,29 +101,6 @@ repos: rev: 23.12.1 hooks: - id: black - - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.8.0 - hooks: - - id: mypy - # warn-unused-ignores is unsafe with pre-commit, see - # https://github.com/python/mypy/issues/2960 - args: - [ - '--no-warn-unused-ignores', - '--no-warn-redundant-casts', - '--ignore-missing-imports', - ] - additional_dependencies: - - flask - - lxml-stubs - - sqlalchemy - - toml - - types-chevron - - types-geoip2 - - types-python-dateutil - - types-pytz - - types-requests - - typing-extensions - repo: https://github.com/PyCQA/flake8 rev: 7.0.0 hooks: @@ -223,3 +201,15 @@ repos: rev: v3.0.1 hooks: - id: docker-compose-check + - repo: local + hooks: + - id: mypy + name: mypy + entry: .venv/bin/mypy + args: + - --no-warn-unused-ignores + - --no-warn-redundant-casts + - . # Required to honour settings in pyproject.toml + language: system + pass_filenames: false + types_or: [python, pyi] diff --git a/funnel/models/account.py b/funnel/models/account.py index cdd9cad84..927d2c2e5 100644 --- a/funnel/models/account.py +++ b/funnel/models/account.py @@ -8,7 +8,7 @@ import itertools from collections.abc import Iterable, Iterator, Sequence from datetime import datetime -from typing import TYPE_CHECKING, ClassVar, Literal, Self, cast, overload +from typing import TYPE_CHECKING, ClassVar, Literal, Self, TypeAlias, cast, overload from uuid import UUID import phonenumbers @@ -2776,7 +2776,9 @@ def get( ) #: Anchor type -Anchor = AccountEmail | AccountEmailClaim | AccountPhone | EmailAddress | PhoneNumber +Anchor: TypeAlias = ( + AccountEmail | AccountEmailClaim | AccountPhone | EmailAddress | PhoneNumber +) # Tail imports from .account_membership import AccountMembership diff --git a/funnel/models/auth_client.py b/funnel/models/auth_client.py index 4ee91d212..82bb7bf90 100644 --- a/funnel/models/auth_client.py +++ b/funnel/models/auth_client.py @@ -266,7 +266,7 @@ def all_for(cls, account: Account | None) -> Query[Self]: return cls.query.order_by(cls.title) return cls.query.filter( sa.or_( - cls.account == account, # type: ignore[arg-type] + cls.account == account, cls.account_id.in_(account.organizations_as_owner_ids()), ) ).order_by(cls.title) diff --git a/funnel/models/contact_exchange.py b/funnel/models/contact_exchange.py index f6c7e77b2..d6f17de2d 100644 --- a/funnel/models/contact_exchange.py +++ b/funnel/models/contact_exchange.py @@ -139,7 +139,7 @@ def grouped_counts_for( ).filter( cls.ticket_participant_id == TicketParticipant.id, TicketParticipant.project_id == Project.id, - cls.account == account, # type: ignore[arg-type] + cls.account == account, ) if not archived: diff --git a/funnel/models/email_address.py b/funnel/models/email_address.py index 1922723ac..2a14e0d2e 100644 --- a/funnel/models/email_address.py +++ b/funnel/models/email_address.py @@ -860,10 +860,7 @@ def _validate_email( if old_value == value: # Old value is new value. Do nothing. Return without validating return - if ( - old_value is NO_VALUE - and inspect(target).has_identity is False # type: ignore[attr-defined] - ): + if old_value is NO_VALUE and inspect(target).has_identity is False: # Old value is unknown and target is a transient object. Continue pass elif value is None: diff --git a/funnel/models/label.py b/funnel/models/label.py index 15a29a1fd..83ecae347 100644 --- a/funnel/models/label.py +++ b/funnel/models/label.py @@ -276,7 +276,7 @@ def icon(self) -> str: result = ''.join(w[0] for w in self.title.strip().title().split(None, 2)) if len(result) <= 1: result = self.title.strip()[:3] - return result # type: ignore[return-value] + return result def __repr__(self) -> str: """Represent :class:`Label` as a string.""" diff --git a/funnel/models/notification.py b/funnel/models/notification.py index 498bfd916..50407cbe4 100644 --- a/funnel/models/notification.py +++ b/funnel/models/notification.py @@ -665,7 +665,6 @@ def dispatch(self) -> Generator[NotificationRecipient, None, None]: Subclasses wanting more control over how their notifications are dispatched should override this method. """ - for account, role in self.role_provider_obj.actors_with( self.roles, with_role=True ): diff --git a/funnel/models/phone_number.py b/funnel/models/phone_number.py index 557d6466d..619faa288 100644 --- a/funnel/models/phone_number.py +++ b/funnel/models/phone_number.py @@ -888,10 +888,7 @@ def _validate_number( if old_value == value: # Old value is new value. Do nothing. Return without validating return value - if ( - old_value is NO_VALUE - and inspect(target).has_identity is False # type: ignore[attr-defined] - ): + if old_value is NO_VALUE and inspect(target).has_identity is False: # Old value is unknown and target is a transient object. Continue pass elif value is None: diff --git a/funnel/models/update.py b/funnel/models/update.py index 6889e5e96..7aca005c5 100644 --- a/funnel/models/update.py +++ b/funnel/models/update.py @@ -284,7 +284,7 @@ def publish(self, actor: Account) -> bool: if self.number is None: self.number = ( sa.select(sa.func.coalesce(sa.func.max(Update.number), 0) + 1) - .where(Update.project == self.project) # type: ignore[arg-type] + .where(Update.project == self.project) .scalar_subquery() ) return first_publishing diff --git a/tests/unit/models/proposal_test.py b/tests/unit/models/proposal_test.py index 908b9827f..9cab9872c 100644 --- a/tests/unit/models/proposal_test.py +++ b/tests/unit/models/proposal_test.py @@ -35,7 +35,7 @@ def test_reorder(db_session, user_twoflower, project_expo2010) -> None: assert proposal3.url_id == 3 assert proposal1.title == "Test Proposal 1" - assert proposal1.url_id < proposal2.url_id < proposal3.url_id # type: ignore[operator] + assert proposal1.url_id < proposal2.url_id < proposal3.url_id proposal1.reorder_after(proposal2) db_session.commit()