mirror of
https://github.com/safishamsi/graphify.git
synced 2026-10-19 02:19:37 +00:00
fix(export): strip control characters from the id drawn for an unlabelled node
A node without a label is drawn under its id (`G.nodes[n].get("label", n)`),
a third path from text to graph.svg. The previous commit cleaned the labels
and left that fallback as it was, so an unlabelled node whose id holds a C0
control character still produced a graph.svg that a strict parser rejects.
Run the fallback through the same strip: one expression,
`_strip_xml_illegal(str(G.nodes[n].get("label", n)))`, now covers the label
and the id. A clean id is drawn as before.
Tests: for each control character the module already lists, an unlabelled
node whose id carries it; the file must parse and still show the cleaned id.
Assisted-by: assayer harness on Claude Code (https://github.com/Yyunozor/assayer-memory-mcp)
This commit is contained in:
2 files changed
+15
-4
No files matched your search
+1
-2
@@ -1393,8 +1393,7 @@ def to_svg(
|
||||
# svg.fonttype) and escapes only &, <, > and --, so a control character in a label
|
||||
# would leave the file malformed XML while the export reports success (same hazard as #2897).
|
||||
nx.draw_networkx_labels(G, pos, ax=ax,
|
||||
labels={n: (_strip_xml_illegal(str(G.nodes[n]["label"]))
|
||||
if "label" in G.nodes[n] else n)
|
||||
labels={n: _strip_xml_illegal(str(G.nodes[n].get("label", n)))
|
||||
for n in G.nodes()},
|
||||
font_size=7, font_color="white")
|
||||
|
||||
|
||||
@@ -138,10 +138,10 @@ def test_a_label_that_is_only_control_characters_still_yields_a_name():
|
||||
# SVG
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _svg(tmp_path, G, community_labels=None):
|
||||
def _svg(tmp_path, G, community_labels=None, communities=COMMUNITIES):
|
||||
pytest.importorskip("matplotlib")
|
||||
out = tmp_path / "g.svg"
|
||||
to_svg(G, COMMUNITIES, str(out), community_labels=community_labels)
|
||||
to_svg(G, communities, str(out), community_labels=community_labels)
|
||||
return out.read_text(encoding="utf-8")
|
||||
|
||||
|
||||
@@ -179,6 +179,18 @@ def test_svg_clean_labels_are_unchanged(tmp_path):
|
||||
assert "Ordinary Community (2)" in svg
|
||||
|
||||
|
||||
@pytest.mark.parametrize("ch", BREAK)
|
||||
def test_svg_survives_a_control_character_in_the_id_of_an_unlabelled_node(tmp_path, ch):
|
||||
"""A node with no label is drawn under its id: a third path from text to the file."""
|
||||
G = build_from_json({
|
||||
"nodes": [{"id": f"we{ch}ird", "file_type": "code", "source_file": "a.py"}],
|
||||
"edges": [], "hyperedges": [],
|
||||
})
|
||||
svg = _svg(tmp_path, G, communities={0: [f"we{ch}ird"]})
|
||||
ET.fromstring(svg)
|
||||
assert "weird" in svg
|
||||
|
||||
|
||||
def test_svg_draws_a_label_that_is_not_a_string(tmp_path):
|
||||
"""nx.draw_networkx_labels str()s a non-string label on its own; the strip
|
||||
must not turn that into a TypeError, nor into an empty label."""
|
||||
|
||||
Reference in new issue
Block a user