mirror of https://github.com/zulip/zulip.git
python: Replace functools.partial with type-safe returns.curry.partial.
The type annotation for functools.partial uses unchecked Any for all the function parameters (both early and late). returns.curry.partial uses a mypy plugin to check the parameters safely. https://returns.readthedocs.io/en/latest/pages/curry.html Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
parent
ccbd834a86
commit
cf4791264c
|
@ -42,7 +42,11 @@ warn_unreachable = true
|
||||||
# with this behavior.
|
# with this behavior.
|
||||||
local_partial_types = true
|
local_partial_types = true
|
||||||
|
|
||||||
plugins = ["mypy_django_plugin.main", "pydantic.mypy"]
|
plugins = [
|
||||||
|
"mypy_django_plugin.main",
|
||||||
|
"pydantic.mypy",
|
||||||
|
"returns.contrib.mypy.returns_plugin",
|
||||||
|
]
|
||||||
|
|
||||||
[[tool.mypy.overrides]]
|
[[tool.mypy.overrides]]
|
||||||
module = [
|
module = [
|
||||||
|
|
|
@ -8,6 +8,9 @@ Django[argon2]==4.2.*
|
||||||
# needed for NotRequired, ParamSpec
|
# needed for NotRequired, ParamSpec
|
||||||
typing-extensions
|
typing-extensions
|
||||||
|
|
||||||
|
# For type-safe returns.curry.partial
|
||||||
|
returns
|
||||||
|
|
||||||
# Needed for rendering backend templates
|
# Needed for rendering backend templates
|
||||||
Jinja2
|
Jinja2
|
||||||
|
|
||||||
|
|
|
@ -2442,6 +2442,10 @@ responses==0.23.3 \
|
||||||
# via
|
# via
|
||||||
# -r requirements/dev.in
|
# -r requirements/dev.in
|
||||||
# moto
|
# moto
|
||||||
|
returns==0.22.0 \
|
||||||
|
--hash=sha256:c7bd85bd1e0041b44fe46c7e2f68fcc76a0546142c876229e395174bcd674f37 \
|
||||||
|
--hash=sha256:d38d6324692eeb29ec4bd698e1b859ec0ac79fb2c17bf0d302f92c8c42ef35c1
|
||||||
|
# via -r requirements/common.in
|
||||||
rfc3339-validator==0.1.4 \
|
rfc3339-validator==0.1.4 \
|
||||||
--hash=sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b \
|
--hash=sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b \
|
||||||
--hash=sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa
|
--hash=sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa
|
||||||
|
@ -3059,6 +3063,7 @@ typing-extensions==4.7.1 \
|
||||||
# pyre-check
|
# pyre-check
|
||||||
# pyre-extensions
|
# pyre-extensions
|
||||||
# qrcode
|
# qrcode
|
||||||
|
# returns
|
||||||
# rich
|
# rich
|
||||||
# semgrep
|
# semgrep
|
||||||
# sqlalchemy2-stubs
|
# sqlalchemy2-stubs
|
||||||
|
|
|
@ -1921,6 +1921,10 @@ requests-oauthlib==1.3.1 \
|
||||||
# -r requirements/common.in
|
# -r requirements/common.in
|
||||||
# python-twitter
|
# python-twitter
|
||||||
# social-auth-core
|
# social-auth-core
|
||||||
|
returns==0.22.0 \
|
||||||
|
--hash=sha256:c7bd85bd1e0041b44fe46c7e2f68fcc76a0546142c876229e395174bcd674f37 \
|
||||||
|
--hash=sha256:d38d6324692eeb29ec4bd698e1b859ec0ac79fb2c17bf0d302f92c8c42ef35c1
|
||||||
|
# via -r requirements/common.in
|
||||||
rfc3339-validator==0.1.4 \
|
rfc3339-validator==0.1.4 \
|
||||||
--hash=sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b \
|
--hash=sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b \
|
||||||
--hash=sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa
|
--hash=sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa
|
||||||
|
@ -2173,6 +2177,7 @@ typing-extensions==4.7.1 \
|
||||||
# pydantic
|
# pydantic
|
||||||
# pydantic-core
|
# pydantic-core
|
||||||
# qrcode
|
# qrcode
|
||||||
|
# returns
|
||||||
# stripe
|
# stripe
|
||||||
# zulip
|
# zulip
|
||||||
# zulip-bots
|
# zulip-bots
|
||||||
|
|
|
@ -250,3 +250,9 @@ rules:
|
||||||
message: 'Use ".exists()" instead; it is more efficient'
|
message: 'Use ".exists()" instead; it is more efficient'
|
||||||
languages: [python]
|
languages: [python]
|
||||||
severity: ERROR
|
severity: ERROR
|
||||||
|
|
||||||
|
- id: functools-partial
|
||||||
|
pattern: functools.partial
|
||||||
|
message: "Replace functools.partial with returns.curry.partial for type safety"
|
||||||
|
languages: [python]
|
||||||
|
severity: ERROR
|
||||||
|
|
|
@ -48,4 +48,4 @@ API_FEATURE_LEVEL = 209
|
||||||
# historical commits sharing the same major version, in which case a
|
# historical commits sharing the same major version, in which case a
|
||||||
# minor version bump suffices.
|
# minor version bump suffices.
|
||||||
|
|
||||||
PROVISION_VERSION = (249, 0)
|
PROVISION_VERSION = (249, 1)
|
||||||
|
|
|
@ -4,7 +4,6 @@ import random
|
||||||
import shutil
|
import shutil
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from concurrent.futures import ProcessPoolExecutor, as_completed
|
from concurrent.futures import ProcessPoolExecutor, as_completed
|
||||||
from functools import partial
|
|
||||||
from typing import (
|
from typing import (
|
||||||
AbstractSet,
|
AbstractSet,
|
||||||
Any,
|
Any,
|
||||||
|
@ -25,6 +24,7 @@ import orjson
|
||||||
import requests
|
import requests
|
||||||
from django.forms.models import model_to_dict
|
from django.forms.models import model_to_dict
|
||||||
from django.utils.timezone import now as timezone_now
|
from django.utils.timezone import now as timezone_now
|
||||||
|
from returns.curry import partial
|
||||||
from typing_extensions import TypeAlias
|
from typing_extensions import TypeAlias
|
||||||
|
|
||||||
from zerver.data_import.sequencer import NEXT_ID
|
from zerver.data_import.sequencer import NEXT_ID
|
||||||
|
|
|
@ -3,7 +3,6 @@ import os
|
||||||
import random
|
import random
|
||||||
import shutil
|
import shutil
|
||||||
import unittest
|
import unittest
|
||||||
from functools import partial
|
|
||||||
from typing import Any, Callable, Dict, Iterable, List, Optional, Set, Tuple, Type, Union
|
from typing import Any, Callable, Dict, Iterable, List, Optional, Set, Tuple, Type, Union
|
||||||
from unittest import TestSuite, runner
|
from unittest import TestSuite, runner
|
||||||
from unittest.result import TestResult
|
from unittest.result import TestResult
|
||||||
|
@ -14,6 +13,7 @@ from django.db import ProgrammingError, connections
|
||||||
from django.test import runner as django_runner
|
from django.test import runner as django_runner
|
||||||
from django.test.runner import DiscoverRunner
|
from django.test.runner import DiscoverRunner
|
||||||
from django.test.signals import template_rendered
|
from django.test.signals import template_rendered
|
||||||
|
from returns.curry import partial
|
||||||
from typing_extensions import TypeAlias
|
from typing_extensions import TypeAlias
|
||||||
|
|
||||||
from scripts.lib.zulip_tools import (
|
from scripts.lib.zulip_tools import (
|
||||||
|
|
|
@ -3,7 +3,6 @@ import json
|
||||||
import random
|
import random
|
||||||
import secrets
|
import secrets
|
||||||
from base64 import b32encode
|
from base64 import b32encode
|
||||||
from functools import partial
|
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
from urllib.parse import quote, urlencode, urljoin
|
from urllib.parse import quote, urlencode, urljoin
|
||||||
|
|
||||||
|
@ -21,6 +20,7 @@ from django.views.decorators.csrf import csrf_exempt
|
||||||
from django.views.decorators.http import require_POST
|
from django.views.decorators.http import require_POST
|
||||||
from oauthlib.oauth2 import OAuth2Error
|
from oauthlib.oauth2 import OAuth2Error
|
||||||
from requests_oauthlib import OAuth2Session
|
from requests_oauthlib import OAuth2Session
|
||||||
|
from returns.curry import partial
|
||||||
|
|
||||||
from zerver.actions.video_calls import do_set_zoom_token
|
from zerver.actions.video_calls import do_set_zoom_token
|
||||||
from zerver.decorator import zulip_login_required
|
from zerver.decorator import zulip_login_required
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
# Webhooks for external integrations.
|
# Webhooks for external integrations.
|
||||||
import re
|
import re
|
||||||
import string
|
import string
|
||||||
from functools import partial
|
|
||||||
from typing import Dict, List, Optional, Protocol
|
from typing import Dict, List, Optional, Protocol
|
||||||
|
|
||||||
from django.http import HttpRequest, HttpResponse
|
from django.http import HttpRequest, HttpResponse
|
||||||
|
from returns.curry import partial
|
||||||
|
|
||||||
from zerver.decorator import log_unsupported_webhook_event, webhook_view
|
from zerver.decorator import log_unsupported_webhook_event, webhook_view
|
||||||
from zerver.lib.exceptions import UnsupportedWebhookEventTypeError
|
from zerver.lib.exceptions import UnsupportedWebhookEventTypeError
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import string
|
import string
|
||||||
from functools import partial
|
|
||||||
from typing import Dict, List, Optional, Protocol
|
from typing import Dict, List, Optional, Protocol
|
||||||
|
|
||||||
from django.http import HttpRequest, HttpResponse
|
from django.http import HttpRequest, HttpResponse
|
||||||
|
from returns.curry import partial
|
||||||
|
|
||||||
from zerver.decorator import webhook_view
|
from zerver.decorator import webhook_view
|
||||||
from zerver.lib.exceptions import UnsupportedWebhookEventTypeError
|
from zerver.lib.exceptions import UnsupportedWebhookEventTypeError
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from functools import partial
|
|
||||||
from typing import Callable, Dict, Iterable, Iterator, List, Optional
|
from typing import Callable, Dict, Iterable, Iterator, List, Optional
|
||||||
|
|
||||||
from django.http import HttpRequest, HttpResponse
|
from django.http import HttpRequest, HttpResponse
|
||||||
|
from returns.curry import partial
|
||||||
|
|
||||||
from zerver.decorator import webhook_view
|
from zerver.decorator import webhook_view
|
||||||
from zerver.lib.exceptions import UnsupportedWebhookEventTypeError
|
from zerver.lib.exceptions import UnsupportedWebhookEventTypeError
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import re
|
import re
|
||||||
from functools import partial
|
|
||||||
from typing import Callable, Dict, Optional
|
from typing import Callable, Dict, Optional
|
||||||
|
|
||||||
from django.http import HttpRequest, HttpResponse
|
from django.http import HttpRequest, HttpResponse
|
||||||
|
from returns.curry import partial
|
||||||
|
|
||||||
from zerver.decorator import log_unsupported_webhook_event, webhook_view
|
from zerver.decorator import log_unsupported_webhook_event, webhook_view
|
||||||
from zerver.lib.exceptions import UnsupportedWebhookEventTypeError
|
from zerver.lib.exceptions import UnsupportedWebhookEventTypeError
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import re
|
import re
|
||||||
from functools import partial
|
|
||||||
from typing import Dict, List, Optional, Protocol, Union
|
from typing import Dict, List, Optional, Protocol, Union
|
||||||
|
|
||||||
from django.http import HttpRequest, HttpResponse
|
from django.http import HttpRequest, HttpResponse
|
||||||
from pydantic import Json
|
from pydantic import Json
|
||||||
|
from returns.curry import partial
|
||||||
|
|
||||||
from zerver.decorator import webhook_view
|
from zerver.decorator import webhook_view
|
||||||
from zerver.lib.exceptions import UnsupportedWebhookEventTypeError
|
from zerver.lib.exceptions import UnsupportedWebhookEventTypeError
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
# Webhooks for external integrations.
|
# Webhooks for external integrations.
|
||||||
from functools import partial
|
|
||||||
from typing import Callable, Dict, Optional
|
from typing import Callable, Dict, Optional
|
||||||
|
|
||||||
from django.http import HttpRequest, HttpResponse
|
from django.http import HttpRequest, HttpResponse
|
||||||
|
from returns.curry import partial
|
||||||
|
|
||||||
from zerver.decorator import webhook_view
|
from zerver.decorator import webhook_view
|
||||||
from zerver.lib.exceptions import UnsupportedWebhookEventTypeError
|
from zerver.lib.exceptions import UnsupportedWebhookEventTypeError
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
from functools import partial
|
|
||||||
from html.parser import HTMLParser
|
from html.parser import HTMLParser
|
||||||
from typing import Callable, Dict, List, Tuple
|
from typing import Callable, Dict, List, Tuple
|
||||||
|
|
||||||
from django.http import HttpRequest, HttpResponse
|
from django.http import HttpRequest, HttpResponse
|
||||||
|
from returns.curry import partial
|
||||||
|
|
||||||
from zerver.decorator import return_success_on_head_request, webhook_view
|
from zerver.decorator import return_success_on_head_request, webhook_view
|
||||||
from zerver.lib.exceptions import UnsupportedWebhookEventTypeError
|
from zerver.lib.exceptions import UnsupportedWebhookEventTypeError
|
||||||
|
|
|
@ -4,7 +4,6 @@ import copy
|
||||||
import datetime
|
import datetime
|
||||||
import email
|
import email
|
||||||
import email.policy
|
import email.policy
|
||||||
import functools
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import signal
|
import signal
|
||||||
|
@ -43,6 +42,7 @@ from django.db.utils import IntegrityError
|
||||||
from django.utils.timezone import now as timezone_now
|
from django.utils.timezone import now as timezone_now
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
from django.utils.translation import override as override_language
|
from django.utils.translation import override as override_language
|
||||||
|
from returns.curry import partial
|
||||||
from sentry_sdk import add_breadcrumb, configure_scope
|
from sentry_sdk import add_breadcrumb, configure_scope
|
||||||
from zulip_bots.lib import extract_query_without_mention
|
from zulip_bots.lib import extract_query_without_mention
|
||||||
|
|
||||||
|
@ -309,7 +309,7 @@ class QueueProcessingWorker(ABC):
|
||||||
try:
|
try:
|
||||||
signal.signal(
|
signal.signal(
|
||||||
signal.SIGALRM,
|
signal.SIGALRM,
|
||||||
functools.partial(self.timer_expired, self.MAX_CONSUME_SECONDS, events),
|
partial(self.timer_expired, self.MAX_CONSUME_SECONDS, events),
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
signal.alarm(self.MAX_CONSUME_SECONDS * len(events))
|
signal.alarm(self.MAX_CONSUME_SECONDS * len(events))
|
||||||
|
@ -357,7 +357,7 @@ class QueueProcessingWorker(ABC):
|
||||||
self.do_consume(consume_func, [event])
|
self.do_consume(consume_func, [event])
|
||||||
|
|
||||||
def timer_expired(
|
def timer_expired(
|
||||||
self, limit: int, events: List[Dict[str, Any]], signal: int, frame: FrameType
|
self, limit: int, events: List[Dict[str, Any]], signal: int, frame: Optional[FrameType]
|
||||||
) -> None:
|
) -> None:
|
||||||
raise WorkerTimeoutError(self.queue_name, limit, len(events))
|
raise WorkerTimeoutError(self.queue_name, limit, len(events))
|
||||||
|
|
||||||
|
@ -911,7 +911,7 @@ class FetchLinksEmbedData(QueueProcessingWorker):
|
||||||
do_update_embedded_data(message.sender, message, message.content, rendering_result)
|
do_update_embedded_data(message.sender, message, message.content, rendering_result)
|
||||||
|
|
||||||
def timer_expired(
|
def timer_expired(
|
||||||
self, limit: int, events: List[Dict[str, Any]], signal: int, frame: FrameType
|
self, limit: int, events: List[Dict[str, Any]], signal: int, frame: Optional[FrameType]
|
||||||
) -> None:
|
) -> None:
|
||||||
assert len(events) == 1
|
assert len(events) == 1
|
||||||
event = events[0]
|
event = events[0]
|
||||||
|
|
Loading…
Reference in New Issue