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.models import F
from psycopg2.sql import SQL, Composable, Identifier, Literal
from typing_extensions import TypeAlias
from typing_extensions import TypeAlias, override
from analytics.models import (
BaseCount,
@ -63,6 +63,7 @@ class CountStat:
else:
self.interval = self.time_increment
@override
def __repr__(self) -> str:
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.utils.timezone import now as timezone_now
from typing_extensions import override
from analytics.lib.counts import COUNT_STATS, CountStat
from analytics.models import installation_epoch
@ -24,6 +25,7 @@ class Command(BaseCommand):
Run as a cron job that runs every hour."""
@override
def handle(self, *args: Any, **options: Any) -> None:
fill_state = self.get_fill_state()
status = fill_state["status"]

View File

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

View File

@ -2,6 +2,7 @@ from argparse import ArgumentParser
from typing import Any
from django.core.management.base import BaseCommand, CommandError
from typing_extensions import override
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):
help = """Clear analytics tables."""
@override
def add_arguments(self, parser: ArgumentParser) -> None:
parser.add_argument("--force", action="store_true", help="Actually do it.")
parser.add_argument("--property", help="The property of the stat to be cleared.")
@override
def handle(self, *args: Any, **options: Any) -> None:
property = options["property"]
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.management.base import BaseCommand
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.fixtures import generate_time_series_data
@ -68,6 +68,7 @@ class Command(BaseCommand):
random_seed=self.random_seed,
)
@override
def handle(self, *args: Any, **options: Any) -> None:
# TODO: This should arguably only delete the objects
# 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.utils.dateparse import parse_datetime
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 scripts.lib.zulip_tools import ENDC, WARNING
@ -21,6 +22,7 @@ class Command(BaseCommand):
Run as a cron job that runs every hour."""
@override
def add_arguments(self, parser: ArgumentParser) -> None:
parser.add_argument(
"--time",
@ -37,6 +39,7 @@ class Command(BaseCommand):
"--verbose", action="store_true", help="Print timing information to stdout."
)
@override
def handle(self, *args: Any, **options: Any) -> None:
try:
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
from django.db import models
from django.db.models import Q, UniqueConstraint
from typing_extensions import override
from zerver.lib.timestamp import floor_to_day
from zerver.models import Realm, Stream, UserProfile
@ -16,6 +20,7 @@ class FillState(models.Model):
STARTED = 2
state = models.PositiveSmallIntegerField()
@override
def __str__(self) -> str:
return f"{self.property} {self.end_time} {self.state}"
@ -58,6 +63,7 @@ class InstallationCount(BaseCount):
),
]
@override
def __str__(self) -> str:
return f"{self.property} {self.subgroup} {self.value}"
@ -86,6 +92,7 @@ class RealmCount(BaseCount):
)
]
@override
def __str__(self) -> str:
return f"{self.realm!r} {self.property} {self.subgroup} {self.value}"
@ -117,6 +124,7 @@ class UserCount(BaseCount):
)
]
@override
def __str__(self) -> str:
return f"{self.user!r} {self.property} {self.subgroup} {self.value}"
@ -148,5 +156,6 @@ class StreamCount(BaseCount):
)
]
@override
def __str__(self) -> str:
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.utils.timezone import now as timezone_now
from psycopg2.sql import SQL, Literal
from typing_extensions import override
from analytics.lib.counts import (
COUNT_STATS,
@ -81,6 +82,7 @@ class AnalyticsTestCase(ZulipTestCase):
TIME_ZERO = datetime(1988, 3, 14, tzinfo=timezone.utc)
TIME_LAST_HOUR = TIME_ZERO - HOUR
@override
def setUp(self) -> None:
super().setUp()
self.default_realm = do_create_realm(
@ -455,6 +457,7 @@ class TestProcessCountStat(AnalyticsTestCase):
class TestCountStats(AnalyticsTestCase):
@override
def setUp(self) -> None:
super().setUp()
# This tests two things for each of the queries/CountStats: Handling
@ -1543,6 +1546,7 @@ class TestDeleteStats(AnalyticsTestCase):
class TestActiveUsersAudit(AnalyticsTestCase):
@override
def setUp(self) -> None:
super().setUp()
self.user = self.create_user()
@ -1725,6 +1729,7 @@ class TestActiveUsersAudit(AnalyticsTestCase):
class TestRealmActiveHumans(AnalyticsTestCase):
@override
def setUp(self) -> None:
super().setUp()
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 django.utils.timezone import now as timezone_now
from typing_extensions import override
from analytics.lib.counts import COUNT_STATS, CountStat
from analytics.lib.time_utils import time_range
@ -68,6 +69,7 @@ class TestStatsEndpoint(ZulipTestCase):
class TestGetChartData(ZulipTestCase):
@override
def setUp(self) -> None:
super().setUp()
self.realm = get_realm("zulip")

View File

@ -4,6 +4,7 @@ from unittest import mock
import orjson
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.models import Customer, CustomerPlan, LicenseLedger, get_customer_by_realm
@ -31,6 +32,7 @@ from zilencer.models import RemoteZulipServer
class TestRemoteServerSupportEndpoint(ZulipTestCase):
@override
def setUp(self) -> None:
super().setUp()

View File

@ -16,7 +16,7 @@ from django.http import HttpRequest, HttpResponse
from django.template.response import TemplateResponse
from django.urls import reverse
from django.utils.timezone import now as timezone_now
from typing_extensions import TypeAlias
from typing_extensions import TypeAlias, override
from confirmation import settings as confirmation_settings
from zerver.lib.types import UnspecifiedValue
@ -190,6 +190,7 @@ class Confirmation(models.Model):
class Meta:
unique_together = ("type", "confirmation_key")
@override
def __str__(self) -> str:
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.db import models
from django.db.models import CASCADE, Q
from typing_extensions import override
from zerver.models import Realm, UserProfile
from zilencer.models import RemoteZulipServer
@ -36,6 +37,7 @@ class Customer(models.Model):
)
]
@override
def __str__(self) -> str:
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.utils.crypto import get_random_string
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 (
DEFAULT_INVOICE_DAYS_UNTIL_DUE,
@ -383,6 +383,7 @@ def mock_stripe(
class StripeTestCase(ZulipTestCase):
@override
def setUp(self) -> None:
super().setUp()
realm = get_realm("zulip")
@ -4171,6 +4172,7 @@ class EventStatusTest(StripeTestCase):
class RequiresBillingAccessTest(StripeTestCase):
@override
def setUp(self, *mocks: Mock) -> None:
super().setUp()
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.color import color_style
from typing_extensions import override
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.
"""
@override
def main_help_text(self, commands_only: bool = False) -> str:
"""Return the script's main help text, as a string."""
if commands_only:

View File

@ -32,6 +32,7 @@ enable_error_code = [
"truthy-iterable",
"ignore-without-code",
"unused-awaitable",
"explicit-override",
]
# 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 xmlrpc import client
from typing_extensions import override
class UnixStreamHTTPConnection(HTTPConnection):
@override
def connect(self) -> None:
self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
connected = False
@ -28,6 +31,7 @@ class UnixStreamTransport(client.Transport):
self.socket_path = socket_path
super().__init__()
@override
def make_connection(
self, host: Union[Tuple[str, Dict[str, str]], str]
) -> UnixStreamHTTPConnection:

View File

@ -3,6 +3,8 @@ from posixpath import basename
from typing import Any, List, Set
from urllib.parse import urlparse
from typing_extensions import override
from .common.spiders import BaseDocumentationSpider
@ -21,6 +23,7 @@ class UnusedImagesLinterSpider(BaseDocumentationSpider):
self.static_images: Set[str] = set()
self.images_static_dir: str = get_images_dir(self.images_path)
@override
def _is_external_url(self, url: str) -> bool:
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:
@ -55,6 +58,7 @@ class APIDocumentationSpider(UnusedImagesLinterSpider):
class PorticoDocumentationSpider(BaseDocumentationSpider):
@override
def _is_external_url(self, url: str) -> bool:
return (
not url.startswith("http://localhost:9981")

View File

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

View File

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

View File

@ -3,6 +3,7 @@ from io import StringIO
from unittest import TestCase
from unittest.mock import patch
from typing_extensions import override
from zulint.custom_rules import RuleList
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):
@override
def setUp(self) -> None:
all_rules = list(python_rules.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.core.cache import cache
from django.db.models.signals import post_migrate
from typing_extensions import override
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):
name: str = "zerver"
@override
def ready(self) -> None:
if settings.SENTRY_DSN: # nocoverage
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.views.debug import SafeExceptionReporterFilter
from typing_extensions import override
class ZulipExceptionReporterFilter(SafeExceptionReporterFilter):
@override
def get_post_parameters(self, request: Optional[HttpRequest]) -> Dict[str, Any]:
post_data = SafeExceptionReporterFilter.get_post_parameters(self, request)
assert isinstance(post_data, dict)

View File

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

View File

@ -1,5 +1,7 @@
import asyncio
from typing_extensions import override
class NoAutoCreateEventLoopPolicy(asyncio.DefaultEventLoopPolicy):
"""
@ -12,5 +14,6 @@ class NoAutoCreateEventLoopPolicy(asyncio.DefaultEventLoopPolicy):
accident.
"""
@override
def get_event_loop(self) -> asyncio.AbstractEventLoop: # nocoverage
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.sql import Composable
from typing_extensions import TypeAlias
from typing_extensions import TypeAlias, override
CursorObj = TypeVar("CursorObj", bound=cursor)
Query: TypeAlias = Union[str, bytes, Composable]
@ -32,9 +32,11 @@ def wrapper_execute(
class TimeTrackingCursor(cursor):
"""A psycopg2 cursor class that tracks the time spent executing queries."""
@override
def execute(self, query: Query, vars: Params = None) -> None:
wrapper_execute(self, super().execute, query, vars)
@override
def executemany(self, query: Query, vars: Iterable[Params]) -> None: # nocoverage
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 django.conf import settings
from typing_extensions import override
from zerver.actions.message_send import (
check_send_message,
@ -527,9 +528,11 @@ class RateLimitedRealmMirror(RateLimitedObject):
self.realm = realm
super().__init__()
@override
def key(self) -> str:
return f"{type(self).__name__}:{self.realm.string_id}"
@override
def rules(self) -> List[Tuple[int, int]]:
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.utils.translation import gettext as _
from django_stubs_ext import StrPromise
from typing_extensions import override
class ErrorCode(Enum):
@ -127,6 +128,7 @@ class JsonableError(Exception):
def data(self) -> Dict[str, Any]:
return dict(((f, getattr(self, f)) for f in self.data_fields), code=self.code.name)
@override
def __str__(self) -> str:
return self.msg
@ -147,6 +149,7 @@ class UnauthorizedError(JsonableError):
raise AssertionError("Invalid www_authenticate value!")
@property
@override
def extra_headers(self) -> Dict[str, Any]:
extra_headers_dict = super().extra_headers
extra_headers_dict["WWW-Authenticate"] = self.www_authenticate
@ -161,6 +164,7 @@ class StreamDoesNotExistError(JsonableError):
self.stream = stream
@staticmethod
@override
def msg_format() -> str:
return _("Stream '{stream}' does not exist")
@ -173,6 +177,7 @@ class StreamWithIDDoesNotExistError(JsonableError):
self.stream_id = stream_id
@staticmethod
@override
def msg_format() -> str:
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")
@staticmethod
@override
def msg_format() -> str:
return _("Cannot deactivate the only {entity}.")
@ -198,6 +204,7 @@ class InvalidMarkdownIncludeStatementError(JsonableError):
self.include_statement = include_statement
@staticmethod
@override
def msg_format() -> str:
return _("Invalid Markdown include statement: {include_statement}")
@ -210,10 +217,12 @@ class RateLimitedError(JsonableError):
self.secs_to_freedom = secs_to_freedom
@staticmethod
@override
def msg_format() -> str:
return _("API usage exceeded rate limit")
@property
@override
def extra_headers(self) -> Dict[str, Any]:
extra_headers_dict = super().extra_headers
if self.secs_to_freedom is not None:
@ -222,6 +231,7 @@ class RateLimitedError(JsonableError):
return extra_headers_dict
@property
@override
def data(self) -> Dict[str, Any]:
data_dict = super().data
data_dict["retry-after"] = self.secs_to_freedom
@ -233,6 +243,7 @@ class InvalidJSONError(JsonableError):
code = ErrorCode.INVALID_JSON
@staticmethod
@override
def msg_format() -> str:
return _("Malformed JSON")
@ -244,6 +255,7 @@ class OrganizationMemberRequiredError(JsonableError):
pass
@staticmethod
@override
def msg_format() -> str:
return _("Must be an organization member")
@ -255,6 +267,7 @@ class OrganizationAdministratorRequiredError(JsonableError):
pass
@staticmethod
@override
def msg_format() -> str:
return _("Must be an organization administrator")
@ -266,6 +279,7 @@ class OrganizationOwnerRequiredError(JsonableError):
pass
@staticmethod
@override
def msg_format() -> str:
return _("Must be an organization owner")
@ -279,6 +293,7 @@ class AuthenticationFailedError(JsonableError):
pass
@staticmethod
@override
def msg_format() -> str:
return _("Your username or password is incorrect")
@ -287,6 +302,7 @@ class UserDeactivatedError(AuthenticationFailedError):
code: ErrorCode = ErrorCode.USER_DEACTIVATED
@staticmethod
@override
def msg_format() -> str:
return _("Account is deactivated")
@ -295,6 +311,7 @@ class RealmDeactivatedError(AuthenticationFailedError):
code: ErrorCode = ErrorCode.REALM_DEACTIVATED
@staticmethod
@override
def msg_format() -> str:
return _("This organization has been deactivated")
@ -303,6 +320,7 @@ class RemoteServerDeactivatedError(AuthenticationFailedError):
code: ErrorCode = ErrorCode.REALM_DEACTIVATED
@staticmethod
@override
def msg_format() -> str:
return _(
"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
@staticmethod
@override
def msg_format() -> str:
return _("Password authentication is disabled in this organization")
@ -321,6 +340,7 @@ class PasswordResetRequiredError(AuthenticationFailedError):
code: ErrorCode = ErrorCode.PASSWORD_RESET_REQUIRED
@staticmethod
@override
def msg_format() -> str:
return _("Your password has been disabled and needs to be reset")
@ -337,12 +357,14 @@ class InvalidAPIKeyError(JsonableError):
pass
@staticmethod
@override
def msg_format() -> str:
return _("Invalid API key")
class InvalidAPIKeyFormatError(InvalidAPIKeyError):
@staticmethod
@override
def msg_format() -> str:
return _("Malformed API key")
@ -381,6 +403,7 @@ class UnsupportedWebhookEventTypeError(WebhookError):
self.event_type = event_type
@staticmethod
@override
def msg_format() -> str:
return _(
"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
@staticmethod
@override
def msg_format() -> str:
return _("Unable to parse request: Did {webhook_name} generate this event?")
@ -424,6 +448,7 @@ class InvalidSubdomainError(JsonableError):
pass
@staticmethod
@override
def msg_format() -> str:
return _("Invalid subdomain")
@ -464,6 +489,7 @@ class AccessDeniedError(JsonableError):
pass
@staticmethod
@override
def msg_format() -> str:
return _("Access denied")
@ -502,6 +528,7 @@ class MessageMoveError(JsonableError):
self.total_messages_allowed_to_move = total_messages_allowed_to_move
@staticmethod
@override
def msg_format() -> str:
return _(
"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
@staticmethod
@override
def msg_format() -> str:
return _("Reaction already exists.")
@ -526,6 +554,7 @@ class ReactionDoesNotExistError(JsonableError):
pass
@staticmethod
@override
def msg_format() -> str:
return _("Reaction doesn't exist.")

View File

@ -13,6 +13,7 @@ from django.conf import settings
from django.core.cache import cache
from django.http import HttpRequest
from django.utils.timezone import now as timezone_now
from typing_extensions import override
class _RateLimitFilter:
@ -108,11 +109,13 @@ class EmailLimiter(_RateLimitFilter):
class ReturnTrue(logging.Filter):
@override
def filter(self, record: logging.LogRecord) -> bool:
return True
class RequireReallyDeployed(logging.Filter):
@override
def filter(self, record: logging.LogRecord) -> bool:
return settings.PRODUCTION
@ -193,6 +196,7 @@ class ZulipFormatter(logging.Formatter):
pieces.extend(["[%(zulip_origin)s]", "%(message)s"])
return " ".join(pieces)
@override
def format(self, record: logging.LogRecord) -> str:
if not hasattr(record, "zulip_decorated"):
record.zulip_level_abbrev = abbrev_log_levelname(record.levelname)
@ -202,6 +206,7 @@ class ZulipFormatter(logging.Formatter):
class ZulipWebhookFormatter(ZulipFormatter):
@override
def _compute_fmt(self) -> str:
basic = super()._compute_fmt()
multiline = [
@ -217,6 +222,7 @@ class ZulipWebhookFormatter(ZulipFormatter):
]
return "\n".join(multiline)
@override
def format(self, record: logging.LogRecord) -> str:
request: Optional[HttpRequest] = getattr(record, "request", 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.management.base import BaseCommand, CommandError, CommandParser
from django.db.models import Q, QuerySet
from typing_extensions import override
from zerver.lib.initial_password import initial_password
from zerver.models import Client, Realm, UserProfile, get_client
@ -45,6 +46,7 @@ class CreateUserParameters:
class ZulipBaseCommand(BaseCommand):
# Fix support for multi-line usage
@override
def create_parser(self, prog_name: str, subcommand: str, **kwargs: Any) -> CommandParser:
parser = super().create_parser(prog_name, subcommand, **kwargs)
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 soupsieve import escape as css_escape
from tlds import tld_set
from typing_extensions import TypeAlias
from typing_extensions import TypeAlias, override
from zerver.lib import mention
from zerver.lib.cache import cache_with_key
@ -524,6 +524,7 @@ class InlineImageProcessor(markdown.treeprocessors.Treeprocessor):
super().__init__(zmd)
self.zmd = zmd
@override
def run(self, root: Element) -> None:
# Get all URLs from the blob
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)
self.zmd = zmd
@override
def run(self, root: Element) -> None:
# Get all URLs from the blob
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):
"""Return a `<code>` element containing the matching text."""
@override
def handleMatch( # type: ignore[override] # https://github.com/python/mypy/issues/10197
self, m: Match[str], data: str
) -> Union[Tuple[None, None, None], Tuple[Element, int, int]]:
@ -1234,6 +1237,7 @@ class InlineInterestingLinkProcessor(markdown.treeprocessors.Treeprocessor):
if info["remove"] is not None:
info["parent"].remove(info["remove"])
@override
def run(self, root: Element) -> None:
# Get all URLs from the blob
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):
@override
def handleMatch(self, match: Match[str]) -> Optional[Element]:
time_input_string = match.group("time")
try:
@ -1475,6 +1480,7 @@ class EmoticonTranslation(markdown.inlinepatterns.Pattern):
super().__init__(pattern, zmd)
self.zmd = zmd
@override
def handleMatch(self, match: Match[str]) -> Optional[Element]:
db_data: Optional[DbData] = self.zmd.zulip_db_data
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):
@override
def handleMatch( # type: ignore[override] # https://github.com/python/mypy/issues/10197
self, match: Match[str], data: str
) -> Union[Tuple[None, None, None], Tuple[Element, int, int]]:
@ -1515,6 +1522,7 @@ class Emoji(markdown.inlinepatterns.Pattern):
super().__init__(pattern, zmd)
self.zmd = zmd
@override
def handleMatch(self, match: Match[str]) -> Optional[Union[str, Element]]:
orig_syntax = match.group("syntax")
name = orig_syntax[1:-1]
@ -1543,6 +1551,7 @@ def content_has_emoji_syntax(content: str) -> bool:
class Tex(markdown.inlinepatterns.Pattern):
@override
def handleMatch(self, match: Match[str]) -> Union[str, Element]:
rendered = render_tex(match.group("body"), is_inline=True)
if rendered is not None:
@ -1633,6 +1642,7 @@ class CompiledPattern(markdown.inlinepatterns.Pattern):
class AutoLink(CompiledPattern):
@override
def handleMatch(self, match: Match[str]) -> ElementStringNone:
url = match.group("url")
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}>[ ]?(.*)")
# run() is very slightly forked from the base class; see notes below.
@override
def run(self, parent: Element, blocks: List[str]) -> None:
block = blocks.pop(0)
m = self.RE.search(block)
@ -1716,6 +1727,7 @@ class BlockQuoteProcessor(markdown.blockprocessors.BlockQuoteProcessor):
self.parser.parseChunk(quote, block)
self.parser.state.reset()
@override
def clean(self, line: str) -> str:
# Silence all the mentions inside blockquotes
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)
@override
def run(self, lines: List[str]) -> List[str]:
"""Insert a newline between a paragraph and ulist if missing"""
inserts = 0
@ -1823,6 +1836,7 @@ class LinkifierPattern(CompiledInlineProcessor):
super().__init__(compiled_re2, zmd)
@override
def handleMatch( # type: ignore[override] # https://github.com/python/mypy/issues/10197
self, m: Match[str], data: str
) -> Union[Tuple[Element, int, int], Tuple[None, None, None]]:
@ -1843,6 +1857,7 @@ class LinkifierPattern(CompiledInlineProcessor):
class UserMentionPattern(CompiledInlineProcessor):
@override
def handleMatch( # type: ignore[override] # https://github.com/python/mypy/issues/10197
self, m: Match[str], data: str
) -> Union[Tuple[None, None, None], Tuple[Element, int, int]]:
@ -1906,6 +1921,7 @@ class UserMentionPattern(CompiledInlineProcessor):
class UserGroupMentionPattern(CompiledInlineProcessor):
@override
def handleMatch( # type: ignore[override] # https://github.com/python/mypy/issues/10197
self, m: Match[str], data: str
) -> Union[Tuple[None, None, None], Tuple[Element, int, int]]:
@ -1946,6 +1962,7 @@ class StreamPattern(CompiledInlineProcessor):
stream_id = db_data.stream_names.get(name)
return stream_id
@override
def handleMatch( # type: ignore[override] # https://github.com/python/mypy/issues/10197
self, m: Match[str], data: str
) -> Union[Tuple[None, None, None], Tuple[Element, int, int]]:
@ -1977,6 +1994,7 @@ class StreamTopicPattern(CompiledInlineProcessor):
stream_id = db_data.stream_names.get(name)
return stream_id
@override
def handleMatch( # type: ignore[override] # https://github.com/python/mypy/issues/10197
self, m: Match[str], data: str
) -> Union[Tuple[None, None, None], Tuple[Element, int, int]]:
@ -2041,6 +2059,7 @@ class AlertWordNotificationProcessor(markdown.preprocessors.Preprocessor):
return True
return False
@override
def run(self, lines: List[str]) -> List[str]:
db_data: Optional[DbData] = self.zmd.zulip_db_data
if db_data is not None:
@ -2096,6 +2115,7 @@ class LinkInlineProcessor(markdown.inlinepatterns.LinkInlineProcessor):
return el
@override
def handleMatch( # type: ignore[override] # https://github.com/python/mypy/issues/10197
self, m: Match[str], data: str
) -> Union[Tuple[None, None, None], Tuple[Element, int, int]]:
@ -2156,6 +2176,7 @@ class ZulipMarkdown(markdown.Markdown):
)
self.set_output_format("html")
@override
def build_parser(self) -> markdown.Markdown:
# Build the parser using selected default features from Python-Markdown.
# 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 markdown.extensions import Extension
from markdown.preprocessors import Preprocessor
from typing_extensions import override
from zerver.lib.markdown.priorities import PREPROCESSOR_PRIORITES
from zerver.openapi.openapi import (
@ -49,6 +50,7 @@ OBJECT_CODE_TEMPLATE = "<code>{value}</code>".strip()
class MarkdownArgumentsTableGenerator(Extension):
@override
def extendMarkdown(self, md: markdown.Markdown) -> None:
md.preprocessors.register(
APIArgumentsTablePreprocessor(md, self.getConfigs()),
@ -61,6 +63,7 @@ class APIArgumentsTablePreprocessor(Preprocessor):
def __init__(self, md: markdown.Markdown, config: Mapping[str, Any]) -> None:
super().__init__(md)
@override
def run(self, lines: List[str]) -> List[str]:
done = False
while not done:

View File

@ -6,6 +6,7 @@ from typing import Any, Dict, List, Mapping, Optional
import markdown
from markdown.extensions import Extension
from markdown.preprocessors import Preprocessor
from typing_extensions import override
from zerver.lib.markdown.priorities import PREPROCESSOR_PRIORITES
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):
@override
def extendMarkdown(self, md: markdown.Markdown) -> None:
md.preprocessors.register(
APIReturnValuesTablePreprocessor(md, self.getConfigs()),
@ -28,6 +30,7 @@ class APIReturnValuesTablePreprocessor(Preprocessor):
def __init__(self, md: markdown.Markdown, config: Mapping[str, Any]) -> None:
super().__init__(md)
@override
def run(self, lines: List[str]) -> List[str]:
done = False
while not done:

View File

@ -86,6 +86,7 @@ from markdown.extensions.codehilite import CodeHiliteExtension, parse_hl_lines
from markdown.preprocessors import Preprocessor
from pygments.lexers import find_lexer_class_by_name
from pygments.util import ClassNotFound
from typing_extensions import override
from zerver.lib.exceptions import MarkdownRenderingError
from zerver.lib.markdown.priorities import PREPROCESSOR_PRIORITES
@ -156,6 +157,7 @@ class FencedCodeExtension(Extension):
for key, value in config.items():
self.setConfig(key, value)
@override
def extendMarkdown(self, md: Markdown) -> None:
"""Add FencedBlockPreprocessor to the Markdown instance."""
md.registerExtension(self)
@ -268,6 +270,7 @@ class OuterHandler(ZulipBaseHandler):
self.default_language = default_language
super().__init__(processor, output)
@override
def handle_line(self, line: str) -> None:
check_for_new_fence(
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
super().__init__(processor, output, fence)
@override
def done(self) -> None:
# run content validators (if any)
if self.run_content_validators:
@ -294,6 +298,7 @@ class CodeHandler(ZulipBaseHandler):
validator(self.lines)
super().done()
@override
def format_text(self, text: str) -> str:
return self.processor.format_code(self.lang, text)
@ -309,6 +314,7 @@ class QuoteHandler(ZulipBaseHandler):
self.default_language = default_language
super().__init__(processor, output, fence, process_contents=True)
@override
def handle_line(self, line: str) -> None:
if line.rstrip() == self.fence:
self.done()
@ -317,6 +323,7 @@ class QuoteHandler(ZulipBaseHandler):
self.processor, self.lines, line, default_language=self.default_language
)
@override
def format_text(self, text: str) -> str:
return self.processor.format_quote(text)
@ -332,17 +339,20 @@ class SpoilerHandler(ZulipBaseHandler):
self.spoiler_header = spoiler_header
super().__init__(processor, output, fence, process_contents=True)
@override
def handle_line(self, line: str) -> None:
if line.rstrip() == self.fence:
self.done()
else:
check_for_new_fence(self.processor, self.lines, line)
@override
def format_text(self, text: str) -> str:
return self.processor.format_spoiler(self.spoiler_header, text)
class TexHandler(ZulipBaseHandler):
@override
def format_text(self, text: str) -> str:
return self.processor.format_tex(text)
@ -411,6 +421,7 @@ class FencedBlockPreprocessor(Preprocessor):
def pop(self) -> None:
self.handlers.pop()
@override
def run(self, lines: Iterable[str]) -> List[str]:
"""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.extensions import Extension
from markdown.preprocessors import Preprocessor
from typing_extensions import override
from zerver.lib.emoji import EMOTICON_CONVERSIONS, name_to_codepoint
from zerver.lib.markdown.priorities import PREPROCESSOR_PRIORITES
@ -38,6 +39,7 @@ ROW_HTML = """\
class EmoticonTranslationsHelpExtension(Extension):
@override
def extendMarkdown(self, md: Markdown) -> None:
"""Add SettingHelpExtension to the Markdown instance."""
md.registerExtension(self)
@ -49,6 +51,7 @@ class EmoticonTranslationsHelpExtension(Extension):
class EmoticonTranslation(Preprocessor):
@override
def run(self, lines: List[str]) -> List[str]:
for loc, line in enumerate(lines):
match = REGEXP.search(line)

View File

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

View File

@ -4,6 +4,7 @@ from typing import Any, List, Match
from markdown import Markdown
from markdown.extensions import Extension
from markdown.preprocessors import Preprocessor
from typing_extensions import override
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):
@override
def extendMarkdown(self, md: Markdown) -> None:
"""Add SettingHelpExtension to the Markdown instance."""
md.registerExtension(self)
@ -146,6 +148,7 @@ def set_relative_settings_links(value: bool) -> None:
class Setting(Preprocessor):
@override
def run(self, lines: List[str]) -> List[str]:
done = False
while not done:

View File

@ -6,6 +6,7 @@ from xml.etree.ElementTree import Element
from markdown import Extension, Markdown
from markdown.blockparser import BlockParser
from markdown.blockprocessors import BlockProcessor
from typing_extensions import override
from zerver.lib.exceptions import InvalidMarkdownIncludeStatementError
from zerver.lib.markdown.priorities import BLOCK_PROCESSOR_PRIORITIES
@ -16,6 +17,7 @@ class IncludeExtension(Extension):
super().__init__()
self.base_path = base_path
@override
def extendMarkdown(self, md: Markdown) -> None:
md.parser.blockprocessors.register(
IncludeBlockProcessor(md.parser, self.base_path),
@ -31,6 +33,7 @@ class IncludeBlockProcessor(BlockProcessor):
super().__init__(parser)
self.base_path = base_path
@override
def test(self, parent: Element, block: str) -> bool:
return bool(self.RE.search(block))
@ -46,6 +49,7 @@ class IncludeBlockProcessor(BlockProcessor):
return "\n".join(lines)
@override
def run(self, parent: Element, blocks: List[str]) -> None:
self.parser.state.set("include")
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
from markdown.extensions import Extension
from typing_extensions import override
from zerver.lib.markdown import ResultWithFamily, walk_tree_with_family
from zerver.lib.markdown.priorities import PREPROCESSOR_PRIORITES
class NestedCodeBlocksRenderer(Extension):
@override
def extendMarkdown(self, md: markdown.Markdown) -> None:
md.treeprocessors.register(
NestedCodeBlocksRendererTreeProcessor(md, self.getConfigs()),
@ -21,6 +23,7 @@ class NestedCodeBlocksRendererTreeProcessor(markdown.treeprocessors.Treeprocesso
def __init__(self, md: markdown.Markdown, config: Mapping[str, Any]) -> None:
super().__init__(md)
@override
def run(self, root: Element) -> None:
code_tags = walk_tree_with_family(root, self.get_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
from django.contrib.staticfiles.storage import staticfiles_storage
from markdown.extensions import Extension
from typing_extensions import override
from zerver.lib.markdown.priorities import PREPROCESSOR_PRIORITES
class MarkdownStaticImagesGenerator(Extension):
@override
def extendMarkdown(self, md: markdown.Markdown) -> None:
md.treeprocessors.register(
StaticImageProcessor(md),
@ -22,6 +24,7 @@ class StaticImageProcessor(markdown.treeprocessors.Treeprocessor):
Rewrite img tags which refer to /static/ to use staticfiles
"""
@override
def run(self, root: Element) -> None:
for img in root.iter("img"):
url = img.get("src")

View File

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

View File

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

View File

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

View File

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

View File

@ -9,6 +9,7 @@ import requests
from django.conf import settings
from django.utils.translation import gettext as _
from requests import Response
from typing_extensions import override
from version import ZULIP_VERSION
from zerver.actions.message_send import check_send_message
@ -52,6 +53,7 @@ class OutgoingWebhookServiceInterface(metaclass=abc.ABCMeta):
class GenericOutgoingWebhookService(OutgoingWebhookServiceInterface):
@override
def make_request(
self, base_url: str, event: Dict[str, Any], realm: Realm
) -> Optional[Response]:
@ -82,6 +84,7 @@ class GenericOutgoingWebhookService(OutgoingWebhookServiceInterface):
return self.session.post(base_url, json=request_data)
@override
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"]:
return None
@ -103,6 +106,7 @@ class GenericOutgoingWebhookService(OutgoingWebhookServiceInterface):
class SlackOutgoingWebhookService(OutgoingWebhookServiceInterface):
@override
def make_request(
self, base_url: str, event: Dict[str, Any], realm: Realm
) -> Optional[Response]:
@ -142,6 +146,7 @@ class SlackOutgoingWebhookService(OutgoingWebhookServiceInterface):
]
return self.session.post(base_url, data=request_data)
@override
def process_success(self, response_json: Dict[str, Any]) -> Optional[Dict[str, Any]]:
if "text" in response_json:
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.translation import gettext as _
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.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
return Q(user_uuid=self.user_uuid) | Q(user_id=self.user_id)
@override
def __str__(self) -> str:
result = ""
if self.user_id is not None:
@ -124,6 +125,7 @@ class UserPushIdentityCompat:
return result
@override
def __eq__(self, other: object) -> bool:
if isinstance(other, UserPushIdentityCompat):
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.spec import Basic
from tornado import ioloop
from typing_extensions import TypeAlias
from typing_extensions import TypeAlias, override
from zerver.lib.utils import assert_is_not_none
@ -147,6 +147,7 @@ class QueueClient(Generic[ChannelT], metaclass=ABCMeta):
class SimpleQueueClient(QueueClient[BlockingChannel]):
connection: Optional[pika.BlockingConnection]
@override
def _connect(self) -> None:
start = time.time()
self.connection = pika.BlockingConnection(self._get_parameters())
@ -154,6 +155,7 @@ class SimpleQueueClient(QueueClient[BlockingChannel]):
self.channel.basic_qos(prefetch_count=self.prefetch)
self.log.info("SimpleQueueClient connected (connecting took %.3fs)", time.time() - start)
@override
def _reconnect(self) -> None:
self.connection = None
self.channel = None
@ -164,6 +166,7 @@ class SimpleQueueClient(QueueClient[BlockingChannel]):
if self.connection is not None:
self.connection.close()
@override
def ensure_queue(self, queue_name: str, callback: Callable[[BlockingChannel], object]) -> None:
"""Ensure that a given queue has been declared, and then call
the callback with no arguments."""
@ -271,6 +274,7 @@ class TornadoQueueClient(QueueClient[Channel]):
self._on_open_cbs: List[Callable[[Channel], None]] = []
self._connection_failure_count = 0
@override
def _connect(self) -> None:
self.log.info("Beginning TornadoQueueClient connection")
self.connection = ExceptionFreeTornadoConnection(
@ -280,6 +284,7 @@ class TornadoQueueClient(QueueClient[Channel]):
on_close_callback=self._on_connection_closed,
)
@override
def _reconnect(self) -> None:
self.connection = None
self.channel = None
@ -350,6 +355,7 @@ class TornadoQueueClient(QueueClient[Channel]):
self.connection.close()
self.connection = None
@override
def ensure_queue(self, queue_name: str, callback: Callable[[Channel], object]) -> None:
def set_qos(frame: Any) -> None:
assert self.channel is not None

View File

@ -9,6 +9,7 @@ import redis
from circuitbreaker import CircuitBreakerError, circuit
from django.conf import settings
from django.http import HttpRequest
from typing_extensions import override
from zerver.lib.cache import cache_with_key
from zerver.lib.exceptions import RateLimitedError
@ -122,9 +123,11 @@ class RateLimitedUser(RateLimitedObject):
backend = None
super().__init__(backend=backend)
@override
def key(self) -> str:
return f"{type(self).__name__}:{self.user_id}:{self.domain}"
@override
def rules(self) -> List[Tuple[int, int]]:
# user.rate_limits are general limits, applicable to the domain 'api_by_user'
if self.rate_limits != "" and self.domain == "api_by_user":
@ -146,10 +149,12 @@ class RateLimitedIPAddr(RateLimitedObject):
backend = None
super().__init__(backend=backend)
@override
def key(self) -> str:
# The angle brackets are important since IPv6 addresses contain :.
return f"{type(self).__name__}:<{self.ip_addr}>:{self.domain}"
@override
def rules(self) -> List[Tuple[int, int]]:
return rules[self.domain]
@ -257,6 +262,7 @@ class TornadoInMemoryRateLimiterBackend(RateLimiterBackend):
return False, 0.0
@classmethod
@override
def get_api_calls_left(
cls, entity_key: str, range_seconds: int, max_calls: int
) -> Tuple[int, float]:
@ -272,21 +278,25 @@ class TornadoInMemoryRateLimiterBackend(RateLimiterBackend):
return int(calls_remaining), reset_time - now
@classmethod
@override
def block_access(cls, entity_key: str, seconds: int) -> None:
now = time.time()
cls.timestamps_blocked_until[entity_key] = now + seconds
@classmethod
@override
def unblock_access(cls, entity_key: str) -> None:
del cls.timestamps_blocked_until[entity_key]
@classmethod
@override
def clear_history(cls, entity_key: str) -> None:
for reset_times_for_rule in cls.reset_times.values():
reset_times_for_rule.pop(entity_key, None)
cls.timestamps_blocked_until.pop(entity_key, None)
@classmethod
@override
def rate_limit_entity(
cls, entity_key: str, rules: List[Tuple[int, int]], max_api_calls: int, max_api_window: int
) -> Tuple[bool, float]:
@ -317,6 +327,7 @@ class RedisRateLimiterBackend(RateLimiterBackend):
]
@classmethod
@override
def block_access(cls, entity_key: str, seconds: int) -> None:
"""Manually blocks an entity for the desired number of seconds"""
_, _, blocking_key = cls.get_keys(entity_key)
@ -326,16 +337,19 @@ class RedisRateLimiterBackend(RateLimiterBackend):
pipe.execute()
@classmethod
@override
def unblock_access(cls, entity_key: str) -> None:
_, _, blocking_key = cls.get_keys(entity_key)
client.delete(blocking_key)
@classmethod
@override
def clear_history(cls, entity_key: str) -> None:
for key in cls.get_keys(entity_key):
client.delete(key)
@classmethod
@override
def get_api_calls_left(
cls, entity_key: str, range_seconds: int, max_calls: int
) -> Tuple[int, float]:
@ -467,6 +481,7 @@ class RedisRateLimiterBackend(RateLimiterBackend):
continue
@classmethod
@override
def rate_limit_entity(
cls, entity_key: str, rules: List[Tuple[int, int]], max_api_calls: int, max_api_window: int
) -> Tuple[bool, float]:
@ -501,9 +516,11 @@ class RateLimitedSpectatorAttachmentAccessByFile(RateLimitedObject):
self.path_id = path_id
super().__init__()
@override
def key(self) -> str:
return f"{type(self).__name__}:{self.path_id}"
@override
def rules(self) -> List[Tuple[int, int]]:
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.http import HttpRequest, HttpResponse
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.exceptions import ErrorCode, InvalidJSONError, JsonableError
@ -74,6 +74,7 @@ class RequestNotes(BaseNotes[HttpRequest, "RequestNotes"]):
is_webhook_view: bool = False
@classmethod
@override
def init_notes(cls) -> "RequestNotes":
return RequestNotes()
@ -87,6 +88,7 @@ class RequestConfusingParamsError(JsonableError):
self.var_name2: str = var_name2
@staticmethod
@override
def msg_format() -> str:
return _("Can't decide between '{var_name1}' and '{var_name2}' arguments")
@ -99,6 +101,7 @@ class RequestVariableMissingError(JsonableError):
self.var_name: str = var_name
@staticmethod
@override
def msg_format() -> str:
return _("Missing '{var_name}' argument")
@ -112,6 +115,7 @@ class RequestVariableConversionError(JsonableError):
self.bad_value = bad_value
@staticmethod
@override
def msg_format() -> str:
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
from django.http import HttpRequest, HttpResponse, HttpResponseNotAllowed
from typing_extensions import override
from zerver.lib.exceptions import JsonableError, UnauthorizedError
@ -36,6 +37,7 @@ class MutableJsonResponse(HttpResponse):
# is used here to encompass all of those return values.
# See https://github.com/typeddjango/django-stubs/commit/799b41fe47cfe2e56be33eee8cfbaf89a9853a8e
# and https://github.com/python/mypy/issues/3004.
@override # type: ignore[explicit-override] # https://github.com/python/mypy/issues/15900
@property
def content(self) -> Any:
"""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
# our lazy content generation, we override the iterator to access `self.content`
# through our getter.
@override
def __iter__(self) -> Iterator[bytes]:
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.db.transaction import get_connection
from typing_extensions import override
class SessionStore(CachedDbSessionStore):
@ -16,10 +17,12 @@ class SessionStore(CachedDbSessionStore):
"""
@override
def save(self, must_create: bool = False) -> None:
assert not get_connection().in_atomic_block
super().save(must_create)
@override
def delete(self, session_key: Optional[str] = None) -> None:
assert not get_connection().in_atomic_block
super().delete(session_key)

View File

@ -4,6 +4,7 @@ from typing import Iterator, Optional
import sqlalchemy
from django.db import connection
from sqlalchemy.engine import Connection, Engine
from typing_extensions import override
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
# existing Django database connections.
class NonClosingPool(sqlalchemy.pool.NullPool):
@override
def status(self) -> str:
return "NonClosingPool"

View File

@ -8,6 +8,7 @@ from django.conf import settings
from django.contrib.staticfiles.storage import ManifestStaticFilesStorage
from django.core.files.base import File
from django.core.files.storage import FileSystemStorage
from typing_extensions import override
if settings.DEBUG:
from django.contrib.staticfiles.finders import find
@ -22,6 +23,7 @@ else:
class IgnoreBundlesManifestStaticFilesStorage(ManifestStaticFilesStorage):
@override
def hashed_name(
self, name: str, content: Optional["File[bytes]"] = None, filename: Optional[str] = None
) -> 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 fakeldap import MockLDAP
from two_factor.plugins.phonenumber.models import PhoneDevice
from typing_extensions import override
from corporate.models import Customer, CustomerPlan, LicenseLedger
from zerver.actions.message_send import check_send_message, check_send_stream_message
@ -133,6 +134,7 @@ class UploadSerializeMixin(SerializeMixin):
lockfile = "var/upload_lock"
@classmethod
@override
def setUpClass(cls: Any) -> None:
if not os.path.exists(cls.lockfile):
with open(cls.lockfile, "w"): # nocoverage - rare locking case
@ -149,6 +151,7 @@ class ZulipTestCaseMixin(SimpleTestCase):
# expectation.
expected_console_output: Optional[str] = None
@override
def setUp(self) -> None:
super().setUp()
self.API_KEYS: Dict[str, str] = {}
@ -157,6 +160,7 @@ class ZulipTestCaseMixin(SimpleTestCase):
bounce_key_prefix_for_testing(test_name)
bounce_redis_key_prefix_for_testing(test_name)
@override
def tearDown(self) -> None:
super().tearDown()
# 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:
return get_user(email, realm)
@override
def run(self, result: Optional[TestResult] = None) -> Optional[TestResult]: # nocoverage
if not settings.BAN_CONSOLE_OUTPUT and self.expected_console_output is None:
return super().run(result)
@ -1975,10 +1980,12 @@ class ZulipTransactionTestCase(ZulipTestCaseMixin, TransactionTestCase):
ZulipTransactionTestCase tests if they leak state.
"""
@override
def setUp(self) -> None:
super().setUp()
self.models_ids_set = dict(get_row_ids_in_all_tables())
@override
def tearDown(self) -> None:
"""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
@ -2029,6 +2036,7 @@ class WebhookTestCase(ZulipTestCase):
def test_user(self) -> UserProfile:
return self.get_user_from_email(self.TEST_USER_EMAIL, get_realm("zulip"))
@override
def setUp(self) -> None:
super().setUp()
self.url = self.build_webhook_url()
@ -2274,6 +2282,7 @@ class MigrationsTestCase(ZulipTestCase): # nocoverage
migrate_from: Optional[str] = None
migrate_to: Optional[str] = None
@override
def setUp(self) -> None:
assert (
self.migrate_from and self.migrate_to

View File

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

View File

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

View File

@ -6,12 +6,15 @@ import time
from types import TracebackType
from typing import Callable, Optional, Tuple, Type, TypeVar
from typing_extensions import override
# Based on https://code.activestate.com/recipes/483752/
class TimeoutExpiredError(Exception):
"""Exception raised when a function times out."""
@override
def __str__(self) -> str:
return "Function call timed out."
@ -49,6 +52,7 @@ def timeout(timeout: float, func: Callable[[], ResultT]) -> ResultT:
# if this is the only thread left.
self.daemon = True
@override
def run(self) -> None:
try:
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 django.conf import settings
from typing_extensions import override
from zerver.lib.avatar_hash import user_avatar_path
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):
@override
def get_public_upload_root_url(self) -> str:
return "/user_avatars/"
@override
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
return "/".join(
@ -80,6 +83,7 @@ class LocalUploadBackend(ZulipUploadBackend):
]
)
@override
def upload_message_attachment(
self,
uploaded_file_name: str,
@ -98,12 +102,15 @@ class LocalUploadBackend(ZulipUploadBackend):
create_attachment(uploaded_file_name, path, user_profile, target_realm, uploaded_file_size)
return "/user_uploads/" + path
@override
def save_attachment_contents(self, path_id: str, filehandle: BinaryIO) -> None:
filehandle.write(read_local_file("files", path_id))
@override
def delete_message_attachment(self, path_id: str) -> bool:
return delete_local_file("files", path_id)
@override
def all_message_attachments(self) -> Iterator[Tuple[str, datetime]]:
assert settings.LOCAL_UPLOADS_DIR is not None
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)),
)
@override
def get_avatar_url(self, hash_key: str, medium: bool = False) -> str:
medium_suffix = "-medium" if medium else ""
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)
write_local_file("avatars", file_path + "-medium.png", resized_medium)
@override
def upload_avatar_image(
self,
user_file: IO[bytes],
@ -139,6 +148,7 @@ class LocalUploadBackend(ZulipUploadBackend):
image_data = user_file.read()
self.write_avatar_images(file_path, image_data)
@override
def copy_avatar(self, source_profile: UserProfile, target_profile: UserProfile) -> None:
source_file_path = user_avatar_path(source_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")
self.write_avatar_images(target_file_path, image_data)
@override
def ensure_avatar_image(self, user_profile: UserProfile, is_medium: bool = False) -> None:
file_extension = "-medium.png" if is_medium else ".png"
file_path = user_avatar_path(user_profile)
@ -169,6 +180,7 @@ class LocalUploadBackend(ZulipUploadBackend):
resized_avatar = resize_avatar(image_data)
write_local_file("avatars", file_path + file_extension, resized_avatar)
@override
def delete_avatar_image(self, user: UserProfile) -> None:
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 + "-medium.png")
@override
def get_realm_icon_url(self, realm_id: int, version: int) -> str:
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:
upload_path = self.realm_avatar_and_logo_path(user_profile.realm)
image_data = icon_file.read()
@ -187,6 +201,7 @@ class LocalUploadBackend(ZulipUploadBackend):
resized_data = resize_avatar(image_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:
if night:
file_name = "night_logo.png"
@ -194,6 +209,7 @@ class LocalUploadBackend(ZulipUploadBackend):
file_name = "logo.png"
return f"/user_avatars/{realm_id}/realm/{file_name}?version={version}"
@override
def upload_realm_logo_image(
self, logo_file: IO[bytes], user_profile: UserProfile, night: bool
) -> None:
@ -210,6 +226,7 @@ class LocalUploadBackend(ZulipUploadBackend):
resized_data = resize_logo(image_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:
if still:
return os.path.join(
@ -227,6 +244,7 @@ class LocalUploadBackend(ZulipUploadBackend):
),
)
@override
def upload_emoji_image(
self, emoji_file: IO[bytes], emoji_file_name: str, user_profile: UserProfile
) -> bool:
@ -248,10 +266,12 @@ class LocalUploadBackend(ZulipUploadBackend):
write_local_file("avatars", still_path, still_image_data)
return is_animated
@override
def get_export_tarball_url(self, realm: Realm, export_path: str) -> str:
# export_path has a leading `/`
return realm.uri + export_path
@override
def upload_export_tarball(
self,
realm: Realm,
@ -270,6 +290,7 @@ class LocalUploadBackend(ZulipUploadBackend):
public_url = realm.uri + "/user_avatars/" + path
return public_url
@override
def delete_export_tarball(self, export_path: str) -> Optional[str]:
# Get the last element of a list in the form ['user_avatars', '<file_path>']
assert export_path.startswith("/")

View File

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

View File

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

View File

@ -1,11 +1,14 @@
from urllib.parse import urlparse
from typing_extensions import override
from zerver.lib.url_preview.types import UrlEmbedData
from .base import BaseParser
class OpenGraphParser(BaseParser):
@override
def extract_data(self) -> UrlEmbedData:
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 pydantic import ValidationInfo, model_validator
from pydantic.functional_validators import ModelWrapValidatorHandler
from typing_extensions import override
from zerver.lib.exceptions import InvalidJSONError, JsonableError
from zerver.lib.timezone import canonicalize_timezone
@ -648,6 +649,7 @@ class WildValue:
def __bool__(self) -> bool:
return bool(self.value)
@override
def __eq__(self, other: object) -> bool:
return self.value == other
@ -658,6 +660,7 @@ class WildValue:
)
return len(self.value)
@override
def __str__(self) -> NoReturn:
raise TypeError("cannot convert WildValue to string; try .tame(check_string)")
@ -698,10 +701,12 @@ class WildValue:
class WildValueList(WildValue):
value: List[object]
@override
def __iter__(self) -> Iterator[WildValue]:
for i, item in enumerate(self.value):
yield wrap_wild_value(f"{self.var_name}[{i}]", item)
@override
def __getitem__(self, key: Union[int, str]) -> WildValue:
if not isinstance(key, int):
return super().__getitem__(key)
@ -719,9 +724,11 @@ class WildValueList(WildValue):
class WildValueDict(WildValue):
value: Dict[str, object]
@override
def __contains__(self, key: str) -> bool:
return key in self.value
@override
def __getitem__(self, key: Union[int, str]) -> WildValue:
if not isinstance(key, str):
return super().__getitem__(key)
@ -735,19 +742,23 @@ class WildValueDict(WildValue):
return wrap_wild_value(var_name, item)
@override
def get(self, key: str, default: object = None) -> WildValue:
item = self.value.get(key, default)
if isinstance(item, WildValue):
return item
return wrap_wild_value(f"{self.var_name}[{key!r}]", item)
@override
def keys(self) -> Iterator[str]:
yield from self.value.keys()
@override
def values(self) -> Iterator[WildValue]:
for key, value in self.value.items():
yield wrap_wild_value(f"{self.var_name}[{key!r}]", value)
@override
def items(self) -> Iterator[Tuple[str, WildValue]]:
for key, value in self.value.items():
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.utils.translation import gettext as _
from pydantic import Json
from typing_extensions import Annotated, TypeAlias
from typing_extensions import Annotated, TypeAlias, override
from zerver.actions.message_send import (
check_send_private_message,
@ -74,6 +74,7 @@ class MissingHTTPEventHeaderError(AnomalousWebhookPayloadError):
self.header = header
@staticmethod
@override
def msg_format() -> str:
return _("Missing the HTTP event header '{header}'")

View File

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

View File

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

View File

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

View File

@ -8,6 +8,7 @@ from django.conf import settings
from django.core.management.base import CommandParser
from django.db import connection
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 version import ZULIP_VERSION
@ -17,16 +18,19 @@ from zerver.logging_handlers import try_git_describe
class Command(ZulipBaseCommand):
# Fix support for multi-line usage strings
@override
def create_parser(self, prog_name: str, subcommand: str, **kwargs: Any) -> CommandParser:
parser = super().create_parser(prog_name, subcommand, **kwargs)
parser.formatter_class = RawTextHelpFormatter
return parser
@override
def add_arguments(self, parser: ArgumentParser) -> None:
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-uploads", action="store_true", help="Skip uploads backup")
@override
def handle(self, *args: Any, **options: Any) -> None:
timestamp = timezone_now().strftime(TIMESTAMP_FORMAT)
with tempfile.TemporaryDirectory(

View File

@ -2,6 +2,7 @@ from argparse import ArgumentParser
from typing import Any
from django.core.management.base import CommandError
from typing_extensions import override
from zerver.actions.user_settings import do_change_full_name
from zerver.lib.management import ZulipBaseCommand
@ -10,6 +11,7 @@ from zerver.lib.management import ZulipBaseCommand
class Command(ZulipBaseCommand):
help = """Change the names for many users."""
@override
def add_arguments(self, parser: ArgumentParser) -> None:
parser.add_argument(
"data_file",
@ -18,6 +20,7 @@ class Command(ZulipBaseCommand):
)
self.add_realm_args(parser, required=True)
@override
def handle(self, *args: Any, **options: str) -> None:
data_file = options["data_file"]
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.core.exceptions import ValidationError
from django.core.management.base import CommandError
from typing_extensions import override
from zerver.lib.management import ZulipBaseCommand
@ -27,10 +28,12 @@ class Command(ZulipBaseCommand):
raise CommandError("aborted")
return p
@override
def add_arguments(self, parser: ArgumentParser) -> None:
parser.add_argument("email", metavar="<email>", help="email of user to change role")
self.add_realm_args(parser, required=True)
@override
def handle(self, *args: Any, **options: Any) -> str:
email = options["email"]
realm = self.get_realm(options)

View File

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

View File

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

View File

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

View File

@ -5,6 +5,7 @@ from typing import Any, Callable, Optional
from django.conf import settings
from django.core.management.base import BaseCommand, CommandError, CommandParser
from returns.curry import partial
from typing_extensions import override
from zerver.lib.rate_limiter import RateLimitedUser, client
from zerver.models import get_user_profile_by_id
@ -16,6 +17,7 @@ class Command(BaseCommand):
Usage: ./manage.py [--trim] check_redis"""
@override
def add_arguments(self, parser: CommandParser) -> None:
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())
trim_func(key, max_calls)
@override
def handle(self, *args: Any, **options: Any) -> None:
if not settings.RATE_LIMITING:
raise CommandError("This machine is not using Redis or rate limiting, aborting")

View File

@ -1,6 +1,7 @@
from typing import Any
from django.core.management.base import BaseCommand
from typing_extensions import override
from zerver.lib.management import check_config
@ -8,5 +9,6 @@ from zerver.lib.management import check_config
class Command(BaseCommand):
help = """Checks /etc/zulip/settings.py for common configuration issues."""
@override
def handle(self, *args: Any, **options: Any) -> None:
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 to_language
from pyuca import Collator
from typing_extensions import override
class Command(compilemessages.Command):
@override
def add_arguments(self, parser: CommandParser) -> None:
super().add_arguments(parser)
@ -24,6 +26,7 @@ class Command(compilemessages.Command):
"--strict", "-s", action="store_true", help="Stop execution in case of errors."
)
@override
def handle(self, *args: Any, **options: Any) -> None:
super().handle(*args, **options)
self.strict = options["strict"]

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +1,8 @@
from argparse import ArgumentParser
from typing import Any
from typing_extensions import override
from zerver.lib.management import ZulipBaseCommand
from zerver.lib.streams import ensure_stream
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
"""
@override
def add_arguments(self, parser: ArgumentParser) -> None:
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."
)
@override
def handle(self, *args: Any, **options: Any) -> None:
realm = self.get_realm(options)
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.management.base import CommandError
from typing_extensions import override
from zerver.actions.create_realm import do_create_realm
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`.
"""
@override
def add_arguments(self, parser: argparse.ArgumentParser) -> None:
parser.add_argument("realm_name", help="Name for the new organization")
parser.add_argument(
@ -44,6 +46,7 @@ workflow as `./manage.py create_user`.
)
self.add_create_user_args(parser)
@override
def handle(self, *args: Any, **options: Any) -> None:
realm_name = options["realm_name"]
string_id = options["string_id"]

View File

@ -1,6 +1,7 @@
from typing import Any
from django.core.management.base import BaseCommand
from typing_extensions import override
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.
"""
@override
def handle(self, *args: Any, **options: Any) -> None:
create_if_missing_realm_internal_bots()
# create_users is idempotent -- it's a no-op when a given email

View File

@ -1,6 +1,8 @@
from argparse import ArgumentParser
from typing import Any
from typing_extensions import override
from zerver.lib.management import ZulipBaseCommand
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
the command."""
@override
def add_arguments(self, parser: ArgumentParser) -> None:
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")
@override
def handle(self, *args: Any, **options: str) -> None:
realm = self.get_realm(options)
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.db.utils import IntegrityError
from typing_extensions import override
from zerver.actions.create_user import do_create_user
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.
"""
@override
def add_arguments(self, parser: argparse.ArgumentParser) -> None:
self.add_create_user_args(parser)
self.add_realm_args(
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:
realm = self.get_realm(options)
assert realm is not None # Should be ensured by parser

View File

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

View File

@ -2,6 +2,7 @@ from argparse import ArgumentParser
from typing import Any
from django.core.management.base import CommandError
from typing_extensions import override
from zerver.actions.users import do_deactivate_user
from zerver.lib.management import ZulipBaseCommand
@ -12,6 +13,7 @@ from zerver.lib.users import get_active_bots_owned_by_user
class Command(ZulipBaseCommand):
help = "Deactivate a user, including forcibly logging them out."
@override
def add_arguments(self, parser: ArgumentParser) -> None:
parser.add_argument(
"-f",
@ -22,6 +24,7 @@ class Command(ZulipBaseCommand):
parser.add_argument("email", metavar="<email>", help="email of user to deactivate")
self.add_realm_args(parser)
@override
def handle(self, *args: Any, **options: Any) -> None:
realm = self.get_realm(options)
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.utils.timezone import now as timezone_now
from typing_extensions import override
from zerver.actions.uploads import do_delete_old_unclaimed_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.
The default is five weeks."""
@override
def add_arguments(self, parser: ArgumentParser) -> None:
parser.add_argument(
"-w",
@ -40,6 +42,7 @@ class Command(BaseCommand):
"any files which are not in the database. This may take a very long time!",
)
@override
def handle(self, *args: Any, **options: Any) -> None:
delta_weeks = options["delta_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.core.management.base import CommandError
from typing_extensions import override
from zerver.actions.realm_settings import do_delete_all_realm_attachments
from zerver.lib.management import ZulipBaseCommand
@ -13,9 +14,11 @@ class Command(ZulipBaseCommand):
help = """Script to permanently delete a realm. Recommended only for removing
realms used for testing; consider using deactivate_realm instead."""
@override
def add_arguments(self, parser: ArgumentParser) -> None:
self.add_realm_args(parser, required=True)
@override
def handle(self, *args: Any, **options: str) -> None:
realm = self.get_realm(options)
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 django.core.management.base import CommandError
from typing_extensions import override
from zerver.actions.users import do_delete_user
from zerver.lib.management import ZulipBaseCommand
@ -38,6 +39,7 @@ This will:
sent/received by them, you can use the command on them individually.
"""
@override
def add_arguments(self, parser: ArgumentParser) -> None:
parser.add_argument(
"-f",
@ -48,6 +50,7 @@ This will:
self.add_realm_args(parser)
self.add_user_list_args(parser)
@override
def handle(self, *args: Any, **options: Any) -> None:
realm = self.get_realm(options)
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.db import transaction
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.send_email import EmailNotDeliveredError, deliver_scheduled_emails
@ -31,6 +32,7 @@ Run this command under supervisor.
Usage: ./manage.py deliver_scheduled_emails
"""
@override
def handle(self, *args: Any, **options: Any) -> None:
try:
while True:

View File

@ -6,6 +6,7 @@ from typing import Any
from django.conf import settings
from django.core.management.base import BaseCommand
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.lib.logging_util import log_to_file
@ -24,6 +25,7 @@ This management command is run via supervisor.
Usage: ./manage.py deliver_scheduled_messages
"""
@override
def handle(self, *args: Any, **options: Any) -> None:
try:
while True:

View File

@ -3,6 +3,7 @@ from argparse import ArgumentParser
from typing import Any
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.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
"""
@override
def add_arguments(self, parser: ArgumentParser) -> None:
parser.add_argument(
"--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)
@override
def handle(self, *args: Any, **options: str) -> None:
realm = self.get_realm(options)
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.core.management.base import BaseCommand, CommandError
from typing_extensions import override
from zerver.lib.email_mirror import logger, process_message
@ -80,6 +81,7 @@ def get_imap_messages() -> Generator[EmailMessage, None, None]:
class Command(BaseCommand):
help = __doc__
@override
def handle(self, *args: Any, **options: str) -> None:
for message in get_imap_messages():
process_message(message)

View File

@ -5,6 +5,7 @@ from typing import Any
from django.conf import settings
from django.core.management.base import BaseCommand
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.logging_util import log_to_file
@ -19,6 +20,7 @@ class Command(BaseCommand):
in a while.
"""
@override
def handle(self, *args: Any, **options: Any) -> None:
cutoff = timezone_now() - datetime.timedelta(days=DIGEST_CUTOFF)
enqueue_emails(cutoff)

View File

@ -4,6 +4,7 @@ from typing import IO, Any
import orjson
from django.core.management.base import BaseCommand
from typing_extensions import override
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.
"""
@override
def add_arguments(self, parser: ArgumentParser) -> None:
parser.add_argument(
"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"
)
@override
def handle(self, *args: Any, **options: str) -> None:
queue_name = options["queue_name"]
file_name = options["file_name"]

View File

@ -5,6 +5,7 @@ from typing import Any
from django.conf import settings
from django.core.management.base import CommandError
from typing_extensions import override
from zerver.actions.realm_settings import do_deactivate_realm
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
of recipients of messages in the realm, hardware, etc."""
@override
def add_arguments(self, parser: ArgumentParser) -> None:
parser.add_argument(
"--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)
@override
def handle(self, *args: Any, **options: Any) -> None:
realm = self.get_realm(options)
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
from django.core.management.base import CommandError
from django.db.models import Q
from typing_extensions import override
from zerver.lib.management import ZulipBaseCommand
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.
"""
@override
def add_arguments(self, parser: ArgumentParser) -> None:
self.add_realm_args(parser, required=True)
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.",
)
@override
def handle(self, *args: Any, **options: Any) -> None:
terms = set()
if options["file"]:

View File

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

View File

@ -5,6 +5,7 @@ from argparse import ArgumentParser
from typing import Any
from django.core.management.base import BaseCommand
from typing_extensions import override
from zerver.lib.export import export_usermessages_batch
@ -12,6 +13,7 @@ from zerver.lib.export import export_usermessages_batch
class Command(BaseCommand):
help = """UserMessage fetching helper for export.py"""
@override
def add_arguments(self, parser: ArgumentParser) -> None:
parser.add_argument("--path", help="Path to find messages.json archives")
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",
)
@override
def handle(self, *args: Any, **options: Any) -> None:
logging.info("Starting UserMessage batch thread %s", options["thread"])
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
from django.conf import settings
from typing_extensions import override
from urllib3.util import Retry
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.
"""
@override
def add_arguments(self, parser: ArgumentParser) -> None:
parser.add_argument(
"--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",
)
@override
def handle(self, *args: Any, **options: Any) -> None:
if not settings.RATE_LIMIT_TOR_TOGETHER:
return

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