Skip to content

Commit

Permalink
More informative GraphML exceptions (networkx#5058)
Browse files Browse the repository at this point in the history
* Add tests that fail on uninformative exception.

* Raise informative exception when non-supported type used.

* Add importorskip to new tests.

* Change naming scheme for backward compatibility.
  • Loading branch information
rossbar authored and cvanelteren committed Apr 22, 2024
1 parent 5bf9cf8 commit 286cc28
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 6 deletions.
23 changes: 17 additions & 6 deletions networkx/readwrite/graphml.py
Expand Up @@ -447,6 +447,17 @@ def construct_types(self):
1: True,
}

def get_xml_type(self, key):
"""Wrapper around the xml_type dict that raises a more informative
exception message when a user attempts to use data of a type not
supported by GraphML."""
try:
return self.xml_type[key]
except KeyError as e:
raise TypeError(
f"GraphML does not support type {type(key)} as data values."
) from e


class GraphMLWriter(GraphML):
def __init__(
Expand Down Expand Up @@ -504,7 +515,7 @@ def attr_type(self, name, scope, value):
types = self.attribute_types[(name, scope)]

if len(types) > 1:
types = {self.xml_type[t] for t in types}
types = {self.get_xml_type(t) for t in types}
if "string" in types:
return str
elif "float" in types or "double" in types:
Expand Down Expand Up @@ -551,7 +562,7 @@ def add_data(self, name, element_type, value, scope="all", default=None):
raise nx.NetworkXError(
f"GraphML writer does not support {element_type} as data values."
)
keyid = self.get_key(name, self.xml_type[element_type], scope, default)
keyid = self.get_key(name, self.get_xml_type(element_type), scope, default)
data_element = self.myElement("data", key=keyid)
data_element.text = str(value)
return data_element
Expand Down Expand Up @@ -765,15 +776,15 @@ def add_graph_element(self, G):
for k, v in graphdata.items():
self.attribute_types[(str(k), "graph")].add(type(v))
for k, v in graphdata.items():
element_type = self.xml_type[self.attr_type(k, "graph", v)]
element_type = self.get_xml_type(self.attr_type(k, "graph", v))
self.get_key(str(k), element_type, "graph", None)
# Nodes and data
for node, d in G.nodes(data=True):
for k, v in d.items():
self.attribute_types[(str(k), "node")].add(type(v))
for node, d in G.nodes(data=True):
for k, v in d.items():
T = self.xml_type[self.attr_type(k, "node", v)]
T = self.get_xml_type(self.attr_type(k, "node", v))
self.get_key(str(k), T, "node", node_default.get(k))
# Edges and data
if G.is_multigraph():
Expand All @@ -782,15 +793,15 @@ def add_graph_element(self, G):
self.attribute_types[(str(k), "edge")].add(type(v))
for u, v, ekey, d in G.edges(keys=True, data=True):
for k, v in d.items():
T = self.xml_type[self.attr_type(k, "edge", v)]
T = self.get_xml_type(self.attr_type(k, "edge", v))
self.get_key(str(k), T, "edge", edge_default.get(k))
else:
for u, v, d in G.edges(data=True):
for k, v in d.items():
self.attribute_types[(str(k), "edge")].add(type(v))
for u, v, d in G.edges(data=True):
for k, v in d.items():
T = self.xml_type[self.attr_type(k, "edge", v)]
T = self.get_xml_type(self.attr_type(k, "edge", v))
self.get_key(str(k), T, "edge", edge_default.get(k))

# Now add attribute keys to the xml file
Expand Down
36 changes: 36 additions & 0 deletions networkx/readwrite/tests/test_graphml.py
Expand Up @@ -1500,3 +1500,39 @@ class TestXMLGraphML(TestWriteGraphML):
@classmethod
def setup_class(cls):
TestWriteGraphML.setup_class()


def test_exception_for_unsupported_datatype_node_attr():
"""Test that a detailed exception is raised when an attribute is of a type
not supported by GraphML, e.g. a list"""
pytest.importorskip("lxml.etree")
# node attribute
G = nx.Graph()
G.add_node(0, my_list_attribute=[0, 1, 2])
fh = io.BytesIO()
with pytest.raises(TypeError, match="GraphML does not support"):
nx.write_graphml(G, fh)


def test_exception_for_unsupported_datatype_edge_attr():
"""Test that a detailed exception is raised when an attribute is of a type
not supported by GraphML, e.g. a list"""
pytest.importorskip("lxml.etree")
# edge attribute
G = nx.Graph()
G.add_edge(0, 1, my_list_attribute=[0, 1, 2])
fh = io.BytesIO()
with pytest.raises(TypeError, match="GraphML does not support"):
nx.write_graphml(G, fh)


def test_exception_for_unsupported_datatype_graph_attr():
"""Test that a detailed exception is raised when an attribute is of a type
not supported by GraphML, e.g. a list"""
pytest.importorskip("lxml.etree")
# graph attribute
G = nx.Graph()
G.graph["my_list_attribute"] = [0, 1, 2]
fh = io.BytesIO()
with pytest.raises(TypeError, match="GraphML does not support"):
nx.write_graphml(G, fh)

0 comments on commit 286cc28

Please sign in to comment.