Kano001's picture
Upload 1329 files
b200bda verified
raw
history blame
21.3 kB
import codecs
import io
import math
import os
import tempfile
from ast import literal_eval
from contextlib import contextmanager
from textwrap import dedent
import pytest
import networkx as nx
from networkx.readwrite.gml import literal_destringizer, literal_stringizer
class TestGraph:
@classmethod
def setup_class(cls):
cls.simple_data = """Creator "me"
Version "xx"
graph [
comment "This is a sample graph"
directed 1
IsPlanar 1
pos [ x 0 y 1 ]
node [
id 1
label "Node 1"
pos [ x 1 y 1 ]
]
node [
id 2
pos [ x 1 y 2 ]
label "Node 2"
]
node [
id 3
label "Node 3"
pos [ x 1 y 3 ]
]
edge [
source 1
target 2
label "Edge from node 1 to node 2"
color [line "blue" thickness 3]
]
edge [
source 2
target 3
label "Edge from node 2 to node 3"
]
edge [
source 3
target 1
label "Edge from node 3 to node 1"
]
]
"""
def test_parse_gml_cytoscape_bug(self):
# example from issue #321, originally #324 in trac
cytoscape_example = """
Creator "Cytoscape"
Version 1.0
graph [
node [
root_index -3
id -3
graphics [
x -96.0
y -67.0
w 40.0
h 40.0
fill "#ff9999"
type "ellipse"
outline "#666666"
outline_width 1.5
]
label "node2"
]
node [
root_index -2
id -2
graphics [
x 63.0
y 37.0
w 40.0
h 40.0
fill "#ff9999"
type "ellipse"
outline "#666666"
outline_width 1.5
]
label "node1"
]
node [
root_index -1
id -1
graphics [
x -31.0
y -17.0
w 40.0
h 40.0
fill "#ff9999"
type "ellipse"
outline "#666666"
outline_width 1.5
]
label "node0"
]
edge [
root_index -2
target -2
source -1
graphics [
width 1.5
fill "#0000ff"
type "line"
Line [
]
source_arrow 0
target_arrow 3
]
label "DirectedEdge"
]
edge [
root_index -1
target -1
source -3
graphics [
width 1.5
fill "#0000ff"
type "line"
Line [
]
source_arrow 0
target_arrow 3
]
label "DirectedEdge"
]
]
"""
nx.parse_gml(cytoscape_example)
def test_parse_gml(self):
G = nx.parse_gml(self.simple_data, label="label")
assert sorted(G.nodes()) == ["Node 1", "Node 2", "Node 3"]
assert sorted(G.edges()) == [
("Node 1", "Node 2"),
("Node 2", "Node 3"),
("Node 3", "Node 1"),
]
assert sorted(G.edges(data=True)) == [
(
"Node 1",
"Node 2",
{
"color": {"line": "blue", "thickness": 3},
"label": "Edge from node 1 to node 2",
},
),
("Node 2", "Node 3", {"label": "Edge from node 2 to node 3"}),
("Node 3", "Node 1", {"label": "Edge from node 3 to node 1"}),
]
def test_read_gml(self):
(fd, fname) = tempfile.mkstemp()
fh = open(fname, "w")
fh.write(self.simple_data)
fh.close()
Gin = nx.read_gml(fname, label="label")
G = nx.parse_gml(self.simple_data, label="label")
assert sorted(G.nodes(data=True)) == sorted(Gin.nodes(data=True))
assert sorted(G.edges(data=True)) == sorted(Gin.edges(data=True))
os.close(fd)
os.unlink(fname)
def test_labels_are_strings(self):
# GML requires labels to be strings (i.e., in quotes)
answer = """graph [
node [
id 0
label "1203"
]
]"""
G = nx.Graph()
G.add_node(1203)
data = "\n".join(nx.generate_gml(G, stringizer=literal_stringizer))
assert data == answer
def test_relabel_duplicate(self):
data = """
graph
[
label ""
directed 1
node
[
id 0
label "same"
]
node
[
id 1
label "same"
]
]
"""
fh = io.BytesIO(data.encode("UTF-8"))
fh.seek(0)
pytest.raises(nx.NetworkXError, nx.read_gml, fh, label="label")
@pytest.mark.parametrize("stringizer", (None, literal_stringizer))
def test_tuplelabels(self, stringizer):
# https://github.com/networkx/networkx/pull/1048
# Writing tuple labels to GML failed.
G = nx.Graph()
G.add_edge((0, 1), (1, 0))
data = "\n".join(nx.generate_gml(G, stringizer=stringizer))
answer = """graph [
node [
id 0
label "(0,1)"
]
node [
id 1
label "(1,0)"
]
edge [
source 0
target 1
]
]"""
assert data == answer
def test_quotes(self):
# https://github.com/networkx/networkx/issues/1061
# Encoding quotes as HTML entities.
G = nx.path_graph(1)
G.name = "path_graph(1)"
attr = 'This is "quoted" and this is a copyright: ' + chr(169)
G.nodes[0]["demo"] = attr
fobj = tempfile.NamedTemporaryFile()
nx.write_gml(G, fobj)
fobj.seek(0)
# Should be bytes in 2.x and 3.x
data = fobj.read().strip().decode("ascii")
answer = """graph [
name "path_graph(1)"
node [
id 0
label "0"
demo "This is "quoted" and this is a copyright: ©"
]
]"""
assert data == answer
def test_unicode_node(self):
node = "node" + chr(169)
G = nx.Graph()
G.add_node(node)
fobj = tempfile.NamedTemporaryFile()
nx.write_gml(G, fobj)
fobj.seek(0)
# Should be bytes in 2.x and 3.x
data = fobj.read().strip().decode("ascii")
answer = """graph [
node [
id 0
label "node©"
]
]"""
assert data == answer
def test_float_label(self):
node = 1.0
G = nx.Graph()
G.add_node(node)
fobj = tempfile.NamedTemporaryFile()
nx.write_gml(G, fobj)
fobj.seek(0)
# Should be bytes in 2.x and 3.x
data = fobj.read().strip().decode("ascii")
answer = """graph [
node [
id 0
label "1.0"
]
]"""
assert data == answer
def test_special_float_label(self):
special_floats = [float("nan"), float("+inf"), float("-inf")]
try:
import numpy as np
special_floats += [np.nan, np.inf, np.inf * -1]
except ImportError:
special_floats += special_floats
G = nx.cycle_graph(len(special_floats))
attrs = dict(enumerate(special_floats))
nx.set_node_attributes(G, attrs, "nodefloat")
edges = list(G.edges)
attrs = {edges[i]: value for i, value in enumerate(special_floats)}
nx.set_edge_attributes(G, attrs, "edgefloat")
fobj = tempfile.NamedTemporaryFile()
nx.write_gml(G, fobj)
fobj.seek(0)
# Should be bytes in 2.x and 3.x
data = fobj.read().strip().decode("ascii")
answer = """graph [
node [
id 0
label "0"
nodefloat NAN
]
node [
id 1
label "1"
nodefloat +INF
]
node [
id 2
label "2"
nodefloat -INF
]
node [
id 3
label "3"
nodefloat NAN
]
node [
id 4
label "4"
nodefloat +INF
]
node [
id 5
label "5"
nodefloat -INF
]
edge [
source 0
target 1
edgefloat NAN
]
edge [
source 0
target 5
edgefloat +INF
]
edge [
source 1
target 2
edgefloat -INF
]
edge [
source 2
target 3
edgefloat NAN
]
edge [
source 3
target 4
edgefloat +INF
]
edge [
source 4
target 5
edgefloat -INF
]
]"""
assert data == answer
fobj.seek(0)
graph = nx.read_gml(fobj)
for indx, value in enumerate(special_floats):
node_value = graph.nodes[str(indx)]["nodefloat"]
if math.isnan(value):
assert math.isnan(node_value)
else:
assert node_value == value
edge = edges[indx]
string_edge = (str(edge[0]), str(edge[1]))
edge_value = graph.edges[string_edge]["edgefloat"]
if math.isnan(value):
assert math.isnan(edge_value)
else:
assert edge_value == value
def test_name(self):
G = nx.parse_gml('graph [ name "x" node [ id 0 label "x" ] ]')
assert "x" == G.graph["name"]
G = nx.parse_gml('graph [ node [ id 0 label "x" ] ]')
assert "" == G.name
assert "name" not in G.graph
def test_graph_types(self):
for directed in [None, False, True]:
for multigraph in [None, False, True]:
gml = "graph ["
if directed is not None:
gml += " directed " + str(int(directed))
if multigraph is not None:
gml += " multigraph " + str(int(multigraph))
gml += ' node [ id 0 label "0" ]'
gml += " edge [ source 0 target 0 ]"
gml += " ]"
G = nx.parse_gml(gml)
assert bool(directed) == G.is_directed()
assert bool(multigraph) == G.is_multigraph()
gml = "graph [\n"
if directed is True:
gml += " directed 1\n"
if multigraph is True:
gml += " multigraph 1\n"
gml += """ node [
id 0
label "0"
]
edge [
source 0
target 0
"""
if multigraph:
gml += " key 0\n"
gml += " ]\n]"
assert gml == "\n".join(nx.generate_gml(G))
def test_data_types(self):
data = [
True,
False,
10**20,
-2e33,
"'",
'"&&&""',
[{(b"\xfd",): "\x7f", chr(0x4444): (1, 2)}, (2, "3")],
]
data.append(chr(0x14444))
data.append(literal_eval("{2.3j, 1 - 2.3j, ()}"))
G = nx.Graph()
G.name = data
G.graph["data"] = data
G.add_node(0, int=-1, data={"data": data})
G.add_edge(0, 0, float=-2.5, data=data)
gml = "\n".join(nx.generate_gml(G, stringizer=literal_stringizer))
G = nx.parse_gml(gml, destringizer=literal_destringizer)
assert data == G.name
assert {"name": data, "data": data} == G.graph
assert list(G.nodes(data=True)) == [(0, {"int": -1, "data": {"data": data}})]
assert list(G.edges(data=True)) == [(0, 0, {"float": -2.5, "data": data})]
G = nx.Graph()
G.graph["data"] = "frozenset([1, 2, 3])"
G = nx.parse_gml(nx.generate_gml(G), destringizer=literal_eval)
assert G.graph["data"] == "frozenset([1, 2, 3])"
def test_escape_unescape(self):
gml = """graph [
name "&"䑄��&unknown;"
]"""
G = nx.parse_gml(gml)
assert (
'&"\x0f' + chr(0x4444) + "��&unknown;"
== G.name
)
gml = "\n".join(nx.generate_gml(G))
alnu = "#1234567890;&#x1234567890abcdef"
answer = (
"""graph [
name "&"䑄&"""
+ alnu
+ """;&unknown;"
]"""
)
assert answer == gml
def test_exceptions(self):
pytest.raises(ValueError, literal_destringizer, "(")
pytest.raises(ValueError, literal_destringizer, "frozenset([1, 2, 3])")
pytest.raises(ValueError, literal_destringizer, literal_destringizer)
pytest.raises(ValueError, literal_stringizer, frozenset([1, 2, 3]))
pytest.raises(ValueError, literal_stringizer, literal_stringizer)
with tempfile.TemporaryFile() as f:
f.write(codecs.BOM_UTF8 + b"graph[]")
f.seek(0)
pytest.raises(nx.NetworkXError, nx.read_gml, f)
def assert_parse_error(gml):
pytest.raises(nx.NetworkXError, nx.parse_gml, gml)
assert_parse_error(["graph [\n\n", "]"])
assert_parse_error("")
assert_parse_error('Creator ""')
assert_parse_error("0")
assert_parse_error("graph ]")
assert_parse_error("graph [ 1 ]")
assert_parse_error("graph [ 1.E+2 ]")
assert_parse_error('graph [ "A" ]')
assert_parse_error("graph [ ] graph ]")
assert_parse_error("graph [ ] graph [ ]")
assert_parse_error("graph [ data [1, 2, 3] ]")
assert_parse_error("graph [ node [ ] ]")
assert_parse_error("graph [ node [ id 0 ] ]")
nx.parse_gml('graph [ node [ id "a" ] ]', label="id")
assert_parse_error("graph [ node [ id 0 label 0 ] node [ id 0 label 1 ] ]")
assert_parse_error("graph [ node [ id 0 label 0 ] node [ id 1 label 0 ] ]")
assert_parse_error("graph [ node [ id 0 label 0 ] edge [ ] ]")
assert_parse_error("graph [ node [ id 0 label 0 ] edge [ source 0 ] ]")
nx.parse_gml("graph [edge [ source 0 target 0 ] node [ id 0 label 0 ] ]")
assert_parse_error("graph [ node [ id 0 label 0 ] edge [ source 1 target 0 ] ]")
assert_parse_error("graph [ node [ id 0 label 0 ] edge [ source 0 target 1 ] ]")
assert_parse_error(
"graph [ node [ id 0 label 0 ] node [ id 1 label 1 ] "
"edge [ source 0 target 1 ] edge [ source 1 target 0 ] ]"
)
nx.parse_gml(
"graph [ node [ id 0 label 0 ] node [ id 1 label 1 ] "
"edge [ source 0 target 1 ] edge [ source 1 target 0 ] "
"directed 1 ]"
)
nx.parse_gml(
"graph [ node [ id 0 label 0 ] node [ id 1 label 1 ] "
"edge [ source 0 target 1 ] edge [ source 0 target 1 ]"
"multigraph 1 ]"
)
nx.parse_gml(
"graph [ node [ id 0 label 0 ] node [ id 1 label 1 ] "
"edge [ source 0 target 1 key 0 ] edge [ source 0 target 1 ]"
"multigraph 1 ]"
)
assert_parse_error(
"graph [ node [ id 0 label 0 ] node [ id 1 label 1 ] "
"edge [ source 0 target 1 key 0 ] edge [ source 0 target 1 key 0 ]"
"multigraph 1 ]"
)
nx.parse_gml(
"graph [ node [ id 0 label 0 ] node [ id 1 label 1 ] "
"edge [ source 0 target 1 key 0 ] edge [ source 1 target 0 key 0 ]"
"directed 1 multigraph 1 ]"
)
# Tests for string convertible alphanumeric id and label values
nx.parse_gml("graph [edge [ source a target a ] node [ id a label b ] ]")
nx.parse_gml(
"graph [ node [ id n42 label 0 ] node [ id x43 label 1 ]"
"edge [ source n42 target x43 key 0 ]"
"edge [ source x43 target n42 key 0 ]"
"directed 1 multigraph 1 ]"
)
assert_parse_error(
"graph [edge [ source u'u\4200' target u'u\4200' ] "
+ "node [ id u'u\4200' label b ] ]"
)
def assert_generate_error(*args, **kwargs):
pytest.raises(
nx.NetworkXError, lambda: list(nx.generate_gml(*args, **kwargs))
)
G = nx.Graph()
G.graph[3] = 3
assert_generate_error(G)
G = nx.Graph()
G.graph["3"] = 3
assert_generate_error(G)
G = nx.Graph()
G.graph["data"] = frozenset([1, 2, 3])
assert_generate_error(G, stringizer=literal_stringizer)
def test_label_kwarg(self):
G = nx.parse_gml(self.simple_data, label="id")
assert sorted(G.nodes) == [1, 2, 3]
labels = [G.nodes[n]["label"] for n in sorted(G.nodes)]
assert labels == ["Node 1", "Node 2", "Node 3"]
G = nx.parse_gml(self.simple_data, label=None)
assert sorted(G.nodes) == [1, 2, 3]
labels = [G.nodes[n]["label"] for n in sorted(G.nodes)]
assert labels == ["Node 1", "Node 2", "Node 3"]
def test_outofrange_integers(self):
# GML restricts integers to 32 signed bits.
# Check that we honor this restriction on export
G = nx.Graph()
# Test export for numbers that barely fit or don't fit into 32 bits,
# and 3 numbers in the middle
numbers = {
"toosmall": (-(2**31)) - 1,
"small": -(2**31),
"med1": -4,
"med2": 0,
"med3": 17,
"big": (2**31) - 1,
"toobig": 2**31,
}
G.add_node("Node", **numbers)
fd, fname = tempfile.mkstemp()
try:
nx.write_gml(G, fname)
# Check that the export wrote the nonfitting numbers as strings
G2 = nx.read_gml(fname)
for attr, value in G2.nodes["Node"].items():
if attr == "toosmall" or attr == "toobig":
assert type(value) == str
else:
assert type(value) == int
finally:
os.close(fd)
os.unlink(fname)
def test_multiline(self):
# example from issue #6836
multiline_example = """
graph
[
node
[
id 0
label "multiline node"
label2 "multiline1
multiline2
multiline3"
alt_name "id 0"
]
]
"""
G = nx.parse_gml(multiline_example)
assert G.nodes["multiline node"] == {
"label2": "multiline1 multiline2 multiline3",
"alt_name": "id 0",
}
@contextmanager
def byte_file():
_file_handle = io.BytesIO()
yield _file_handle
_file_handle.seek(0)
class TestPropertyLists:
def test_writing_graph_with_multi_element_property_list(self):
g = nx.Graph()
g.add_node("n1", properties=["element", 0, 1, 2.5, True, False])
with byte_file() as f:
nx.write_gml(g, f)
result = f.read().decode()
assert result == dedent(
"""\
graph [
node [
id 0
label "n1"
properties "element"
properties 0
properties 1
properties 2.5
properties 1
properties 0
]
]
"""
)
def test_writing_graph_with_one_element_property_list(self):
g = nx.Graph()
g.add_node("n1", properties=["element"])
with byte_file() as f:
nx.write_gml(g, f)
result = f.read().decode()
assert result == dedent(
"""\
graph [
node [
id 0
label "n1"
properties "_networkx_list_start"
properties "element"
]
]
"""
)
def test_reading_graph_with_list_property(self):
with byte_file() as f:
f.write(
dedent(
"""
graph [
node [
id 0
label "n1"
properties "element"
properties 0
properties 1
properties 2.5
]
]
"""
).encode("ascii")
)
f.seek(0)
graph = nx.read_gml(f)
assert graph.nodes(data=True)["n1"] == {"properties": ["element", 0, 1, 2.5]}
def test_reading_graph_with_single_element_list_property(self):
with byte_file() as f:
f.write(
dedent(
"""
graph [
node [
id 0
label "n1"
properties "_networkx_list_start"
properties "element"
]
]
"""
).encode("ascii")
)
f.seek(0)
graph = nx.read_gml(f)
assert graph.nodes(data=True)["n1"] == {"properties": ["element"]}
@pytest.mark.parametrize("coll", ([], ()))
def test_stringize_empty_list_tuple(coll):
G = nx.path_graph(2)
G.nodes[0]["test"] = coll # test serializing an empty collection
f = io.BytesIO()
nx.write_gml(G, f) # Smoke test - should not raise
f.seek(0)
H = nx.read_gml(f)
assert H.nodes["0"]["test"] == coll # Check empty list round-trips properly
# Check full round-tripping. Note that nodes are loaded as strings by
# default, so there needs to be some remapping prior to comparison
H = nx.relabel_nodes(H, {"0": 0, "1": 1})
assert nx.utils.graphs_equal(G, H)
# Same as above, but use destringizer for node remapping. Should have no
# effect on node attr
f.seek(0)
H = nx.read_gml(f, destringizer=int)
assert nx.utils.graphs_equal(G, H)