diff --git a/confirmation/models.py b/confirmation/models.py index 262d196fee..6df9203e45 100644 --- a/confirmation/models.py +++ b/confirmation/models.py @@ -20,7 +20,7 @@ from django.utils.timezone import now as timezone_now from typing_extensions import override from confirmation import settings as confirmation_settings -from zerver.lib.types import UnspecifiedValue +from zerver.lib.types import UNSET, Unset from zerver.models import ( EmailChangeStatus, MultiuseInvite, @@ -129,7 +129,7 @@ def create_confirmation_object( obj: ConfirmationObjT, confirmation_type: int, *, - validity_in_minutes: int | None | UnspecifiedValue = UnspecifiedValue(), + validity_in_minutes: int | None | Unset = UNSET, no_associated_realm_object: bool = False, ) -> "Confirmation": # validity_in_minutes is an override for the default values which are @@ -149,7 +149,7 @@ def create_confirmation_object( current_time = timezone_now() expiry_date = None - if not isinstance(validity_in_minutes, UnspecifiedValue): + if not isinstance(validity_in_minutes, Unset): if validity_in_minutes is None: expiry_date = None else: @@ -172,7 +172,7 @@ def create_confirmation_link( obj: ConfirmationObjT, confirmation_type: int, *, - validity_in_minutes: int | None | UnspecifiedValue = UnspecifiedValue(), + validity_in_minutes: int | None | Unset = UNSET, url_args: Mapping[str, str] = {}, no_associated_realm_object: bool = False, ) -> str: diff --git a/pyproject.toml b/pyproject.toml index 3bd365fad2..73a721d0ba 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -145,7 +145,6 @@ ignore = [ "ANN102", # Missing type annotation for `cls` in classmethod "ANN401", # Dynamically typed expressions (typing.Any) are disallowed "B007", # Loop control variable not used within the loop body - "B008", # Do not perform function calls in argument defaults "B904", # Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling "C408", # Unnecessary `dict` call (rewrite as a literal) "COM812", # Trailing comma missing diff --git a/zerver/lib/events.py b/zerver/lib/events.py index 704243d9fd..d426418ec6 100644 --- a/zerver/lib/events.py +++ b/zerver/lib/events.py @@ -1746,6 +1746,9 @@ class ClientCapabilities(TypedDict): archived_channels: NotRequired[bool] +DEFAULT_CLIENT_CAPABILITIES = ClientCapabilities(notification_settings_null=False) + + def do_events_register( user_profile: UserProfile | None, realm: Realm, @@ -1760,7 +1763,7 @@ def do_events_register( all_public_streams: bool = False, include_subscribers: bool = True, include_streams: bool = True, - client_capabilities: ClientCapabilities = ClientCapabilities(notification_settings_null=False), + client_capabilities: ClientCapabilities = DEFAULT_CLIENT_CAPABILITIES, narrow: Collection[NarrowTerm] = [], fetch_event_types: Collection[str] | None = None, spectator_requested_language: str | None = None, diff --git a/zerver/lib/types.py b/zerver/lib/types.py index f6af9f30fc..b9cc54df94 100644 --- a/zerver/lib/types.py +++ b/zerver/lib/types.py @@ -69,7 +69,7 @@ class LinkifierDict(TypedDict): id: int -class UnspecifiedValue: +class Unset: """In most API endpoints, we use a default value of `None"` to encode parameters that the client did not pass, which is nicely Pythonic. @@ -84,6 +84,9 @@ class UnspecifiedValue: """ +UNSET = Unset() + + class EditHistoryEvent(TypedDict, total=False): """ Database format for edit history events. diff --git a/zerver/tests/test_typed_endpoint.py b/zerver/tests/test_typed_endpoint.py index 4a378616b9..f179850713 100644 --- a/zerver/tests/test_typed_endpoint.py +++ b/zerver/tests/test_typed_endpoint.py @@ -73,6 +73,8 @@ class TestEndpoint(ZulipTestCase): __pydantic_config__ = ConfigDict(extra="forbid") + default_foo = Foo(10, 10) + @typed_endpoint def view( request: HttpRequest, @@ -81,7 +83,7 @@ class TestEndpoint(ZulipTestCase): json_str: Json[str], json_data: Json[Foo], json_optional: Json[int | None] | None = None, - json_default: Json[Foo] = Foo(10, 10), + json_default: Json[Foo] = default_foo, non_json: str = "ok", non_json_optional: str | None = None, ) -> HttpResponse: diff --git a/zerver/views/events_register.py b/zerver/views/events_register.py index 0d48abc20e..63ae7068d8 100644 --- a/zerver/views/events_register.py +++ b/zerver/views/events_register.py @@ -9,7 +9,7 @@ from pydantic import Json from zerver.context_processors import get_valid_realm_from_request from zerver.lib.compatibility import is_pronouns_field_type_supported -from zerver.lib.events import ClientCapabilities, do_events_register +from zerver.lib.events import DEFAULT_CLIENT_CAPABILITIES, ClientCapabilities, do_events_register from zerver.lib.exceptions import JsonableError, MissingAuthenticationError from zerver.lib.narrow_helpers import narrow_dataclasses_from_tuples from zerver.lib.request import RequestNotes @@ -46,7 +46,7 @@ def events_register_backend( presence_history_limit_days: Json[int] | None = None, all_public_streams: Json[bool] | None = None, include_subscribers: Json[bool] = False, - client_capabilities: Json[ClientCapabilities] | None = None, + client_capabilities: Json[ClientCapabilities] = DEFAULT_CLIENT_CAPABILITIES, event_types: Json[list[str]] | None = None, fetch_event_types: Json[list[str]] | None = None, narrow: Json[NarrowT] | None = None, @@ -99,9 +99,6 @@ def events_register_backend( all_public_streams = False include_streams = False - if client_capabilities is None: - client_capabilities = ClientCapabilities(notification_settings_null=False) - client = RequestNotes.get_notes(request).client assert client is not None