Fix-up the contrib/graph scripts. (#13013)

* Clarifies comments and documentation.
* Adds type-hints.
* Fixes Python 3 compatibility (and runs pyupgrade).
* Updates for changes in Synapse internals.
This commit is contained in:
Patrick Cloke 2022-06-10 08:30:14 -04:00 committed by GitHub
parent 81608490e3
commit 84cd0fe4e2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 70 additions and 43 deletions

1
changelog.d/13013.misc Normal file
View File

@ -0,0 +1 @@
Modernize the `contrib/graph/` scripts.

View File

@ -1,11 +1,3 @@
import argparse
import cgi
import datetime
import json
import pydot
import urllib2
# Copyright 2014-2016 OpenMarket Ltd # Copyright 2014-2016 OpenMarket Ltd
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
@ -20,12 +12,25 @@ import urllib2
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import argparse
import cgi
import datetime
import json
import urllib.request
from typing import List
def make_name(pdu_id, origin): import pydot
return "%s@%s" % (pdu_id, origin)
def make_graph(pdus, room, filename_prefix): def make_name(pdu_id: str, origin: str) -> str:
return f"{pdu_id}@{origin}"
def make_graph(pdus: List[dict], filename_prefix: str) -> None:
"""
Generate a dot and SVG file for a graph of events in the room based on the
topological ordering by querying a homeserver.
"""
pdu_map = {} pdu_map = {}
node_map = {} node_map = {}
@ -111,10 +116,10 @@ def make_graph(pdus, room, filename_prefix):
graph.write_svg("%s.svg" % filename_prefix, prog="dot") graph.write_svg("%s.svg" % filename_prefix, prog="dot")
def get_pdus(host, room): def get_pdus(host: str, room: str) -> List[dict]:
transaction = json.loads( transaction = json.loads(
urllib2.urlopen( urllib.request.urlopen(
"http://%s/_matrix/federation/v1/context/%s/" % (host, room) f"http://{host}/_matrix/federation/v1/context/{room}/"
).read() ).read()
) )
@ -141,4 +146,4 @@ if __name__ == "__main__":
pdus = get_pdus(host, room) pdus = get_pdus(host, room)
make_graph(pdus, room, prefix) make_graph(pdus, prefix)

View File

@ -14,22 +14,31 @@
import argparse import argparse
import cgi
import datetime import datetime
import html
import json import json
import sqlite3 import sqlite3
import pydot import pydot
from synapse.events import FrozenEvent from synapse.api.room_versions import KNOWN_ROOM_VERSIONS
from synapse.events import make_event_from_dict
from synapse.util.frozenutils import unfreeze from synapse.util.frozenutils import unfreeze
def make_graph(db_name, room_id, file_prefix, limit): def make_graph(db_name: str, room_id: str, file_prefix: str, limit: int) -> None:
"""
Generate a dot and SVG file for a graph of events in the room based on the
topological ordering by reading from a Synapse SQLite database.
"""
conn = sqlite3.connect(db_name) conn = sqlite3.connect(db_name)
sql = "SELECT room_version FROM rooms WHERE room_id = ?"
c = conn.execute(sql, (room_id,))
room_version = KNOWN_ROOM_VERSIONS[c.fetchone()[0]]
sql = ( sql = (
"SELECT json FROM event_json as j " "SELECT json, internal_metadata FROM event_json as j "
"INNER JOIN events as e ON e.event_id = j.event_id " "INNER JOIN events as e ON e.event_id = j.event_id "
"WHERE j.room_id = ?" "WHERE j.room_id = ?"
) )
@ -43,7 +52,10 @@ def make_graph(db_name, room_id, file_prefix, limit):
c = conn.execute(sql, args) c = conn.execute(sql, args)
events = [FrozenEvent(json.loads(e[0])) for e in c.fetchall()] events = [
make_event_from_dict(json.loads(e[0]), room_version, json.loads(e[1]))
for e in c.fetchall()
]
events.sort(key=lambda e: e.depth) events.sort(key=lambda e: e.depth)
@ -84,7 +96,7 @@ def make_graph(db_name, room_id, file_prefix, limit):
"name": event.event_id, "name": event.event_id,
"type": event.type, "type": event.type,
"state_key": event.get("state_key", None), "state_key": event.get("state_key", None),
"content": cgi.escape(content, quote=True), "content": html.escape(content, quote=True),
"time": t, "time": t,
"depth": event.depth, "depth": event.depth,
"state_group": state_group, "state_group": state_group,
@ -96,11 +108,11 @@ def make_graph(db_name, room_id, file_prefix, limit):
graph.add_node(node) graph.add_node(node)
for event in events: for event in events:
for prev_id, _ in event.prev_events: for prev_id in event.prev_event_ids():
try: try:
end_node = node_map[prev_id] end_node = node_map[prev_id]
except Exception: except Exception:
end_node = pydot.Node(name=prev_id, label="<<b>%s</b>>" % (prev_id,)) end_node = pydot.Node(name=prev_id, label=f"<<b>{prev_id}</b>>")
node_map[prev_id] = end_node node_map[prev_id] = end_node
graph.add_node(end_node) graph.add_node(end_node)
@ -112,7 +124,7 @@ def make_graph(db_name, room_id, file_prefix, limit):
if len(event_ids) <= 1: if len(event_ids) <= 1:
continue continue
cluster = pydot.Cluster(str(group), label="<State Group: %s>" % (str(group),)) cluster = pydot.Cluster(str(group), label=f"<State Group: {str(group)}>")
for event_id in event_ids: for event_id in event_ids:
cluster.add_node(node_map[event_id]) cluster.add_node(node_map[event_id])
@ -126,7 +138,7 @@ def make_graph(db_name, room_id, file_prefix, limit):
if __name__ == "__main__": if __name__ == "__main__":
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description="Generate a PDU graph for a given room by talking " description="Generate a PDU graph for a given room by talking "
"to the given homeserver to get the list of PDUs. \n" "to the given Synapse SQLite file to get the list of PDUs. \n"
"Requires pydot." "Requires pydot."
) )
parser.add_argument( parser.add_argument(

View File

@ -1,13 +1,3 @@
import argparse
import cgi
import datetime
import pydot
import simplejson as json
from synapse.events import FrozenEvent
from synapse.util.frozenutils import unfreeze
# Copyright 2016 OpenMarket Ltd # Copyright 2016 OpenMarket Ltd
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
@ -22,15 +12,35 @@ from synapse.util.frozenutils import unfreeze
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import argparse
import datetime
import html
import json
def make_graph(file_name, room_id, file_prefix, limit): import pydot
from synapse.api.room_versions import KNOWN_ROOM_VERSIONS
from synapse.events import make_event_from_dict
from synapse.util.frozenutils import unfreeze
def make_graph(file_name: str, file_prefix: str, limit: int) -> None:
"""
Generate a dot and SVG file for a graph of events in the room based on the
topological ordering by reading line-delimited JSON from a file.
"""
print("Reading lines") print("Reading lines")
with open(file_name) as f: with open(file_name) as f:
lines = f.readlines() lines = f.readlines()
print("Read lines") print("Read lines")
events = [FrozenEvent(json.loads(line)) for line in lines] # Figure out the room version, assume the first line is the create event.
room_version = KNOWN_ROOM_VERSIONS[
json.loads(lines[0]).get("content", {}).get("room_version")
]
events = [make_event_from_dict(json.loads(line), room_version) for line in lines]
print("Loaded events.") print("Loaded events.")
@ -66,8 +76,8 @@ def make_graph(file_name, room_id, file_prefix, limit):
content.append( content.append(
"<b>%s</b>: %s," "<b>%s</b>: %s,"
% ( % (
cgi.escape(key, quote=True).encode("ascii", "xmlcharrefreplace"), html.escape(key, quote=True).encode("ascii", "xmlcharrefreplace"),
cgi.escape(value, quote=True).encode("ascii", "xmlcharrefreplace"), html.escape(value, quote=True).encode("ascii", "xmlcharrefreplace"),
) )
) )
@ -101,11 +111,11 @@ def make_graph(file_name, room_id, file_prefix, limit):
print("Created Nodes") print("Created Nodes")
for event in events: for event in events:
for prev_id, _ in event.prev_events: for prev_id in event.prev_event_ids():
try: try:
end_node = node_map[prev_id] end_node = node_map[prev_id]
except Exception: except Exception:
end_node = pydot.Node(name=prev_id, label="<<b>%s</b>>" % (prev_id,)) end_node = pydot.Node(name=prev_id, label=f"<<b>{prev_id}</b>>")
node_map[prev_id] = end_node node_map[prev_id] = end_node
graph.add_node(end_node) graph.add_node(end_node)
@ -139,8 +149,7 @@ if __name__ == "__main__":
) )
parser.add_argument("-l", "--limit", help="Only retrieve the last N events.") parser.add_argument("-l", "--limit", help="Only retrieve the last N events.")
parser.add_argument("event_file") parser.add_argument("event_file")
parser.add_argument("room")
args = parser.parse_args() args = parser.parse_args()
make_graph(args.event_file, args.room, args.prefix, args.limit) make_graph(args.event_file, args.prefix, args.limit)