Skip to content

Commit 489369e

Browse files
committed
Fix EventProtocol to work with the Rust class
We have to take a slightly different approach here as we can't subclass the native Event type.
1 parent 2a29081 commit 489369e

2 files changed

Lines changed: 29 additions & 9 deletions

File tree

synapse/events/py_protocol.py

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333
attributes.
3434
"""
3535

36-
import abc
3736
from typing import (
3837
TYPE_CHECKING,
3938
Sequence,
@@ -42,12 +41,14 @@
4241
from typing_extensions import TypeIs
4342

4443
from synapse.events import EventBase
44+
from synapse.synapse_rust.events import Event
45+
from synapse.types import StrCollection
4546

4647
if TYPE_CHECKING:
4748
from synapse.events.snapshot import EventContext, EventPersistencePair
4849

4950

50-
class _DisableIsInstance(abc.ABCMeta):
51+
class _DisableIsInstance(type):
5152
"""Metaclass which disables isinstance checks on classes which use it, by
5253
making isinstance() raise NotImplementedError.
5354
@@ -61,15 +62,38 @@ def __instancecheck__(cls, instance: object) -> bool:
6162
raise NotImplementedError("Instance cannot be used.")
6263

6364

64-
class EventProtocol(EventBase, metaclass=_DisableIsInstance):
65-
"""Helper subclass that allows type narrowing for `EventBase` objects."""
65+
# We now define `EventProtocol` as a helper class for type narrowing.
66+
#
67+
# During type checking, we want the type narrowed event classes to still have
68+
# all the fields as a normal `Event`, so we make `EventProtocol` a subclass of
69+
# `Event`.
70+
#
71+
# However, at runtime we a) can't subclass `Event` because it's a Rust class,
72+
# and b) don't want to allow `isinstance` checks against `EventProtocol` (as
73+
# it's purely a type annotation helper, not a real class). So at runtime, we
74+
# make `EventProtocol` a class with a metaclass that raises on `isinstance`
75+
# checks.
76+
if TYPE_CHECKING:
77+
78+
class EventProtocol(Event):
79+
"""Helper subclass that allows type narrowing for `EventBase` objects."""
80+
81+
else:
82+
83+
class EventProtocol(metaclass=_DisableIsInstance):
84+
"""Helper subclass that allows type narrowing for `EventBase` objects."""
85+
86+
def __new__(cls):
87+
raise NotImplementedError(
88+
f"{cls.__name__} cannot be instantiated as it is not a real class."
89+
)
6690

6791

6892
class MSC4242Event(EventProtocol):
6993
"""Marker protocol for events in MSC4242 rooms. This allows us to narrow the
7094
type of events."""
7195

72-
prev_state_events: list[str]
96+
prev_state_events: StrCollection
7397

7498

7599
def supports_msc4242_state_dag(event: EventBase) -> TypeIs[MSC4242Event]:

tests/events/test_py_protocol.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
from synapse.api.room_versions import RoomVersion, RoomVersions
1919
from synapse.events import EventBase
2020
from synapse.events.py_protocol import (
21-
EventProtocol,
2221
MSC4242Event,
2322
all_supports_msc4242_state_dag,
2423
supports_msc4242_state_dag,
@@ -49,9 +48,6 @@ def test_is_instance(self) -> None:
4948
# EventBase should work as normal
5049
self.assertFalse(isinstance(object(), EventBase))
5150

52-
with self.assertRaises(NotImplementedError):
53-
isinstance(object(), EventProtocol)
54-
5551
with self.assertRaises(NotImplementedError):
5652
isinstance(object(), MSC4242Event)
5753

0 commit comments

Comments
 (0)