python: Annotate type aliases with TypeAlias.

This is not strictly necessary but it’s clearer and improves mypy’s
error messages.

https://docs.python.org/3/library/typing.html#typing.TypeAlias
https://mypy.readthedocs.io/en/stable/kinds_of_types.html#type-aliases

Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
Anders Kaseorg 2023-08-02 14:53:10 -07:00 committed by Tim Abbott
parent f55711dae3
commit c2c96eb0cf
24 changed files with 81 additions and 52 deletions

View File

@ -8,6 +8,7 @@ from django.conf import settings
from django.db import connection, models
from django.db.models import F
from psycopg2.sql import SQL, Composable, Identifier, Literal
from typing_extensions import TypeAlias
from analytics.models import (
BaseCount,
@ -346,7 +347,7 @@ def do_drop_single_stat(property: str) -> None:
## DataCollector-level operations ##
QueryFn = Callable[[Dict[str, Composable]], Composable]
QueryFn: TypeAlias = Callable[[Dict[str, Composable]], Composable]
def do_pull_by_sql_query(

View File

@ -5,6 +5,7 @@ from typing import Any, Dict, List, Mapping, Type, Union
from django.core.files.uploadedfile import UploadedFile
from django.core.management.base import BaseCommand
from django.utils.timezone import now as timezone_now
from typing_extensions import TypeAlias
from analytics.lib.counts import COUNT_STATS, CountStat, do_drop_all_analytics_tables
from analytics.lib.fixtures import generate_time_series_data
@ -147,7 +148,7 @@ class Command(BaseCommand):
with open(IMAGE_FILE_PATH, "rb") as fp:
upload_message_attachment_from_request(UploadedFile(fp), shylock, file_size)
FixtureData = Mapping[Union[str, int, None], List[int]]
FixtureData: TypeAlias = Mapping[Union[str, int, None], List[int]]
def insert_fixture_data(
stat: CountStat,

View File

@ -10,6 +10,7 @@ from django.shortcuts import render
from django.utils import translation
from django.utils.timezone import now as timezone_now
from django.utils.translation import gettext as _
from typing_extensions import TypeAlias
from analytics.lib.counts import COUNT_STATS, CountStat
from analytics.lib.time_utils import time_range
@ -242,7 +243,7 @@ def get_chart_data(
remote_realm_id: Optional[int] = None,
server: Optional["RemoteZulipServer"] = None,
) -> HttpResponse:
TableType = Union[
TableType: TypeAlias = Union[
Type["RemoteInstallationCount"],
Type[InstallationCount],
Type["RemoteRealmCount"],

View File

@ -16,6 +16,7 @@ from django.http import HttpRequest, HttpResponse
from django.template.response import TemplateResponse
from django.urls import reverse
from django.utils.timezone import now as timezone_now
from typing_extensions import TypeAlias
from confirmation import settings as confirmation_settings
from zerver.lib.types import UnspecifiedValue
@ -55,7 +56,7 @@ def generate_key() -> str:
return b32encode(secrets.token_bytes(15)).decode().lower()
ConfirmationObjT = Union[
ConfirmationObjT: TypeAlias = Union[
MultiuseInvite,
PreregistrationRealm,
PreregistrationUser,

View File

@ -11,6 +11,7 @@ from django.utils.timezone import now as timezone_now
from django.utils.translation import gettext as _
from django.utils.translation import override as override_language
from django_stubs_ext import ValuesQuerySet
from typing_extensions import TypeAlias
from zerver.actions.default_streams import (
do_remove_default_stream,
@ -560,7 +561,7 @@ def send_peer_subscriber_events(
send_event_on_commit(realm, event, peer_user_ids)
SubT = Tuple[List[SubInfo], List[SubInfo]]
SubT: TypeAlias = Tuple[List[SubInfo], List[SubInfo]]
def bulk_add_subscriptions(
@ -729,7 +730,9 @@ def notify_subscriptions_removed(
send_event(realm, event, [user_profile.id])
SubAndRemovedT = Tuple[List[Tuple[UserProfile, Stream]], List[Tuple[UserProfile, Stream]]]
SubAndRemovedT: TypeAlias = Tuple[
List[Tuple[UserProfile, Stream]], List[Tuple[UserProfile, Stream]]
]
def send_subscription_remove_events(

View File

@ -7,6 +7,7 @@ import orjson
from django.conf import settings
from django.forms.models import model_to_dict
from django.utils.timezone import now as timezone_now
from typing_extensions import TypeAlias
from zerver.data_import.import_util import (
ZerverFieldsT,
@ -29,7 +30,7 @@ from zerver.models import Recipient, UserProfile
from zproject.backends import GitHubAuthBackend
# stubs
GitterDataT = List[Dict[str, Any]]
GitterDataT: TypeAlias = List[Dict[str, Any]]
realm_id = 0

View File

@ -25,6 +25,7 @@ import orjson
import requests
from django.forms.models import model_to_dict
from django.utils.timezone import now as timezone_now
from typing_extensions import TypeAlias
from zerver.data_import.sequencer import NEXT_ID
from zerver.lib.avatar_hash import user_avatar_path_from_ids
@ -43,7 +44,7 @@ from zerver.models import (
from zproject.backends import all_implemented_backend_names
# stubs
ZerverFieldsT = Dict[str, Any]
ZerverFieldsT: TypeAlias = Dict[str, Any]
class SubscriberHandler:

View File

@ -16,6 +16,7 @@ import requests
from django.conf import settings
from django.forms.models import model_to_dict
from django.utils.timezone import now as timezone_now
from typing_extensions import TypeAlias
from zerver.data_import.import_util import (
ZerverFieldsT,
@ -56,11 +57,11 @@ from zerver.models import (
UserProfile,
)
SlackToZulipUserIDT = Dict[str, int]
AddedChannelsT = Dict[str, Tuple[str, int]]
AddedMPIMsT = Dict[str, Tuple[str, int]]
DMMembersT = Dict[str, Tuple[str, str]]
SlackToZulipRecipientT = Dict[str, int]
SlackToZulipUserIDT: TypeAlias = Dict[str, int]
AddedChannelsT: TypeAlias = Dict[str, Tuple[str, int]]
AddedMPIMsT: TypeAlias = Dict[str, Tuple[str, int]]
DMMembersT: TypeAlias = Dict[str, Tuple[str, str]]
SlackToZulipRecipientT: TypeAlias = Dict[str, int]
# Generic type for SlackBotEmail class
SlackBotEmailT = TypeVar("SlackBotEmailT", bound="SlackBotEmail")

View File

@ -1,10 +1,12 @@
import re
from typing import Any, Dict, List, Optional, Tuple
from typing_extensions import TypeAlias
# stubs
ZerverFieldsT = Dict[str, Any]
SlackToZulipUserIDT = Dict[str, int]
AddedChannelsT = Dict[str, Tuple[str, int]]
ZerverFieldsT: TypeAlias = Dict[str, Any]
SlackToZulipUserIDT: TypeAlias = Dict[str, int]
AddedChannelsT: TypeAlias = Dict[str, Tuple[str, int]]
# Slack link can be in the format <http://www.foo.com|www.foo.com> and <http://foo.com/>
LINK_REGEX = r"""

View File

@ -3,10 +3,11 @@ from typing import Any, Callable, Dict, Iterable, List, Mapping, Sequence, TypeV
from psycopg2.extensions import connection, cursor
from psycopg2.sql import Composable
from typing_extensions import TypeAlias
CursorObj = TypeVar("CursorObj", bound=cursor)
Query = Union[str, bytes, Composable]
Params = Union[Sequence[object], Mapping[str, object], None]
Query: TypeAlias = Union[str, bytes, Composable]
Params: TypeAlias = Union[Sequence[object], Mapping[str, object], None]
ParamsT = TypeVar("ParamsT")

View File

@ -7,6 +7,7 @@ from typing import Any, Collection, Dict, List, Set, Tuple
from django.conf import settings
from django.db import transaction
from django.utils.timezone import now as timezone_now
from typing_extensions import TypeAlias
from confirmation.models import one_click_unsubscribe_link
from zerver.context_processors import common_context
@ -34,7 +35,7 @@ log_to_file(logger, settings.DIGEST_LOG_PATH)
DIGEST_CUTOFF = 5
MAX_HOT_TOPICS_TO_BE_INCLUDED_IN_DIGEST = 4
TopicKey = Tuple[int, str]
TopicKey: TypeAlias = Tuple[int, str]
class DigestTopic:

View File

@ -24,6 +24,7 @@ from django.db.models import Exists, OuterRef, Q
from django.forms.models import model_to_dict
from django.utils.timezone import is_naive as timezone_is_naive
from mypy_boto3_s3.service_resource import Object
from typing_extensions import TypeAlias
import zerver.lib.upload
from analytics.models import RealmCount, StreamCount, UserCount
@ -74,17 +75,17 @@ from zerver.models import (
)
# Custom mypy types follow:
Record = Dict[str, Any]
Record: TypeAlias = Dict[str, Any]
TableName = str
TableData = Dict[TableName, List[Record]]
TableData: TypeAlias = Dict[TableName, List[Record]]
Field = str
Path = str
Context = Dict[str, Any]
FilterArgs = Dict[str, Any]
IdSource = Tuple[TableName, Field]
SourceFilter = Callable[[Record], bool]
Context: TypeAlias = Dict[str, Any]
FilterArgs: TypeAlias = Dict[str, Any]
IdSource: TypeAlias = Tuple[TableName, Field]
SourceFilter: TypeAlias = Callable[[Record], bool]
CustomFetch = Callable[[TableData, Context], None]
CustomFetch: TypeAlias = Callable[[TableData, Context], None]
class MessagePartial(TypedDict):

View File

@ -7,6 +7,7 @@ from django.urls import URLResolver, path
from django.utils.module_loading import import_string
from django.utils.translation import gettext_lazy
from django_stubs_ext import StrPromise
from typing_extensions import TypeAlias
from zerver.lib.storage import static_path
@ -31,7 +32,7 @@ Over time, we expect this registry to grow additional convenience
features for writing and configuring integrations efficiently.
"""
OptionValidator = Callable[[str, str], Optional[str]]
OptionValidator: TypeAlias = Callable[[str, str], Optional[str]]
META_CATEGORY: Dict[str, StrPromise] = {
"meta-integration": gettext_lazy("Integration frameworks"),

View File

@ -47,6 +47,7 @@ from markdown.blockparser import BlockParser
from markdown.extensions import codehilite, nl2br, sane_lists, tables
from soupsieve import escape as css_escape
from tlds import tld_set
from typing_extensions import TypeAlias
from zerver.lib import mention
from zerver.lib.cache import cache_with_key
@ -148,7 +149,7 @@ class DbData:
version = 1
_T = TypeVar("_T")
ElementStringNone = Union[Element, Optional[str]]
ElementStringNone: TypeAlias = Union[Element, Optional[str]]
EMOJI_REGEX = r"(?P<syntax>:[\w\-\+]+:)"

View File

@ -43,6 +43,7 @@ from sqlalchemy.sql import (
)
from sqlalchemy.sql.selectable import SelectBase
from sqlalchemy.types import ARRAY, Boolean, Integer, Text
from typing_extensions import TypeAlias
from zerver.lib.addressee import get_user_profiles, get_user_profiles_by_ids
from zerver.lib.exceptions import ErrorCode, JsonableError
@ -207,9 +208,9 @@ class BadNarrowOperatorError(JsonableError):
return _("Invalid narrow operator: {desc}")
ConditionTransform = Callable[[ClauseElement], ClauseElement]
ConditionTransform: TypeAlias = Callable[[ClauseElement], ClauseElement]
OptionalNarrowListT = Optional[List[Dict[str, Any]]]
OptionalNarrowListT: TypeAlias = Optional[List[Dict[str, Any]]]
# These delimiters will not appear in rendered messages or HTML-escaped topics.
TS_START = "<ts-match>"

View File

@ -17,6 +17,7 @@ from django.db.models import F, Q
from django.utils.timezone import now as timezone_now
from django.utils.translation import gettext as _
from django.utils.translation import override as override_language
from typing_extensions import TypeAlias
from zerver.lib.avatar import absolute_avatar_url
from zerver.lib.exceptions import JsonableError
@ -47,7 +48,7 @@ logger = logging.getLogger(__name__)
if settings.ZILENCER_ENABLED:
from zilencer.models import RemotePushDeviceToken, RemoteZulipServer
DeviceToken = Union[PushDeviceToken, "RemotePushDeviceToken"]
DeviceToken: TypeAlias = Union[PushDeviceToken, "RemotePushDeviceToken"]
# We store the token as b64, but apns-client wants hex strings

View File

@ -17,12 +17,13 @@ from pika.adapters.blocking_connection import BlockingChannel
from pika.channel import Channel
from pika.spec import Basic
from tornado import ioloop
from typing_extensions import TypeAlias
from zerver.lib.utils import assert_is_not_none
MAX_REQUEST_RETRIES = 3
ChannelT = TypeVar("ChannelT", Channel, BlockingChannel)
Consumer = Callable[[ChannelT, Basic.Deliver, pika.BasicProperties, bytes], None]
Consumer: TypeAlias = Callable[[ChannelT, Basic.Deliver, pika.BasicProperties, bytes], None]
# This simple queuing library doesn't expose much of the power of

View File

@ -14,6 +14,7 @@ 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 typing_extensions import TypeAlias
from scripts.lib.zulip_tools import (
TEMPLATE_DATABASE_DIR,
@ -103,8 +104,8 @@ def process_instrumented_calls(func: Callable[[Dict[str, Any]], None]) -> None:
func(call)
SerializedSubsuite = Tuple[Type[TestSuite], List[str]]
SubsuiteArgs = Tuple[Type["RemoteTestRunner"], int, SerializedSubsuite, bool, bool]
SerializedSubsuite: TypeAlias = Tuple[Type[TestSuite], List[str]]
SubsuiteArgs: TypeAlias = Tuple[Type["RemoteTestRunner"], int, SerializedSubsuite, bool, bool]
def run_subsuite(args: SubsuiteArgs) -> Tuple[int, Any]:

View File

@ -3,17 +3,17 @@ from dataclasses import dataclass
from typing import Any, Callable, Dict, List, Optional, Tuple, TypedDict, TypeVar, Union
from django_stubs_ext import StrPromise
from typing_extensions import NotRequired
from typing_extensions import NotRequired, TypeAlias
# See zerver/lib/validator.py for more details of Validators,
# including many examples
ResultT = TypeVar("ResultT")
Validator = Callable[[str, object], ResultT]
ExtendedValidator = Callable[[str, str, object], str]
RealmUserValidator = Callable[[int, object, bool], List[int]]
Validator: TypeAlias = Callable[[str, object], ResultT]
ExtendedValidator: TypeAlias = Callable[[str, str, object], str]
RealmUserValidator: TypeAlias = Callable[[int, object, bool], List[int]]
ProfileDataElementValue = Union[str, List[int]]
ProfileDataElementValue: TypeAlias = Union[str, List[int]]
class ProfileDataElementBase(TypedDict, total=False):
@ -36,13 +36,17 @@ class ProfileDataElementUpdateDict(TypedDict):
value: ProfileDataElementValue
ProfileData = List[ProfileDataElement]
ProfileData: TypeAlias = List[ProfileDataElement]
FieldElement = Tuple[int, StrPromise, Validator[ProfileDataElementValue], Callable[[Any], Any], str]
ExtendedFieldElement = Tuple[int, StrPromise, ExtendedValidator, Callable[[Any], Any], str]
UserFieldElement = Tuple[int, StrPromise, RealmUserValidator, Callable[[Any], Any], str]
FieldElement: TypeAlias = Tuple[
int, StrPromise, Validator[ProfileDataElementValue], Callable[[Any], Any], str
]
ExtendedFieldElement: TypeAlias = Tuple[
int, StrPromise, ExtendedValidator, Callable[[Any], Any], str
]
UserFieldElement: TypeAlias = Tuple[int, StrPromise, RealmUserValidator, Callable[[Any], Any], str]
ProfileFieldData = Dict[str, Union[Dict[str, str], str]]
ProfileFieldData: TypeAlias = Dict[str, Union[Dict[str, str], str]]
class UserDisplayRecipient(TypedDict):
@ -52,7 +56,7 @@ class UserDisplayRecipient(TypedDict):
is_mirror_dummy: bool
DisplayRecipientT = Union[str, List[UserDisplayRecipient]]
DisplayRecipientT: TypeAlias = Union[str, List[UserDisplayRecipient]]
class LinkifierDict(TypedDict):

View File

@ -15,6 +15,7 @@ import time_machine
from django.conf import settings
from django.db.utils import IntegrityError
from django.test import override_settings
from typing_extensions import TypeAlias
from zerver.lib.email_mirror import RateLimitedRealmMirror
from zerver.lib.email_mirror_helpers import encode_email_address
@ -43,7 +44,7 @@ from zerver.worker.queue_processors import (
get_active_worker_queues,
)
Event = Dict[str, Any]
Event: TypeAlias = Dict[str, Any]
class FakeClient:

View File

@ -27,7 +27,7 @@ from django.views.decorators.http import require_safe
from social_django.utils import load_backend, load_strategy
from two_factor.forms import BackupTokenForm
from two_factor.views import LoginView as BaseTwoFactorLoginView
from typing_extensions import Concatenate, ParamSpec
from typing_extensions import Concatenate, ParamSpec, TypeAlias
from confirmation.models import (
Confirmation,
@ -103,7 +103,7 @@ if TYPE_CHECKING:
from django.http.request import _ImmutableQueryDict
ParamT = ParamSpec("ParamT")
ExtraContext = Optional[Dict[str, Any]]
ExtraContext: TypeAlias = Optional[Dict[str, Any]]
EXPIRABLE_SESSION_VAR_DEFAULT_EXPIRY_SECS = 3600

View File

@ -4,6 +4,7 @@ from django.conf import settings
from django.contrib.auth.models import AnonymousUser
from django.http import HttpRequest, HttpResponse
from django.utils.translation import gettext as _
from typing_extensions import TypeAlias
from zerver.context_processors import get_valid_realm_from_request
from zerver.lib.compatibility import is_pronouns_field_type_supported
@ -34,7 +35,7 @@ def _default_narrow(
return narrow
NarrowT = Sequence[Sequence[str]]
NarrowT: TypeAlias = Sequence[Sequence[str]]
@has_request_variables

View File

@ -2,6 +2,7 @@ from email.headerregistry import Address
from typing import Dict, Union
from django.http import HttpRequest, HttpResponse
from typing_extensions import TypeAlias
from zerver.decorator import webhook_view
from zerver.lib.exceptions import UnsupportedWebhookEventTypeError
@ -11,7 +12,7 @@ from zerver.lib.validator import WildValue, check_int, check_none_or, check_stri
from zerver.lib.webhooks.common import check_send_webhook_message
from zerver.models import UserProfile
FormatDictType = Dict[str, Union[str, int]]
FormatDictType: TypeAlias = Dict[str, Union[str, int]]
PAGER_DUTY_EVENT_NAMES = {
"incident.trigger": "triggered",

View File

@ -9,6 +9,7 @@ should be in bold.
from typing import Dict, List, Optional, Tuple, Union
from django.http import HttpRequest, HttpResponse
from typing_extensions import TypeAlias
from zerver.decorator import webhook_view
from zerver.lib.request import REQ, has_request_variables
@ -17,8 +18,8 @@ from zerver.lib.validator import WildValue, check_bool, check_none_or, check_str
from zerver.lib.webhooks.common import check_send_webhook_message
from zerver.models import UserProfile
EventType = Dict[str, Union[str, Dict[str, Optional[Union[str, bool]]]]]
ReturnType = Tuple[WildValue, WildValue]
EventType: TypeAlias = Dict[str, Union[str, Dict[str, Optional[Union[str, bool]]]]]
ReturnType: TypeAlias = Tuple[WildValue, WildValue]
@webhook_view("Taiga")