mirror of https://github.com/zulip/zulip.git
mypy: Enable new error explicit-override.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
parent
d257002ad8
commit
a50eb2e809
|
@ -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}>"
|
||||
|
||||
|
|
|
@ -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"]
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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}"
|
||||
|
|
|
@ -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"]
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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}"
|
||||
|
||||
|
|
|
@ -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}"
|
||||
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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.")
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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."""
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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}")
|
||||
|
||||
|
|
|
@ -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"):
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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"]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"]
|
||||
|
||||
|
|
|
@ -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}")
|
||||
|
||||
|
|
|
@ -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])
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]],
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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],
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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("/")
|
||||
|
|
|
@ -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:]
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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}'")
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"]
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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"]
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"]:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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")))
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue