summary refs log tree commit diff
path: root/synapse/events/__init__.py
diff options
context:
space:
mode:
Diffstat (limited to 'synapse/events/__init__.py')
-rw-r--r--synapse/events/__init__.py237
1 files changed, 231 insertions, 6 deletions
diff --git a/synapse/events/__init__.py b/synapse/events/__init__.py
index e673e96cc0..d4e35d03ac 100644
--- a/synapse/events/__init__.py
+++ b/synapse/events/__init__.py
@@ -13,8 +13,10 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-from synapse.util.frozenutils import freeze
-from synapse.util.caches import intern_dict
+from synapse.util.frozenutils import freeze, unfreeze
+from synapse.util.caches import intern_dict, intern_string
+
+import ujson as json
 
 
 # Whether we should use frozen_dict in FrozenEvent. Using frozen_dicts prevents
@@ -24,11 +26,30 @@ USE_FROZEN_DICTS = True
 
 
 class _EventInternalMetadata(object):
+    __slots__ = [
+        "outlier",
+        "invite_from_remote",
+        "send_on_behalf_of",
+        "stream_ordering",
+        "token_id",
+        "txn_id",
+        "before",
+        "after",
+        "order",
+    ]
+
     def __init__(self, internal_metadata_dict):
-        self.__dict__ = dict(internal_metadata_dict)
+        # self.__dict__ = dict(internal_metadata_dict)
+        for key, value in internal_metadata_dict.iteritems():
+            setattr(self, key, value)
 
     def get_dict(self):
-        return dict(self.__dict__)
+        return {
+            key: getattr(self, key)
+            for key in self.__slots__
+            if hasattr(self, key)
+        }
+        # return dict(self.__dict__)
 
     def is_outlier(self):
         return getattr(self, "outlier", False)
@@ -144,8 +165,8 @@ class FrozenEvent(EventBase):
         # Signatures is a dict of dicts, and this is faster than doing a
         # copy.deepcopy
         signatures = {
-            name: {sig_id: sig for sig_id, sig in sigs.items()}
-            for name, sigs in event_dict.pop("signatures", {}).items()
+            name: {sig_id: sig for sig_id, sig in sigs.iteritems()}
+            for name, sigs in event_dict.pop("signatures", {}).iteritems()
         }
 
         unsigned = dict(event_dict.pop("unsigned", {}))
@@ -191,3 +212,207 @@ class FrozenEvent(EventBase):
             self.get("type", None),
             self.get("state_key", None),
         )
+
+
+def _compact_property(key):
+    def getter(self):
+        try:
+            return self[key]
+        except KeyError:
+            raise AttributeError(
+                "AttributeError: '%s' object has no attribute '%s'" % (
+                    self.__name__, key,
+                )
+            )
+
+    return property(getter)
+
+
+class _Unsigned(object):
+    __slots__ = [
+        "age_ts",
+        "replaces_state",
+        "redacted_because",
+        "invite_room_state",
+        "prev_content",
+        "prev_sender",
+        "redacted_by",
+    ]
+
+    def __init__(self, **kwargs):
+        for s in self.__slots__:
+            try:
+                setattr(self, s, kwargs[s])
+            except KeyError:
+                continue
+
+    def __getitem__(self, field):
+        try:
+            return getattr(self, field)
+        except AttributeError:
+            raise KeyError(field)
+
+    def __setitem__(self, field, value):
+        try:
+            setattr(self, field, value)
+        except AttributeError:
+            raise KeyError(field)
+
+    def __delitem__(self, field):
+        try:
+            return delattr(self, field)
+        except AttributeError:
+            raise KeyError(field)
+
+    def __contains__(self, field):
+        return hasattr(self, field)
+
+    def get(self, key, default=None):
+        return getattr(self, key, default)
+
+    def pop(self, key, default):
+        r = self.get(key, default)
+        try:
+            delattr(self, key)
+        except AttributeError:
+            pass
+        return r
+
+    def __iter__(self):
+        for key in self.__slots__:
+            if hasattr(self, key):
+                yield (key, getattr(self, key))
+
+
+class CompactEvent(object):
+    __slots__ = [
+        "event_json",
+
+        "internal_metadata",
+        "rejected_reason",
+
+        "signatures",
+        "unsigned",
+
+        "event_id",
+        "room_id",
+        "type",
+        "state_key",
+        "sender",
+    ]
+
+    def __init__(self, event_dict, internal_metadata_dict={}, rejected_reason=None):
+        event_dict = dict(unfreeze(event_dict))
+
+        object.__setattr__(self, "unsigned", _Unsigned(**event_dict.pop("unsigned", {})))
+
+        signatures = {
+            intern_string(name): {
+                intern_string(sig_id): sig.encode("utf-8")
+                for sig_id, sig in sigs.iteritems()
+            }
+            for name, sigs in event_dict.pop("signatures", {}).iteritems()
+        }
+        object.__setattr__(self, "signatures", signatures)
+
+        object.__setattr__(self, "event_json", json.dumps(event_dict))
+
+        object.__setattr__(self, "rejected_reason", rejected_reason)
+        object.__setattr__(self, "internal_metadata", _EventInternalMetadata(
+            internal_metadata_dict
+        ))
+
+        object.__setattr__(self, "event_id", event_dict["event_id"])
+        object.__setattr__(self, "room_id", event_dict["room_id"])
+        object.__setattr__(self, "type", event_dict["type"])
+        if "state_key" in event_dict:
+            object.__setattr__(self, "state_key", event_dict["state_key"])
+        object.__setattr__(self, "sender", event_dict["sender"])
+
+    auth_events = _compact_property("auth_events")
+    depth = _compact_property("depth")
+    content = _compact_property("content")
+    hashes = _compact_property("hashes")
+    origin = _compact_property("origin")
+    origin_server_ts = _compact_property("origin_server_ts")
+    prev_events = _compact_property("prev_events")
+    prev_state = _compact_property("prev_state")
+    redacts = _compact_property("redacts")
+
+    @property
+    def user_id(self):
+        return self.sender
+
+    @property
+    def membership(self):
+        return self.content["membership"]
+
+    def is_state(self):
+        return hasattr(self, "state_key") and self.state_key is not None
+
+    def get_dict(self):
+        d = json.loads(self.event_json)
+        d.update({
+            "signatures": dict(self.signatures),
+            "unsigned": dict(self.unsigned),
+        })
+
+        return d
+
+    def get_pdu_json(self, time_now=None):
+        pdu_json = self.get_dict()
+
+        if time_now is not None and "age_ts" in pdu_json["unsigned"]:
+            age = time_now - pdu_json["unsigned"]["age_ts"]
+            pdu_json.setdefault("unsigned", {})["age"] = int(age)
+            del pdu_json["unsigned"]["age_ts"]
+
+        # This may be a frozen event
+        pdu_json["unsigned"].pop("redacted_because", None)
+
+        return pdu_json
+
+    def get(self, key, default=None):
+        if key in self.__slots__:
+            return freeze(getattr(self, key, default))
+
+        d = json.loads(self.event_json)
+        return d.get(key, default)
+
+    def get_internal_metadata_dict(self):
+        return self.internal_metadata.get_dict()
+
+    def __getitem__(self, field):
+        if field in self.__slots__:
+            try:
+                return freeze(getattr(self, field))
+            except AttributeError:
+                raise KeyError(field)
+
+        d = json.loads(self.event_json)
+        return d[field]
+
+    def __contains__(self, field):
+        if field in self.__slots__:
+            return hasattr(self, field)
+
+        d = json.loads(self.event_json)
+        return field in d
+
+    @staticmethod
+    def from_event(event):
+        return CompactEvent(
+            event.get_pdu_json(),
+            event.get_internal_metadata_dict(),
+            event.rejected_reason,
+        )
+
+    def __str__(self):
+        return self.__repr__()
+
+    def __repr__(self):
+        return "<CompactEvent event_id='%s', type='%s', state_key='%s'>" % (
+            self.get("event_id", None),
+            self.get("type", None),
+            self.get("state_key", None),
+        )