mypy: Enable new error explicit-override.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
Anders Kaseorg 2023-10-12 10:43:45 -07:00 committed by Anders Kaseorg
parent d257002ad8
commit a50eb2e809
223 changed files with 936 additions and 18 deletions

View File

@ -8,7 +8,7 @@ from django.conf import settings
from django.db import connection, models from django.db import connection, models
from django.db.models import F from django.db.models import F
from psycopg2.sql import SQL, Composable, Identifier, Literal from psycopg2.sql import SQL, Composable, Identifier, Literal
from typing_extensions import TypeAlias from typing_extensions import TypeAlias, override
from analytics.models import ( from analytics.models import (
BaseCount, BaseCount,
@ -63,6 +63,7 @@ class CountStat:
else: else:
self.interval = self.time_increment self.interval = self.time_increment
@override
def __repr__(self) -> str: def __repr__(self) -> str:
return f"<CountStat: {self.property}>" return f"<CountStat: {self.property}>"

View File

@ -5,6 +5,7 @@ from typing import Any, Dict
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from django.utils.timezone import now as timezone_now from django.utils.timezone import now as timezone_now
from typing_extensions import override
from analytics.lib.counts import COUNT_STATS, CountStat from analytics.lib.counts import COUNT_STATS, CountStat
from analytics.models import installation_epoch from analytics.models import installation_epoch
@ -24,6 +25,7 @@ class Command(BaseCommand):
Run as a cron job that runs every hour.""" Run as a cron job that runs every hour."""
@override
def handle(self, *args: Any, **options: Any) -> None: def handle(self, *args: Any, **options: Any) -> None:
fill_state = self.get_fill_state() fill_state = self.get_fill_state()
status = fill_state["status"] status = fill_state["status"]

View File

@ -2,6 +2,7 @@ from argparse import ArgumentParser
from typing import Any from typing import Any
from django.core.management.base import BaseCommand, CommandError from django.core.management.base import BaseCommand, CommandError
from typing_extensions import override
from analytics.lib.counts import do_drop_all_analytics_tables from analytics.lib.counts import do_drop_all_analytics_tables
@ -9,9 +10,11 @@ from analytics.lib.counts import do_drop_all_analytics_tables
class Command(BaseCommand): class Command(BaseCommand):
help = """Clear analytics tables.""" help = """Clear analytics tables."""
@override
def add_arguments(self, parser: ArgumentParser) -> None: def add_arguments(self, parser: ArgumentParser) -> None:
parser.add_argument("--force", action="store_true", help="Clear analytics tables.") parser.add_argument("--force", action="store_true", help="Clear analytics tables.")
@override
def handle(self, *args: Any, **options: Any) -> None: def handle(self, *args: Any, **options: Any) -> None:
if options["force"]: if options["force"]:
do_drop_all_analytics_tables() do_drop_all_analytics_tables()

View File

@ -2,6 +2,7 @@ from argparse import ArgumentParser
from typing import Any from typing import Any
from django.core.management.base import BaseCommand, CommandError from django.core.management.base import BaseCommand, CommandError
from typing_extensions import override
from analytics.lib.counts import COUNT_STATS, do_drop_single_stat from analytics.lib.counts import COUNT_STATS, do_drop_single_stat
@ -9,10 +10,12 @@ from analytics.lib.counts import COUNT_STATS, do_drop_single_stat
class Command(BaseCommand): class Command(BaseCommand):
help = """Clear analytics tables.""" help = """Clear analytics tables."""
@override
def add_arguments(self, parser: ArgumentParser) -> None: def add_arguments(self, parser: ArgumentParser) -> None:
parser.add_argument("--force", action="store_true", help="Actually do it.") parser.add_argument("--force", action="store_true", help="Actually do it.")
parser.add_argument("--property", help="The property of the stat to be cleared.") parser.add_argument("--property", help="The property of the stat to be cleared.")
@override
def handle(self, *args: Any, **options: Any) -> None: def handle(self, *args: Any, **options: Any) -> None:
property = options["property"] property = options["property"]
if property not in COUNT_STATS: if property not in COUNT_STATS:

View File

@ -5,7 +5,7 @@ from typing import Any, Dict, List, Mapping, Type, Union
from django.core.files.uploadedfile import UploadedFile from django.core.files.uploadedfile import UploadedFile
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from django.utils.timezone import now as timezone_now from django.utils.timezone import now as timezone_now
from typing_extensions import TypeAlias from typing_extensions import TypeAlias, override
from analytics.lib.counts import COUNT_STATS, CountStat, do_drop_all_analytics_tables from analytics.lib.counts import COUNT_STATS, CountStat, do_drop_all_analytics_tables
from analytics.lib.fixtures import generate_time_series_data from analytics.lib.fixtures import generate_time_series_data
@ -68,6 +68,7 @@ class Command(BaseCommand):
random_seed=self.random_seed, random_seed=self.random_seed,
) )
@override
def handle(self, *args: Any, **options: Any) -> None: def handle(self, *args: Any, **options: Any) -> None:
# TODO: This should arguably only delete the objects # TODO: This should arguably only delete the objects
# associated with the "analytics" realm. # associated with the "analytics" realm.

View File

@ -8,6 +8,7 @@ from django.conf import settings
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from django.utils.dateparse import parse_datetime from django.utils.dateparse import parse_datetime
from django.utils.timezone import now as timezone_now from django.utils.timezone import now as timezone_now
from typing_extensions import override
from analytics.lib.counts import COUNT_STATS, logger, process_count_stat from analytics.lib.counts import COUNT_STATS, logger, process_count_stat
from scripts.lib.zulip_tools import ENDC, WARNING from scripts.lib.zulip_tools import ENDC, WARNING
@ -21,6 +22,7 @@ class Command(BaseCommand):
Run as a cron job that runs every hour.""" Run as a cron job that runs every hour."""
@override
def add_arguments(self, parser: ArgumentParser) -> None: def add_arguments(self, parser: ArgumentParser) -> None:
parser.add_argument( parser.add_argument(
"--time", "--time",
@ -37,6 +39,7 @@ class Command(BaseCommand):
"--verbose", action="store_true", help="Print timing information to stdout." "--verbose", action="store_true", help="Print timing information to stdout."
) )
@override
def handle(self, *args: Any, **options: Any) -> None: def handle(self, *args: Any, **options: Any) -> None:
try: try:
os.mkdir(settings.ANALYTICS_LOCK_DIR) os.mkdir(settings.ANALYTICS_LOCK_DIR)

View File

@ -1,7 +1,11 @@
# https://github.com/typeddjango/django-stubs/issues/1698
# mypy: disable-error-code="explicit-override"
import datetime import datetime
from django.db import models from django.db import models
from django.db.models import Q, UniqueConstraint from django.db.models import Q, UniqueConstraint
from typing_extensions import override
from zerver.lib.timestamp import floor_to_day from zerver.lib.timestamp import floor_to_day
from zerver.models import Realm, Stream, UserProfile from zerver.models import Realm, Stream, UserProfile
@ -16,6 +20,7 @@ class FillState(models.Model):
STARTED = 2 STARTED = 2
state = models.PositiveSmallIntegerField() state = models.PositiveSmallIntegerField()
@override
def __str__(self) -> str: def __str__(self) -> str:
return f"{self.property} {self.end_time} {self.state}" return f"{self.property} {self.end_time} {self.state}"
@ -58,6 +63,7 @@ class InstallationCount(BaseCount):
), ),
] ]
@override
def __str__(self) -> str: def __str__(self) -> str:
return f"{self.property} {self.subgroup} {self.value}" return f"{self.property} {self.subgroup} {self.value}"
@ -86,6 +92,7 @@ class RealmCount(BaseCount):
) )
] ]
@override
def __str__(self) -> str: def __str__(self) -> str:
return f"{self.realm!r} {self.property} {self.subgroup} {self.value}" return f"{self.realm!r} {self.property} {self.subgroup} {self.value}"
@ -117,6 +124,7 @@ class UserCount(BaseCount):
) )
] ]
@override
def __str__(self) -> str: def __str__(self) -> str:
return f"{self.user!r} {self.property} {self.subgroup} {self.value}" return f"{self.user!r} {self.property} {self.subgroup} {self.value}"
@ -148,5 +156,6 @@ class StreamCount(BaseCount):
) )
] ]
@override
def __str__(self) -> str: def __str__(self) -> str:
return f"{self.stream!r} {self.property} {self.subgroup} {self.value} {self.id}" return f"{self.stream!r} {self.property} {self.subgroup} {self.value} {self.id}"

View File

@ -8,6 +8,7 @@ from django.db import models
from django.db.models import Sum from django.db.models import Sum
from django.utils.timezone import now as timezone_now from django.utils.timezone import now as timezone_now
from psycopg2.sql import SQL, Literal from psycopg2.sql import SQL, Literal
from typing_extensions import override
from analytics.lib.counts import ( from analytics.lib.counts import (
COUNT_STATS, COUNT_STATS,
@ -81,6 +82,7 @@ class AnalyticsTestCase(ZulipTestCase):
TIME_ZERO = datetime(1988, 3, 14, tzinfo=timezone.utc) TIME_ZERO = datetime(1988, 3, 14, tzinfo=timezone.utc)
TIME_LAST_HOUR = TIME_ZERO - HOUR TIME_LAST_HOUR = TIME_ZERO - HOUR
@override
def setUp(self) -> None: def setUp(self) -> None:
super().setUp() super().setUp()
self.default_realm = do_create_realm( self.default_realm = do_create_realm(
@ -455,6 +457,7 @@ class TestProcessCountStat(AnalyticsTestCase):
class TestCountStats(AnalyticsTestCase): class TestCountStats(AnalyticsTestCase):
@override
def setUp(self) -> None: def setUp(self) -> None:
super().setUp() super().setUp()
# This tests two things for each of the queries/CountStats: Handling # This tests two things for each of the queries/CountStats: Handling
@ -1543,6 +1546,7 @@ class TestDeleteStats(AnalyticsTestCase):
class TestActiveUsersAudit(AnalyticsTestCase): class TestActiveUsersAudit(AnalyticsTestCase):
@override
def setUp(self) -> None: def setUp(self) -> None:
super().setUp() super().setUp()
self.user = self.create_user() self.user = self.create_user()
@ -1725,6 +1729,7 @@ class TestActiveUsersAudit(AnalyticsTestCase):
class TestRealmActiveHumans(AnalyticsTestCase): class TestRealmActiveHumans(AnalyticsTestCase):
@override
def setUp(self) -> None: def setUp(self) -> None:
super().setUp() super().setUp()
self.stat = COUNT_STATS["realm_active_humans::day"] self.stat = COUNT_STATS["realm_active_humans::day"]

View File

@ -2,6 +2,7 @@ from datetime import datetime, timedelta, timezone
from typing import List, Optional from typing import List, Optional
from django.utils.timezone import now as timezone_now from django.utils.timezone import now as timezone_now
from typing_extensions import override
from analytics.lib.counts import COUNT_STATS, CountStat from analytics.lib.counts import COUNT_STATS, CountStat
from analytics.lib.time_utils import time_range from analytics.lib.time_utils import time_range
@ -68,6 +69,7 @@ class TestStatsEndpoint(ZulipTestCase):
class TestGetChartData(ZulipTestCase): class TestGetChartData(ZulipTestCase):
@override
def setUp(self) -> None: def setUp(self) -> None:
super().setUp() super().setUp()
self.realm = get_realm("zulip") self.realm = get_realm("zulip")

View File

@ -4,6 +4,7 @@ from unittest import mock
import orjson import orjson
from django.utils.timezone import now as timezone_now from django.utils.timezone import now as timezone_now
from typing_extensions import override
from corporate.lib.stripe import add_months, update_sponsorship_status from corporate.lib.stripe import add_months, update_sponsorship_status
from corporate.models import Customer, CustomerPlan, LicenseLedger, get_customer_by_realm from corporate.models import Customer, CustomerPlan, LicenseLedger, get_customer_by_realm
@ -31,6 +32,7 @@ from zilencer.models import RemoteZulipServer
class TestRemoteServerSupportEndpoint(ZulipTestCase): class TestRemoteServerSupportEndpoint(ZulipTestCase):
@override
def setUp(self) -> None: def setUp(self) -> None:
super().setUp() super().setUp()

View File

@ -16,7 +16,7 @@ from django.http import HttpRequest, HttpResponse
from django.template.response import TemplateResponse from django.template.response import TemplateResponse
from django.urls import reverse from django.urls import reverse
from django.utils.timezone import now as timezone_now from django.utils.timezone import now as timezone_now
from typing_extensions import TypeAlias from typing_extensions import TypeAlias, override
from confirmation import settings as confirmation_settings from confirmation import settings as confirmation_settings
from zerver.lib.types import UnspecifiedValue from zerver.lib.types import UnspecifiedValue
@ -190,6 +190,7 @@ class Confirmation(models.Model):
class Meta: class Meta:
unique_together = ("type", "confirmation_key") unique_together = ("type", "confirmation_key")
@override
def __str__(self) -> str: def __str__(self) -> str:
return f"{self.content_object!r}" return f"{self.content_object!r}"

View File

@ -4,6 +4,7 @@ from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.db import models from django.db import models
from django.db.models import CASCADE, Q from django.db.models import CASCADE, Q
from typing_extensions import override
from zerver.models import Realm, UserProfile from zerver.models import Realm, UserProfile
from zilencer.models import RemoteZulipServer from zilencer.models import RemoteZulipServer
@ -36,6 +37,7 @@ class Customer(models.Model):
) )
] ]
@override
def __str__(self) -> str: def __str__(self) -> str:
return f"{self.realm!r} {self.stripe_customer_id}" return f"{self.realm!r} {self.stripe_customer_id}"

View File

@ -33,7 +33,7 @@ from django.core import signing
from django.urls.resolvers import get_resolver from django.urls.resolvers import get_resolver
from django.utils.crypto import get_random_string from django.utils.crypto import get_random_string
from django.utils.timezone import now as timezone_now from django.utils.timezone import now as timezone_now
from typing_extensions import ParamSpec from typing_extensions import ParamSpec, override
from corporate.lib.stripe import ( from corporate.lib.stripe import (
DEFAULT_INVOICE_DAYS_UNTIL_DUE, DEFAULT_INVOICE_DAYS_UNTIL_DUE,
@ -383,6 +383,7 @@ def mock_stripe(
class StripeTestCase(ZulipTestCase): class StripeTestCase(ZulipTestCase):
@override
def setUp(self) -> None: def setUp(self) -> None:
super().setUp() super().setUp()
realm = get_realm("zulip") realm = get_realm("zulip")
@ -4171,6 +4172,7 @@ class EventStatusTest(StripeTestCase):
class RequiresBillingAccessTest(StripeTestCase): class RequiresBillingAccessTest(StripeTestCase):
@override
def setUp(self, *mocks: Mock) -> None: def setUp(self, *mocks: Mock) -> None:
super().setUp() super().setUp()
hamlet = self.example_user("hamlet") hamlet = self.example_user("hamlet")

View File

@ -13,6 +13,7 @@ setup_path()
from django.core.management import ManagementUtility, get_commands from django.core.management import ManagementUtility, get_commands
from django.core.management.color import color_style from django.core.management.color import color_style
from typing_extensions import override
from scripts.lib.zulip_tools import assert_not_running_as_root from scripts.lib.zulip_tools import assert_not_running_as_root
@ -73,6 +74,7 @@ class FilteredManagementUtility(ManagementUtility):
All other change are just code style differences to pass the Zulip linter. All other change are just code style differences to pass the Zulip linter.
""" """
@override
def main_help_text(self, commands_only: bool = False) -> str: def main_help_text(self, commands_only: bool = False) -> str:
"""Return the script's main help text, as a string.""" """Return the script's main help text, as a string."""
if commands_only: if commands_only:

View File

@ -32,6 +32,7 @@ enable_error_code = [
"truthy-iterable", "truthy-iterable",
"ignore-without-code", "ignore-without-code",
"unused-awaitable", "unused-awaitable",
"explicit-override",
] ]
# Display the codes needed for # type: ignore[code] annotations. # Display the codes needed for # type: ignore[code] annotations.

View File

@ -4,8 +4,11 @@ from http.client import HTTPConnection
from typing import Dict, List, Optional, Tuple, Union from typing import Dict, List, Optional, Tuple, Union
from xmlrpc import client from xmlrpc import client
from typing_extensions import override
class UnixStreamHTTPConnection(HTTPConnection): class UnixStreamHTTPConnection(HTTPConnection):
@override
def connect(self) -> None: def connect(self) -> None:
self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
connected = False connected = False
@ -28,6 +31,7 @@ class UnixStreamTransport(client.Transport):
self.socket_path = socket_path self.socket_path = socket_path
super().__init__() super().__init__()
@override
def make_connection( def make_connection(
self, host: Union[Tuple[str, Dict[str, str]], str] self, host: Union[Tuple[str, Dict[str, str]], str]
) -> UnixStreamHTTPConnection: ) -> UnixStreamHTTPConnection:

View File

@ -3,6 +3,8 @@ from posixpath import basename
from typing import Any, List, Set from typing import Any, List, Set
from urllib.parse import urlparse from urllib.parse import urlparse
from typing_extensions import override
from .common.spiders import BaseDocumentationSpider from .common.spiders import BaseDocumentationSpider
@ -21,6 +23,7 @@ class UnusedImagesLinterSpider(BaseDocumentationSpider):
self.static_images: Set[str] = set() self.static_images: Set[str] = set()
self.images_static_dir: str = get_images_dir(self.images_path) self.images_static_dir: str = get_images_dir(self.images_path)
@override
def _is_external_url(self, url: str) -> bool: def _is_external_url(self, url: str) -> bool:
is_external = url.startswith("http") and self.start_urls[0] not in url is_external = url.startswith("http") and self.start_urls[0] not in url
if self._has_extension(url) and f"localhost:9981/{self.images_path}" in url: if self._has_extension(url) and f"localhost:9981/{self.images_path}" in url:
@ -55,6 +58,7 @@ class APIDocumentationSpider(UnusedImagesLinterSpider):
class PorticoDocumentationSpider(BaseDocumentationSpider): class PorticoDocumentationSpider(BaseDocumentationSpider):
@override
def _is_external_url(self, url: str) -> bool: def _is_external_url(self, url: str) -> bool:
return ( return (
not url.startswith("http://localhost:9981") not url.startswith("http://localhost:9981")

View File

@ -1,5 +1,7 @@
from typing import Callable, List, Optional from typing import Callable, List, Optional
from typing_extensions import override
class FormattedError(Exception): class FormattedError(Exception):
pass pass
@ -9,6 +11,7 @@ class TemplateParserError(Exception):
def __init__(self, message: str) -> None: def __init__(self, message: str) -> None:
self.message = message self.message = message
@override
def __str__(self) -> str: def __str__(self) -> str:
return self.message return self.message

View File

@ -20,6 +20,7 @@ sanity_check.check_venv(__file__)
from tornado import httpclient, httputil, web from tornado import httpclient, httputil, web
from tornado.platform.asyncio import AsyncIOMainLoop from tornado.platform.asyncio import AsyncIOMainLoop
from typing_extensions import override
from tools.lib.test_script import add_provision_check_override_param, assert_provisioning_status_ok from tools.lib.test_script import add_provision_check_override_param, assert_provisioning_status_ok
@ -218,27 +219,35 @@ class BaseHandler(web.RequestHandler):
headers.add(header, v) headers.add(header, v)
return headers return headers
@override
def get(self) -> None: def get(self) -> None:
pass pass
@override
def head(self) -> None: def head(self) -> None:
pass pass
@override
def post(self) -> None: def post(self) -> None:
pass pass
@override
def put(self) -> None: def put(self) -> None:
pass pass
@override
def patch(self) -> None: def patch(self) -> None:
pass pass
@override
def options(self) -> None: def options(self) -> None:
pass pass
@override
def delete(self) -> None: def delete(self) -> None:
pass pass
@override
async def prepare(self) -> None: async def prepare(self) -> None:
assert self.request.method is not None assert self.request.method is not None
assert self.request.remote_ip is not None assert self.request.remote_ip is not None
@ -307,6 +316,7 @@ class Application(web.Application):
enable_logging=enable_logging, enable_logging=enable_logging,
) )
@override
def log_request(self, handler: web.RequestHandler) -> None: def log_request(self, handler: web.RequestHandler) -> None:
if self.settings["enable_logging"]: if self.settings["enable_logging"]:
super().log_request(handler) super().log_request(handler)

View File

@ -3,6 +3,7 @@ from io import StringIO
from unittest import TestCase from unittest import TestCase
from unittest.mock import patch from unittest.mock import patch
from typing_extensions import override
from zulint.custom_rules import RuleList from zulint.custom_rules import RuleList
from tools.linter_lib.custom_check import non_py_rules, python_rules from tools.linter_lib.custom_check import non_py_rules, python_rules
@ -12,6 +13,7 @@ CHECK_MESSAGE = "Fix the corresponding rule in `tools/linter_lib/custom_check.py
class TestRuleList(TestCase): class TestRuleList(TestCase):
@override
def setUp(self) -> None: def setUp(self) -> None:
all_rules = list(python_rules.rules) all_rules = list(python_rules.rules)
for rule in non_py_rules: for rule in non_py_rules:

View File

@ -5,6 +5,7 @@ from django.apps import AppConfig
from django.conf import settings from django.conf import settings
from django.core.cache import cache from django.core.cache import cache
from django.db.models.signals import post_migrate from django.db.models.signals import post_migrate
from typing_extensions import override
def flush_cache(sender: Optional[AppConfig], **kwargs: Any) -> None: def flush_cache(sender: Optional[AppConfig], **kwargs: Any) -> None:
@ -15,6 +16,7 @@ def flush_cache(sender: Optional[AppConfig], **kwargs: Any) -> None:
class ZerverConfig(AppConfig): class ZerverConfig(AppConfig):
name: str = "zerver" name: str = "zerver"
@override
def ready(self) -> None: def ready(self) -> None:
if settings.SENTRY_DSN: # nocoverage if settings.SENTRY_DSN: # nocoverage
from zproject.config import get_config from zproject.config import get_config

View File

@ -2,9 +2,11 @@ from typing import Any, Dict, Optional
from django.http import HttpRequest from django.http import HttpRequest
from django.views.debug import SafeExceptionReporterFilter from django.views.debug import SafeExceptionReporterFilter
from typing_extensions import override
class ZulipExceptionReporterFilter(SafeExceptionReporterFilter): class ZulipExceptionReporterFilter(SafeExceptionReporterFilter):
@override
def get_post_parameters(self, request: Optional[HttpRequest]) -> Dict[str, Any]: def get_post_parameters(self, request: Optional[HttpRequest]) -> Dict[str, Any]:
post_data = SafeExceptionReporterFilter.get_post_parameters(self, request) post_data = SafeExceptionReporterFilter.get_post_parameters(self, request)
assert isinstance(post_data, dict) assert isinstance(post_data, dict)

View File

@ -20,6 +20,7 @@ from django.utils.translation import gettext as _
from markupsafe import Markup from markupsafe import Markup
from two_factor.forms import AuthenticationTokenForm as TwoFactorAuthenticationTokenForm from two_factor.forms import AuthenticationTokenForm as TwoFactorAuthenticationTokenForm
from two_factor.utils import totp_digits from two_factor.utils import totp_digits
from typing_extensions import override
from zerver.actions.user_settings import do_change_password from zerver.actions.user_settings import do_change_password
from zerver.lib.email_validation import ( from zerver.lib.email_validation import (
@ -324,6 +325,7 @@ class LoggingSetPasswordForm(SetPasswordForm):
return new_password return new_password
@override
def save(self, commit: bool = True) -> UserProfile: def save(self, commit: bool = True) -> UserProfile:
assert isinstance(self.user, UserProfile) assert isinstance(self.user, UserProfile)
do_change_password(self.user, self.cleaned_data["new_password1"], commit=commit) do_change_password(self.user, self.cleaned_data["new_password1"], commit=commit)
@ -340,6 +342,7 @@ def generate_password_reset_url(
class ZulipPasswordResetForm(PasswordResetForm): class ZulipPasswordResetForm(PasswordResetForm):
@override
def save( def save(
self, self,
domain_override: Optional[str] = None, domain_override: Optional[str] = None,
@ -452,9 +455,11 @@ class RateLimitedPasswordResetByEmail(RateLimitedObject):
self.email = email self.email = email
super().__init__() super().__init__()
@override
def key(self) -> str: def key(self) -> str:
return f"{type(self).__name__}:{self.email}" return f"{type(self).__name__}:{self.email}"
@override
def rules(self) -> List[Tuple[int, int]]: def rules(self) -> List[Tuple[int, int]]:
return settings.RATE_LIMITING_RULES["password_reset_form_by_email"] return settings.RATE_LIMITING_RULES["password_reset_form_by_email"]
@ -473,6 +478,7 @@ class CreateUserForm(forms.Form):
class OurAuthenticationForm(AuthenticationForm): class OurAuthenticationForm(AuthenticationForm):
logger = logging.getLogger("zulip.auth.OurAuthenticationForm") logger = logging.getLogger("zulip.auth.OurAuthenticationForm")
@override
def clean(self) -> Dict[str, Any]: def clean(self) -> Dict[str, Any]:
username = self.cleaned_data.get("username") username = self.cleaned_data.get("username")
password = self.cleaned_data.get("password") password = self.cleaned_data.get("password")
@ -540,6 +546,7 @@ class OurAuthenticationForm(AuthenticationForm):
return self.cleaned_data return self.cleaned_data
@override
def add_prefix(self, field_name: str) -> str: def add_prefix(self, field_name: str) -> str:
"""Disable prefix, since Zulip doesn't use this Django forms feature """Disable prefix, since Zulip doesn't use this Django forms feature
(and django-two-factor does use it), and we'd like both to be (and django-two-factor does use it), and we'd like both to be
@ -561,6 +568,7 @@ class AuthenticationTokenForm(TwoFactorAuthenticationTokenForm):
class MultiEmailField(forms.Field): class MultiEmailField(forms.Field):
@override
def to_python(self, emails: Optional[str]) -> List[str]: def to_python(self, emails: Optional[str]) -> List[str]:
"""Normalize data to a list of strings.""" """Normalize data to a list of strings."""
if not emails: if not emails:
@ -568,6 +576,7 @@ class MultiEmailField(forms.Field):
return [email.strip() for email in emails.split(",")] return [email.strip() for email in emails.split(",")]
@override
def validate(self, emails: List[str]) -> None: def validate(self, emails: List[str]) -> None:
"""Check if value consists only of valid emails.""" """Check if value consists only of valid emails."""
super().validate(emails) super().validate(emails)

View File

@ -1,5 +1,7 @@
import asyncio import asyncio
from typing_extensions import override
class NoAutoCreateEventLoopPolicy(asyncio.DefaultEventLoopPolicy): class NoAutoCreateEventLoopPolicy(asyncio.DefaultEventLoopPolicy):
""" """
@ -12,5 +14,6 @@ class NoAutoCreateEventLoopPolicy(asyncio.DefaultEventLoopPolicy):
accident. accident.
""" """
@override
def get_event_loop(self) -> asyncio.AbstractEventLoop: # nocoverage def get_event_loop(self) -> asyncio.AbstractEventLoop: # nocoverage
return asyncio.get_running_loop() return asyncio.get_running_loop()

View File

@ -3,7 +3,7 @@ from typing import Any, Callable, Dict, Iterable, List, Mapping, Sequence, TypeV
from psycopg2.extensions import connection, cursor from psycopg2.extensions import connection, cursor
from psycopg2.sql import Composable from psycopg2.sql import Composable
from typing_extensions import TypeAlias from typing_extensions import TypeAlias, override
CursorObj = TypeVar("CursorObj", bound=cursor) CursorObj = TypeVar("CursorObj", bound=cursor)
Query: TypeAlias = Union[str, bytes, Composable] Query: TypeAlias = Union[str, bytes, Composable]
@ -32,9 +32,11 @@ def wrapper_execute(
class TimeTrackingCursor(cursor): class TimeTrackingCursor(cursor):
"""A psycopg2 cursor class that tracks the time spent executing queries.""" """A psycopg2 cursor class that tracks the time spent executing queries."""
@override
def execute(self, query: Query, vars: Params = None) -> None: def execute(self, query: Query, vars: Params = None) -> None:
wrapper_execute(self, super().execute, query, vars) wrapper_execute(self, super().execute, query, vars)
@override
def executemany(self, query: Query, vars: Iterable[Params]) -> None: # nocoverage def executemany(self, query: Query, vars: Iterable[Params]) -> None: # nocoverage
wrapper_execute(self, super().executemany, query, vars) wrapper_execute(self, super().executemany, query, vars)

View File

@ -6,6 +6,7 @@ from email.message import EmailMessage
from typing import Dict, List, Match, Optional, Tuple from typing import Dict, List, Match, Optional, Tuple
from django.conf import settings from django.conf import settings
from typing_extensions import override
from zerver.actions.message_send import ( from zerver.actions.message_send import (
check_send_message, check_send_message,
@ -527,9 +528,11 @@ class RateLimitedRealmMirror(RateLimitedObject):
self.realm = realm self.realm = realm
super().__init__() super().__init__()
@override
def key(self) -> str: def key(self) -> str:
return f"{type(self).__name__}:{self.realm.string_id}" return f"{type(self).__name__}:{self.realm.string_id}"
@override
def rules(self) -> List[Tuple[int, int]]: def rules(self) -> List[Tuple[int, int]]:
return settings.RATE_LIMITING_MIRROR_REALM_RULES return settings.RATE_LIMITING_MIRROR_REALM_RULES

View File

@ -4,6 +4,7 @@ from typing import Any, Dict, List, Optional, Tuple, Union
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from django_stubs_ext import StrPromise from django_stubs_ext import StrPromise
from typing_extensions import override
class ErrorCode(Enum): class ErrorCode(Enum):
@ -127,6 +128,7 @@ class JsonableError(Exception):
def data(self) -> Dict[str, Any]: def data(self) -> Dict[str, Any]:
return dict(((f, getattr(self, f)) for f in self.data_fields), code=self.code.name) return dict(((f, getattr(self, f)) for f in self.data_fields), code=self.code.name)
@override
def __str__(self) -> str: def __str__(self) -> str:
return self.msg return self.msg
@ -147,6 +149,7 @@ class UnauthorizedError(JsonableError):
raise AssertionError("Invalid www_authenticate value!") raise AssertionError("Invalid www_authenticate value!")
@property @property
@override
def extra_headers(self) -> Dict[str, Any]: def extra_headers(self) -> Dict[str, Any]:
extra_headers_dict = super().extra_headers extra_headers_dict = super().extra_headers
extra_headers_dict["WWW-Authenticate"] = self.www_authenticate extra_headers_dict["WWW-Authenticate"] = self.www_authenticate
@ -161,6 +164,7 @@ class StreamDoesNotExistError(JsonableError):
self.stream = stream self.stream = stream
@staticmethod @staticmethod
@override
def msg_format() -> str: def msg_format() -> str:
return _("Stream '{stream}' does not exist") return _("Stream '{stream}' does not exist")
@ -173,6 +177,7 @@ class StreamWithIDDoesNotExistError(JsonableError):
self.stream_id = stream_id self.stream_id = stream_id
@staticmethod @staticmethod
@override
def msg_format() -> str: def msg_format() -> str:
return _("Stream with ID '{stream_id}' does not exist") return _("Stream with ID '{stream_id}' does not exist")
@ -186,6 +191,7 @@ class CannotDeactivateLastUserError(JsonableError):
self.entity = _("organization owner") if is_last_owner else _("user") self.entity = _("organization owner") if is_last_owner else _("user")
@staticmethod @staticmethod
@override
def msg_format() -> str: def msg_format() -> str:
return _("Cannot deactivate the only {entity}.") return _("Cannot deactivate the only {entity}.")
@ -198,6 +204,7 @@ class InvalidMarkdownIncludeStatementError(JsonableError):
self.include_statement = include_statement self.include_statement = include_statement
@staticmethod @staticmethod
@override
def msg_format() -> str: def msg_format() -> str:
return _("Invalid Markdown include statement: {include_statement}") return _("Invalid Markdown include statement: {include_statement}")
@ -210,10 +217,12 @@ class RateLimitedError(JsonableError):
self.secs_to_freedom = secs_to_freedom self.secs_to_freedom = secs_to_freedom
@staticmethod @staticmethod
@override
def msg_format() -> str: def msg_format() -> str:
return _("API usage exceeded rate limit") return _("API usage exceeded rate limit")
@property @property
@override
def extra_headers(self) -> Dict[str, Any]: def extra_headers(self) -> Dict[str, Any]:
extra_headers_dict = super().extra_headers extra_headers_dict = super().extra_headers
if self.secs_to_freedom is not None: if self.secs_to_freedom is not None:
@ -222,6 +231,7 @@ class RateLimitedError(JsonableError):
return extra_headers_dict return extra_headers_dict
@property @property
@override
def data(self) -> Dict[str, Any]: def data(self) -> Dict[str, Any]:
data_dict = super().data data_dict = super().data
data_dict["retry-after"] = self.secs_to_freedom data_dict["retry-after"] = self.secs_to_freedom
@ -233,6 +243,7 @@ class InvalidJSONError(JsonableError):
code = ErrorCode.INVALID_JSON code = ErrorCode.INVALID_JSON
@staticmethod @staticmethod
@override
def msg_format() -> str: def msg_format() -> str:
return _("Malformed JSON") return _("Malformed JSON")
@ -244,6 +255,7 @@ class OrganizationMemberRequiredError(JsonableError):
pass pass
@staticmethod @staticmethod
@override
def msg_format() -> str: def msg_format() -> str:
return _("Must be an organization member") return _("Must be an organization member")
@ -255,6 +267,7 @@ class OrganizationAdministratorRequiredError(JsonableError):
pass pass
@staticmethod @staticmethod
@override
def msg_format() -> str: def msg_format() -> str:
return _("Must be an organization administrator") return _("Must be an organization administrator")
@ -266,6 +279,7 @@ class OrganizationOwnerRequiredError(JsonableError):
pass pass
@staticmethod @staticmethod
@override
def msg_format() -> str: def msg_format() -> str:
return _("Must be an organization owner") return _("Must be an organization owner")
@ -279,6 +293,7 @@ class AuthenticationFailedError(JsonableError):
pass pass
@staticmethod @staticmethod
@override
def msg_format() -> str: def msg_format() -> str:
return _("Your username or password is incorrect") return _("Your username or password is incorrect")
@ -287,6 +302,7 @@ class UserDeactivatedError(AuthenticationFailedError):
code: ErrorCode = ErrorCode.USER_DEACTIVATED code: ErrorCode = ErrorCode.USER_DEACTIVATED
@staticmethod @staticmethod
@override
def msg_format() -> str: def msg_format() -> str:
return _("Account is deactivated") return _("Account is deactivated")
@ -295,6 +311,7 @@ class RealmDeactivatedError(AuthenticationFailedError):
code: ErrorCode = ErrorCode.REALM_DEACTIVATED code: ErrorCode = ErrorCode.REALM_DEACTIVATED
@staticmethod @staticmethod
@override
def msg_format() -> str: def msg_format() -> str:
return _("This organization has been deactivated") return _("This organization has been deactivated")
@ -303,6 +320,7 @@ class RemoteServerDeactivatedError(AuthenticationFailedError):
code: ErrorCode = ErrorCode.REALM_DEACTIVATED code: ErrorCode = ErrorCode.REALM_DEACTIVATED
@staticmethod @staticmethod
@override
def msg_format() -> str: def msg_format() -> str:
return _( return _(
"The mobile push notification service registration for your server has been deactivated" "The mobile push notification service registration for your server has been deactivated"
@ -313,6 +331,7 @@ class PasswordAuthDisabledError(AuthenticationFailedError):
code: ErrorCode = ErrorCode.PASSWORD_AUTH_DISABLED code: ErrorCode = ErrorCode.PASSWORD_AUTH_DISABLED
@staticmethod @staticmethod
@override
def msg_format() -> str: def msg_format() -> str:
return _("Password authentication is disabled in this organization") return _("Password authentication is disabled in this organization")
@ -321,6 +340,7 @@ class PasswordResetRequiredError(AuthenticationFailedError):
code: ErrorCode = ErrorCode.PASSWORD_RESET_REQUIRED code: ErrorCode = ErrorCode.PASSWORD_RESET_REQUIRED
@staticmethod @staticmethod
@override
def msg_format() -> str: def msg_format() -> str:
return _("Your password has been disabled and needs to be reset") return _("Your password has been disabled and needs to be reset")
@ -337,12 +357,14 @@ class InvalidAPIKeyError(JsonableError):
pass pass
@staticmethod @staticmethod
@override
def msg_format() -> str: def msg_format() -> str:
return _("Invalid API key") return _("Invalid API key")
class InvalidAPIKeyFormatError(InvalidAPIKeyError): class InvalidAPIKeyFormatError(InvalidAPIKeyError):
@staticmethod @staticmethod
@override
def msg_format() -> str: def msg_format() -> str:
return _("Malformed API key") return _("Malformed API key")
@ -381,6 +403,7 @@ class UnsupportedWebhookEventTypeError(WebhookError):
self.event_type = event_type self.event_type = event_type
@staticmethod @staticmethod
@override
def msg_format() -> str: def msg_format() -> str:
return _( return _(
"The '{event_type}' event isn't currently supported by the {webhook_name} webhook; ignoring" "The '{event_type}' event isn't currently supported by the {webhook_name} webhook; ignoring"
@ -401,6 +424,7 @@ class AnomalousWebhookPayloadError(WebhookError):
code = ErrorCode.ANOMALOUS_WEBHOOK_PAYLOAD code = ErrorCode.ANOMALOUS_WEBHOOK_PAYLOAD
@staticmethod @staticmethod
@override
def msg_format() -> str: def msg_format() -> str:
return _("Unable to parse request: Did {webhook_name} generate this event?") return _("Unable to parse request: Did {webhook_name} generate this event?")
@ -424,6 +448,7 @@ class InvalidSubdomainError(JsonableError):
pass pass
@staticmethod @staticmethod
@override
def msg_format() -> str: def msg_format() -> str:
return _("Invalid subdomain") return _("Invalid subdomain")
@ -464,6 +489,7 @@ class AccessDeniedError(JsonableError):
pass pass
@staticmethod @staticmethod
@override
def msg_format() -> str: def msg_format() -> str:
return _("Access denied") return _("Access denied")
@ -502,6 +528,7 @@ class MessageMoveError(JsonableError):
self.total_messages_allowed_to_move = total_messages_allowed_to_move self.total_messages_allowed_to_move = total_messages_allowed_to_move
@staticmethod @staticmethod
@override
def msg_format() -> str: def msg_format() -> str:
return _( return _(
"You only have permission to move the {total_messages_allowed_to_move}/{total_messages_in_topic} most recent messages in this topic." "You only have permission to move the {total_messages_allowed_to_move}/{total_messages_in_topic} most recent messages in this topic."
@ -515,6 +542,7 @@ class ReactionExistsError(JsonableError):
pass pass
@staticmethod @staticmethod
@override
def msg_format() -> str: def msg_format() -> str:
return _("Reaction already exists.") return _("Reaction already exists.")
@ -526,6 +554,7 @@ class ReactionDoesNotExistError(JsonableError):
pass pass
@staticmethod @staticmethod
@override
def msg_format() -> str: def msg_format() -> str:
return _("Reaction doesn't exist.") return _("Reaction doesn't exist.")

View File

@ -13,6 +13,7 @@ from django.conf import settings
from django.core.cache import cache from django.core.cache import cache
from django.http import HttpRequest from django.http import HttpRequest
from django.utils.timezone import now as timezone_now from django.utils.timezone import now as timezone_now
from typing_extensions import override
class _RateLimitFilter: class _RateLimitFilter:
@ -108,11 +109,13 @@ class EmailLimiter(_RateLimitFilter):
class ReturnTrue(logging.Filter): class ReturnTrue(logging.Filter):
@override
def filter(self, record: logging.LogRecord) -> bool: def filter(self, record: logging.LogRecord) -> bool:
return True return True
class RequireReallyDeployed(logging.Filter): class RequireReallyDeployed(logging.Filter):
@override
def filter(self, record: logging.LogRecord) -> bool: def filter(self, record: logging.LogRecord) -> bool:
return settings.PRODUCTION return settings.PRODUCTION
@ -193,6 +196,7 @@ class ZulipFormatter(logging.Formatter):
pieces.extend(["[%(zulip_origin)s]", "%(message)s"]) pieces.extend(["[%(zulip_origin)s]", "%(message)s"])
return " ".join(pieces) return " ".join(pieces)
@override
def format(self, record: logging.LogRecord) -> str: def format(self, record: logging.LogRecord) -> str:
if not hasattr(record, "zulip_decorated"): if not hasattr(record, "zulip_decorated"):
record.zulip_level_abbrev = abbrev_log_levelname(record.levelname) record.zulip_level_abbrev = abbrev_log_levelname(record.levelname)
@ -202,6 +206,7 @@ class ZulipFormatter(logging.Formatter):
class ZulipWebhookFormatter(ZulipFormatter): class ZulipWebhookFormatter(ZulipFormatter):
@override
def _compute_fmt(self) -> str: def _compute_fmt(self) -> str:
basic = super()._compute_fmt() basic = super()._compute_fmt()
multiline = [ multiline = [
@ -217,6 +222,7 @@ class ZulipWebhookFormatter(ZulipFormatter):
] ]
return "\n".join(multiline) return "\n".join(multiline)
@override
def format(self, record: logging.LogRecord) -> str: def format(self, record: logging.LogRecord) -> str:
request: Optional[HttpRequest] = getattr(record, "request", None) request: Optional[HttpRequest] = getattr(record, "request", None)
if request is None: if request is None:

View File

@ -10,6 +10,7 @@ from django.core import validators
from django.core.exceptions import MultipleObjectsReturned, ValidationError from django.core.exceptions import MultipleObjectsReturned, ValidationError
from django.core.management.base import BaseCommand, CommandError, CommandParser from django.core.management.base import BaseCommand, CommandError, CommandParser
from django.db.models import Q, QuerySet from django.db.models import Q, QuerySet
from typing_extensions import override
from zerver.lib.initial_password import initial_password from zerver.lib.initial_password import initial_password
from zerver.models import Client, Realm, UserProfile, get_client from zerver.models import Client, Realm, UserProfile, get_client
@ -45,6 +46,7 @@ class CreateUserParameters:
class ZulipBaseCommand(BaseCommand): class ZulipBaseCommand(BaseCommand):
# Fix support for multi-line usage # Fix support for multi-line usage
@override
def create_parser(self, prog_name: str, subcommand: str, **kwargs: Any) -> CommandParser: def create_parser(self, prog_name: str, subcommand: str, **kwargs: Any) -> CommandParser:
parser = super().create_parser(prog_name, subcommand, **kwargs) parser = super().create_parser(prog_name, subcommand, **kwargs)
parser.formatter_class = RawTextHelpFormatter parser.formatter_class = RawTextHelpFormatter

View File

@ -50,7 +50,7 @@ from markdown.blockparser import BlockParser
from markdown.extensions import codehilite, nl2br, sane_lists, tables from markdown.extensions import codehilite, nl2br, sane_lists, tables
from soupsieve import escape as css_escape from soupsieve import escape as css_escape
from tlds import tld_set from tlds import tld_set
from typing_extensions import TypeAlias from typing_extensions import TypeAlias, override
from zerver.lib import mention from zerver.lib import mention
from zerver.lib.cache import cache_with_key from zerver.lib.cache import cache_with_key
@ -524,6 +524,7 @@ class InlineImageProcessor(markdown.treeprocessors.Treeprocessor):
super().__init__(zmd) super().__init__(zmd)
self.zmd = zmd self.zmd = zmd
@override
def run(self, root: Element) -> None: def run(self, root: Element) -> None:
# Get all URLs from the blob # Get all URLs from the blob
found_imgs = walk_tree(root, lambda e: e if e.tag == "img" else None) found_imgs = walk_tree(root, lambda e: e if e.tag == "img" else None)
@ -553,6 +554,7 @@ class InlineVideoProcessor(markdown.treeprocessors.Treeprocessor):
super().__init__(zmd) super().__init__(zmd)
self.zmd = zmd self.zmd = zmd
@override
def run(self, root: Element) -> None: def run(self, root: Element) -> None:
# Get all URLs from the blob # Get all URLs from the blob
found_videos = walk_tree(root, lambda e: e if e.tag == "video" else None) found_videos = walk_tree(root, lambda e: e if e.tag == "video" else None)
@ -573,6 +575,7 @@ class InlineVideoProcessor(markdown.treeprocessors.Treeprocessor):
class BacktickInlineProcessor(markdown.inlinepatterns.BacktickInlineProcessor): class BacktickInlineProcessor(markdown.inlinepatterns.BacktickInlineProcessor):
"""Return a `<code>` element containing the matching text.""" """Return a `<code>` element containing the matching text."""
@override
def handleMatch( # type: ignore[override] # https://github.com/python/mypy/issues/10197 def handleMatch( # type: ignore[override] # https://github.com/python/mypy/issues/10197
self, m: Match[str], data: str self, m: Match[str], data: str
) -> Union[Tuple[None, None, None], Tuple[Element, int, int]]: ) -> Union[Tuple[None, None, None], Tuple[Element, int, int]]:
@ -1234,6 +1237,7 @@ class InlineInterestingLinkProcessor(markdown.treeprocessors.Treeprocessor):
if info["remove"] is not None: if info["remove"] is not None:
info["parent"].remove(info["remove"]) info["parent"].remove(info["remove"])
@override
def run(self, root: Element) -> None: def run(self, root: Element) -> None:
# Get all URLs from the blob # Get all URLs from the blob
found_urls = walk_tree_with_family(root, self.get_url_data) found_urls = walk_tree_with_family(root, self.get_url_data)
@ -1391,6 +1395,7 @@ class CompiledInlineProcessor(markdown.inlinepatterns.InlineProcessor):
class Timestamp(markdown.inlinepatterns.Pattern): class Timestamp(markdown.inlinepatterns.Pattern):
@override
def handleMatch(self, match: Match[str]) -> Optional[Element]: def handleMatch(self, match: Match[str]) -> Optional[Element]:
time_input_string = match.group("time") time_input_string = match.group("time")
try: try:
@ -1475,6 +1480,7 @@ class EmoticonTranslation(markdown.inlinepatterns.Pattern):
super().__init__(pattern, zmd) super().__init__(pattern, zmd)
self.zmd = zmd self.zmd = zmd
@override
def handleMatch(self, match: Match[str]) -> Optional[Element]: def handleMatch(self, match: Match[str]) -> Optional[Element]:
db_data: Optional[DbData] = self.zmd.zulip_db_data db_data: Optional[DbData] = self.zmd.zulip_db_data
if db_data is None or not db_data.translate_emoticons: if db_data is None or not db_data.translate_emoticons:
@ -1490,6 +1496,7 @@ TEXT_PRESENTATION_RE = regex.compile(r"\P{Emoji_Presentation}\u20E3?")
class UnicodeEmoji(CompiledInlineProcessor): class UnicodeEmoji(CompiledInlineProcessor):
@override
def handleMatch( # type: ignore[override] # https://github.com/python/mypy/issues/10197 def handleMatch( # type: ignore[override] # https://github.com/python/mypy/issues/10197
self, match: Match[str], data: str self, match: Match[str], data: str
) -> Union[Tuple[None, None, None], Tuple[Element, int, int]]: ) -> Union[Tuple[None, None, None], Tuple[Element, int, int]]:
@ -1515,6 +1522,7 @@ class Emoji(markdown.inlinepatterns.Pattern):
super().__init__(pattern, zmd) super().__init__(pattern, zmd)
self.zmd = zmd self.zmd = zmd
@override
def handleMatch(self, match: Match[str]) -> Optional[Union[str, Element]]: def handleMatch(self, match: Match[str]) -> Optional[Union[str, Element]]:
orig_syntax = match.group("syntax") orig_syntax = match.group("syntax")
name = orig_syntax[1:-1] name = orig_syntax[1:-1]
@ -1543,6 +1551,7 @@ def content_has_emoji_syntax(content: str) -> bool:
class Tex(markdown.inlinepatterns.Pattern): class Tex(markdown.inlinepatterns.Pattern):
@override
def handleMatch(self, match: Match[str]) -> Union[str, Element]: def handleMatch(self, match: Match[str]) -> Union[str, Element]:
rendered = render_tex(match.group("body"), is_inline=True) rendered = render_tex(match.group("body"), is_inline=True)
if rendered is not None: if rendered is not None:
@ -1633,6 +1642,7 @@ class CompiledPattern(markdown.inlinepatterns.Pattern):
class AutoLink(CompiledPattern): class AutoLink(CompiledPattern):
@override
def handleMatch(self, match: Match[str]) -> ElementStringNone: def handleMatch(self, match: Match[str]) -> ElementStringNone:
url = match.group("url") url = match.group("url")
db_data: Optional[DbData] = self.zmd.zulip_db_data db_data: Optional[DbData] = self.zmd.zulip_db_data
@ -1691,6 +1701,7 @@ class BlockQuoteProcessor(markdown.blockprocessors.BlockQuoteProcessor):
RE = re.compile(r"(^|\n)(?!(?:[ ]{0,3}>\s*(?:$|\n))*(?:$|\n))[ ]{0,3}>[ ]?(.*)") RE = re.compile(r"(^|\n)(?!(?:[ ]{0,3}>\s*(?:$|\n))*(?:$|\n))[ ]{0,3}>[ ]?(.*)")
# run() is very slightly forked from the base class; see notes below. # run() is very slightly forked from the base class; see notes below.
@override
def run(self, parent: Element, blocks: List[str]) -> None: def run(self, parent: Element, blocks: List[str]) -> None:
block = blocks.pop(0) block = blocks.pop(0)
m = self.RE.search(block) m = self.RE.search(block)
@ -1716,6 +1727,7 @@ class BlockQuoteProcessor(markdown.blockprocessors.BlockQuoteProcessor):
self.parser.parseChunk(quote, block) self.parser.parseChunk(quote, block)
self.parser.state.reset() self.parser.state.reset()
@override
def clean(self, line: str) -> str: def clean(self, line: str) -> str:
# Silence all the mentions inside blockquotes # Silence all the mentions inside blockquotes
line = mention.MENTIONS_RE.sub(lambda m: "@_**{}**".format(m.group("match")), line) line = mention.MENTIONS_RE.sub(lambda m: "@_**{}**".format(m.group("match")), line)
@ -1742,6 +1754,7 @@ class MarkdownListPreprocessor(markdown.preprocessors.Preprocessor):
LI_RE = re.compile(r"^[ ]*([*+-]|\d\.)[ ]+(.*)", re.MULTILINE) LI_RE = re.compile(r"^[ ]*([*+-]|\d\.)[ ]+(.*)", re.MULTILINE)
@override
def run(self, lines: List[str]) -> List[str]: def run(self, lines: List[str]) -> List[str]:
"""Insert a newline between a paragraph and ulist if missing""" """Insert a newline between a paragraph and ulist if missing"""
inserts = 0 inserts = 0
@ -1823,6 +1836,7 @@ class LinkifierPattern(CompiledInlineProcessor):
super().__init__(compiled_re2, zmd) super().__init__(compiled_re2, zmd)
@override
def handleMatch( # type: ignore[override] # https://github.com/python/mypy/issues/10197 def handleMatch( # type: ignore[override] # https://github.com/python/mypy/issues/10197
self, m: Match[str], data: str self, m: Match[str], data: str
) -> Union[Tuple[Element, int, int], Tuple[None, None, None]]: ) -> Union[Tuple[Element, int, int], Tuple[None, None, None]]:
@ -1843,6 +1857,7 @@ class LinkifierPattern(CompiledInlineProcessor):
class UserMentionPattern(CompiledInlineProcessor): class UserMentionPattern(CompiledInlineProcessor):
@override
def handleMatch( # type: ignore[override] # https://github.com/python/mypy/issues/10197 def handleMatch( # type: ignore[override] # https://github.com/python/mypy/issues/10197
self, m: Match[str], data: str self, m: Match[str], data: str
) -> Union[Tuple[None, None, None], Tuple[Element, int, int]]: ) -> Union[Tuple[None, None, None], Tuple[Element, int, int]]:
@ -1906,6 +1921,7 @@ class UserMentionPattern(CompiledInlineProcessor):
class UserGroupMentionPattern(CompiledInlineProcessor): class UserGroupMentionPattern(CompiledInlineProcessor):
@override
def handleMatch( # type: ignore[override] # https://github.com/python/mypy/issues/10197 def handleMatch( # type: ignore[override] # https://github.com/python/mypy/issues/10197
self, m: Match[str], data: str self, m: Match[str], data: str
) -> Union[Tuple[None, None, None], Tuple[Element, int, int]]: ) -> Union[Tuple[None, None, None], Tuple[Element, int, int]]:
@ -1946,6 +1962,7 @@ class StreamPattern(CompiledInlineProcessor):
stream_id = db_data.stream_names.get(name) stream_id = db_data.stream_names.get(name)
return stream_id return stream_id
@override
def handleMatch( # type: ignore[override] # https://github.com/python/mypy/issues/10197 def handleMatch( # type: ignore[override] # https://github.com/python/mypy/issues/10197
self, m: Match[str], data: str self, m: Match[str], data: str
) -> Union[Tuple[None, None, None], Tuple[Element, int, int]]: ) -> Union[Tuple[None, None, None], Tuple[Element, int, int]]:
@ -1977,6 +1994,7 @@ class StreamTopicPattern(CompiledInlineProcessor):
stream_id = db_data.stream_names.get(name) stream_id = db_data.stream_names.get(name)
return stream_id return stream_id
@override
def handleMatch( # type: ignore[override] # https://github.com/python/mypy/issues/10197 def handleMatch( # type: ignore[override] # https://github.com/python/mypy/issues/10197
self, m: Match[str], data: str self, m: Match[str], data: str
) -> Union[Tuple[None, None, None], Tuple[Element, int, int]]: ) -> Union[Tuple[None, None, None], Tuple[Element, int, int]]:
@ -2041,6 +2059,7 @@ class AlertWordNotificationProcessor(markdown.preprocessors.Preprocessor):
return True return True
return False return False
@override
def run(self, lines: List[str]) -> List[str]: def run(self, lines: List[str]) -> List[str]:
db_data: Optional[DbData] = self.zmd.zulip_db_data db_data: Optional[DbData] = self.zmd.zulip_db_data
if db_data is not None: if db_data is not None:
@ -2096,6 +2115,7 @@ class LinkInlineProcessor(markdown.inlinepatterns.LinkInlineProcessor):
return el return el
@override
def handleMatch( # type: ignore[override] # https://github.com/python/mypy/issues/10197 def handleMatch( # type: ignore[override] # https://github.com/python/mypy/issues/10197
self, m: Match[str], data: str self, m: Match[str], data: str
) -> Union[Tuple[None, None, None], Tuple[Element, int, int]]: ) -> Union[Tuple[None, None, None], Tuple[Element, int, int]]:
@ -2156,6 +2176,7 @@ class ZulipMarkdown(markdown.Markdown):
) )
self.set_output_format("html") self.set_output_format("html")
@override
def build_parser(self) -> markdown.Markdown: def build_parser(self) -> markdown.Markdown:
# Build the parser using selected default features from Python-Markdown. # Build the parser using selected default features from Python-Markdown.
# The complete list of all available processors can be found in the # The complete list of all available processors can be found in the

View File

@ -6,6 +6,7 @@ import markdown
from django.utils.html import escape as escape_html from django.utils.html import escape as escape_html
from markdown.extensions import Extension from markdown.extensions import Extension
from markdown.preprocessors import Preprocessor from markdown.preprocessors import Preprocessor
from typing_extensions import override
from zerver.lib.markdown.priorities import PREPROCESSOR_PRIORITES from zerver.lib.markdown.priorities import PREPROCESSOR_PRIORITES
from zerver.openapi.openapi import ( from zerver.openapi.openapi import (
@ -49,6 +50,7 @@ OBJECT_CODE_TEMPLATE = "<code>{value}</code>".strip()
class MarkdownArgumentsTableGenerator(Extension): class MarkdownArgumentsTableGenerator(Extension):
@override
def extendMarkdown(self, md: markdown.Markdown) -> None: def extendMarkdown(self, md: markdown.Markdown) -> None:
md.preprocessors.register( md.preprocessors.register(
APIArgumentsTablePreprocessor(md, self.getConfigs()), APIArgumentsTablePreprocessor(md, self.getConfigs()),
@ -61,6 +63,7 @@ class APIArgumentsTablePreprocessor(Preprocessor):
def __init__(self, md: markdown.Markdown, config: Mapping[str, Any]) -> None: def __init__(self, md: markdown.Markdown, config: Mapping[str, Any]) -> None:
super().__init__(md) super().__init__(md)
@override
def run(self, lines: List[str]) -> List[str]: def run(self, lines: List[str]) -> List[str]:
done = False done = False
while not done: while not done:

View File

@ -6,6 +6,7 @@ from typing import Any, Dict, List, Mapping, Optional
import markdown import markdown
from markdown.extensions import Extension from markdown.extensions import Extension
from markdown.preprocessors import Preprocessor from markdown.preprocessors import Preprocessor
from typing_extensions import override
from zerver.lib.markdown.priorities import PREPROCESSOR_PRIORITES from zerver.lib.markdown.priorities import PREPROCESSOR_PRIORITES
from zerver.openapi.openapi import check_deprecated_consistency, get_openapi_return_values from zerver.openapi.openapi import check_deprecated_consistency, get_openapi_return_values
@ -16,6 +17,7 @@ REGEXP = re.compile(r"\{generate_return_values_table\|\s*(.+?)\s*\|\s*(.+)\s*\}"
class MarkdownReturnValuesTableGenerator(Extension): class MarkdownReturnValuesTableGenerator(Extension):
@override
def extendMarkdown(self, md: markdown.Markdown) -> None: def extendMarkdown(self, md: markdown.Markdown) -> None:
md.preprocessors.register( md.preprocessors.register(
APIReturnValuesTablePreprocessor(md, self.getConfigs()), APIReturnValuesTablePreprocessor(md, self.getConfigs()),
@ -28,6 +30,7 @@ class APIReturnValuesTablePreprocessor(Preprocessor):
def __init__(self, md: markdown.Markdown, config: Mapping[str, Any]) -> None: def __init__(self, md: markdown.Markdown, config: Mapping[str, Any]) -> None:
super().__init__(md) super().__init__(md)
@override
def run(self, lines: List[str]) -> List[str]: def run(self, lines: List[str]) -> List[str]:
done = False done = False
while not done: while not done:

View File

@ -86,6 +86,7 @@ from markdown.extensions.codehilite import CodeHiliteExtension, parse_hl_lines
from markdown.preprocessors import Preprocessor from markdown.preprocessors import Preprocessor
from pygments.lexers import find_lexer_class_by_name from pygments.lexers import find_lexer_class_by_name
from pygments.util import ClassNotFound from pygments.util import ClassNotFound
from typing_extensions import override
from zerver.lib.exceptions import MarkdownRenderingError from zerver.lib.exceptions import MarkdownRenderingError
from zerver.lib.markdown.priorities import PREPROCESSOR_PRIORITES from zerver.lib.markdown.priorities import PREPROCESSOR_PRIORITES
@ -156,6 +157,7 @@ class FencedCodeExtension(Extension):
for key, value in config.items(): for key, value in config.items():
self.setConfig(key, value) self.setConfig(key, value)
@override
def extendMarkdown(self, md: Markdown) -> None: def extendMarkdown(self, md: Markdown) -> None:
"""Add FencedBlockPreprocessor to the Markdown instance.""" """Add FencedBlockPreprocessor to the Markdown instance."""
md.registerExtension(self) md.registerExtension(self)
@ -268,6 +270,7 @@ class OuterHandler(ZulipBaseHandler):
self.default_language = default_language self.default_language = default_language
super().__init__(processor, output) super().__init__(processor, output)
@override
def handle_line(self, line: str) -> None: def handle_line(self, line: str) -> None:
check_for_new_fence( check_for_new_fence(
self.processor, self.output, line, self.run_content_validators, self.default_language self.processor, self.output, line, self.run_content_validators, self.default_language
@ -287,6 +290,7 @@ class CodeHandler(ZulipBaseHandler):
self.run_content_validators = run_content_validators self.run_content_validators = run_content_validators
super().__init__(processor, output, fence) super().__init__(processor, output, fence)
@override
def done(self) -> None: def done(self) -> None:
# run content validators (if any) # run content validators (if any)
if self.run_content_validators: if self.run_content_validators:
@ -294,6 +298,7 @@ class CodeHandler(ZulipBaseHandler):
validator(self.lines) validator(self.lines)
super().done() super().done()
@override
def format_text(self, text: str) -> str: def format_text(self, text: str) -> str:
return self.processor.format_code(self.lang, text) return self.processor.format_code(self.lang, text)
@ -309,6 +314,7 @@ class QuoteHandler(ZulipBaseHandler):
self.default_language = default_language self.default_language = default_language
super().__init__(processor, output, fence, process_contents=True) super().__init__(processor, output, fence, process_contents=True)
@override
def handle_line(self, line: str) -> None: def handle_line(self, line: str) -> None:
if line.rstrip() == self.fence: if line.rstrip() == self.fence:
self.done() self.done()
@ -317,6 +323,7 @@ class QuoteHandler(ZulipBaseHandler):
self.processor, self.lines, line, default_language=self.default_language self.processor, self.lines, line, default_language=self.default_language
) )
@override
def format_text(self, text: str) -> str: def format_text(self, text: str) -> str:
return self.processor.format_quote(text) return self.processor.format_quote(text)
@ -332,17 +339,20 @@ class SpoilerHandler(ZulipBaseHandler):
self.spoiler_header = spoiler_header self.spoiler_header = spoiler_header
super().__init__(processor, output, fence, process_contents=True) super().__init__(processor, output, fence, process_contents=True)
@override
def handle_line(self, line: str) -> None: def handle_line(self, line: str) -> None:
if line.rstrip() == self.fence: if line.rstrip() == self.fence:
self.done() self.done()
else: else:
check_for_new_fence(self.processor, self.lines, line) check_for_new_fence(self.processor, self.lines, line)
@override
def format_text(self, text: str) -> str: def format_text(self, text: str) -> str:
return self.processor.format_spoiler(self.spoiler_header, text) return self.processor.format_spoiler(self.spoiler_header, text)
class TexHandler(ZulipBaseHandler): class TexHandler(ZulipBaseHandler):
@override
def format_text(self, text: str) -> str: def format_text(self, text: str) -> str:
return self.processor.format_tex(text) return self.processor.format_tex(text)
@ -411,6 +421,7 @@ class FencedBlockPreprocessor(Preprocessor):
def pop(self) -> None: def pop(self) -> None:
self.handlers.pop() self.handlers.pop()
@override
def run(self, lines: Iterable[str]) -> List[str]: def run(self, lines: Iterable[str]) -> List[str]:
"""Match and store Fenced Code Blocks in the HtmlStash.""" """Match and store Fenced Code Blocks in the HtmlStash."""

View File

@ -4,6 +4,7 @@ from typing import Any, List, Match
from markdown import Markdown from markdown import Markdown
from markdown.extensions import Extension from markdown.extensions import Extension
from markdown.preprocessors import Preprocessor from markdown.preprocessors import Preprocessor
from typing_extensions import override
from zerver.lib.emoji import EMOTICON_CONVERSIONS, name_to_codepoint from zerver.lib.emoji import EMOTICON_CONVERSIONS, name_to_codepoint
from zerver.lib.markdown.priorities import PREPROCESSOR_PRIORITES from zerver.lib.markdown.priorities import PREPROCESSOR_PRIORITES
@ -38,6 +39,7 @@ ROW_HTML = """\
class EmoticonTranslationsHelpExtension(Extension): class EmoticonTranslationsHelpExtension(Extension):
@override
def extendMarkdown(self, md: Markdown) -> None: def extendMarkdown(self, md: Markdown) -> None:
"""Add SettingHelpExtension to the Markdown instance.""" """Add SettingHelpExtension to the Markdown instance."""
md.registerExtension(self) md.registerExtension(self)
@ -49,6 +51,7 @@ class EmoticonTranslationsHelpExtension(Extension):
class EmoticonTranslation(Preprocessor): class EmoticonTranslation(Preprocessor):
@override
def run(self, lines: List[str]) -> List[str]: def run(self, lines: List[str]) -> List[str]:
for loc, line in enumerate(lines): for loc, line in enumerate(lines):
match = REGEXP.search(line) match = REGEXP.search(line)

View File

@ -4,6 +4,7 @@ from typing import Any, List, Match
from markdown import Markdown from markdown import Markdown
from markdown.extensions import Extension from markdown.extensions import Extension
from markdown.preprocessors import Preprocessor from markdown.preprocessors import Preprocessor
from typing_extensions import override
from zerver.lib.markdown.priorities import PREPROCESSOR_PRIORITES from zerver.lib.markdown.priorities import PREPROCESSOR_PRIORITES
@ -133,6 +134,7 @@ LINK_TYPE_HANDLERS = {
class RelativeLinksHelpExtension(Extension): class RelativeLinksHelpExtension(Extension):
@override
def extendMarkdown(self, md: Markdown) -> None: def extendMarkdown(self, md: Markdown) -> None:
"""Add RelativeLinksHelpExtension to the Markdown instance.""" """Add RelativeLinksHelpExtension to the Markdown instance."""
md.registerExtension(self) md.registerExtension(self)
@ -150,6 +152,7 @@ def set_relative_help_links(value: bool) -> None:
class RelativeLinks(Preprocessor): class RelativeLinks(Preprocessor):
@override
def run(self, lines: List[str]) -> List[str]: def run(self, lines: List[str]) -> List[str]:
done = False done = False
while not done: while not done:

View File

@ -4,6 +4,7 @@ from typing import Any, List, Match
from markdown import Markdown from markdown import Markdown
from markdown.extensions import Extension from markdown.extensions import Extension
from markdown.preprocessors import Preprocessor from markdown.preprocessors import Preprocessor
from typing_extensions import override
from zerver.lib.markdown.priorities import PREPROCESSOR_PRIORITES from zerver.lib.markdown.priorities import PREPROCESSOR_PRIORITES
@ -131,6 +132,7 @@ def getMarkdown(setting_type_name: str, setting_name: str, setting_link: str) ->
class SettingHelpExtension(Extension): class SettingHelpExtension(Extension):
@override
def extendMarkdown(self, md: Markdown) -> None: def extendMarkdown(self, md: Markdown) -> None:
"""Add SettingHelpExtension to the Markdown instance.""" """Add SettingHelpExtension to the Markdown instance."""
md.registerExtension(self) md.registerExtension(self)
@ -146,6 +148,7 @@ def set_relative_settings_links(value: bool) -> None:
class Setting(Preprocessor): class Setting(Preprocessor):
@override
def run(self, lines: List[str]) -> List[str]: def run(self, lines: List[str]) -> List[str]:
done = False done = False
while not done: while not done:

View File

@ -6,6 +6,7 @@ from xml.etree.ElementTree import Element
from markdown import Extension, Markdown from markdown import Extension, Markdown
from markdown.blockparser import BlockParser from markdown.blockparser import BlockParser
from markdown.blockprocessors import BlockProcessor from markdown.blockprocessors import BlockProcessor
from typing_extensions import override
from zerver.lib.exceptions import InvalidMarkdownIncludeStatementError from zerver.lib.exceptions import InvalidMarkdownIncludeStatementError
from zerver.lib.markdown.priorities import BLOCK_PROCESSOR_PRIORITIES from zerver.lib.markdown.priorities import BLOCK_PROCESSOR_PRIORITIES
@ -16,6 +17,7 @@ class IncludeExtension(Extension):
super().__init__() super().__init__()
self.base_path = base_path self.base_path = base_path
@override
def extendMarkdown(self, md: Markdown) -> None: def extendMarkdown(self, md: Markdown) -> None:
md.parser.blockprocessors.register( md.parser.blockprocessors.register(
IncludeBlockProcessor(md.parser, self.base_path), IncludeBlockProcessor(md.parser, self.base_path),
@ -31,6 +33,7 @@ class IncludeBlockProcessor(BlockProcessor):
super().__init__(parser) super().__init__(parser)
self.base_path = base_path self.base_path = base_path
@override
def test(self, parent: Element, block: str) -> bool: def test(self, parent: Element, block: str) -> bool:
return bool(self.RE.search(block)) return bool(self.RE.search(block))
@ -46,6 +49,7 @@ class IncludeBlockProcessor(BlockProcessor):
return "\n".join(lines) return "\n".join(lines)
@override
def run(self, parent: Element, blocks: List[str]) -> None: def run(self, parent: Element, blocks: List[str]) -> None:
self.parser.state.set("include") self.parser.state.set("include")
self.parser.parseChunk(parent, self.RE.sub(self.expand_include, blocks.pop(0))) self.parser.parseChunk(parent, self.RE.sub(self.expand_include, blocks.pop(0)))

View File

@ -3,12 +3,14 @@ from xml.etree.ElementTree import Element, SubElement
import markdown import markdown
from markdown.extensions import Extension from markdown.extensions import Extension
from typing_extensions import override
from zerver.lib.markdown import ResultWithFamily, walk_tree_with_family from zerver.lib.markdown import ResultWithFamily, walk_tree_with_family
from zerver.lib.markdown.priorities import PREPROCESSOR_PRIORITES from zerver.lib.markdown.priorities import PREPROCESSOR_PRIORITES
class NestedCodeBlocksRenderer(Extension): class NestedCodeBlocksRenderer(Extension):
@override
def extendMarkdown(self, md: markdown.Markdown) -> None: def extendMarkdown(self, md: markdown.Markdown) -> None:
md.treeprocessors.register( md.treeprocessors.register(
NestedCodeBlocksRendererTreeProcessor(md, self.getConfigs()), NestedCodeBlocksRendererTreeProcessor(md, self.getConfigs()),
@ -21,6 +23,7 @@ class NestedCodeBlocksRendererTreeProcessor(markdown.treeprocessors.Treeprocesso
def __init__(self, md: markdown.Markdown, config: Mapping[str, Any]) -> None: def __init__(self, md: markdown.Markdown, config: Mapping[str, Any]) -> None:
super().__init__(md) super().__init__(md)
@override
def run(self, root: Element) -> None: def run(self, root: Element) -> None:
code_tags = walk_tree_with_family(root, self.get_code_tags) code_tags = walk_tree_with_family(root, self.get_code_tags)
nested_code_blocks = self.get_nested_code_blocks(code_tags) nested_code_blocks = self.get_nested_code_blocks(code_tags)

View File

@ -4,11 +4,13 @@ from xml.etree.ElementTree import Element
import markdown import markdown
from django.contrib.staticfiles.storage import staticfiles_storage from django.contrib.staticfiles.storage import staticfiles_storage
from markdown.extensions import Extension from markdown.extensions import Extension
from typing_extensions import override
from zerver.lib.markdown.priorities import PREPROCESSOR_PRIORITES from zerver.lib.markdown.priorities import PREPROCESSOR_PRIORITES
class MarkdownStaticImagesGenerator(Extension): class MarkdownStaticImagesGenerator(Extension):
@override
def extendMarkdown(self, md: markdown.Markdown) -> None: def extendMarkdown(self, md: markdown.Markdown) -> None:
md.treeprocessors.register( md.treeprocessors.register(
StaticImageProcessor(md), StaticImageProcessor(md),
@ -22,6 +24,7 @@ class StaticImageProcessor(markdown.treeprocessors.Treeprocessor):
Rewrite img tags which refer to /static/ to use staticfiles Rewrite img tags which refer to /static/ to use staticfiles
""" """
@override
def run(self, root: Element) -> None: def run(self, root: Element) -> None:
for img in root.iter("img"): for img in root.iter("img"):
url = img.get("src") url = img.get("src")

View File

@ -4,6 +4,7 @@ from typing import Any, Dict, List, Mapping, Optional
import markdown import markdown
from markdown.extensions import Extension from markdown.extensions import Extension
from markdown.preprocessors import Preprocessor from markdown.preprocessors import Preprocessor
from typing_extensions import override
from zerver.lib.markdown.priorities import PREPROCESSOR_PRIORITES from zerver.lib.markdown.priorities import PREPROCESSOR_PRIORITES
@ -105,6 +106,7 @@ TAB_SECTION_LABELS = {
class TabbedSectionsGenerator(Extension): class TabbedSectionsGenerator(Extension):
@override
def extendMarkdown(self, md: markdown.Markdown) -> None: def extendMarkdown(self, md: markdown.Markdown) -> None:
md.preprocessors.register( md.preprocessors.register(
TabbedSectionsPreprocessor(md, self.getConfigs()), TabbedSectionsPreprocessor(md, self.getConfigs()),
@ -117,6 +119,7 @@ class TabbedSectionsPreprocessor(Preprocessor):
def __init__(self, md: markdown.Markdown, config: Mapping[str, Any]) -> None: def __init__(self, md: markdown.Markdown, config: Mapping[str, Any]) -> None:
super().__init__(md) super().__init__(md)
@override
def run(self, lines: List[str]) -> List[str]: def run(self, lines: List[str]) -> List[str]:
tab_section = self.parse_tabs(lines) tab_section = self.parse_tabs(lines)
while tab_section: while tab_section:

View File

@ -43,7 +43,7 @@ from sqlalchemy.sql import (
) )
from sqlalchemy.sql.selectable import SelectBase from sqlalchemy.sql.selectable import SelectBase
from sqlalchemy.types import ARRAY, Boolean, Integer, Text from sqlalchemy.types import ARRAY, Boolean, Integer, Text
from typing_extensions import TypeAlias from typing_extensions import TypeAlias, override
from zerver.lib.addressee import get_user_profiles, get_user_profiles_by_ids from zerver.lib.addressee import get_user_profiles, get_user_profiles_by_ids
from zerver.lib.exceptions import ErrorCode, JsonableError from zerver.lib.exceptions import ErrorCode, JsonableError
@ -204,6 +204,7 @@ class BadNarrowOperatorError(JsonableError):
self.desc: str = desc self.desc: str = desc
@staticmethod @staticmethod
@override
def msg_format() -> str: def msg_format() -> str:
return _("Invalid narrow operator: {desc}") return _("Invalid narrow operator: {desc}")

View File

@ -2,6 +2,8 @@ import weakref
from abc import ABCMeta, abstractmethod from abc import ABCMeta, abstractmethod
from typing import Any, ClassVar, Generic, MutableMapping, TypeVar from typing import Any, ClassVar, Generic, MutableMapping, TypeVar
from typing_extensions import override
_KeyT = TypeVar("_KeyT") _KeyT = TypeVar("_KeyT")
_DataT = TypeVar("_DataT") _DataT = TypeVar("_DataT")
@ -28,6 +30,7 @@ class BaseNotes(Generic[_KeyT, _DataT], metaclass=ABCMeta):
__notes_map: ClassVar[MutableMapping[Any, Any]] __notes_map: ClassVar[MutableMapping[Any, Any]]
@override
def __init_subclass__(cls, **kwargs: object) -> None: def __init_subclass__(cls, **kwargs: object) -> None:
super().__init_subclass__(**kwargs) super().__init_subclass__(**kwargs)
if not hasattr(cls, "__notes_map"): if not hasattr(cls, "__notes_map"):

View File

@ -1,6 +1,7 @@
from typing import Any, Dict, Optional, Union from typing import Any, Dict, Optional, Union
import requests import requests
from typing_extensions import override
from urllib3.util import Retry from urllib3.util import Retry
@ -35,10 +36,12 @@ class OutgoingHTTPAdapter(requests.adapters.HTTPAdapter):
self.timeout = timeout self.timeout = timeout
super().__init__(max_retries=max_retries) super().__init__(max_retries=max_retries)
@override
def send(self, *args: Any, **kwargs: Any) -> requests.Response: def send(self, *args: Any, **kwargs: Any) -> requests.Response:
if kwargs.get("timeout") is None: if kwargs.get("timeout") is None:
kwargs["timeout"] = self.timeout kwargs["timeout"] = self.timeout
return super().send(*args, **kwargs) return super().send(*args, **kwargs)
@override
def proxy_headers(self, proxy: str) -> Dict[str, str]: def proxy_headers(self, proxy: str) -> Dict[str, str]:
return {"X-Smokescreen-Role": self.role} return {"X-Smokescreen-Role": self.role}

View File

@ -9,6 +9,7 @@ import requests
from django.conf import settings from django.conf import settings
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from requests import Response from requests import Response
from typing_extensions import override
from version import ZULIP_VERSION from version import ZULIP_VERSION
from zerver.actions.message_send import check_send_message from zerver.actions.message_send import check_send_message
@ -52,6 +53,7 @@ class OutgoingWebhookServiceInterface(metaclass=abc.ABCMeta):
class GenericOutgoingWebhookService(OutgoingWebhookServiceInterface): class GenericOutgoingWebhookService(OutgoingWebhookServiceInterface):
@override
def make_request( def make_request(
self, base_url: str, event: Dict[str, Any], realm: Realm self, base_url: str, event: Dict[str, Any], realm: Realm
) -> Optional[Response]: ) -> Optional[Response]:
@ -82,6 +84,7 @@ class GenericOutgoingWebhookService(OutgoingWebhookServiceInterface):
return self.session.post(base_url, json=request_data) return self.session.post(base_url, json=request_data)
@override
def process_success(self, response_json: Dict[str, Any]) -> Optional[Dict[str, Any]]: def process_success(self, response_json: Dict[str, Any]) -> Optional[Dict[str, Any]]:
if "response_not_required" in response_json and response_json["response_not_required"]: if "response_not_required" in response_json and response_json["response_not_required"]:
return None return None
@ -103,6 +106,7 @@ class GenericOutgoingWebhookService(OutgoingWebhookServiceInterface):
class SlackOutgoingWebhookService(OutgoingWebhookServiceInterface): class SlackOutgoingWebhookService(OutgoingWebhookServiceInterface):
@override
def make_request( def make_request(
self, base_url: str, event: Dict[str, Any], realm: Realm self, base_url: str, event: Dict[str, Any], realm: Realm
) -> Optional[Response]: ) -> Optional[Response]:
@ -142,6 +146,7 @@ class SlackOutgoingWebhookService(OutgoingWebhookServiceInterface):
] ]
return self.session.post(base_url, data=request_data) return self.session.post(base_url, data=request_data)
@override
def process_success(self, response_json: Dict[str, Any]) -> Optional[Dict[str, Any]]: def process_success(self, response_json: Dict[str, Any]) -> Optional[Dict[str, Any]]:
if "text" in response_json: if "text" in response_json:
content = response_json["text"] content = response_json["text"]

View File

@ -29,7 +29,7 @@ from django.db.models import F, Q
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 typing_extensions import TypeAlias from typing_extensions import TypeAlias, override
from zerver.lib.avatar import absolute_avatar_url from zerver.lib.avatar import absolute_avatar_url
from zerver.lib.emoji_utils import hex_codepoint_to_emoji from zerver.lib.emoji_utils import hex_codepoint_to_emoji
@ -115,6 +115,7 @@ class UserPushIdentityCompat:
assert self.user_id is not None and self.user_uuid is not None assert self.user_id is not None and self.user_uuid is not None
return Q(user_uuid=self.user_uuid) | Q(user_id=self.user_id) return Q(user_uuid=self.user_uuid) | Q(user_id=self.user_id)
@override
def __str__(self) -> str: def __str__(self) -> str:
result = "" result = ""
if self.user_id is not None: if self.user_id is not None:
@ -124,6 +125,7 @@ class UserPushIdentityCompat:
return result return result
@override
def __eq__(self, other: object) -> bool: def __eq__(self, other: object) -> bool:
if isinstance(other, UserPushIdentityCompat): if isinstance(other, UserPushIdentityCompat):
return self.user_id == other.user_id and self.user_uuid == other.user_uuid return self.user_id == other.user_id and self.user_uuid == other.user_uuid

View File

@ -18,7 +18,7 @@ from pika.adapters.blocking_connection import BlockingChannel
from pika.channel import Channel from pika.channel import Channel
from pika.spec import Basic from pika.spec import Basic
from tornado import ioloop from tornado import ioloop
from typing_extensions import TypeAlias from typing_extensions import TypeAlias, override
from zerver.lib.utils import assert_is_not_none from zerver.lib.utils import assert_is_not_none
@ -147,6 +147,7 @@ class QueueClient(Generic[ChannelT], metaclass=ABCMeta):
class SimpleQueueClient(QueueClient[BlockingChannel]): class SimpleQueueClient(QueueClient[BlockingChannel]):
connection: Optional[pika.BlockingConnection] connection: Optional[pika.BlockingConnection]
@override
def _connect(self) -> None: def _connect(self) -> None:
start = time.time() start = time.time()
self.connection = pika.BlockingConnection(self._get_parameters()) self.connection = pika.BlockingConnection(self._get_parameters())
@ -154,6 +155,7 @@ class SimpleQueueClient(QueueClient[BlockingChannel]):
self.channel.basic_qos(prefetch_count=self.prefetch) self.channel.basic_qos(prefetch_count=self.prefetch)
self.log.info("SimpleQueueClient connected (connecting took %.3fs)", time.time() - start) self.log.info("SimpleQueueClient connected (connecting took %.3fs)", time.time() - start)
@override
def _reconnect(self) -> None: def _reconnect(self) -> None:
self.connection = None self.connection = None
self.channel = None self.channel = None
@ -164,6 +166,7 @@ class SimpleQueueClient(QueueClient[BlockingChannel]):
if self.connection is not None: if self.connection is not None:
self.connection.close() self.connection.close()
@override
def ensure_queue(self, queue_name: str, callback: Callable[[BlockingChannel], object]) -> None: def ensure_queue(self, queue_name: str, callback: Callable[[BlockingChannel], object]) -> None:
"""Ensure that a given queue has been declared, and then call """Ensure that a given queue has been declared, and then call
the callback with no arguments.""" the callback with no arguments."""
@ -271,6 +274,7 @@ class TornadoQueueClient(QueueClient[Channel]):
self._on_open_cbs: List[Callable[[Channel], None]] = [] self._on_open_cbs: List[Callable[[Channel], None]] = []
self._connection_failure_count = 0 self._connection_failure_count = 0
@override
def _connect(self) -> None: def _connect(self) -> None:
self.log.info("Beginning TornadoQueueClient connection") self.log.info("Beginning TornadoQueueClient connection")
self.connection = ExceptionFreeTornadoConnection( self.connection = ExceptionFreeTornadoConnection(
@ -280,6 +284,7 @@ class TornadoQueueClient(QueueClient[Channel]):
on_close_callback=self._on_connection_closed, on_close_callback=self._on_connection_closed,
) )
@override
def _reconnect(self) -> None: def _reconnect(self) -> None:
self.connection = None self.connection = None
self.channel = None self.channel = None
@ -350,6 +355,7 @@ class TornadoQueueClient(QueueClient[Channel]):
self.connection.close() self.connection.close()
self.connection = None self.connection = None
@override
def ensure_queue(self, queue_name: str, callback: Callable[[Channel], object]) -> None: def ensure_queue(self, queue_name: str, callback: Callable[[Channel], object]) -> None:
def set_qos(frame: Any) -> None: def set_qos(frame: Any) -> None:
assert self.channel is not None assert self.channel is not None

View File

@ -9,6 +9,7 @@ import redis
from circuitbreaker import CircuitBreakerError, circuit from circuitbreaker import CircuitBreakerError, circuit
from django.conf import settings from django.conf import settings
from django.http import HttpRequest from django.http import HttpRequest
from typing_extensions import override
from zerver.lib.cache import cache_with_key from zerver.lib.cache import cache_with_key
from zerver.lib.exceptions import RateLimitedError from zerver.lib.exceptions import RateLimitedError
@ -122,9 +123,11 @@ class RateLimitedUser(RateLimitedObject):
backend = None backend = None
super().__init__(backend=backend) super().__init__(backend=backend)
@override
def key(self) -> str: def key(self) -> str:
return f"{type(self).__name__}:{self.user_id}:{self.domain}" return f"{type(self).__name__}:{self.user_id}:{self.domain}"
@override
def rules(self) -> List[Tuple[int, int]]: def rules(self) -> List[Tuple[int, int]]:
# user.rate_limits are general limits, applicable to the domain 'api_by_user' # user.rate_limits are general limits, applicable to the domain 'api_by_user'
if self.rate_limits != "" and self.domain == "api_by_user": if self.rate_limits != "" and self.domain == "api_by_user":
@ -146,10 +149,12 @@ class RateLimitedIPAddr(RateLimitedObject):
backend = None backend = None
super().__init__(backend=backend) super().__init__(backend=backend)
@override
def key(self) -> str: def key(self) -> str:
# The angle brackets are important since IPv6 addresses contain :. # The angle brackets are important since IPv6 addresses contain :.
return f"{type(self).__name__}:<{self.ip_addr}>:{self.domain}" return f"{type(self).__name__}:<{self.ip_addr}>:{self.domain}"
@override
def rules(self) -> List[Tuple[int, int]]: def rules(self) -> List[Tuple[int, int]]:
return rules[self.domain] return rules[self.domain]
@ -257,6 +262,7 @@ class TornadoInMemoryRateLimiterBackend(RateLimiterBackend):
return False, 0.0 return False, 0.0
@classmethod @classmethod
@override
def get_api_calls_left( def get_api_calls_left(
cls, entity_key: str, range_seconds: int, max_calls: int cls, entity_key: str, range_seconds: int, max_calls: int
) -> Tuple[int, float]: ) -> Tuple[int, float]:
@ -272,21 +278,25 @@ class TornadoInMemoryRateLimiterBackend(RateLimiterBackend):
return int(calls_remaining), reset_time - now return int(calls_remaining), reset_time - now
@classmethod @classmethod
@override
def block_access(cls, entity_key: str, seconds: int) -> None: def block_access(cls, entity_key: str, seconds: int) -> None:
now = time.time() now = time.time()
cls.timestamps_blocked_until[entity_key] = now + seconds cls.timestamps_blocked_until[entity_key] = now + seconds
@classmethod @classmethod
@override
def unblock_access(cls, entity_key: str) -> None: def unblock_access(cls, entity_key: str) -> None:
del cls.timestamps_blocked_until[entity_key] del cls.timestamps_blocked_until[entity_key]
@classmethod @classmethod
@override
def clear_history(cls, entity_key: str) -> None: def clear_history(cls, entity_key: str) -> None:
for reset_times_for_rule in cls.reset_times.values(): for reset_times_for_rule in cls.reset_times.values():
reset_times_for_rule.pop(entity_key, None) reset_times_for_rule.pop(entity_key, None)
cls.timestamps_blocked_until.pop(entity_key, None) cls.timestamps_blocked_until.pop(entity_key, None)
@classmethod @classmethod
@override
def rate_limit_entity( def rate_limit_entity(
cls, entity_key: str, rules: List[Tuple[int, int]], max_api_calls: int, max_api_window: int cls, entity_key: str, rules: List[Tuple[int, int]], max_api_calls: int, max_api_window: int
) -> Tuple[bool, float]: ) -> Tuple[bool, float]:
@ -317,6 +327,7 @@ class RedisRateLimiterBackend(RateLimiterBackend):
] ]
@classmethod @classmethod
@override
def block_access(cls, entity_key: str, seconds: int) -> None: def block_access(cls, entity_key: str, seconds: int) -> None:
"""Manually blocks an entity for the desired number of seconds""" """Manually blocks an entity for the desired number of seconds"""
_, _, blocking_key = cls.get_keys(entity_key) _, _, blocking_key = cls.get_keys(entity_key)
@ -326,16 +337,19 @@ class RedisRateLimiterBackend(RateLimiterBackend):
pipe.execute() pipe.execute()
@classmethod @classmethod
@override
def unblock_access(cls, entity_key: str) -> None: def unblock_access(cls, entity_key: str) -> None:
_, _, blocking_key = cls.get_keys(entity_key) _, _, blocking_key = cls.get_keys(entity_key)
client.delete(blocking_key) client.delete(blocking_key)
@classmethod @classmethod
@override
def clear_history(cls, entity_key: str) -> None: def clear_history(cls, entity_key: str) -> None:
for key in cls.get_keys(entity_key): for key in cls.get_keys(entity_key):
client.delete(key) client.delete(key)
@classmethod @classmethod
@override
def get_api_calls_left( def get_api_calls_left(
cls, entity_key: str, range_seconds: int, max_calls: int cls, entity_key: str, range_seconds: int, max_calls: int
) -> Tuple[int, float]: ) -> Tuple[int, float]:
@ -467,6 +481,7 @@ class RedisRateLimiterBackend(RateLimiterBackend):
continue continue
@classmethod @classmethod
@override
def rate_limit_entity( def rate_limit_entity(
cls, entity_key: str, rules: List[Tuple[int, int]], max_api_calls: int, max_api_window: int cls, entity_key: str, rules: List[Tuple[int, int]], max_api_calls: int, max_api_window: int
) -> Tuple[bool, float]: ) -> Tuple[bool, float]:
@ -501,9 +516,11 @@ class RateLimitedSpectatorAttachmentAccessByFile(RateLimitedObject):
self.path_id = path_id self.path_id = path_id
super().__init__() super().__init__()
@override
def key(self) -> str: def key(self) -> str:
return f"{type(self).__name__}:{self.path_id}" return f"{type(self).__name__}:{self.path_id}"
@override
def rules(self) -> List[Tuple[int, int]]: def rules(self) -> List[Tuple[int, int]]:
return settings.RATE_LIMITING_RULES["spectator_attachment_access_by_file"] return settings.RATE_LIMITING_RULES["spectator_attachment_access_by_file"]

View File

@ -24,7 +24,7 @@ from django.conf import settings
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.http import HttpRequest, HttpResponse from django.http import HttpRequest, HttpResponse
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from typing_extensions import Concatenate, ParamSpec from typing_extensions import Concatenate, ParamSpec, override
from zerver.lib import rate_limiter from zerver.lib import rate_limiter
from zerver.lib.exceptions import ErrorCode, InvalidJSONError, JsonableError from zerver.lib.exceptions import ErrorCode, InvalidJSONError, JsonableError
@ -74,6 +74,7 @@ class RequestNotes(BaseNotes[HttpRequest, "RequestNotes"]):
is_webhook_view: bool = False is_webhook_view: bool = False
@classmethod @classmethod
@override
def init_notes(cls) -> "RequestNotes": def init_notes(cls) -> "RequestNotes":
return RequestNotes() return RequestNotes()
@ -87,6 +88,7 @@ class RequestConfusingParamsError(JsonableError):
self.var_name2: str = var_name2 self.var_name2: str = var_name2
@staticmethod @staticmethod
@override
def msg_format() -> str: def msg_format() -> str:
return _("Can't decide between '{var_name1}' and '{var_name2}' arguments") return _("Can't decide between '{var_name1}' and '{var_name2}' arguments")
@ -99,6 +101,7 @@ class RequestVariableMissingError(JsonableError):
self.var_name: str = var_name self.var_name: str = var_name
@staticmethod @staticmethod
@override
def msg_format() -> str: def msg_format() -> str:
return _("Missing '{var_name}' argument") return _("Missing '{var_name}' argument")
@ -112,6 +115,7 @@ class RequestVariableConversionError(JsonableError):
self.bad_value = bad_value self.bad_value = bad_value
@staticmethod @staticmethod
@override
def msg_format() -> str: def msg_format() -> str:
return _("Bad value for '{var_name}': {bad_value}") return _("Bad value for '{var_name}': {bad_value}")

View File

@ -2,6 +2,7 @@ from typing import Any, Dict, Iterator, List, Mapping, Optional
import orjson import orjson
from django.http import HttpRequest, HttpResponse, HttpResponseNotAllowed from django.http import HttpRequest, HttpResponse, HttpResponseNotAllowed
from typing_extensions import override
from zerver.lib.exceptions import JsonableError, UnauthorizedError from zerver.lib.exceptions import JsonableError, UnauthorizedError
@ -36,6 +37,7 @@ class MutableJsonResponse(HttpResponse):
# is used here to encompass all of those return values. # is used here to encompass all of those return values.
# See https://github.com/typeddjango/django-stubs/commit/799b41fe47cfe2e56be33eee8cfbaf89a9853a8e # See https://github.com/typeddjango/django-stubs/commit/799b41fe47cfe2e56be33eee8cfbaf89a9853a8e
# and https://github.com/python/mypy/issues/3004. # and https://github.com/python/mypy/issues/3004.
@override # type: ignore[explicit-override] # https://github.com/python/mypy/issues/15900
@property @property
def content(self) -> Any: def content(self) -> Any:
"""Get content for the response. If the content hasn't been """Get content for the response. If the content hasn't been
@ -68,6 +70,7 @@ class MutableJsonResponse(HttpResponse):
# property, so in order to not break the implementation of the superclass with # property, so in order to not break the implementation of the superclass with
# our lazy content generation, we override the iterator to access `self.content` # our lazy content generation, we override the iterator to access `self.content`
# through our getter. # through our getter.
@override
def __iter__(self) -> Iterator[bytes]: def __iter__(self) -> Iterator[bytes]:
return iter([self.content]) return iter([self.content])

View File

@ -2,6 +2,7 @@ from typing import Optional
from django.contrib.sessions.backends.cached_db import SessionStore as CachedDbSessionStore from django.contrib.sessions.backends.cached_db import SessionStore as CachedDbSessionStore
from django.db.transaction import get_connection from django.db.transaction import get_connection
from typing_extensions import override
class SessionStore(CachedDbSessionStore): class SessionStore(CachedDbSessionStore):
@ -16,10 +17,12 @@ class SessionStore(CachedDbSessionStore):
""" """
@override
def save(self, must_create: bool = False) -> None: def save(self, must_create: bool = False) -> None:
assert not get_connection().in_atomic_block assert not get_connection().in_atomic_block
super().save(must_create) super().save(must_create)
@override
def delete(self, session_key: Optional[str] = None) -> None: def delete(self, session_key: Optional[str] = None) -> None:
assert not get_connection().in_atomic_block assert not get_connection().in_atomic_block
super().delete(session_key) super().delete(session_key)

View File

@ -4,6 +4,7 @@ from typing import Iterator, Optional
import sqlalchemy import sqlalchemy
from django.db import connection from django.db import connection
from sqlalchemy.engine import Connection, Engine from sqlalchemy.engine import Connection, Engine
from typing_extensions import override
from zerver.lib.db import TimeTrackingConnection from zerver.lib.db import TimeTrackingConnection
@ -11,6 +12,7 @@ from zerver.lib.db import TimeTrackingConnection
# This is a Pool that doesn't close connections. Therefore it can be used with # This is a Pool that doesn't close connections. Therefore it can be used with
# existing Django database connections. # existing Django database connections.
class NonClosingPool(sqlalchemy.pool.NullPool): class NonClosingPool(sqlalchemy.pool.NullPool):
@override
def status(self) -> str: def status(self) -> str:
return "NonClosingPool" return "NonClosingPool"

View File

@ -8,6 +8,7 @@ from django.conf import settings
from django.contrib.staticfiles.storage import ManifestStaticFilesStorage from django.contrib.staticfiles.storage import ManifestStaticFilesStorage
from django.core.files.base import File from django.core.files.base import File
from django.core.files.storage import FileSystemStorage from django.core.files.storage import FileSystemStorage
from typing_extensions import override
if settings.DEBUG: if settings.DEBUG:
from django.contrib.staticfiles.finders import find from django.contrib.staticfiles.finders import find
@ -22,6 +23,7 @@ else:
class IgnoreBundlesManifestStaticFilesStorage(ManifestStaticFilesStorage): class IgnoreBundlesManifestStaticFilesStorage(ManifestStaticFilesStorage):
@override
def hashed_name( def hashed_name(
self, name: str, content: Optional["File[bytes]"] = None, filename: Optional[str] = None self, name: str, content: Optional["File[bytes]"] = None, filename: Optional[str] = None
) -> str: ) -> str:

View File

@ -45,6 +45,7 @@ from django.utils.module_loading import import_string
from django.utils.timezone import now as timezone_now from django.utils.timezone import now as timezone_now
from fakeldap import MockLDAP from fakeldap import MockLDAP
from two_factor.plugins.phonenumber.models import PhoneDevice from two_factor.plugins.phonenumber.models import PhoneDevice
from typing_extensions import override
from corporate.models import Customer, CustomerPlan, LicenseLedger from corporate.models import Customer, CustomerPlan, LicenseLedger
from zerver.actions.message_send import check_send_message, check_send_stream_message from zerver.actions.message_send import check_send_message, check_send_stream_message
@ -133,6 +134,7 @@ class UploadSerializeMixin(SerializeMixin):
lockfile = "var/upload_lock" lockfile = "var/upload_lock"
@classmethod @classmethod
@override
def setUpClass(cls: Any) -> None: def setUpClass(cls: Any) -> None:
if not os.path.exists(cls.lockfile): if not os.path.exists(cls.lockfile):
with open(cls.lockfile, "w"): # nocoverage - rare locking case with open(cls.lockfile, "w"): # nocoverage - rare locking case
@ -149,6 +151,7 @@ class ZulipTestCaseMixin(SimpleTestCase):
# expectation. # expectation.
expected_console_output: Optional[str] = None expected_console_output: Optional[str] = None
@override
def setUp(self) -> None: def setUp(self) -> None:
super().setUp() super().setUp()
self.API_KEYS: Dict[str, str] = {} self.API_KEYS: Dict[str, str] = {}
@ -157,6 +160,7 @@ class ZulipTestCaseMixin(SimpleTestCase):
bounce_key_prefix_for_testing(test_name) bounce_key_prefix_for_testing(test_name)
bounce_redis_key_prefix_for_testing(test_name) bounce_redis_key_prefix_for_testing(test_name)
@override
def tearDown(self) -> None: def tearDown(self) -> None:
super().tearDown() super().tearDown()
# Important: we need to clear event queues to avoid leaking data to future tests. # Important: we need to clear event queues to avoid leaking data to future tests.
@ -179,6 +183,7 @@ class ZulipTestCaseMixin(SimpleTestCase):
def get_user_from_email(self, email: str, realm: Realm) -> UserProfile: def get_user_from_email(self, email: str, realm: Realm) -> UserProfile:
return get_user(email, realm) return get_user(email, realm)
@override
def run(self, result: Optional[TestResult] = None) -> Optional[TestResult]: # nocoverage def run(self, result: Optional[TestResult] = None) -> Optional[TestResult]: # nocoverage
if not settings.BAN_CONSOLE_OUTPUT and self.expected_console_output is None: if not settings.BAN_CONSOLE_OUTPUT and self.expected_console_output is None:
return super().run(result) return super().run(result)
@ -1975,10 +1980,12 @@ class ZulipTransactionTestCase(ZulipTestCaseMixin, TransactionTestCase):
ZulipTransactionTestCase tests if they leak state. ZulipTransactionTestCase tests if they leak state.
""" """
@override
def setUp(self) -> None: def setUp(self) -> None:
super().setUp() super().setUp()
self.models_ids_set = dict(get_row_ids_in_all_tables()) self.models_ids_set = dict(get_row_ids_in_all_tables())
@override
def tearDown(self) -> None: def tearDown(self) -> None:
"""Verifies that the test did not adjust the set of rows in the test """Verifies that the test did not adjust the set of rows in the test
database. This is a sanity check to help ensure that tests database. This is a sanity check to help ensure that tests
@ -2029,6 +2036,7 @@ class WebhookTestCase(ZulipTestCase):
def test_user(self) -> UserProfile: def test_user(self) -> UserProfile:
return self.get_user_from_email(self.TEST_USER_EMAIL, get_realm("zulip")) return self.get_user_from_email(self.TEST_USER_EMAIL, get_realm("zulip"))
@override
def setUp(self) -> None: def setUp(self) -> None:
super().setUp() super().setUp()
self.url = self.build_webhook_url() self.url = self.build_webhook_url()
@ -2274,6 +2282,7 @@ class MigrationsTestCase(ZulipTestCase): # nocoverage
migrate_from: Optional[str] = None migrate_from: Optional[str] = None
migrate_to: Optional[str] = None migrate_to: Optional[str] = None
@override
def setUp(self) -> None: def setUp(self) -> None:
assert ( assert (
self.migrate_from and self.migrate_to self.migrate_from and self.migrate_to

View File

@ -7,6 +7,8 @@ from io import SEEK_SET, TextIOWrapper
from types import TracebackType from types import TracebackType
from typing import IO, TYPE_CHECKING, Iterable, Iterator, List, Optional, Type from typing import IO, TYPE_CHECKING, Iterable, Iterator, List, Optional, Type
from typing_extensions import override
if TYPE_CHECKING: if TYPE_CHECKING:
from _typeshed import ReadableBuffer from _typeshed import ReadableBuffer
@ -50,77 +52,99 @@ class WrappedIO(IO[bytes]):
self.extra_output_finder = extra_output_finder self.extra_output_finder = extra_output_finder
@property @property
@override
def mode(self) -> str: def mode(self) -> str:
return self.stream.mode return self.stream.mode
@property @property
@override
def name(self) -> str: def name(self) -> str:
return self.stream.name return self.stream.name
@override
def close(self) -> None: def close(self) -> None:
pass pass
@property @property
@override
def closed(self) -> bool: def closed(self) -> bool:
return self.stream.closed return self.stream.closed
@override
def fileno(self) -> int: def fileno(self) -> int:
return self.stream.fileno() return self.stream.fileno()
@override
def flush(self) -> None: def flush(self) -> None:
self.stream.flush() self.stream.flush()
@override
def isatty(self) -> bool: def isatty(self) -> bool:
return self.stream.isatty() return self.stream.isatty()
@override
def read(self, n: int = -1) -> bytes: def read(self, n: int = -1) -> bytes:
return self.stream.read(n) return self.stream.read(n)
@override
def readable(self) -> bool: def readable(self) -> bool:
return self.stream.readable() return self.stream.readable()
@override
def readline(self, limit: int = -1) -> bytes: def readline(self, limit: int = -1) -> bytes:
return self.stream.readline(limit) return self.stream.readline(limit)
@override
def readlines(self, hint: int = -1) -> List[bytes]: def readlines(self, hint: int = -1) -> List[bytes]:
return self.stream.readlines(hint) return self.stream.readlines(hint)
@override
def seek(self, offset: int, whence: int = SEEK_SET) -> int: def seek(self, offset: int, whence: int = SEEK_SET) -> int:
return self.stream.seek(offset, whence) return self.stream.seek(offset, whence)
@override
def seekable(self) -> bool: def seekable(self) -> bool:
return self.stream.seekable() return self.stream.seekable()
@override
def tell(self) -> int: def tell(self) -> int:
return self.stream.tell() return self.stream.tell()
@override
def truncate(self, size: Optional[int] = None) -> int: def truncate(self, size: Optional[int] = None) -> int:
return self.truncate(size) return self.truncate(size)
@override
def writable(self) -> bool: def writable(self) -> bool:
return self.stream.writable() return self.stream.writable()
@override
def write(self, data: "ReadableBuffer") -> int: def write(self, data: "ReadableBuffer") -> int:
num_chars = self.stream.write(data) num_chars = self.stream.write(data)
self.extra_output_finder.find_extra_output(bytes(data)) self.extra_output_finder.find_extra_output(bytes(data))
return num_chars return num_chars
@override
def writelines(self, data: "Iterable[ReadableBuffer]") -> None: def writelines(self, data: "Iterable[ReadableBuffer]") -> None:
data, data_copy = itertools.tee(data) data, data_copy = itertools.tee(data)
self.stream.writelines(data) self.stream.writelines(data)
lines = b"".join(data_copy) lines = b"".join(data_copy)
self.extra_output_finder.find_extra_output(lines) self.extra_output_finder.find_extra_output(lines)
@override
def __next__(self) -> bytes: def __next__(self) -> bytes:
return next(self.stream) return next(self.stream)
@override
def __iter__(self) -> Iterator[bytes]: def __iter__(self) -> Iterator[bytes]:
return self return self
@override
def __enter__(self) -> IO[bytes]: def __enter__(self) -> IO[bytes]:
self.stream.__enter__() self.stream.__enter__()
return self return self
@override
def __exit__( def __exit__(
self, self,
exc_type: Optional[Type[BaseException]], exc_type: Optional[Type[BaseException]],

View File

@ -38,6 +38,7 @@ from django.test import override_settings
from django.urls import URLResolver from django.urls import URLResolver
from moto.s3 import mock_s3 from moto.s3 import mock_s3
from mypy_boto3_s3.service_resource import Bucket from mypy_boto3_s3.service_resource import Bucket
from typing_extensions import override
from zerver.actions.realm_settings import do_set_realm_user_default_setting from zerver.actions.realm_settings import do_set_realm_user_default_setting
from zerver.actions.user_settings import do_change_user_setting from zerver.actions.user_settings import do_change_user_setting
@ -372,6 +373,7 @@ class HostRequestMock(HttpRequest):
), ),
) )
@override
def get_host(self) -> str: def get_host(self) -> str:
return self.host return self.host

View File

@ -14,7 +14,7 @@ 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 returns.curry import partial
from typing_extensions import TypeAlias from typing_extensions import TypeAlias, override
from scripts.lib.zulip_tools import ( from scripts.lib.zulip_tools import (
TEMPLATE_DATABASE_DIR, TEMPLATE_DATABASE_DIR,
@ -61,24 +61,29 @@ class TextTestResult(runner.TextTestResult):
def addInstrumentation(self, test: unittest.TestCase, data: Dict[str, Any]) -> None: def addInstrumentation(self, test: unittest.TestCase, data: Dict[str, Any]) -> None:
append_instrumentation_data(data) append_instrumentation_data(data)
@override
def startTest(self, test: unittest.TestCase) -> None: def startTest(self, test: unittest.TestCase) -> None:
TestResult.startTest(self, test) TestResult.startTest(self, test)
self.stream.write(f"Running {test.id()}\n") self.stream.write(f"Running {test.id()}\n")
self.stream.flush() self.stream.flush()
@override
def addSuccess(self, *args: Any, **kwargs: Any) -> None: def addSuccess(self, *args: Any, **kwargs: Any) -> None:
TestResult.addSuccess(self, *args, **kwargs) TestResult.addSuccess(self, *args, **kwargs)
@override
def addError(self, *args: Any, **kwargs: Any) -> None: def addError(self, *args: Any, **kwargs: Any) -> None:
TestResult.addError(self, *args, **kwargs) TestResult.addError(self, *args, **kwargs)
test_name = args[0].id() test_name = args[0].id()
self.failed_tests.append(test_name) self.failed_tests.append(test_name)
@override
def addFailure(self, *args: Any, **kwargs: Any) -> None: def addFailure(self, *args: Any, **kwargs: Any) -> None:
TestResult.addFailure(self, *args, **kwargs) TestResult.addFailure(self, *args, **kwargs)
test_name = args[0].id() test_name = args[0].id()
self.failed_tests.append(test_name) self.failed_tests.append(test_name)
@override
def addSkip(self, test: unittest.TestCase, reason: str) -> None: def addSkip(self, test: unittest.TestCase, reason: str) -> None:
TestResult.addSkip(self, test, reason) TestResult.addSkip(self, test, reason)
self.stream.write(f"** Skipping {test.id()}: {reason}\n") self.stream.write(f"** Skipping {test.id()}: {reason}\n")
@ -259,6 +264,7 @@ class Runner(DiscoverRunner):
self.shallow_tested_templates: Set[str] = set() self.shallow_tested_templates: Set[str] = set()
template_rendered.connect(self.on_template_rendered) template_rendered.connect(self.on_template_rendered)
@override
def get_resultclass(self) -> Optional[Type[TextTestResult]]: def get_resultclass(self) -> Optional[Type[TextTestResult]]:
return TextTestResult return TextTestResult
@ -275,6 +281,7 @@ class Runner(DiscoverRunner):
def get_shallow_tested_templates(self) -> Set[str]: def get_shallow_tested_templates(self) -> Set[str]:
return self.shallow_tested_templates return self.shallow_tested_templates
@override
def setup_test_environment(self, *args: Any, **kwargs: Any) -> Any: def setup_test_environment(self, *args: Any, **kwargs: Any) -> Any:
settings.DATABASES["default"]["NAME"] = BACKEND_DATABASE_TEMPLATE settings.DATABASES["default"]["NAME"] = BACKEND_DATABASE_TEMPLATE
# We create/destroy the test databases in run_tests to avoid # We create/destroy the test databases in run_tests to avoid
@ -298,6 +305,7 @@ class Runner(DiscoverRunner):
return super().setup_test_environment(*args, **kwargs) return super().setup_test_environment(*args, **kwargs)
@override
def teardown_test_environment(self, *args: Any, **kwargs: Any) -> Any: def teardown_test_environment(self, *args: Any, **kwargs: Any) -> Any:
# The test environment setup clones the zulip_test_template # The test environment setup clones the zulip_test_template
# database, creating databases with names: # database, creating databases with names:
@ -347,6 +355,7 @@ class Runner(DiscoverRunner):
break break
check_import_error(test_name) check_import_error(test_name)
@override
def run_tests( def run_tests(
self, self,
test_labels: List[str], test_labels: List[str],

View File

@ -6,12 +6,15 @@ import time
from types import TracebackType from types import TracebackType
from typing import Callable, Optional, Tuple, Type, TypeVar from typing import Callable, Optional, Tuple, Type, TypeVar
from typing_extensions import override
# Based on https://code.activestate.com/recipes/483752/ # Based on https://code.activestate.com/recipes/483752/
class TimeoutExpiredError(Exception): class TimeoutExpiredError(Exception):
"""Exception raised when a function times out.""" """Exception raised when a function times out."""
@override
def __str__(self) -> str: def __str__(self) -> str:
return "Function call timed out." return "Function call timed out."
@ -49,6 +52,7 @@ def timeout(timeout: float, func: Callable[[], ResultT]) -> ResultT:
# if this is the only thread left. # if this is the only thread left.
self.daemon = True self.daemon = True
@override
def run(self) -> None: def run(self) -> None:
try: try:
self.result = func() self.result = func()

View File

@ -7,6 +7,7 @@ from datetime import datetime
from typing import IO, Any, BinaryIO, Callable, Iterator, Literal, Optional, Tuple from typing import IO, Any, BinaryIO, Callable, Iterator, Literal, Optional, Tuple
from django.conf import settings from django.conf import settings
from typing_extensions import override
from zerver.lib.avatar_hash import user_avatar_path from zerver.lib.avatar_hash import user_avatar_path
from zerver.lib.timestamp import timestamp_to_datetime from zerver.lib.timestamp import timestamp_to_datetime
@ -66,9 +67,11 @@ def delete_local_file(type: Literal["avatars", "files"], path: str) -> bool:
class LocalUploadBackend(ZulipUploadBackend): class LocalUploadBackend(ZulipUploadBackend):
@override
def get_public_upload_root_url(self) -> str: def get_public_upload_root_url(self) -> str:
return "/user_avatars/" return "/user_avatars/"
@override
def generate_message_upload_path(self, realm_id: str, uploaded_file_name: str) -> str: def generate_message_upload_path(self, realm_id: str, uploaded_file_name: str) -> str:
# Split into 256 subdirectories to prevent directories from getting too big # Split into 256 subdirectories to prevent directories from getting too big
return "/".join( return "/".join(
@ -80,6 +83,7 @@ class LocalUploadBackend(ZulipUploadBackend):
] ]
) )
@override
def upload_message_attachment( def upload_message_attachment(
self, self,
uploaded_file_name: str, uploaded_file_name: str,
@ -98,12 +102,15 @@ class LocalUploadBackend(ZulipUploadBackend):
create_attachment(uploaded_file_name, path, user_profile, target_realm, uploaded_file_size) create_attachment(uploaded_file_name, path, user_profile, target_realm, uploaded_file_size)
return "/user_uploads/" + path return "/user_uploads/" + path
@override
def save_attachment_contents(self, path_id: str, filehandle: BinaryIO) -> None: def save_attachment_contents(self, path_id: str, filehandle: BinaryIO) -> None:
filehandle.write(read_local_file("files", path_id)) filehandle.write(read_local_file("files", path_id))
@override
def delete_message_attachment(self, path_id: str) -> bool: def delete_message_attachment(self, path_id: str) -> bool:
return delete_local_file("files", path_id) return delete_local_file("files", path_id)
@override
def all_message_attachments(self) -> Iterator[Tuple[str, datetime]]: def all_message_attachments(self) -> Iterator[Tuple[str, datetime]]:
assert settings.LOCAL_UPLOADS_DIR is not None assert settings.LOCAL_UPLOADS_DIR is not None
for dirname, _, files in os.walk(settings.LOCAL_UPLOADS_DIR + "/files"): for dirname, _, files in os.walk(settings.LOCAL_UPLOADS_DIR + "/files"):
@ -114,6 +121,7 @@ class LocalUploadBackend(ZulipUploadBackend):
timestamp_to_datetime(os.path.getmtime(fullpath)), timestamp_to_datetime(os.path.getmtime(fullpath)),
) )
@override
def get_avatar_url(self, hash_key: str, medium: bool = False) -> str: def get_avatar_url(self, hash_key: str, medium: bool = False) -> str:
medium_suffix = "-medium" if medium else "" medium_suffix = "-medium" if medium else ""
return f"/user_avatars/{hash_key}{medium_suffix}.png" return f"/user_avatars/{hash_key}{medium_suffix}.png"
@ -127,6 +135,7 @@ class LocalUploadBackend(ZulipUploadBackend):
resized_medium = resize_avatar(image_data, MEDIUM_AVATAR_SIZE) resized_medium = resize_avatar(image_data, MEDIUM_AVATAR_SIZE)
write_local_file("avatars", file_path + "-medium.png", resized_medium) write_local_file("avatars", file_path + "-medium.png", resized_medium)
@override
def upload_avatar_image( def upload_avatar_image(
self, self,
user_file: IO[bytes], user_file: IO[bytes],
@ -139,6 +148,7 @@ class LocalUploadBackend(ZulipUploadBackend):
image_data = user_file.read() image_data = user_file.read()
self.write_avatar_images(file_path, image_data) self.write_avatar_images(file_path, image_data)
@override
def copy_avatar(self, source_profile: UserProfile, target_profile: UserProfile) -> None: def copy_avatar(self, source_profile: UserProfile, target_profile: UserProfile) -> None:
source_file_path = user_avatar_path(source_profile) source_file_path = user_avatar_path(source_profile)
target_file_path = user_avatar_path(target_profile) target_file_path = user_avatar_path(target_profile)
@ -146,6 +156,7 @@ class LocalUploadBackend(ZulipUploadBackend):
image_data = read_local_file("avatars", source_file_path + ".original") image_data = read_local_file("avatars", source_file_path + ".original")
self.write_avatar_images(target_file_path, image_data) self.write_avatar_images(target_file_path, image_data)
@override
def ensure_avatar_image(self, user_profile: UserProfile, is_medium: bool = False) -> None: def ensure_avatar_image(self, user_profile: UserProfile, is_medium: bool = False) -> None:
file_extension = "-medium.png" if is_medium else ".png" file_extension = "-medium.png" if is_medium else ".png"
file_path = user_avatar_path(user_profile) file_path = user_avatar_path(user_profile)
@ -169,6 +180,7 @@ class LocalUploadBackend(ZulipUploadBackend):
resized_avatar = resize_avatar(image_data) resized_avatar = resize_avatar(image_data)
write_local_file("avatars", file_path + file_extension, resized_avatar) write_local_file("avatars", file_path + file_extension, resized_avatar)
@override
def delete_avatar_image(self, user: UserProfile) -> None: def delete_avatar_image(self, user: UserProfile) -> None:
path_id = user_avatar_path(user) path_id = user_avatar_path(user)
@ -176,9 +188,11 @@ class LocalUploadBackend(ZulipUploadBackend):
delete_local_file("avatars", path_id + ".png") delete_local_file("avatars", path_id + ".png")
delete_local_file("avatars", path_id + "-medium.png") delete_local_file("avatars", path_id + "-medium.png")
@override
def get_realm_icon_url(self, realm_id: int, version: int) -> str: def get_realm_icon_url(self, realm_id: int, version: int) -> str:
return f"/user_avatars/{realm_id}/realm/icon.png?version={version}" return f"/user_avatars/{realm_id}/realm/icon.png?version={version}"
@override
def upload_realm_icon_image(self, icon_file: IO[bytes], user_profile: UserProfile) -> None: def upload_realm_icon_image(self, icon_file: IO[bytes], user_profile: UserProfile) -> None:
upload_path = self.realm_avatar_and_logo_path(user_profile.realm) upload_path = self.realm_avatar_and_logo_path(user_profile.realm)
image_data = icon_file.read() image_data = icon_file.read()
@ -187,6 +201,7 @@ class LocalUploadBackend(ZulipUploadBackend):
resized_data = resize_avatar(image_data) resized_data = resize_avatar(image_data)
write_local_file("avatars", os.path.join(upload_path, "icon.png"), resized_data) write_local_file("avatars", os.path.join(upload_path, "icon.png"), resized_data)
@override
def get_realm_logo_url(self, realm_id: int, version: int, night: bool) -> str: def get_realm_logo_url(self, realm_id: int, version: int, night: bool) -> str:
if night: if night:
file_name = "night_logo.png" file_name = "night_logo.png"
@ -194,6 +209,7 @@ class LocalUploadBackend(ZulipUploadBackend):
file_name = "logo.png" file_name = "logo.png"
return f"/user_avatars/{realm_id}/realm/{file_name}?version={version}" return f"/user_avatars/{realm_id}/realm/{file_name}?version={version}"
@override
def upload_realm_logo_image( def upload_realm_logo_image(
self, logo_file: IO[bytes], user_profile: UserProfile, night: bool self, logo_file: IO[bytes], user_profile: UserProfile, night: bool
) -> None: ) -> None:
@ -210,6 +226,7 @@ class LocalUploadBackend(ZulipUploadBackend):
resized_data = resize_logo(image_data) resized_data = resize_logo(image_data)
write_local_file("avatars", os.path.join(upload_path, resized_file), resized_data) write_local_file("avatars", os.path.join(upload_path, resized_file), resized_data)
@override
def get_emoji_url(self, emoji_file_name: str, realm_id: int, still: bool = False) -> str: def get_emoji_url(self, emoji_file_name: str, realm_id: int, still: bool = False) -> str:
if still: if still:
return os.path.join( return os.path.join(
@ -227,6 +244,7 @@ class LocalUploadBackend(ZulipUploadBackend):
), ),
) )
@override
def upload_emoji_image( def upload_emoji_image(
self, emoji_file: IO[bytes], emoji_file_name: str, user_profile: UserProfile self, emoji_file: IO[bytes], emoji_file_name: str, user_profile: UserProfile
) -> bool: ) -> bool:
@ -248,10 +266,12 @@ class LocalUploadBackend(ZulipUploadBackend):
write_local_file("avatars", still_path, still_image_data) write_local_file("avatars", still_path, still_image_data)
return is_animated return is_animated
@override
def get_export_tarball_url(self, realm: Realm, export_path: str) -> str: def get_export_tarball_url(self, realm: Realm, export_path: str) -> str:
# export_path has a leading `/` # export_path has a leading `/`
return realm.uri + export_path return realm.uri + export_path
@override
def upload_export_tarball( def upload_export_tarball(
self, self,
realm: Realm, realm: Realm,
@ -270,6 +290,7 @@ class LocalUploadBackend(ZulipUploadBackend):
public_url = realm.uri + "/user_avatars/" + path public_url = realm.uri + "/user_avatars/" + path
return public_url return public_url
@override
def delete_export_tarball(self, export_path: str) -> Optional[str]: def delete_export_tarball(self, export_path: str) -> Optional[str]:
# Get the last element of a list in the form ['user_avatars', '<file_path>'] # Get the last element of a list in the form ['user_avatars', '<file_path>']
assert export_path.startswith("/") assert export_path.startswith("/")

View File

@ -13,6 +13,7 @@ from botocore.client import Config
from django.conf import settings from django.conf import settings
from mypy_boto3_s3.client import S3Client from mypy_boto3_s3.client import S3Client
from mypy_boto3_s3.service_resource import Bucket, Object from mypy_boto3_s3.service_resource import Bucket, Object
from typing_extensions import override
from zerver.lib.avatar_hash import user_avatar_path from zerver.lib.avatar_hash import user_avatar_path
from zerver.lib.upload.base import ( from zerver.lib.upload.base import (
@ -194,6 +195,7 @@ class S3UploadBackend(ZulipUploadBackend):
(split_url.scheme, split_url.netloc, split_url.path[: -len(DUMMY_KEY)], "", "") (split_url.scheme, split_url.netloc, split_url.path[: -len(DUMMY_KEY)], "", "")
) )
@override
def get_public_upload_root_url(self) -> str: def get_public_upload_root_url(self) -> str:
return self.public_upload_url_base return self.public_upload_url_base
@ -204,6 +206,7 @@ class S3UploadBackend(ZulipUploadBackend):
assert not key.startswith("/") assert not key.startswith("/")
return urllib.parse.urljoin(self.public_upload_url_base, key) return urllib.parse.urljoin(self.public_upload_url_base, key)
@override
def generate_message_upload_path(self, realm_id: str, uploaded_file_name: str) -> str: def generate_message_upload_path(self, realm_id: str, uploaded_file_name: str) -> str:
return "/".join( return "/".join(
[ [
@ -213,6 +216,7 @@ class S3UploadBackend(ZulipUploadBackend):
] ]
) )
@override
def upload_message_attachment( def upload_message_attachment(
self, self,
uploaded_file_name: str, uploaded_file_name: str,
@ -241,18 +245,22 @@ class S3UploadBackend(ZulipUploadBackend):
) )
return url return url
@override
def save_attachment_contents(self, path_id: str, filehandle: BinaryIO) -> None: def save_attachment_contents(self, path_id: str, filehandle: BinaryIO) -> None:
for chunk in self.uploads_bucket.Object(path_id).get()["Body"]: for chunk in self.uploads_bucket.Object(path_id).get()["Body"]:
filehandle.write(chunk) filehandle.write(chunk)
@override
def delete_message_attachment(self, path_id: str) -> bool: def delete_message_attachment(self, path_id: str) -> bool:
return self.delete_file_from_s3(path_id, self.uploads_bucket) return self.delete_file_from_s3(path_id, self.uploads_bucket)
@override
def delete_message_attachments(self, path_ids: List[str]) -> None: def delete_message_attachments(self, path_ids: List[str]) -> None:
self.uploads_bucket.delete_objects( self.uploads_bucket.delete_objects(
Delete={"Objects": [{"Key": path_id} for path_id in path_ids]} Delete={"Objects": [{"Key": path_id} for path_id in path_ids]}
) )
@override
def all_message_attachments(self) -> Iterator[Tuple[str, datetime]]: def all_message_attachments(self) -> Iterator[Tuple[str, datetime]]:
client = self.session.client( client = self.session.client(
"s3", region_name=settings.S3_REGION, endpoint_url=settings.S3_ENDPOINT_URL "s3", region_name=settings.S3_REGION, endpoint_url=settings.S3_ENDPOINT_URL
@ -308,10 +316,12 @@ class S3UploadBackend(ZulipUploadBackend):
key = self.avatar_bucket.Object(file_name) key = self.avatar_bucket.Object(file_name)
return key return key
@override
def get_avatar_url(self, hash_key: str, medium: bool = False) -> str: def get_avatar_url(self, hash_key: str, medium: bool = False) -> str:
medium_suffix = "-medium.png" if medium else "" medium_suffix = "-medium.png" if medium else ""
return self.get_public_upload_url(f"{hash_key}{medium_suffix}") return self.get_public_upload_url(f"{hash_key}{medium_suffix}")
@override
def upload_avatar_image( def upload_avatar_image(
self, self,
user_file: IO[bytes], user_file: IO[bytes],
@ -326,6 +336,7 @@ class S3UploadBackend(ZulipUploadBackend):
image_data = user_file.read() image_data = user_file.read()
self.write_avatar_images(s3_file_name, target_user_profile, image_data, content_type) self.write_avatar_images(s3_file_name, target_user_profile, image_data, content_type)
@override
def copy_avatar(self, source_profile: UserProfile, target_profile: UserProfile) -> None: def copy_avatar(self, source_profile: UserProfile, target_profile: UserProfile) -> None:
s3_source_file_name = user_avatar_path(source_profile) s3_source_file_name = user_avatar_path(source_profile)
s3_target_file_name = user_avatar_path(target_profile) s3_target_file_name = user_avatar_path(target_profile)
@ -336,6 +347,7 @@ class S3UploadBackend(ZulipUploadBackend):
self.write_avatar_images(s3_target_file_name, target_profile, image_data, content_type) self.write_avatar_images(s3_target_file_name, target_profile, image_data, content_type)
@override
def ensure_avatar_image(self, user_profile: UserProfile, is_medium: bool = False) -> None: def ensure_avatar_image(self, user_profile: UserProfile, is_medium: bool = False) -> None:
# BUG: The else case should be user_avatar_path(user_profile) + ".png". # BUG: The else case should be user_avatar_path(user_profile) + ".png".
# See #12852 for details on this bug and how to migrate it. # See #12852 for details on this bug and how to migrate it.
@ -358,6 +370,7 @@ class S3UploadBackend(ZulipUploadBackend):
resized_avatar, resized_avatar,
) )
@override
def delete_avatar_image(self, user: UserProfile) -> None: def delete_avatar_image(self, user: UserProfile) -> None:
path_id = user_avatar_path(user) path_id = user_avatar_path(user)
@ -365,10 +378,12 @@ class S3UploadBackend(ZulipUploadBackend):
self.delete_file_from_s3(path_id + "-medium.png", self.avatar_bucket) self.delete_file_from_s3(path_id + "-medium.png", self.avatar_bucket)
self.delete_file_from_s3(path_id, self.avatar_bucket) self.delete_file_from_s3(path_id, self.avatar_bucket)
@override
def get_realm_icon_url(self, realm_id: int, version: int) -> str: def get_realm_icon_url(self, realm_id: int, version: int) -> str:
public_url = self.get_public_upload_url(f"{realm_id}/realm/icon.png") public_url = self.get_public_upload_url(f"{realm_id}/realm/icon.png")
return public_url + f"?version={version}" return public_url + f"?version={version}"
@override
def upload_realm_icon_image(self, icon_file: IO[bytes], user_profile: UserProfile) -> None: def upload_realm_icon_image(self, icon_file: IO[bytes], user_profile: UserProfile) -> None:
content_type = guess_type(icon_file.name)[0] content_type = guess_type(icon_file.name)[0]
s3_file_name = os.path.join(self.realm_avatar_and_logo_path(user_profile.realm), "icon") s3_file_name = os.path.join(self.realm_avatar_and_logo_path(user_profile.realm), "icon")
@ -393,6 +408,7 @@ class S3UploadBackend(ZulipUploadBackend):
# See avatar_url in avatar.py for URL. (That code also handles the case # See avatar_url in avatar.py for URL. (That code also handles the case
# that users use gravatar.) # that users use gravatar.)
@override
def get_realm_logo_url(self, realm_id: int, version: int, night: bool) -> str: def get_realm_logo_url(self, realm_id: int, version: int, night: bool) -> str:
if not night: if not night:
file_name = "logo.png" file_name = "logo.png"
@ -401,6 +417,7 @@ class S3UploadBackend(ZulipUploadBackend):
public_url = self.get_public_upload_url(f"{realm_id}/realm/{file_name}") public_url = self.get_public_upload_url(f"{realm_id}/realm/{file_name}")
return public_url + f"?version={version}" return public_url + f"?version={version}"
@override
def upload_realm_logo_image( def upload_realm_logo_image(
self, logo_file: IO[bytes], user_profile: UserProfile, night: bool self, logo_file: IO[bytes], user_profile: UserProfile, night: bool
) -> None: ) -> None:
@ -431,6 +448,7 @@ class S3UploadBackend(ZulipUploadBackend):
# See avatar_url in avatar.py for URL. (That code also handles the case # See avatar_url in avatar.py for URL. (That code also handles the case
# that users use gravatar.) # that users use gravatar.)
@override
def get_emoji_url(self, emoji_file_name: str, realm_id: int, still: bool = False) -> str: def get_emoji_url(self, emoji_file_name: str, realm_id: int, still: bool = False) -> str:
if still: if still:
emoji_path = RealmEmoji.STILL_PATH_ID_TEMPLATE.format( emoji_path = RealmEmoji.STILL_PATH_ID_TEMPLATE.format(
@ -444,6 +462,7 @@ class S3UploadBackend(ZulipUploadBackend):
) )
return self.get_public_upload_url(emoji_path) return self.get_public_upload_url(emoji_path)
@override
def upload_emoji_image( def upload_emoji_image(
self, emoji_file: IO[bytes], emoji_file_name: str, user_profile: UserProfile self, emoji_file: IO[bytes], emoji_file_name: str, user_profile: UserProfile
) -> bool: ) -> bool:
@ -486,10 +505,12 @@ class S3UploadBackend(ZulipUploadBackend):
return is_animated return is_animated
@override
def get_export_tarball_url(self, realm: Realm, export_path: str) -> str: def get_export_tarball_url(self, realm: Realm, export_path: str) -> str:
# export_path has a leading / # export_path has a leading /
return self.get_public_upload_url(export_path[1:]) return self.get_public_upload_url(export_path[1:])
@override
def upload_export_tarball( def upload_export_tarball(
self, self,
realm: Optional[Realm], realm: Optional[Realm],
@ -509,6 +530,7 @@ class S3UploadBackend(ZulipUploadBackend):
public_url = self.get_public_upload_url(key.key) public_url = self.get_public_upload_url(key.key)
return public_url return public_url
@override
def delete_export_tarball(self, export_path: str) -> Optional[str]: def delete_export_tarball(self, export_path: str) -> Optional[str]:
assert export_path.startswith("/") assert export_path.startswith("/")
path_id = export_path[1:] path_id = export_path[1:]

View File

@ -2,12 +2,14 @@ from typing import Optional
from urllib.parse import urlparse from urllib.parse import urlparse
from bs4.element import Tag from bs4.element import Tag
from typing_extensions import override
from zerver.lib.url_preview.parsers.base import BaseParser from zerver.lib.url_preview.parsers.base import BaseParser
from zerver.lib.url_preview.types import UrlEmbedData from zerver.lib.url_preview.types import UrlEmbedData
class GenericParser(BaseParser): class GenericParser(BaseParser):
@override
def extract_data(self) -> UrlEmbedData: def extract_data(self) -> UrlEmbedData:
return UrlEmbedData( return UrlEmbedData(
title=self._get_title(), title=self._get_title(),

View File

@ -1,11 +1,14 @@
from urllib.parse import urlparse from urllib.parse import urlparse
from typing_extensions import override
from zerver.lib.url_preview.types import UrlEmbedData from zerver.lib.url_preview.types import UrlEmbedData
from .base import BaseParser from .base import BaseParser
class OpenGraphParser(BaseParser): class OpenGraphParser(BaseParser):
@override
def extract_data(self) -> UrlEmbedData: def extract_data(self) -> UrlEmbedData:
meta = self._soup.findAll("meta") meta = self._soup.findAll("meta")

View File

@ -56,6 +56,7 @@ from django.core.validators import URLValidator, validate_email
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from pydantic import ValidationInfo, model_validator from pydantic import ValidationInfo, model_validator
from pydantic.functional_validators import ModelWrapValidatorHandler from pydantic.functional_validators import ModelWrapValidatorHandler
from typing_extensions import override
from zerver.lib.exceptions import InvalidJSONError, JsonableError from zerver.lib.exceptions import InvalidJSONError, JsonableError
from zerver.lib.timezone import canonicalize_timezone from zerver.lib.timezone import canonicalize_timezone
@ -648,6 +649,7 @@ class WildValue:
def __bool__(self) -> bool: def __bool__(self) -> bool:
return bool(self.value) return bool(self.value)
@override
def __eq__(self, other: object) -> bool: def __eq__(self, other: object) -> bool:
return self.value == other return self.value == other
@ -658,6 +660,7 @@ class WildValue:
) )
return len(self.value) return len(self.value)
@override
def __str__(self) -> NoReturn: def __str__(self) -> NoReturn:
raise TypeError("cannot convert WildValue to string; try .tame(check_string)") raise TypeError("cannot convert WildValue to string; try .tame(check_string)")
@ -698,10 +701,12 @@ class WildValue:
class WildValueList(WildValue): class WildValueList(WildValue):
value: List[object] value: List[object]
@override
def __iter__(self) -> Iterator[WildValue]: def __iter__(self) -> Iterator[WildValue]:
for i, item in enumerate(self.value): for i, item in enumerate(self.value):
yield wrap_wild_value(f"{self.var_name}[{i}]", item) yield wrap_wild_value(f"{self.var_name}[{i}]", item)
@override
def __getitem__(self, key: Union[int, str]) -> WildValue: def __getitem__(self, key: Union[int, str]) -> WildValue:
if not isinstance(key, int): if not isinstance(key, int):
return super().__getitem__(key) return super().__getitem__(key)
@ -719,9 +724,11 @@ class WildValueList(WildValue):
class WildValueDict(WildValue): class WildValueDict(WildValue):
value: Dict[str, object] value: Dict[str, object]
@override
def __contains__(self, key: str) -> bool: def __contains__(self, key: str) -> bool:
return key in self.value return key in self.value
@override
def __getitem__(self, key: Union[int, str]) -> WildValue: def __getitem__(self, key: Union[int, str]) -> WildValue:
if not isinstance(key, str): if not isinstance(key, str):
return super().__getitem__(key) return super().__getitem__(key)
@ -735,19 +742,23 @@ class WildValueDict(WildValue):
return wrap_wild_value(var_name, item) return wrap_wild_value(var_name, item)
@override
def get(self, key: str, default: object = None) -> WildValue: def get(self, key: str, default: object = None) -> WildValue:
item = self.value.get(key, default) item = self.value.get(key, default)
if isinstance(item, WildValue): if isinstance(item, WildValue):
return item return item
return wrap_wild_value(f"{self.var_name}[{key!r}]", item) return wrap_wild_value(f"{self.var_name}[{key!r}]", item)
@override
def keys(self) -> Iterator[str]: def keys(self) -> Iterator[str]:
yield from self.value.keys() yield from self.value.keys()
@override
def values(self) -> Iterator[WildValue]: def values(self) -> Iterator[WildValue]:
for key, value in self.value.items(): for key, value in self.value.items():
yield wrap_wild_value(f"{self.var_name}[{key!r}]", value) yield wrap_wild_value(f"{self.var_name}[{key!r}]", value)
@override
def items(self) -> Iterator[Tuple[str, WildValue]]: def items(self) -> Iterator[Tuple[str, WildValue]]:
for key, value in self.value.items(): for key, value in self.value.items():
yield key, wrap_wild_value(f"{self.var_name}[{key!r}]", value) yield key, wrap_wild_value(f"{self.var_name}[{key!r}]", value)

View File

@ -7,7 +7,7 @@ from urllib.parse import unquote
from django.http import HttpRequest from django.http import HttpRequest
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from pydantic import Json from pydantic import Json
from typing_extensions import Annotated, TypeAlias from typing_extensions import Annotated, TypeAlias, override
from zerver.actions.message_send import ( from zerver.actions.message_send import (
check_send_private_message, check_send_private_message,
@ -74,6 +74,7 @@ class MissingHTTPEventHeaderError(AnomalousWebhookPayloadError):
self.header = header self.header = header
@staticmethod @staticmethod
@override
def msg_format() -> str: def msg_format() -> str:
return _("Missing the HTTP event header '{header}'") return _("Missing the HTTP event header '{header}'")

View File

@ -1,6 +1,7 @@
from typing import Any from typing import Any
from django.core.management.base import CommandParser from django.core.management.base import CommandParser
from typing_extensions import override
from zerver.actions.streams import bulk_add_subscriptions from zerver.actions.streams import bulk_add_subscriptions
from zerver.lib.management import ZulipBaseCommand from zerver.lib.management import ZulipBaseCommand
@ -10,6 +11,7 @@ from zerver.lib.streams import ensure_stream
class Command(ZulipBaseCommand): class Command(ZulipBaseCommand):
help = """Add some or all users in a realm to a set of streams.""" help = """Add some or all users in a realm to a set of streams."""
@override
def add_arguments(self, parser: CommandParser) -> None: def add_arguments(self, parser: CommandParser) -> None:
self.add_realm_args(parser, required=True) self.add_realm_args(parser, required=True)
self.add_user_list_args(parser, all_users_help="Add all users in realm to these streams.") self.add_user_list_args(parser, all_users_help="Add all users in realm to these streams.")
@ -18,6 +20,7 @@ class Command(ZulipBaseCommand):
"-s", "--streams", required=True, help="A comma-separated list of stream names." "-s", "--streams", required=True, help="A comma-separated list of stream names."
) )
@override
def handle(self, *args: Any, **options: Any) -> None: def handle(self, *args: Any, **options: Any) -> None:
realm = self.get_realm(options) realm = self.get_realm(options)
assert realm is not None # Should be ensured by parser assert realm is not None # Should be ensured by parser

View File

@ -1,11 +1,13 @@
from typing import Any from typing import Any
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from typing_extensions import override
from zerver.lib.retention import archive_messages, clean_archived_data from zerver.lib.retention import archive_messages, clean_archived_data
class Command(BaseCommand): class Command(BaseCommand):
@override
def handle(self, *args: Any, **options: str) -> None: def handle(self, *args: Any, **options: str) -> None:
clean_archived_data() clean_archived_data()
archive_messages() archive_messages()

View File

@ -1,11 +1,13 @@
from typing import Any from typing import Any
from django.db import connection from django.db import connection
from typing_extensions import override
from zerver.lib.management import ZulipBaseCommand from zerver.lib.management import ZulipBaseCommand
class Command(ZulipBaseCommand): class Command(ZulipBaseCommand):
@override
def handle(self, *args: Any, **kwargs: str) -> None: def handle(self, *args: Any, **kwargs: str) -> None:
with connection.cursor() as cursor: with connection.cursor() as cursor:
cursor.execute( cursor.execute(

View File

@ -8,6 +8,7 @@ from django.conf import settings
from django.core.management.base import CommandParser from django.core.management.base import CommandParser
from django.db import connection from django.db import connection
from django.utils.timezone import now as timezone_now from django.utils.timezone import now as timezone_now
from typing_extensions import override
from scripts.lib.zulip_tools import TIMESTAMP_FORMAT, parse_os_release, run from scripts.lib.zulip_tools import TIMESTAMP_FORMAT, parse_os_release, run
from version import ZULIP_VERSION from version import ZULIP_VERSION
@ -17,16 +18,19 @@ from zerver.logging_handlers import try_git_describe
class Command(ZulipBaseCommand): class Command(ZulipBaseCommand):
# Fix support for multi-line usage strings # Fix support for multi-line usage strings
@override
def create_parser(self, prog_name: str, subcommand: str, **kwargs: Any) -> CommandParser: def create_parser(self, prog_name: str, subcommand: str, **kwargs: Any) -> CommandParser:
parser = super().create_parser(prog_name, subcommand, **kwargs) parser = super().create_parser(prog_name, subcommand, **kwargs)
parser.formatter_class = RawTextHelpFormatter parser.formatter_class = RawTextHelpFormatter
return parser return parser
@override
def add_arguments(self, parser: ArgumentParser) -> None: def add_arguments(self, parser: ArgumentParser) -> None:
parser.add_argument("--output", help="Filename of output tarball") parser.add_argument("--output", help="Filename of output tarball")
parser.add_argument("--skip-db", action="store_true", help="Skip database backup") parser.add_argument("--skip-db", action="store_true", help="Skip database backup")
parser.add_argument("--skip-uploads", action="store_true", help="Skip uploads backup") parser.add_argument("--skip-uploads", action="store_true", help="Skip uploads backup")
@override
def handle(self, *args: Any, **options: Any) -> None: def handle(self, *args: Any, **options: Any) -> None:
timestamp = timezone_now().strftime(TIMESTAMP_FORMAT) timestamp = timezone_now().strftime(TIMESTAMP_FORMAT)
with tempfile.TemporaryDirectory( with tempfile.TemporaryDirectory(

View File

@ -2,6 +2,7 @@ from argparse import ArgumentParser
from typing import Any from typing import Any
from django.core.management.base import CommandError from django.core.management.base import CommandError
from typing_extensions import override
from zerver.actions.user_settings import do_change_full_name from zerver.actions.user_settings import do_change_full_name
from zerver.lib.management import ZulipBaseCommand from zerver.lib.management import ZulipBaseCommand
@ -10,6 +11,7 @@ from zerver.lib.management import ZulipBaseCommand
class Command(ZulipBaseCommand): class Command(ZulipBaseCommand):
help = """Change the names for many users.""" help = """Change the names for many users."""
@override
def add_arguments(self, parser: ArgumentParser) -> None: def add_arguments(self, parser: ArgumentParser) -> None:
parser.add_argument( parser.add_argument(
"data_file", "data_file",
@ -18,6 +20,7 @@ class Command(ZulipBaseCommand):
) )
self.add_realm_args(parser, required=True) self.add_realm_args(parser, required=True)
@override
def handle(self, *args: Any, **options: str) -> None: def handle(self, *args: Any, **options: str) -> None:
data_file = options["data_file"] data_file = options["data_file"]
realm = self.get_realm(options) realm = self.get_realm(options)

View File

@ -5,6 +5,7 @@ from typing import Any, List
from django.contrib.auth.password_validation import validate_password from django.contrib.auth.password_validation import validate_password
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.core.management.base import CommandError from django.core.management.base import CommandError
from typing_extensions import override
from zerver.lib.management import ZulipBaseCommand from zerver.lib.management import ZulipBaseCommand
@ -27,10 +28,12 @@ class Command(ZulipBaseCommand):
raise CommandError("aborted") raise CommandError("aborted")
return p return p
@override
def add_arguments(self, parser: ArgumentParser) -> None: def add_arguments(self, parser: ArgumentParser) -> None:
parser.add_argument("email", metavar="<email>", help="email of user to change role") parser.add_argument("email", metavar="<email>", help="email of user to change role")
self.add_realm_args(parser, required=True) self.add_realm_args(parser, required=True)
@override
def handle(self, *args: Any, **options: Any) -> str: def handle(self, *args: Any, **options: Any) -> str:
email = options["email"] email = options["email"]
realm = self.get_realm(options) realm = self.get_realm(options)

View File

@ -3,6 +3,7 @@ from typing import Any
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.core.management.base import CommandError from django.core.management.base import CommandError
from typing_extensions import override
from zerver.actions.create_realm import do_change_realm_subdomain from zerver.actions.create_realm import do_change_realm_subdomain
from zerver.forms import check_subdomain_available from zerver.forms import check_subdomain_available
@ -12,6 +13,7 @@ from zerver.lib.management import ZulipBaseCommand
class Command(ZulipBaseCommand): class Command(ZulipBaseCommand):
help = """Change realm's subdomain.""" help = """Change realm's subdomain."""
@override
def add_arguments(self, parser: ArgumentParser) -> None: def add_arguments(self, parser: ArgumentParser) -> None:
self.add_realm_args(parser, required=True) self.add_realm_args(parser, required=True)
parser.add_argument("new_subdomain", metavar="<new subdomain>", help="realm new subdomain") parser.add_argument("new_subdomain", metavar="<new subdomain>", help="realm new subdomain")
@ -26,6 +28,7 @@ class Command(ZulipBaseCommand):
help="Allow use of reserved subdomains", help="Allow use of reserved subdomains",
) )
@override
def handle(self, *args: Any, **options: Any) -> None: def handle(self, *args: Any, **options: Any) -> None:
realm = self.get_realm(options) realm = self.get_realm(options)
assert realm is not None # Should be ensured by parser assert realm is not None # Should be ensured by parser

View File

@ -1,6 +1,8 @@
from argparse import ArgumentParser from argparse import ArgumentParser
from typing import Any from typing import Any
from typing_extensions import override
from zerver.actions.user_settings import do_change_user_delivery_email from zerver.actions.user_settings import do_change_user_delivery_email
from zerver.lib.management import ZulipBaseCommand from zerver.lib.management import ZulipBaseCommand
@ -8,11 +10,13 @@ from zerver.lib.management import ZulipBaseCommand
class Command(ZulipBaseCommand): class Command(ZulipBaseCommand):
help = """Change the email address for a user.""" help = """Change the email address for a user."""
@override
def add_arguments(self, parser: ArgumentParser) -> None: def add_arguments(self, parser: ArgumentParser) -> None:
self.add_realm_args(parser) self.add_realm_args(parser)
parser.add_argument("old_email", metavar="<old email>", help="email address to change") parser.add_argument("old_email", metavar="<old email>", help="email address to change")
parser.add_argument("new_email", metavar="<new email>", help="new email address") parser.add_argument("new_email", metavar="<new email>", help="new email address")
@override
def handle(self, *args: Any, **options: str) -> None: def handle(self, *args: Any, **options: str) -> None:
old_email = options["old_email"] old_email = options["old_email"]
new_email = options["new_email"] new_email = options["new_email"]

View File

@ -2,6 +2,7 @@ from argparse import ArgumentParser
from typing import Any from typing import Any
from django.core.management.base import CommandError from django.core.management.base import CommandError
from typing_extensions import override
from zerver.actions.users import ( from zerver.actions.users import (
do_change_can_create_users, do_change_can_create_users,
@ -18,6 +19,7 @@ class Command(ZulipBaseCommand):
ONLY perform this on customer request from an authorized person. ONLY perform this on customer request from an authorized person.
""" """
@override
def add_arguments(self, parser: ArgumentParser) -> None: def add_arguments(self, parser: ArgumentParser) -> None:
parser.add_argument("email", metavar="<email>", help="email of user to change role") parser.add_argument("email", metavar="<email>", help="email of user to change role")
parser.add_argument( parser.add_argument(
@ -42,6 +44,7 @@ ONLY perform this on customer request from an authorized person.
) )
self.add_realm_args(parser, required=True) self.add_realm_args(parser, required=True)
@override
def handle(self, *args: Any, **options: Any) -> None: def handle(self, *args: Any, **options: Any) -> None:
email = options["email"] email = options["email"]
realm = self.get_realm(options) realm = self.get_realm(options)

View File

@ -5,6 +5,7 @@ from typing import Any, Callable, Optional
from django.conf import settings from django.conf import settings
from django.core.management.base import BaseCommand, CommandError, CommandParser from django.core.management.base import BaseCommand, CommandError, CommandParser
from returns.curry import partial from returns.curry import partial
from typing_extensions import override
from zerver.lib.rate_limiter import RateLimitedUser, client from zerver.lib.rate_limiter import RateLimitedUser, client
from zerver.models import get_user_profile_by_id from zerver.models import get_user_profile_by_id
@ -16,6 +17,7 @@ class Command(BaseCommand):
Usage: ./manage.py [--trim] check_redis""" Usage: ./manage.py [--trim] check_redis"""
@override
def add_arguments(self, parser: CommandParser) -> None: def add_arguments(self, parser: CommandParser) -> None:
parser.add_argument("-t", "--trim", action="store_true", help="Actually trim excess") parser.add_argument("-t", "--trim", action="store_true", help="Actually trim excess")
@ -46,6 +48,7 @@ than max_api_calls! (trying to trim) %s %s",
client.expire(key, entity.max_api_window()) client.expire(key, entity.max_api_window())
trim_func(key, max_calls) trim_func(key, max_calls)
@override
def handle(self, *args: Any, **options: Any) -> None: def handle(self, *args: Any, **options: Any) -> None:
if not settings.RATE_LIMITING: if not settings.RATE_LIMITING:
raise CommandError("This machine is not using Redis or rate limiting, aborting") raise CommandError("This machine is not using Redis or rate limiting, aborting")

View File

@ -1,6 +1,7 @@
from typing import Any from typing import Any
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from typing_extensions import override
from zerver.lib.management import check_config from zerver.lib.management import check_config
@ -8,5 +9,6 @@ from zerver.lib.management import check_config
class Command(BaseCommand): class Command(BaseCommand):
help = """Checks /etc/zulip/settings.py for common configuration issues.""" help = """Checks /etc/zulip/settings.py for common configuration issues."""
@override
def handle(self, *args: Any, **options: Any) -> None: def handle(self, *args: Any, **options: Any) -> None:
check_config() check_config()

View File

@ -14,9 +14,11 @@ 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 django.utils.translation import to_language from django.utils.translation import to_language
from pyuca import Collator from pyuca import Collator
from typing_extensions import override
class Command(compilemessages.Command): class Command(compilemessages.Command):
@override
def add_arguments(self, parser: CommandParser) -> None: def add_arguments(self, parser: CommandParser) -> None:
super().add_arguments(parser) super().add_arguments(parser)
@ -24,6 +26,7 @@ class Command(compilemessages.Command):
"--strict", "-s", action="store_true", help="Stop execution in case of errors." "--strict", "-s", action="store_true", help="Stop execution in case of errors."
) )
@override
def handle(self, *args: Any, **options: Any) -> None: def handle(self, *args: Any, **options: Any) -> None:
super().handle(*args, **options) super().handle(*args, **options)
self.strict = options["strict"] self.strict = options["strict"]

View File

@ -5,6 +5,7 @@ from typing import Any
from django.conf import settings from django.conf import settings
from django.core.management.base import BaseCommand, CommandError, CommandParser from django.core.management.base import BaseCommand, CommandError, CommandParser
from typing_extensions import override
from zerver.data_import.gitter import do_convert_data from zerver.data_import.gitter import do_convert_data
@ -12,6 +13,7 @@ from zerver.data_import.gitter import do_convert_data
class Command(BaseCommand): class Command(BaseCommand):
help = """Convert the Gitter data into Zulip data format.""" help = """Convert the Gitter data into Zulip data format."""
@override
def add_arguments(self, parser: CommandParser) -> None: def add_arguments(self, parser: CommandParser) -> None:
parser.add_argument( parser.add_argument(
"gitter_data", nargs="+", metavar="<gitter data>", help="Gitter data in json format" "gitter_data", nargs="+", metavar="<gitter data>", help="Gitter data in json format"
@ -29,6 +31,7 @@ class Command(BaseCommand):
parser.formatter_class = argparse.RawTextHelpFormatter parser.formatter_class = argparse.RawTextHelpFormatter
@override
def handle(self, *args: Any, **options: Any) -> None: def handle(self, *args: Any, **options: Any) -> None:
output_dir = options["output_dir"] output_dir = options["output_dir"]
if output_dir is None: if output_dir is None:

View File

@ -2,6 +2,8 @@ import argparse
import os import os
from typing import Any from typing import Any
from typing_extensions import override
""" """
Example usage for testing purposes. For testing data see the mattermost_fixtures Example usage for testing purposes. For testing data see the mattermost_fixtures
in zerver/tests/. in zerver/tests/.
@ -22,6 +24,7 @@ from zerver.data_import.mattermost import do_convert_data
class Command(BaseCommand): class Command(BaseCommand):
help = """Convert the mattermost data into Zulip data format.""" help = """Convert the mattermost data into Zulip data format."""
@override
def add_arguments(self, parser: CommandParser) -> None: def add_arguments(self, parser: CommandParser) -> None:
dir_help = ( dir_help = (
"Directory containing exported JSON file and exported_emoji (optional) directory." "Directory containing exported JSON file and exported_emoji (optional) directory."
@ -43,6 +46,7 @@ class Command(BaseCommand):
parser.formatter_class = argparse.RawTextHelpFormatter parser.formatter_class = argparse.RawTextHelpFormatter
@override
def handle(self, *args: Any, **options: Any) -> None: def handle(self, *args: Any, **options: Any) -> None:
output_dir = options["output_dir"] output_dir = options["output_dir"]
if output_dir is None: if output_dir is None:

View File

@ -3,6 +3,7 @@ import os
from typing import Any from typing import Any
from django.core.management.base import BaseCommand, CommandError, CommandParser from django.core.management.base import BaseCommand, CommandError, CommandParser
from typing_extensions import override
from zerver.data_import.rocketchat import do_convert_data from zerver.data_import.rocketchat import do_convert_data
@ -10,6 +11,7 @@ from zerver.data_import.rocketchat import do_convert_data
class Command(BaseCommand): class Command(BaseCommand):
help = """Convert the Rocketchat data into Zulip data format.""" help = """Convert the Rocketchat data into Zulip data format."""
@override
def add_arguments(self, parser: CommandParser) -> None: def add_arguments(self, parser: CommandParser) -> None:
dir_help = "Directory containing all the `bson` files from mongodb dump of rocketchat." dir_help = "Directory containing all the `bson` files from mongodb dump of rocketchat."
parser.add_argument( parser.add_argument(
@ -22,6 +24,7 @@ class Command(BaseCommand):
parser.formatter_class = argparse.RawTextHelpFormatter parser.formatter_class = argparse.RawTextHelpFormatter
@override
def handle(self, *args: Any, **options: Any) -> None: def handle(self, *args: Any, **options: Any) -> None:
output_dir = options["output_dir"] output_dir = options["output_dir"]
if output_dir is None: if output_dir is None:

View File

@ -5,6 +5,7 @@ from typing import Any
from django.conf import settings from django.conf import settings
from django.core.management.base import BaseCommand, CommandError, CommandParser from django.core.management.base import BaseCommand, CommandError, CommandParser
from typing_extensions import override
from zerver.data_import.slack import do_convert_data from zerver.data_import.slack import do_convert_data
@ -12,6 +13,7 @@ from zerver.data_import.slack import do_convert_data
class Command(BaseCommand): class Command(BaseCommand):
help = """Convert the Slack data into Zulip data format.""" help = """Convert the Slack data into Zulip data format."""
@override
def add_arguments(self, parser: CommandParser) -> None: def add_arguments(self, parser: CommandParser) -> None:
parser.add_argument( parser.add_argument(
"slack_data_path", "slack_data_path",
@ -42,6 +44,7 @@ class Command(BaseCommand):
parser.formatter_class = argparse.RawTextHelpFormatter parser.formatter_class = argparse.RawTextHelpFormatter
@override
def handle(self, *args: Any, **options: Any) -> None: def handle(self, *args: Any, **options: Any) -> None:
output_dir = options["output_dir"] output_dir = options["output_dir"]
if output_dir is None: if output_dir is None:

View File

@ -1,6 +1,8 @@
from argparse import ArgumentParser from argparse import ArgumentParser
from typing import Any from typing import Any
from typing_extensions import override
from zerver.lib.management import ZulipBaseCommand from zerver.lib.management import ZulipBaseCommand
from zerver.lib.streams import ensure_stream from zerver.lib.streams import ensure_stream
from zerver.models import DefaultStreamGroup from zerver.models import DefaultStreamGroup
@ -13,6 +15,7 @@ Create default stream groups which the users can choose during sign up.
./manage.py create_default_stream_groups -s gsoc-1,gsoc-2,gsoc-3 -d "Google summer of code" -r zulip ./manage.py create_default_stream_groups -s gsoc-1,gsoc-2,gsoc-3 -d "Google summer of code" -r zulip
""" """
@override
def add_arguments(self, parser: ArgumentParser) -> None: def add_arguments(self, parser: ArgumentParser) -> None:
self.add_realm_args(parser, required=True) self.add_realm_args(parser, required=True)
@ -34,6 +37,7 @@ Create default stream groups which the users can choose during sign up.
"-s", "--streams", required=True, help="A comma-separated list of stream names." "-s", "--streams", required=True, help="A comma-separated list of stream names."
) )
@override
def handle(self, *args: Any, **options: Any) -> None: def handle(self, *args: Any, **options: Any) -> None:
realm = self.get_realm(options) realm = self.get_realm(options)
assert realm is not None # Should be ensured by parser assert realm is not None # Should be ensured by parser

View File

@ -3,6 +3,7 @@ from typing import Any
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.core.management.base import CommandError from django.core.management.base import CommandError
from typing_extensions import override
from zerver.actions.create_realm import do_create_realm from zerver.actions.create_realm import do_create_realm
from zerver.actions.create_user import do_create_user from zerver.actions.create_user import do_create_user
@ -30,6 +31,7 @@ initial organization owner user for the new realm, using the same
workflow as `./manage.py create_user`. workflow as `./manage.py create_user`.
""" """
@override
def add_arguments(self, parser: argparse.ArgumentParser) -> None: def add_arguments(self, parser: argparse.ArgumentParser) -> None:
parser.add_argument("realm_name", help="Name for the new organization") parser.add_argument("realm_name", help="Name for the new organization")
parser.add_argument( parser.add_argument(
@ -44,6 +46,7 @@ workflow as `./manage.py create_user`.
) )
self.add_create_user_args(parser) self.add_create_user_args(parser)
@override
def handle(self, *args: Any, **options: Any) -> None: def handle(self, *args: Any, **options: Any) -> None:
realm_name = options["realm_name"] realm_name = options["realm_name"]
string_id = options["string_id"] string_id = options["string_id"]

View File

@ -1,6 +1,7 @@
from typing import Any from typing import Any
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from typing_extensions import override
from zerver.lib.onboarding import create_if_missing_realm_internal_bots from zerver.lib.onboarding import create_if_missing_realm_internal_bots
@ -13,6 +14,7 @@ These are normally created when the realm is, so this should be a no-op
except when upgrading to a version that adds a new realm internal bot. except when upgrading to a version that adds a new realm internal bot.
""" """
@override
def handle(self, *args: Any, **options: Any) -> None: def handle(self, *args: Any, **options: Any) -> None:
create_if_missing_realm_internal_bots() create_if_missing_realm_internal_bots()
# create_users is idempotent -- it's a no-op when a given email # create_users is idempotent -- it's a no-op when a given email

View File

@ -1,6 +1,8 @@
from argparse import ArgumentParser from argparse import ArgumentParser
from typing import Any from typing import Any
from typing_extensions import override
from zerver.lib.management import ZulipBaseCommand from zerver.lib.management import ZulipBaseCommand
from zerver.lib.streams import create_stream_if_needed from zerver.lib.streams import create_stream_if_needed
@ -11,10 +13,12 @@ class Command(ZulipBaseCommand):
This should be used for TESTING only, unless you understand the limitations of This should be used for TESTING only, unless you understand the limitations of
the command.""" the command."""
@override
def add_arguments(self, parser: ArgumentParser) -> None: def add_arguments(self, parser: ArgumentParser) -> None:
self.add_realm_args(parser, required=True, help="realm in which to create the stream") self.add_realm_args(parser, required=True, help="realm in which to create the stream")
parser.add_argument("stream_name", metavar="<stream name>", help="name of stream to create") parser.add_argument("stream_name", metavar="<stream name>", help="name of stream to create")
@override
def handle(self, *args: Any, **options: str) -> None: def handle(self, *args: Any, **options: str) -> None:
realm = self.get_realm(options) realm = self.get_realm(options)
assert realm is not None # Should be ensured by parser assert realm is not None # Should be ensured by parser

View File

@ -3,6 +3,7 @@ from typing import Any
from django.core.management.base import CommandError from django.core.management.base import CommandError
from django.db.utils import IntegrityError from django.db.utils import IntegrityError
from typing_extensions import override
from zerver.actions.create_user import do_create_user from zerver.actions.create_user import do_create_user
from zerver.lib.management import ZulipBaseCommand from zerver.lib.management import ZulipBaseCommand
@ -21,12 +22,14 @@ If the server has Terms of Service configured, the user will be
prompted to accept the Terms of Service the first time they login. prompted to accept the Terms of Service the first time they login.
""" """
@override
def add_arguments(self, parser: argparse.ArgumentParser) -> None: def add_arguments(self, parser: argparse.ArgumentParser) -> None:
self.add_create_user_args(parser) self.add_create_user_args(parser)
self.add_realm_args( self.add_realm_args(
parser, required=True, help="The name of the existing realm to which to add the user." parser, required=True, help="The name of the existing realm to which to add the user."
) )
@override
def handle(self, *args: Any, **options: Any) -> None: def handle(self, *args: Any, **options: Any) -> None:
realm = self.get_realm(options) realm = self.get_realm(options)
assert realm is not None # Should be ensured by parser assert realm is not None # Should be ensured by parser

View File

@ -1,6 +1,8 @@
from argparse import ArgumentParser from argparse import ArgumentParser
from typing import Any from typing import Any
from typing_extensions import override
from zerver.actions.realm_settings import do_add_deactivated_redirect, do_deactivate_realm from zerver.actions.realm_settings import do_add_deactivated_redirect, do_deactivate_realm
from zerver.lib.management import ZulipBaseCommand from zerver.lib.management import ZulipBaseCommand
@ -8,12 +10,14 @@ from zerver.lib.management import ZulipBaseCommand
class Command(ZulipBaseCommand): class Command(ZulipBaseCommand):
help = """Script to deactivate a realm.""" help = """Script to deactivate a realm."""
@override
def add_arguments(self, parser: ArgumentParser) -> None: def add_arguments(self, parser: ArgumentParser) -> None:
parser.add_argument( parser.add_argument(
"--redirect_url", metavar="<redirect_url>", help="URL to which the realm has moved" "--redirect_url", metavar="<redirect_url>", help="URL to which the realm has moved"
) )
self.add_realm_args(parser, required=True) self.add_realm_args(parser, required=True)
@override
def handle(self, *args: Any, **options: str) -> None: def handle(self, *args: Any, **options: str) -> None:
realm = self.get_realm(options) realm = self.get_realm(options)

View File

@ -2,6 +2,7 @@ from argparse import ArgumentParser
from typing import Any from typing import Any
from django.core.management.base import CommandError from django.core.management.base import CommandError
from typing_extensions import override
from zerver.actions.users import do_deactivate_user from zerver.actions.users import do_deactivate_user
from zerver.lib.management import ZulipBaseCommand from zerver.lib.management import ZulipBaseCommand
@ -12,6 +13,7 @@ from zerver.lib.users import get_active_bots_owned_by_user
class Command(ZulipBaseCommand): class Command(ZulipBaseCommand):
help = "Deactivate a user, including forcibly logging them out." help = "Deactivate a user, including forcibly logging them out."
@override
def add_arguments(self, parser: ArgumentParser) -> None: def add_arguments(self, parser: ArgumentParser) -> None:
parser.add_argument( parser.add_argument(
"-f", "-f",
@ -22,6 +24,7 @@ class Command(ZulipBaseCommand):
parser.add_argument("email", metavar="<email>", help="email of user to deactivate") parser.add_argument("email", metavar="<email>", help="email of user to deactivate")
self.add_realm_args(parser) self.add_realm_args(parser)
@override
def handle(self, *args: Any, **options: Any) -> None: def handle(self, *args: Any, **options: Any) -> None:
realm = self.get_realm(options) realm = self.get_realm(options)
user_profile = self.get_user(options["email"], realm) user_profile = self.get_user(options["email"], realm)

View File

@ -4,6 +4,7 @@ from typing import Any
from django.core.management.base import BaseCommand, CommandError from django.core.management.base import BaseCommand, CommandError
from django.utils.timezone import now as timezone_now from django.utils.timezone import now as timezone_now
from typing_extensions import override
from zerver.actions.uploads import do_delete_old_unclaimed_attachments from zerver.actions.uploads import do_delete_old_unclaimed_attachments
from zerver.lib.upload import all_message_attachments, delete_message_attachments from zerver.lib.upload import all_message_attachments, delete_message_attachments
@ -15,6 +16,7 @@ class Command(BaseCommand):
numerical value indicating the limit of how old the attachment can be. numerical value indicating the limit of how old the attachment can be.
The default is five weeks.""" The default is five weeks."""
@override
def add_arguments(self, parser: ArgumentParser) -> None: def add_arguments(self, parser: ArgumentParser) -> None:
parser.add_argument( parser.add_argument(
"-w", "-w",
@ -40,6 +42,7 @@ class Command(BaseCommand):
"any files which are not in the database. This may take a very long time!", "any files which are not in the database. This may take a very long time!",
) )
@override
def handle(self, *args: Any, **options: Any) -> None: def handle(self, *args: Any, **options: Any) -> None:
delta_weeks = options["delta_weeks"] delta_weeks = options["delta_weeks"]
print(f"Deleting unclaimed attached files older than {delta_weeks} weeks") print(f"Deleting unclaimed attached files older than {delta_weeks} weeks")

View File

@ -3,6 +3,7 @@ from typing import Any
from django.conf import settings from django.conf import settings
from django.core.management.base import CommandError from django.core.management.base import CommandError
from typing_extensions import override
from zerver.actions.realm_settings import do_delete_all_realm_attachments from zerver.actions.realm_settings import do_delete_all_realm_attachments
from zerver.lib.management import ZulipBaseCommand from zerver.lib.management import ZulipBaseCommand
@ -13,9 +14,11 @@ class Command(ZulipBaseCommand):
help = """Script to permanently delete a realm. Recommended only for removing help = """Script to permanently delete a realm. Recommended only for removing
realms used for testing; consider using deactivate_realm instead.""" realms used for testing; consider using deactivate_realm instead."""
@override
def add_arguments(self, parser: ArgumentParser) -> None: def add_arguments(self, parser: ArgumentParser) -> None:
self.add_realm_args(parser, required=True) self.add_realm_args(parser, required=True)
@override
def handle(self, *args: Any, **options: str) -> None: def handle(self, *args: Any, **options: str) -> None:
realm = self.get_realm(options) realm = self.get_realm(options)
assert realm is not None # Should be ensured by parser assert realm is not None # Should be ensured by parser

View File

@ -2,6 +2,7 @@ from argparse import ArgumentParser
from typing import Any from typing import Any
from django.core.management.base import CommandError from django.core.management.base import CommandError
from typing_extensions import override
from zerver.actions.users import do_delete_user from zerver.actions.users import do_delete_user
from zerver.lib.management import ZulipBaseCommand from zerver.lib.management import ZulipBaseCommand
@ -38,6 +39,7 @@ This will:
sent/received by them, you can use the command on them individually. sent/received by them, you can use the command on them individually.
""" """
@override
def add_arguments(self, parser: ArgumentParser) -> None: def add_arguments(self, parser: ArgumentParser) -> None:
parser.add_argument( parser.add_argument(
"-f", "-f",
@ -48,6 +50,7 @@ This will:
self.add_realm_args(parser) self.add_realm_args(parser)
self.add_user_list_args(parser) self.add_user_list_args(parser)
@override
def handle(self, *args: Any, **options: Any) -> None: def handle(self, *args: Any, **options: Any) -> None:
realm = self.get_realm(options) realm = self.get_realm(options)
user_profiles = self.get_users(options, realm) user_profiles = self.get_users(options, realm)

View File

@ -12,6 +12,7 @@ from django.conf import settings
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from django.db import transaction from django.db import transaction
from django.utils.timezone import now as timezone_now from django.utils.timezone import now as timezone_now
from typing_extensions import override
from zerver.lib.logging_util import log_to_file from zerver.lib.logging_util import log_to_file
from zerver.lib.send_email import EmailNotDeliveredError, deliver_scheduled_emails from zerver.lib.send_email import EmailNotDeliveredError, deliver_scheduled_emails
@ -31,6 +32,7 @@ Run this command under supervisor.
Usage: ./manage.py deliver_scheduled_emails Usage: ./manage.py deliver_scheduled_emails
""" """
@override
def handle(self, *args: Any, **options: Any) -> None: def handle(self, *args: Any, **options: Any) -> None:
try: try:
while True: while True:

View File

@ -6,6 +6,7 @@ from typing import Any
from django.conf import settings from django.conf import settings
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from django.utils.timezone import now as timezone_now from django.utils.timezone import now as timezone_now
from typing_extensions import override
from zerver.actions.scheduled_messages import try_deliver_one_scheduled_message from zerver.actions.scheduled_messages import try_deliver_one_scheduled_message
from zerver.lib.logging_util import log_to_file from zerver.lib.logging_util import log_to_file
@ -24,6 +25,7 @@ This management command is run via supervisor.
Usage: ./manage.py deliver_scheduled_messages Usage: ./manage.py deliver_scheduled_messages
""" """
@override
def handle(self, *args: Any, **options: Any) -> None: def handle(self, *args: Any, **options: Any) -> None:
try: try:
while True: while True:

View File

@ -3,6 +3,7 @@ from argparse import ArgumentParser
from typing import Any from typing import Any
from django.core.management.base import CommandError from django.core.management.base import CommandError
from typing_extensions import override
from zerver.actions.realm_linkifiers import do_add_linkifier, do_remove_linkifier from zerver.actions.realm_linkifiers import do_add_linkifier, do_remove_linkifier
from zerver.lib.management import ZulipBaseCommand from zerver.lib.management import ZulipBaseCommand
@ -24,6 +25,7 @@ Example: ./manage.py edit_linkifiers --realm=zulip --op=remove '#(?P<id>[0-9]{2,
Example: ./manage.py edit_linkifiers --realm=zulip --op=show Example: ./manage.py edit_linkifiers --realm=zulip --op=show
""" """
@override
def add_arguments(self, parser: ArgumentParser) -> None: def add_arguments(self, parser: ArgumentParser) -> None:
parser.add_argument( parser.add_argument(
"--op", default="show", help="What operation to do (add, show, remove)." "--op", default="show", help="What operation to do (add, show, remove)."
@ -39,6 +41,7 @@ Example: ./manage.py edit_linkifiers --realm=zulip --op=show
) )
self.add_realm_args(parser, required=True) self.add_realm_args(parser, required=True)
@override
def handle(self, *args: Any, **options: str) -> None: def handle(self, *args: Any, **options: str) -> None:
realm = self.get_realm(options) realm = self.get_realm(options)
assert realm is not None # Should be ensured by parser assert realm is not None # Should be ensured by parser

View File

@ -26,6 +26,7 @@ from typing import Any, Generator
from django.conf import settings from django.conf import settings
from django.core.management.base import BaseCommand, CommandError from django.core.management.base import BaseCommand, CommandError
from typing_extensions import override
from zerver.lib.email_mirror import logger, process_message from zerver.lib.email_mirror import logger, process_message
@ -80,6 +81,7 @@ def get_imap_messages() -> Generator[EmailMessage, None, None]:
class Command(BaseCommand): class Command(BaseCommand):
help = __doc__ help = __doc__
@override
def handle(self, *args: Any, **options: str) -> None: def handle(self, *args: Any, **options: str) -> None:
for message in get_imap_messages(): for message in get_imap_messages():
process_message(message) process_message(message)

View File

@ -5,6 +5,7 @@ from typing import Any
from django.conf import settings from django.conf import settings
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from django.utils.timezone import now as timezone_now from django.utils.timezone import now as timezone_now
from typing_extensions import override
from zerver.lib.digest import DIGEST_CUTOFF, enqueue_emails from zerver.lib.digest import DIGEST_CUTOFF, enqueue_emails
from zerver.lib.logging_util import log_to_file from zerver.lib.logging_util import log_to_file
@ -19,6 +20,7 @@ class Command(BaseCommand):
in a while. in a while.
""" """
@override
def handle(self, *args: Any, **options: Any) -> None: def handle(self, *args: Any, **options: Any) -> None:
cutoff = timezone_now() - datetime.timedelta(days=DIGEST_CUTOFF) cutoff = timezone_now() - datetime.timedelta(days=DIGEST_CUTOFF)
enqueue_emails(cutoff) enqueue_emails(cutoff)

View File

@ -4,6 +4,7 @@ from typing import IO, Any
import orjson import orjson
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from typing_extensions import override
from zerver.lib.queue import queue_json_publish from zerver.lib.queue import queue_json_publish
@ -41,6 +42,7 @@ first field is a timestamp that we ignore.)
You can use "-" to represent stdin. You can use "-" to represent stdin.
""" """
@override
def add_arguments(self, parser: ArgumentParser) -> None: def add_arguments(self, parser: ArgumentParser) -> None:
parser.add_argument( parser.add_argument(
"queue_name", metavar="<queue>", help="name of worker queue to enqueue to" "queue_name", metavar="<queue>", help="name of worker queue to enqueue to"
@ -49,6 +51,7 @@ You can use "-" to represent stdin.
"file_name", metavar="<file>", help="name of file containing JSON lines" "file_name", metavar="<file>", help="name of file containing JSON lines"
) )
@override
def handle(self, *args: Any, **options: str) -> None: def handle(self, *args: Any, **options: str) -> None:
queue_name = options["queue_name"] queue_name = options["queue_name"]
file_name = options["file_name"] file_name = options["file_name"]

View File

@ -5,6 +5,7 @@ from typing import Any
from django.conf import settings from django.conf import settings
from django.core.management.base import CommandError from django.core.management.base import CommandError
from typing_extensions import override
from zerver.actions.realm_settings import do_deactivate_realm from zerver.actions.realm_settings import do_deactivate_realm
from zerver.lib.export import export_realm_wrapper from zerver.lib.export import export_realm_wrapper
@ -72,6 +73,7 @@ class Command(ZulipBaseCommand):
minutes. But this will vary a lot depending on the average number minutes. But this will vary a lot depending on the average number
of recipients of messages in the realm, hardware, etc.""" of recipients of messages in the realm, hardware, etc."""
@override
def add_arguments(self, parser: ArgumentParser) -> None: def add_arguments(self, parser: ArgumentParser) -> None:
parser.add_argument( parser.add_argument(
"--output", dest="output_dir", help="Directory to write exported data to." "--output", dest="output_dir", help="Directory to write exported data to."
@ -106,6 +108,7 @@ class Command(ZulipBaseCommand):
) )
self.add_realm_args(parser, required=True) self.add_realm_args(parser, required=True)
@override
def handle(self, *args: Any, **options: Any) -> None: def handle(self, *args: Any, **options: Any) -> None:
realm = self.get_realm(options) realm = self.get_realm(options)
assert realm is not None # Should be ensured by parser assert realm is not None # Should be ensured by parser

View File

@ -11,6 +11,7 @@ from typing import Any, Dict, Set, Tuple
import orjson import orjson
from django.core.management.base import CommandError from django.core.management.base import CommandError
from django.db.models import Q from django.db.models import Q
from typing_extensions import override
from zerver.lib.management import ZulipBaseCommand from zerver.lib.management import ZulipBaseCommand
from zerver.lib.soft_deactivation import reactivate_user_if_soft_deactivated from zerver.lib.soft_deactivation import reactivate_user_if_soft_deactivated
@ -40,6 +41,7 @@ senders/recipients.
This is most often used for legal compliance. This is most often used for legal compliance.
""" """
@override
def add_arguments(self, parser: ArgumentParser) -> None: def add_arguments(self, parser: ArgumentParser) -> None:
self.add_realm_args(parser, required=True) self.add_realm_args(parser, required=True)
parser.add_argument( parser.add_argument(
@ -94,6 +96,7 @@ This is most often used for legal compliance.
help="Limit to messages received by users with any of these emails (may be specified more than once). This is a superset of --sender, since senders receive every message they send.", help="Limit to messages received by users with any of these emails (may be specified more than once). This is a superset of --sender, since senders receive every message they send.",
) )
@override
def handle(self, *args: Any, **options: Any) -> None: def handle(self, *args: Any, **options: Any) -> None:
terms = set() terms = set()
if options["file"]: if options["file"]:

View File

@ -5,6 +5,7 @@ from argparse import ArgumentParser
from typing import Any from typing import Any
from django.core.management.base import CommandError from django.core.management.base import CommandError
from typing_extensions import override
from zerver.lib.export import do_export_user from zerver.lib.export import do_export_user
from zerver.lib.management import ZulipBaseCommand from zerver.lib.management import ZulipBaseCommand
@ -19,6 +20,7 @@ class Command(ZulipBaseCommand):
realm-public metadata needed to understand it; it does nothing realm-public metadata needed to understand it; it does nothing
with (for example) any bots owned by the user.""" with (for example) any bots owned by the user."""
@override
def add_arguments(self, parser: ArgumentParser) -> None: def add_arguments(self, parser: ArgumentParser) -> None:
parser.add_argument("email", metavar="<email>", help="email of user to export") parser.add_argument("email", metavar="<email>", help="email of user to export")
parser.add_argument( parser.add_argument(
@ -26,6 +28,7 @@ class Command(ZulipBaseCommand):
) )
self.add_realm_args(parser) self.add_realm_args(parser)
@override
def handle(self, *args: Any, **options: Any) -> None: def handle(self, *args: Any, **options: Any) -> None:
realm = self.get_realm(options) realm = self.get_realm(options)
user_profile = self.get_user(options["email"], realm) user_profile = self.get_user(options["email"], realm)

View File

@ -5,6 +5,7 @@ from argparse import ArgumentParser
from typing import Any from typing import Any
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from typing_extensions import override
from zerver.lib.export import export_usermessages_batch from zerver.lib.export import export_usermessages_batch
@ -12,6 +13,7 @@ from zerver.lib.export import export_usermessages_batch
class Command(BaseCommand): class Command(BaseCommand):
help = """UserMessage fetching helper for export.py""" help = """UserMessage fetching helper for export.py"""
@override
def add_arguments(self, parser: ArgumentParser) -> None: def add_arguments(self, parser: ArgumentParser) -> None:
parser.add_argument("--path", help="Path to find messages.json archives") parser.add_argument("--path", help="Path to find messages.json archives")
parser.add_argument("--thread", help="Thread ID") parser.add_argument("--thread", help="Thread ID")
@ -21,6 +23,7 @@ class Command(BaseCommand):
help="ID of the message advertising users to react with thumbs up", help="ID of the message advertising users to react with thumbs up",
) )
@override
def handle(self, *args: Any, **options: Any) -> None: def handle(self, *args: Any, **options: Any) -> None:
logging.info("Starting UserMessage batch thread %s", options["thread"]) logging.info("Starting UserMessage batch thread %s", options["thread"])
files = set(glob.glob(os.path.join(options["path"], "messages-*.json.partial"))) files = set(glob.glob(os.path.join(options["path"], "messages-*.json.partial")))

View File

@ -4,6 +4,7 @@ from typing import Any, Set
import orjson import orjson
from django.conf import settings from django.conf import settings
from typing_extensions import override
from urllib3.util import Retry from urllib3.util import Retry
from zerver.lib.management import ZulipBaseCommand from zerver.lib.management import ZulipBaseCommand
@ -33,6 +34,7 @@ to a file for access from Django for rate-limiting purposes.
Does nothing unless RATE_LIMIT_TOR_TOGETHER is enabled. Does nothing unless RATE_LIMIT_TOR_TOGETHER is enabled.
""" """
@override
def add_arguments(self, parser: ArgumentParser) -> None: def add_arguments(self, parser: ArgumentParser) -> None:
parser.add_argument( parser.add_argument(
"--max-retries", "--max-retries",
@ -41,6 +43,7 @@ Does nothing unless RATE_LIMIT_TOR_TOGETHER is enabled.
help="Number of times to retry fetching data from TOR", help="Number of times to retry fetching data from TOR",
) )
@override
def handle(self, *args: Any, **options: Any) -> None: def handle(self, *args: Any, **options: Any) -> None:
if not settings.RATE_LIMIT_TOR_TOGETHER: if not settings.RATE_LIMIT_TOR_TOGETHER:
return return

Some files were not shown because too many files have changed in this diff Show More