diff --git a/graphify/export.py b/graphify/export.py index f5f8e3fb0..9894000b6 100644 --- a/graphify/export.py +++ b/graphify/export.py @@ -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") diff --git a/tests/test_export_control_characters.py b/tests/test_export_control_characters.py index 8988bc880..5103fb7ca 100644 --- a/tests/test_export_control_characters.py +++ b/tests/test_export_control_characters.py @@ -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."""