From d32d4434ddb867568e50c3af52cc401d05effb81 Mon Sep 17 00:00:00 2001 From: Anders Kaseorg Date: Mon, 29 Apr 2024 14:20:36 -0700 Subject: [PATCH] partial: Replace returns plugin with an annotation. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The returns plugin hasn’t been updated for mypy ≥ 1.6. This annotation is more limited in that it only supports a fixed number of positional arguments and no keyword arguments, but is good enough for our purposes. Signed-off-by: Anders Kaseorg --- pyproject.toml | 1 - tools/run-dev | 2 +- tools/semgrep-py.yml | 2 +- zerver/data_import/import_util.py | 2 +- zerver/lib/message.py | 2 +- zerver/lib/partial.py | 36 +++++++++++++++++++++++ zerver/lib/test_runner.py | 2 +- zerver/management/commands/check_redis.py | 2 +- zerver/tornado/django_api.py | 2 +- zerver/views/video_calls.py | 2 +- zerver/webhooks/bitbucket2/view.py | 2 +- zerver/webhooks/bitbucket3/view.py | 2 +- zerver/webhooks/clubhouse/view.py | 2 +- zerver/webhooks/github/view.py | 2 +- zerver/webhooks/gitlab/view.py | 2 +- zerver/webhooks/groove/view.py | 2 +- zerver/webhooks/intercom/view.py | 2 +- zerver/worker/base.py | 2 +- 18 files changed, 52 insertions(+), 17 deletions(-) create mode 100644 zerver/lib/partial.py diff --git a/pyproject.toml b/pyproject.toml index f5c10c9b99..2b5906ee77 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,7 +48,6 @@ local_partial_types = true plugins = [ "mypy_django_plugin.main", "pydantic.mypy", - "returns.contrib.mypy.returns_plugin", ] [[tool.mypy.overrides]] diff --git a/tools/run-dev b/tools/run-dev index 37e336400e..2a3e473564 100755 --- a/tools/run-dev +++ b/tools/run-dev @@ -21,9 +21,9 @@ sanity_check.check_venv(__file__) import aiohttp from aiohttp import hdrs, web from aiohttp.http_exceptions import BadStatusLine -from returns.curry import partial from tools.lib.test_script import add_provision_check_override_param, assert_provisioning_status_ok +from zerver.lib.partial import partial if "posix" in os.name and os.geteuid() == 0: raise RuntimeError("run-dev should not be run as root.") diff --git a/tools/semgrep-py.yml b/tools/semgrep-py.yml index f789b8f33f..f56bbccbc0 100644 --- a/tools/semgrep-py.yml +++ b/tools/semgrep-py.yml @@ -318,7 +318,7 @@ rules: - id: functools-partial pattern: functools.partial - message: "Replace functools.partial with returns.curry.partial for type safety" + message: "Replace functools.partial with zerver.lib.partial.partial for type safety" languages: [python] severity: ERROR diff --git a/zerver/data_import/import_util.py b/zerver/data_import/import_util.py index fc8b10a8a1..6e0cf5003f 100644 --- a/zerver/data_import/import_util.py +++ b/zerver/data_import/import_util.py @@ -24,11 +24,11 @@ import orjson import requests from django.forms.models import model_to_dict from django.utils.timezone import now as timezone_now -from returns.curry import partial from typing_extensions import TypeAlias from zerver.data_import.sequencer import NEXT_ID from zerver.lib.avatar_hash import user_avatar_path_from_ids +from zerver.lib.partial import partial from zerver.lib.stream_color import STREAM_ASSIGNMENT_COLORS as STREAM_COLORS from zerver.models import ( Attachment, diff --git a/zerver/lib/message.py b/zerver/lib/message.py index cb3bdb4ee6..b873e310ec 100644 --- a/zerver/lib/message.py +++ b/zerver/lib/message.py @@ -23,7 +23,6 @@ from django.utils.timezone import now as timezone_now from django.utils.translation import gettext as _ from django_stubs_ext import ValuesQuerySet from psycopg2.sql import SQL -from returns.curry import partial from analytics.lib.counts import COUNT_STATS from analytics.models import RealmCount @@ -33,6 +32,7 @@ from zerver.lib.exceptions import JsonableError, MissingAuthenticationError from zerver.lib.markdown import MessageRenderingResult from zerver.lib.mention import MentionData from zerver.lib.message_cache import MessageDict, extract_message_dict, stringify_message_dict +from zerver.lib.partial import partial from zerver.lib.request import RequestVariableConversionError from zerver.lib.stream_subscription import ( get_stream_subscriptions_for_user, diff --git a/zerver/lib/partial.py b/zerver/lib/partial.py new file mode 100644 index 0000000000..0a20fbebd5 --- /dev/null +++ b/zerver/lib/partial.py @@ -0,0 +1,36 @@ +# Workaround for missing functools.partial support in mypy +# (https://github.com/python/mypy/issues/1484). + +from typing import TYPE_CHECKING, Callable, TypeVar, overload + +if TYPE_CHECKING: + from typing_extensions import Concatenate, ParamSpec + + P = ParamSpec("P") + T1 = TypeVar("T1") + T2 = TypeVar("T2") + T3 = TypeVar("T3") + T4 = TypeVar("T4") + R = TypeVar("R") + + @overload + def partial(func: Callable[P, R], /) -> Callable[P, R]: ... + @overload + def partial(func: Callable[Concatenate[T1, P], R], arg1: T1, /) -> Callable[P, R]: ... + @overload + def partial( + func: Callable[Concatenate[T1, T2, P], R], arg1: T1, arg2: T2, / + ) -> Callable[P, R]: ... + @overload + def partial( + func: Callable[Concatenate[T1, T2, T3, P], R], arg1: T1, arg2: T2, arg3: T3, / + ) -> Callable[P, R]: ... + @overload + def partial( + func: Callable[Concatenate[T1, T2, T3, T4, P], R], arg1: T1, arg2: T2, arg3: T3, arg4: T4, / + ) -> Callable[P, R]: ... + + def partial(func: Callable[..., R], /, *args: object) -> Callable[..., R]: ... + +else: + from functools import partial as partial diff --git a/zerver/lib/test_runner.py b/zerver/lib/test_runner.py index 6aff6b6ccb..68d8e33c57 100644 --- a/zerver/lib/test_runner.py +++ b/zerver/lib/test_runner.py @@ -13,7 +13,6 @@ from django.db import ProgrammingError, connections from django.test import runner as django_runner from django.test.runner import DiscoverRunner from django.test.signals import template_rendered -from returns.curry import partial from typing_extensions import TypeAlias, override from scripts.lib.zulip_tools import ( @@ -22,6 +21,7 @@ from scripts.lib.zulip_tools import ( get_or_create_dev_uuid_var_path, ) from zerver.lib import test_helpers +from zerver.lib.partial import partial from zerver.lib.sqlalchemy_utils import get_sqlalchemy_connection from zerver.lib.test_fixtures import BACKEND_DATABASE_TEMPLATE from zerver.lib.test_helpers import append_instrumentation_data, write_instrumentation_reports diff --git a/zerver/management/commands/check_redis.py b/zerver/management/commands/check_redis.py index b68d5e8492..d15b6e678e 100644 --- a/zerver/management/commands/check_redis.py +++ b/zerver/management/commands/check_redis.py @@ -4,9 +4,9 @@ from typing import Any, Callable, Optional from django.conf import settings from django.core.management.base import BaseCommand, CommandError, CommandParser -from returns.curry import partial from typing_extensions import override +from zerver.lib.partial import partial from zerver.lib.rate_limiter import RateLimitedUser, client from zerver.models.users import get_user_profile_by_id diff --git a/zerver/tornado/django_api.py b/zerver/tornado/django_api.py index 115ffa6690..a69f5f75ea 100644 --- a/zerver/tornado/django_api.py +++ b/zerver/tornado/django_api.py @@ -9,10 +9,10 @@ from django.conf import settings from django.db import transaction from requests.adapters import ConnectionError, HTTPAdapter from requests.models import PreparedRequest, Response -from returns.curry import partial from typing_extensions import override from urllib3.util import Retry +from zerver.lib.partial import partial from zerver.lib.queue import queue_json_publish from zerver.models import Client, Realm, UserProfile from zerver.tornado.sharding import ( diff --git a/zerver/views/video_calls.py b/zerver/views/video_calls.py index 8db6822f7f..9842871dfb 100644 --- a/zerver/views/video_calls.py +++ b/zerver/views/video_calls.py @@ -20,12 +20,12 @@ from django.views.decorators.csrf import csrf_exempt from django.views.decorators.http import require_POST from oauthlib.oauth2 import OAuth2Error from requests_oauthlib import OAuth2Session -from returns.curry import partial from zerver.actions.video_calls import do_set_zoom_token from zerver.decorator import zulip_login_required from zerver.lib.exceptions import ErrorCode, JsonableError from zerver.lib.outgoing_http import OutgoingSession +from zerver.lib.partial import partial from zerver.lib.pysa import mark_sanitized from zerver.lib.request import REQ, has_request_variables from zerver.lib.response import json_success diff --git a/zerver/webhooks/bitbucket2/view.py b/zerver/webhooks/bitbucket2/view.py index 4449860440..d604f545e1 100644 --- a/zerver/webhooks/bitbucket2/view.py +++ b/zerver/webhooks/bitbucket2/view.py @@ -4,10 +4,10 @@ import string from typing import Dict, List, Optional, Protocol from django.http import HttpRequest, HttpResponse -from returns.curry import partial from zerver.decorator import log_unsupported_webhook_event, webhook_view from zerver.lib.exceptions import UnsupportedWebhookEventTypeError +from zerver.lib.partial import partial from zerver.lib.response import json_success from zerver.lib.typed_endpoint import JsonBodyPayload, typed_endpoint from zerver.lib.validator import WildValue, check_bool, check_int, check_string diff --git a/zerver/webhooks/bitbucket3/view.py b/zerver/webhooks/bitbucket3/view.py index 186c190b9d..afc2afc5c8 100644 --- a/zerver/webhooks/bitbucket3/view.py +++ b/zerver/webhooks/bitbucket3/view.py @@ -2,10 +2,10 @@ import string from typing import Dict, List, Optional, Protocol from django.http import HttpRequest, HttpResponse -from returns.curry import partial from zerver.decorator import webhook_view from zerver.lib.exceptions import UnsupportedWebhookEventTypeError +from zerver.lib.partial import partial from zerver.lib.response import json_success from zerver.lib.typed_endpoint import JsonBodyPayload, typed_endpoint from zerver.lib.validator import WildValue, check_int, check_none_or, check_string diff --git a/zerver/webhooks/clubhouse/view.py b/zerver/webhooks/clubhouse/view.py index 58b034cc8b..c95c2b98af 100644 --- a/zerver/webhooks/clubhouse/view.py +++ b/zerver/webhooks/clubhouse/view.py @@ -1,10 +1,10 @@ from typing import Callable, Dict, Iterable, Iterator, List, Optional from django.http import HttpRequest, HttpResponse -from returns.curry import partial from zerver.decorator import webhook_view from zerver.lib.exceptions import UnsupportedWebhookEventTypeError +from zerver.lib.partial import partial from zerver.lib.response import json_success from zerver.lib.typed_endpoint import JsonBodyPayload, typed_endpoint from zerver.lib.validator import ( diff --git a/zerver/webhooks/github/view.py b/zerver/webhooks/github/view.py index 75e4dfd246..8bd4a1444c 100644 --- a/zerver/webhooks/github/view.py +++ b/zerver/webhooks/github/view.py @@ -3,10 +3,10 @@ from datetime import datetime, timezone from typing import Callable, Dict, Optional from django.http import HttpRequest, HttpResponse -from returns.curry import partial from zerver.decorator import log_unsupported_webhook_event, webhook_view from zerver.lib.exceptions import UnsupportedWebhookEventTypeError +from zerver.lib.partial import partial from zerver.lib.response import json_success from zerver.lib.typed_endpoint import JsonBodyPayload, typed_endpoint from zerver.lib.validator import WildValue, check_bool, check_int, check_none_or, check_string diff --git a/zerver/webhooks/gitlab/view.py b/zerver/webhooks/gitlab/view.py index 66255e6dcb..5537653f3f 100644 --- a/zerver/webhooks/gitlab/view.py +++ b/zerver/webhooks/gitlab/view.py @@ -3,10 +3,10 @@ from typing import Dict, List, Optional, Protocol, Union from django.http import HttpRequest, HttpResponse from pydantic import Json -from returns.curry import partial from zerver.decorator import webhook_view from zerver.lib.exceptions import UnsupportedWebhookEventTypeError +from zerver.lib.partial import partial from zerver.lib.response import json_success from zerver.lib.typed_endpoint import JsonBodyPayload, typed_endpoint from zerver.lib.validator import WildValue, check_int, check_none_or, check_string diff --git a/zerver/webhooks/groove/view.py b/zerver/webhooks/groove/view.py index 51275a5675..bdfbe101bd 100644 --- a/zerver/webhooks/groove/view.py +++ b/zerver/webhooks/groove/view.py @@ -2,10 +2,10 @@ from typing import Callable, Dict, Optional from django.http import HttpRequest, HttpResponse -from returns.curry import partial from zerver.decorator import webhook_view from zerver.lib.exceptions import UnsupportedWebhookEventTypeError +from zerver.lib.partial import partial from zerver.lib.response import json_success from zerver.lib.typed_endpoint import JsonBodyPayload, typed_endpoint from zerver.lib.validator import WildValue, check_int, check_none_or, check_string, check_url diff --git a/zerver/webhooks/intercom/view.py b/zerver/webhooks/intercom/view.py index c5c9135cd0..18598726e8 100644 --- a/zerver/webhooks/intercom/view.py +++ b/zerver/webhooks/intercom/view.py @@ -2,11 +2,11 @@ from html.parser import HTMLParser from typing import Callable, Dict, List, Tuple from django.http import HttpRequest, HttpResponse -from returns.curry import partial from typing_extensions import override from zerver.decorator import return_success_on_head_request, webhook_view from zerver.lib.exceptions import UnsupportedWebhookEventTypeError +from zerver.lib.partial import partial from zerver.lib.response import json_success from zerver.lib.typed_endpoint import JsonBodyPayload, typed_endpoint from zerver.lib.validator import WildValue, check_int, check_none_or, check_string diff --git a/zerver/worker/base.py b/zerver/worker/base.py index 8430b974a9..e86482dfdc 100644 --- a/zerver/worker/base.py +++ b/zerver/worker/base.py @@ -12,11 +12,11 @@ import orjson import sentry_sdk from django.conf import settings from django.db import connection -from returns.curry import partial from typing_extensions import override from zerver.lib.context_managers import lockfile from zerver.lib.db_connections import reset_queries +from zerver.lib.partial import partial from zerver.lib.per_request_cache import flush_per_request_caches from zerver.lib.pysa import mark_sanitized from zerver.lib.queue import SimpleQueueClient