Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Annotate string constants in synapse.api.constants with Final #11356

Merged
merged 1 commit into from
Nov 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/11356.misc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add `Final` annotation to string constants in `synapse.api.constants` so that they get typed as `Literal`s.
198 changes: 100 additions & 98 deletions synapse/api/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

"""Contains constants from the specification."""

from typing_extensions import Final

# the max size of a (canonical-json-encoded) event
MAX_PDU_SIZE = 65536

Expand All @@ -39,217 +41,217 @@ class Membership:

"""Represents the membership states of a user in a room."""

INVITE = "invite"
JOIN = "join"
KNOCK = "knock"
LEAVE = "leave"
BAN = "ban"
LIST = (INVITE, JOIN, KNOCK, LEAVE, BAN)
INVITE: Final = "invite"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was wondering if this should say Final[str]. Answer seems to be a big fat NO.
If you make it Final[str], then it types as str rather than Literal['invite'].
So I think this is fine.

JOIN: Final = "join"
KNOCK: Final = "knock"
LEAVE: Final = "leave"
BAN: Final = "ban"
LIST: Final = (INVITE, JOIN, KNOCK, LEAVE, BAN)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revealed type is "Tuple[Literal['invite']?, Literal['join']?, Literal['knock']?, Literal['leave']?, Literal['ban']?]"

Actually, the earlier lines print things like Revealed type is "Literal['invite']?" too.

Do we know why it says a question mark after each Literal? ... It makes me worry that something secret is going on.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question. https://mypy.readthedocs.io/en/stable/literal_types.html#declaring-literal-variables says

If you do not provide an explicit type in the Final, the type of c becomes context-sensitive: mypy will basically try “substituting” the original assigned value whenever it’s used before performing type checking. This is why the revealed type of c is Literal[19]?: the question mark at the end reflects this context-sensitive nature.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the link David. It took me a few reads to see what it's going on about, but interesting — question is: do we want the behaviour given by Final or the one given by Literal[…]?

Copy link
Contributor Author

@squahtx squahtx Nov 19, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we want the behaviour given by Final or the one given by Literal[…]?

I'm having trouble coming up with an example which demonstrates the difference. Do you have one in mind?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's really hard to come up with an example where it makes a difference. Here's one:

T = TypeVar("T", bound=str)
def validate_or_throw(value: str, allowed_values: List[T]) -> T:
    ...

ACTIONS_IMPLICIT: Final = ["ban", "unban"]
ACTIONS_EXPLICIT: List[Literal["ban", "unban"]] = ["ban", "unban"]

def do_action(action: Literal["ban", "unban"]):
    pass

def on_GET1(json: Dict):
    action = validate_or_throw(json["action"], ACTIONS_IMPLICIT)
    do_action(action)  # Argument 1 to "do_action" has incompatible type "str"

def on_GET2(json: Dict):
    action = validate_or_throw(json["action"], ACTIONS_EXPLICIT)
    do_action(action)  # ok

I'm tempted to stick with Final until the context-sensitive nature becomes a problem.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm tempted to stick with Final until the context-sensitive nature becomes a problem.

I'm fine with this---no worse than the current situation as far as I can see

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aye yeah, let's just stick with it



class PresenceState:
"""Represents the presence state of a user."""

OFFLINE = "offline"
UNAVAILABLE = "unavailable"
ONLINE = "online"
BUSY = "org.matrix.msc3026.busy"
OFFLINE: Final = "offline"
UNAVAILABLE: Final = "unavailable"
ONLINE: Final = "online"
BUSY: Final = "org.matrix.msc3026.busy"


class JoinRules:
PUBLIC = "public"
KNOCK = "knock"
INVITE = "invite"
PRIVATE = "private"
PUBLIC: Final = "public"
KNOCK: Final = "knock"
INVITE: Final = "invite"
PRIVATE: Final = "private"
# As defined for MSC3083.
RESTRICTED = "restricted"
RESTRICTED: Final = "restricted"


class RestrictedJoinRuleTypes:
"""Understood types for the allow rules in restricted join rules."""

ROOM_MEMBERSHIP = "m.room_membership"
ROOM_MEMBERSHIP: Final = "m.room_membership"


class LoginType:
PASSWORD = "m.login.password"
EMAIL_IDENTITY = "m.login.email.identity"
MSISDN = "m.login.msisdn"
RECAPTCHA = "m.login.recaptcha"
TERMS = "m.login.terms"
SSO = "m.login.sso"
DUMMY = "m.login.dummy"
REGISTRATION_TOKEN = "org.matrix.msc3231.login.registration_token"
PASSWORD: Final = "m.login.password"
EMAIL_IDENTITY: Final = "m.login.email.identity"
MSISDN: Final = "m.login.msisdn"
RECAPTCHA: Final = "m.login.recaptcha"
TERMS: Final = "m.login.terms"
SSO: Final = "m.login.sso"
DUMMY: Final = "m.login.dummy"
REGISTRATION_TOKEN: Final = "org.matrix.msc3231.login.registration_token"


# This is used in the `type` parameter for /register when called by
# an appservice to register a new user.
APP_SERVICE_REGISTRATION_TYPE = "m.login.application_service"
APP_SERVICE_REGISTRATION_TYPE: Final = "m.login.application_service"


class EventTypes:
Member = "m.room.member"
Create = "m.room.create"
Tombstone = "m.room.tombstone"
JoinRules = "m.room.join_rules"
PowerLevels = "m.room.power_levels"
Aliases = "m.room.aliases"
Redaction = "m.room.redaction"
ThirdPartyInvite = "m.room.third_party_invite"
RelatedGroups = "m.room.related_groups"

RoomHistoryVisibility = "m.room.history_visibility"
CanonicalAlias = "m.room.canonical_alias"
Encrypted = "m.room.encrypted"
RoomAvatar = "m.room.avatar"
RoomEncryption = "m.room.encryption"
GuestAccess = "m.room.guest_access"
Member: Final = "m.room.member"
Create: Final = "m.room.create"
Tombstone: Final = "m.room.tombstone"
JoinRules: Final = "m.room.join_rules"
PowerLevels: Final = "m.room.power_levels"
Aliases: Final = "m.room.aliases"
Redaction: Final = "m.room.redaction"
ThirdPartyInvite: Final = "m.room.third_party_invite"
RelatedGroups: Final = "m.room.related_groups"

RoomHistoryVisibility: Final = "m.room.history_visibility"
CanonicalAlias: Final = "m.room.canonical_alias"
Encrypted: Final = "m.room.encrypted"
RoomAvatar: Final = "m.room.avatar"
RoomEncryption: Final = "m.room.encryption"
GuestAccess: Final = "m.room.guest_access"

# These are used for validation
Message = "m.room.message"
Topic = "m.room.topic"
Name = "m.room.name"
Message: Final = "m.room.message"
Topic: Final = "m.room.topic"
Name: Final = "m.room.name"

ServerACL = "m.room.server_acl"
Pinned = "m.room.pinned_events"
ServerACL: Final = "m.room.server_acl"
Pinned: Final = "m.room.pinned_events"

Retention = "m.room.retention"
Retention: Final = "m.room.retention"

Dummy = "org.matrix.dummy_event"
Dummy: Final = "org.matrix.dummy_event"

SpaceChild = "m.space.child"
SpaceParent = "m.space.parent"
SpaceChild: Final = "m.space.child"
SpaceParent: Final = "m.space.parent"

MSC2716_INSERTION = "org.matrix.msc2716.insertion"
MSC2716_BATCH = "org.matrix.msc2716.batch"
MSC2716_MARKER = "org.matrix.msc2716.marker"
MSC2716_INSERTION: Final = "org.matrix.msc2716.insertion"
MSC2716_BATCH: Final = "org.matrix.msc2716.batch"
MSC2716_MARKER: Final = "org.matrix.msc2716.marker"


class ToDeviceEventTypes:
RoomKeyRequest = "m.room_key_request"
RoomKeyRequest: Final = "m.room_key_request"


class DeviceKeyAlgorithms:
"""Spec'd algorithms for the generation of per-device keys"""

ED25519 = "ed25519"
CURVE25519 = "curve25519"
SIGNED_CURVE25519 = "signed_curve25519"
ED25519: Final = "ed25519"
CURVE25519: Final = "curve25519"
SIGNED_CURVE25519: Final = "signed_curve25519"


class EduTypes:
Presence = "m.presence"
Presence: Final = "m.presence"


class RejectedReason:
AUTH_ERROR = "auth_error"
AUTH_ERROR: Final = "auth_error"


class RoomCreationPreset:
PRIVATE_CHAT = "private_chat"
PUBLIC_CHAT = "public_chat"
TRUSTED_PRIVATE_CHAT = "trusted_private_chat"
PRIVATE_CHAT: Final = "private_chat"
PUBLIC_CHAT: Final = "public_chat"
TRUSTED_PRIVATE_CHAT: Final = "trusted_private_chat"


class ThirdPartyEntityKind:
USER = "user"
LOCATION = "location"
USER: Final = "user"
LOCATION: Final = "location"


ServerNoticeMsgType = "m.server_notice"
ServerNoticeLimitReached = "m.server_notice.usage_limit_reached"
ServerNoticeMsgType: Final = "m.server_notice"
ServerNoticeLimitReached: Final = "m.server_notice.usage_limit_reached"


class UserTypes:
"""Allows for user type specific behaviour. With the benefit of hindsight
'admin' and 'guest' users should also be UserTypes. Normal users are type None
"""

SUPPORT = "support"
BOT = "bot"
ALL_USER_TYPES = (SUPPORT, BOT)
SUPPORT: Final = "support"
BOT: Final = "bot"
ALL_USER_TYPES: Final = (SUPPORT, BOT)


class RelationTypes:
"""The types of relations known to this server."""

ANNOTATION = "m.annotation"
REPLACE = "m.replace"
REFERENCE = "m.reference"
THREAD = "io.element.thread"
ANNOTATION: Final = "m.annotation"
REPLACE: Final = "m.replace"
REFERENCE: Final = "m.reference"
THREAD: Final = "io.element.thread"


class LimitBlockingTypes:
"""Reasons that a server may be blocked"""

MONTHLY_ACTIVE_USER = "monthly_active_user"
HS_DISABLED = "hs_disabled"
MONTHLY_ACTIVE_USER: Final = "monthly_active_user"
HS_DISABLED: Final = "hs_disabled"


class EventContentFields:
"""Fields found in events' content, regardless of type."""

# Labels for the event, cf https://github.com/matrix-org/matrix-doc/pull/2326
LABELS = "org.matrix.labels"
LABELS: Final = "org.matrix.labels"

# Timestamp to delete the event after
# cf https://github.com/matrix-org/matrix-doc/pull/2228
SELF_DESTRUCT_AFTER = "org.matrix.self_destruct_after"
SELF_DESTRUCT_AFTER: Final = "org.matrix.self_destruct_after"

# cf https://github.com/matrix-org/matrix-doc/pull/1772
ROOM_TYPE = "type"
ROOM_TYPE: Final = "type"

# Whether a room can federate.
FEDERATE = "m.federate"
FEDERATE: Final = "m.federate"

# The creator of the room, as used in `m.room.create` events.
ROOM_CREATOR = "creator"
ROOM_CREATOR: Final = "creator"

# Used in m.room.guest_access events.
GUEST_ACCESS = "guest_access"
GUEST_ACCESS: Final = "guest_access"

# Used on normal messages to indicate they were historically imported after the fact
MSC2716_HISTORICAL = "org.matrix.msc2716.historical"
MSC2716_HISTORICAL: Final = "org.matrix.msc2716.historical"
# For "insertion" events to indicate what the next batch ID should be in
# order to connect to it
MSC2716_NEXT_BATCH_ID = "org.matrix.msc2716.next_batch_id"
MSC2716_NEXT_BATCH_ID: Final = "org.matrix.msc2716.next_batch_id"
# Used on "batch" events to indicate which insertion event it connects to
MSC2716_BATCH_ID = "org.matrix.msc2716.batch_id"
MSC2716_BATCH_ID: Final = "org.matrix.msc2716.batch_id"
# For "marker" events
MSC2716_MARKER_INSERTION = "org.matrix.msc2716.marker.insertion"
MSC2716_MARKER_INSERTION: Final = "org.matrix.msc2716.marker.insertion"

# The authorising user for joining a restricted room.
AUTHORISING_USER = "join_authorised_via_users_server"
AUTHORISING_USER: Final = "join_authorised_via_users_server"


class RoomTypes:
"""Understood values of the room_type field of m.room.create events."""

SPACE = "m.space"
SPACE: Final = "m.space"


class RoomEncryptionAlgorithms:
MEGOLM_V1_AES_SHA2 = "m.megolm.v1.aes-sha2"
DEFAULT = MEGOLM_V1_AES_SHA2
MEGOLM_V1_AES_SHA2: Final = "m.megolm.v1.aes-sha2"
DEFAULT: Final = MEGOLM_V1_AES_SHA2


class AccountDataTypes:
DIRECT = "m.direct"
IGNORED_USER_LIST = "m.ignored_user_list"
DIRECT: Final = "m.direct"
IGNORED_USER_LIST: Final = "m.ignored_user_list"


class HistoryVisibility:
INVITED = "invited"
JOINED = "joined"
SHARED = "shared"
WORLD_READABLE = "world_readable"
INVITED: Final = "invited"
JOINED: Final = "joined"
SHARED: Final = "shared"
WORLD_READABLE: Final = "world_readable"


class GuestAccess:
CAN_JOIN = "can_join"
CAN_JOIN: Final = "can_join"
# anything that is not "can_join" is considered "forbidden", but for completeness:
FORBIDDEN = "forbidden"
FORBIDDEN: Final = "forbidden"


class ReadReceiptEventFields:
MSC2285_HIDDEN = "org.matrix.msc2285.hidden"
MSC2285_HIDDEN: Final = "org.matrix.msc2285.hidden"