chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,318 @@
|
||||
"""
|
||||
Unit tests for edgelists.
|
||||
"""
|
||||
import io
|
||||
import os
|
||||
import tempfile
|
||||
import textwrap
|
||||
|
||||
import easygraph as eg
|
||||
import pytest
|
||||
|
||||
from easygraph.utils import edges_equal
|
||||
from easygraph.utils import graphs_equal
|
||||
from easygraph.utils import nodes_equal
|
||||
|
||||
|
||||
edges_no_data = textwrap.dedent(
|
||||
"""
|
||||
# comment line
|
||||
1 2
|
||||
# comment line
|
||||
2 3
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
edges_with_values = textwrap.dedent(
|
||||
"""
|
||||
# comment line
|
||||
1 2 2.0
|
||||
# comment line
|
||||
2 3 3.0
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
edges_with_weight = textwrap.dedent(
|
||||
"""
|
||||
# comment line
|
||||
1 2 {'weight':2.0}
|
||||
# comment line
|
||||
2 3 {'weight':3.0}
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
edges_with_multiple_attrs = textwrap.dedent(
|
||||
"""
|
||||
# comment line
|
||||
1 2 {'weight':2.0, 'color':'green'}
|
||||
# comment line
|
||||
2 3 {'weight':3.0, 'color':'red'}
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
edges_with_multiple_attrs_csv = textwrap.dedent(
|
||||
"""
|
||||
# comment line
|
||||
1, 2, {'weight':2.0, 'color':'green'}
|
||||
# comment line
|
||||
2, 3, {'weight':3.0, 'color':'red'}
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
_expected_edges_weights = [(1, 2, {"weight": 2.0}), (2, 3, {"weight": 3.0})]
|
||||
_expected_edges_multiattr = [
|
||||
(1, 2, {"weight": 2.0, "color": "green"}),
|
||||
(2, 3, {"weight": 3.0, "color": "red"}),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("data", "extra_kwargs"),
|
||||
(
|
||||
(edges_no_data, {}),
|
||||
(edges_with_values, {}),
|
||||
(edges_with_weight, {}),
|
||||
(edges_with_multiple_attrs, {}),
|
||||
(edges_with_multiple_attrs_csv, {"delimiter": ","}),
|
||||
),
|
||||
)
|
||||
def test_read_edgelist_no_data(data, extra_kwargs):
|
||||
bytesIO = io.BytesIO(data.encode("utf-8"))
|
||||
G = eg.read_edgelist(bytesIO, nodetype=int, data=False, **extra_kwargs)
|
||||
assert edges_equal(G.edges, [(1, 2, {}), (2, 3, {})])
|
||||
|
||||
|
||||
def test_read_weighted_edgelist():
|
||||
bytesIO = io.BytesIO(edges_with_values.encode("utf-8"))
|
||||
G = eg.read_weighted_edgelist(bytesIO, nodetype=int)
|
||||
assert edges_equal(G.edges, _expected_edges_weights)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("data", "extra_kwargs", "expected"),
|
||||
(
|
||||
(edges_with_weight, {}, _expected_edges_weights),
|
||||
(edges_with_multiple_attrs, {}, _expected_edges_multiattr),
|
||||
(edges_with_multiple_attrs_csv, {"delimiter": ","}, _expected_edges_multiattr),
|
||||
),
|
||||
)
|
||||
def test_read_edgelist_with_data(data, extra_kwargs, expected):
|
||||
bytesIO = io.BytesIO(data.encode("utf-8"))
|
||||
G = eg.read_edgelist(bytesIO, nodetype=int, **extra_kwargs)
|
||||
assert edges_equal(G.edges, expected)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def example_graph():
|
||||
G = eg.Graph()
|
||||
G.add_weighted_edges_from([(1, 2, 3.0), (2, 3, 27.0), (3, 4, 3.0)])
|
||||
return G
|
||||
|
||||
|
||||
def test_parse_edgelist_no_data(example_graph):
|
||||
G = example_graph
|
||||
H = eg.parse_edgelist(["1 2", "2 3", "3 4"], nodetype=int)
|
||||
assert nodes_equal(G.nodes, H.nodes)
|
||||
assert edges_equal(G.edges, H.edges, need_data=False)
|
||||
|
||||
|
||||
def test_parse_edgelist_with_data_dict(example_graph):
|
||||
G = example_graph
|
||||
H = eg.parse_edgelist(
|
||||
["1 2 {'weight': 3}", "2 3 {'weight': 27}", "3 4 {'weight': 3.0}"], nodetype=int
|
||||
)
|
||||
assert nodes_equal(G.nodes, H.nodes)
|
||||
assert edges_equal(G.edges, H.edges)
|
||||
|
||||
|
||||
def test_parse_edgelist_with_data_list(example_graph):
|
||||
G = example_graph
|
||||
H = eg.parse_edgelist(
|
||||
["1 2 3", "2 3 27", "3 4 3.0"], nodetype=int, data=(("weight", float),)
|
||||
)
|
||||
assert nodes_equal(G.nodes, H.nodes)
|
||||
assert edges_equal(G.edges, H.edges)
|
||||
|
||||
|
||||
def test_parse_edgelist():
|
||||
# ignore lines with less than 2 nodes
|
||||
lines = ["1;2", "2 3", "3 4"]
|
||||
G = eg.parse_edgelist(lines, nodetype=int)
|
||||
# assert list(G.edges) == [(2, 3), (3, 4)]
|
||||
assert edges_equal(G.edges, [(2, 3), (3, 4)], need_data=False)
|
||||
# unknown nodetype
|
||||
with pytest.raises(TypeError, match="Failed to convert nodes"):
|
||||
lines = ["1 2", "2 3", "3 4"]
|
||||
eg.parse_edgelist(lines, nodetype="nope")
|
||||
# lines have invalid edge format
|
||||
with pytest.raises(TypeError, match="Failed to convert edge data"):
|
||||
lines = ["1 2 3", "2 3", "3 4"]
|
||||
eg.parse_edgelist(lines, nodetype=int)
|
||||
# edge data and data_keys not the same length
|
||||
with pytest.raises(IndexError, match="not the same length"):
|
||||
lines = ["1 2 3", "2 3 27", "3 4 3.0"]
|
||||
eg.parse_edgelist(
|
||||
lines, nodetype=int, data=(("weight", float), ("capacity", int))
|
||||
)
|
||||
# edge data can't be converted to edge type
|
||||
with pytest.raises(TypeError, match="Failed to convert"):
|
||||
lines = ["1 2 't1'", "2 3 't3'", "3 4 't3'"]
|
||||
eg.parse_edgelist(lines, nodetype=int, data=(("weight", float),))
|
||||
|
||||
|
||||
def test_comments_None():
|
||||
edgelist = ["node#1 node#2", "node#2 node#3"]
|
||||
# comments=None supported to ignore all comment characters
|
||||
G = eg.parse_edgelist(edgelist, comments=None)
|
||||
H = eg.Graph([e.split(" ") for e in edgelist])
|
||||
assert edges_equal(G.edges, H.edges)
|
||||
|
||||
|
||||
class TestEdgelist:
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
cls.G = eg.Graph(name="test")
|
||||
e = [("a", "b"), ("b", "c"), ("c", "d"), ("d", "e"), ("e", "f"), ("a", "f")]
|
||||
cls.G.add_edges_from(e)
|
||||
cls.G.add_node("g")
|
||||
cls.DG = eg.DiGraph(cls.G)
|
||||
cls.XG = eg.MultiGraph()
|
||||
cls.XG.add_weighted_edges_from([(1, 2, 5), (1, 2, 5), (1, 2, 1), (3, 3, 42)])
|
||||
cls.XDG = eg.MultiDiGraph(cls.XG)
|
||||
|
||||
def test_write_edgelist_1(self):
|
||||
fh = io.BytesIO()
|
||||
G = eg.Graph()
|
||||
G.add_edges_from([(1, 2), (2, 3)])
|
||||
eg.write_edgelist(G, fh, data=False)
|
||||
fh.seek(0)
|
||||
assert fh.read() == b"1 2\n2 3\n"
|
||||
|
||||
def test_write_edgelist_2(self):
|
||||
fh = io.BytesIO()
|
||||
G = eg.Graph()
|
||||
G.add_edges_from([(1, 2), (2, 3)])
|
||||
eg.write_edgelist(G, fh, data=True)
|
||||
fh.seek(0)
|
||||
assert fh.read() == b"1 2 {}\n2 3 {}\n"
|
||||
|
||||
def test_write_edgelist_3(self):
|
||||
fh = io.BytesIO()
|
||||
G = eg.Graph()
|
||||
G.add_edge(1, 2, weight=2.0)
|
||||
G.add_edge(2, 3, weight=3.0)
|
||||
eg.write_edgelist(G, fh, data=True)
|
||||
fh.seek(0)
|
||||
assert fh.read() == b"1 2 {'weight': 2.0}\n2 3 {'weight': 3.0}\n"
|
||||
|
||||
def test_write_edgelist_4(self):
|
||||
fh = io.BytesIO()
|
||||
G = eg.Graph()
|
||||
G.add_edge(1, 2, weight=2.0)
|
||||
G.add_edge(2, 3, weight=3.0)
|
||||
eg.write_edgelist(G, fh, data=["weight"])
|
||||
fh.seek(0)
|
||||
assert fh.read() == b"1 2 2.0\n2 3 3.0\n"
|
||||
|
||||
def test_unicode(self):
|
||||
G = eg.Graph()
|
||||
name1 = chr(2344) + chr(123) + chr(6543)
|
||||
name2 = chr(5543) + chr(1543) + chr(324)
|
||||
G.add_edge(name1, "Radiohead", **{name2: 3})
|
||||
fd, fname = tempfile.mkstemp()
|
||||
eg.write_edgelist(G, fname)
|
||||
H = eg.read_edgelist(fname)
|
||||
assert graphs_equal(G, H)
|
||||
os.close(fd)
|
||||
os.unlink(fname)
|
||||
|
||||
def test_latin1_issue(self):
|
||||
G = eg.Graph()
|
||||
name1 = chr(2344) + chr(123) + chr(6543)
|
||||
name2 = chr(5543) + chr(1543) + chr(324)
|
||||
G.add_edge(name1, "Radiohead", **{name2: 3})
|
||||
fd, fname = tempfile.mkstemp()
|
||||
pytest.raises(
|
||||
UnicodeEncodeError, eg.write_edgelist, G, fname, encoding="latin-1"
|
||||
)
|
||||
os.close(fd)
|
||||
os.unlink(fname)
|
||||
|
||||
def test_latin1(self):
|
||||
G = eg.Graph()
|
||||
name1 = "Bj" + chr(246) + "rk"
|
||||
name2 = chr(220) + "ber"
|
||||
G.add_edge(name1, "Radiohead", **{name2: 3})
|
||||
fd, fname = tempfile.mkstemp()
|
||||
eg.write_edgelist(G, fname, encoding="latin-1")
|
||||
H = eg.read_edgelist(fname, encoding="latin-1")
|
||||
assert graphs_equal(G, H)
|
||||
os.close(fd)
|
||||
os.unlink(fname)
|
||||
|
||||
def test_edgelist_graph(self):
|
||||
G = self.G
|
||||
(fd, fname) = tempfile.mkstemp()
|
||||
eg.write_edgelist(G, fname)
|
||||
H = eg.read_edgelist(fname)
|
||||
H2 = eg.read_edgelist(fname)
|
||||
assert H is not H2 # they should be different graphs
|
||||
G.remove_node("g") # isolated nodes are not written in edgelist
|
||||
assert nodes_equal(list(H), list(G))
|
||||
assert edges_equal(list(H.edges), list(G.edges))
|
||||
os.close(fd)
|
||||
os.unlink(fname)
|
||||
|
||||
def test_edgelist_digraph(self):
|
||||
G = self.DG
|
||||
(fd, fname) = tempfile.mkstemp()
|
||||
eg.write_edgelist(G, fname)
|
||||
H = eg.read_edgelist(fname, create_using=eg.DiGraph())
|
||||
H2 = eg.read_edgelist(fname, create_using=eg.DiGraph())
|
||||
assert H is not H2 # they should be different graphs
|
||||
G.remove_node("g") # isolated nodes are not written in edgelist
|
||||
assert nodes_equal(list(H), list(G))
|
||||
assert edges_equal(list(H.edges), list(G.edges))
|
||||
os.close(fd)
|
||||
os.unlink(fname)
|
||||
|
||||
def test_edgelist_integers(self):
|
||||
G = eg.convert_node_labels_to_integers(self.G)
|
||||
(fd, fname) = tempfile.mkstemp()
|
||||
eg.write_edgelist(G, fname)
|
||||
H = eg.read_edgelist(fname, nodetype=int)
|
||||
# isolated nodes are not written in edgelist
|
||||
G.remove_nodes_from(list(eg.isolates(G)))
|
||||
assert nodes_equal(list(H), list(G))
|
||||
assert edges_equal(list(H.edges), list(G.edges))
|
||||
os.close(fd)
|
||||
os.unlink(fname)
|
||||
|
||||
def test_edgelist_multigraph(self):
|
||||
G = self.XG
|
||||
(fd, fname) = tempfile.mkstemp()
|
||||
eg.write_edgelist(G, fname)
|
||||
H = eg.read_edgelist(fname, nodetype=int, create_using=eg.MultiGraph())
|
||||
H2 = eg.read_edgelist(fname, nodetype=int, create_using=eg.MultiGraph())
|
||||
assert H is not H2 # they should be different graphs
|
||||
assert nodes_equal(list(H), list(G))
|
||||
assert edges_equal(list(H.edges), list(G.edges), need_data=False)
|
||||
os.close(fd)
|
||||
os.unlink(fname)
|
||||
|
||||
def test_edgelist_multidigraph(self):
|
||||
G = self.XDG
|
||||
(fd, fname) = tempfile.mkstemp()
|
||||
eg.write_edgelist(G, fname)
|
||||
H = eg.read_edgelist(fname, nodetype=int, create_using=eg.MultiDiGraph())
|
||||
H2 = eg.read_edgelist(fname, nodetype=int, create_using=eg.MultiDiGraph())
|
||||
assert H is not H2 # they should be different graphs
|
||||
assert nodes_equal(list(H), list(G))
|
||||
assert edges_equal(list(H.edges), list(G.edges), need_data=False)
|
||||
os.close(fd)
|
||||
os.unlink(fname)
|
||||
@@ -0,0 +1,447 @@
|
||||
import io
|
||||
import sys
|
||||
import time
|
||||
|
||||
import easygraph as eg
|
||||
import pytest
|
||||
|
||||
|
||||
class TestGEXF:
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
cls.simple_directed_data = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<gexf xmlns="http://www.gexf.net/1.2draft" version="1.2">
|
||||
<graph mode="static" defaultedgetype="directed">
|
||||
<nodes>
|
||||
<node id="0" label="Hello" />
|
||||
<node id="1" label="Word" />
|
||||
</nodes>
|
||||
<edges>
|
||||
<edge id="0" source="0" target="1" />
|
||||
</edges>
|
||||
</graph>
|
||||
</gexf>
|
||||
"""
|
||||
cls.simple_directed_graph = eg.DiGraph()
|
||||
cls.simple_directed_graph.add_node("0", label="Hello")
|
||||
cls.simple_directed_graph.add_node("1", label="World")
|
||||
cls.simple_directed_graph.add_edge("0", "1", id="0")
|
||||
|
||||
cls.simple_directed_fh = io.BytesIO(cls.simple_directed_data.encode("UTF-8"))
|
||||
|
||||
cls.attribute_data = """<?xml version="1.0" encoding="UTF-8"?>\
|
||||
<gexf xmlns="http://www.gexf.net/1.2draft" xmlns:xsi="http://www.w3.\
|
||||
org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.gexf.net/\
|
||||
1.2draft http://www.gexf.net/1.2draft/gexf.xsd" version="1.2">
|
||||
<meta lastmodifieddate="2009-03-20">
|
||||
<creator>Gephi.org</creator>
|
||||
<description>A Web network</description>
|
||||
</meta>
|
||||
<graph defaultedgetype="directed">
|
||||
<attributes class="node">
|
||||
<attribute id="0" title="url" type="string"/>
|
||||
<attribute id="1" title="indegree" type="integer"/>
|
||||
<attribute id="2" title="frog" type="boolean">
|
||||
<default>true</default>
|
||||
</attribute>
|
||||
</attributes>
|
||||
<nodes>
|
||||
<node id="0" label="Gephi">
|
||||
<attvalues>
|
||||
<attvalue for="0" value="https://gephi.org"/>
|
||||
<attvalue for="1" value="1"/>
|
||||
<attvalue for="2" value="false"/>
|
||||
</attvalues>
|
||||
</node>
|
||||
<node id="1" label="Webatlas">
|
||||
<attvalues>
|
||||
<attvalue for="0" value="http://webatlas.fr"/>
|
||||
<attvalue for="1" value="2"/>
|
||||
<attvalue for="2" value="false"/>
|
||||
</attvalues>
|
||||
</node>
|
||||
<node id="2" label="RTGI">
|
||||
<attvalues>
|
||||
<attvalue for="0" value="http://rtgi.fr"/>
|
||||
<attvalue for="1" value="1"/>
|
||||
<attvalue for="2" value="true"/>
|
||||
</attvalues>
|
||||
</node>
|
||||
<node id="3" label="BarabasiLab">
|
||||
<attvalues>
|
||||
<attvalue for="0" value="http://barabasilab.com"/>
|
||||
<attvalue for="1" value="1"/>
|
||||
<attvalue for="2" value="true"/>
|
||||
</attvalues>
|
||||
</node>
|
||||
</nodes>
|
||||
<edges>
|
||||
<edge id="0" source="0" target="1" label="foo"/>
|
||||
<edge id="1" source="0" target="2"/>
|
||||
<edge id="2" source="1" target="0"/>
|
||||
<edge id="3" source="2" target="1"/>
|
||||
<edge id="4" source="0" target="3"/>
|
||||
</edges>
|
||||
</graph>
|
||||
</gexf>
|
||||
"""
|
||||
cls.attribute_graph = eg.DiGraph()
|
||||
cls.attribute_graph.graph["node_default"] = {"frog": True}
|
||||
cls.attribute_graph.add_node(
|
||||
"0", label="Gephi", url="https://gephi.org", indegree=1, frog=False
|
||||
)
|
||||
cls.attribute_graph.add_node(
|
||||
"1", label="Webatlas", url="http://webatlas.fr", indegree=2, frog=False
|
||||
)
|
||||
cls.attribute_graph.add_node(
|
||||
"2", label="RTGI", url="http://rtgi.fr", indegree=1, frog=True
|
||||
)
|
||||
cls.attribute_graph.add_node(
|
||||
"3",
|
||||
label="BarabasiLab",
|
||||
url="http://barabasilab.com",
|
||||
indegree=1,
|
||||
frog=True,
|
||||
)
|
||||
cls.attribute_graph.add_edge("0", "1", id="0", label="foo")
|
||||
cls.attribute_graph.add_edge("0", "2", id="1")
|
||||
cls.attribute_graph.add_edge("1", "0", id="2")
|
||||
cls.attribute_graph.add_edge("2", "1", id="3")
|
||||
cls.attribute_graph.add_edge("0", "3", id="4")
|
||||
cls.attribute_fh = io.BytesIO(cls.attribute_data.encode("UTF-8"))
|
||||
|
||||
cls.simple_undirected_data = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<gexf xmlns="http://www.gexf.net/1.2draft" version="1.2">
|
||||
<graph mode="static" defaultedgetype="undirected">
|
||||
<nodes>
|
||||
<node id="0" label="Hello" />
|
||||
<node id="1" label="Word" />
|
||||
</nodes>
|
||||
<edges>
|
||||
<edge id="0" source="0" target="1" />
|
||||
</edges>
|
||||
</graph>
|
||||
</gexf>
|
||||
"""
|
||||
cls.simple_undirected_graph = eg.Graph()
|
||||
cls.simple_undirected_graph.add_node("0", label="Hello")
|
||||
cls.simple_undirected_graph.add_node("1", label="World")
|
||||
cls.simple_undirected_graph.add_edge("0", "1", id="0")
|
||||
|
||||
cls.simple_undirected_fh = io.BytesIO(
|
||||
cls.simple_undirected_data.encode("UTF-8")
|
||||
)
|
||||
|
||||
def test_read_simple_directed_graphml(self):
|
||||
G = self.simple_directed_graph
|
||||
H = eg.read_gexf(self.simple_directed_fh)
|
||||
assert sorted(G.nodes) == sorted(H.nodes)
|
||||
assert sorted(G.edges) == sorted(H.edges)
|
||||
self.simple_directed_fh.seek(0)
|
||||
|
||||
def test_write_read_simple_directed_graphml(self):
|
||||
G = self.simple_directed_graph
|
||||
fh = io.BytesIO()
|
||||
eg.write_gexf(G, fh)
|
||||
fh.seek(0)
|
||||
H = eg.read_gexf(fh)
|
||||
assert sorted(G.nodes) == sorted(H.nodes)
|
||||
assert sorted(G.edges) == sorted(H.edges)
|
||||
self.simple_directed_fh.seek(0)
|
||||
|
||||
def test_read_simple_undirected_graphml(self):
|
||||
G = self.simple_undirected_graph
|
||||
H = eg.read_gexf(self.simple_undirected_fh)
|
||||
assert sorted(G.nodes) == sorted(H.nodes)
|
||||
assert sorted(G.edges) == sorted(H.edges)
|
||||
self.simple_undirected_fh.seek(0)
|
||||
|
||||
def test_read_attribute_graphml(self):
|
||||
G = self.attribute_graph
|
||||
H = eg.read_gexf(self.attribute_fh)
|
||||
assert sorted(G.nodes) == sorted(H.nodes)
|
||||
ge = sorted(G.edges)
|
||||
he = sorted(H.edges)
|
||||
for a, b in zip(ge, he):
|
||||
assert a == b
|
||||
self.attribute_fh.seek(0)
|
||||
|
||||
def test_directed_edge_in_undirected(self):
|
||||
s = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<gexf xmlns="http://www.gexf.net/1.2draft" version='1.2'>
|
||||
<graph mode="static" defaultedgetype="undirected" name="">
|
||||
<nodes>
|
||||
<node id="0" label="Hello" />
|
||||
<node id="1" label="Word" />
|
||||
</nodes>
|
||||
<edges>
|
||||
<edge id="0" source="0" target="1" type="directed"/>
|
||||
</edges>
|
||||
</graph>
|
||||
</gexf>
|
||||
"""
|
||||
fh = io.BytesIO(s.encode("UTF-8"))
|
||||
pytest.raises(eg.EasyGraphError, eg.read_gexf, fh)
|
||||
|
||||
def test_undirected_edge_in_directed(self):
|
||||
s = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<gexf xmlns="http://www.gexf.net/1.2draft" version='1.2'>
|
||||
<graph mode="static" defaultedgetype="directed" name="">
|
||||
<nodes>
|
||||
<node id="0" label="Hello" />
|
||||
<node id="1" label="Word" />
|
||||
</nodes>
|
||||
<edges>
|
||||
<edge id="0" source="0" target="1" type="undirected"/>
|
||||
</edges>
|
||||
</graph>
|
||||
</gexf>
|
||||
"""
|
||||
fh = io.BytesIO(s.encode("UTF-8"))
|
||||
pytest.raises(eg.EasyGraphError, eg.read_gexf, fh)
|
||||
|
||||
def test_key_raises(self):
|
||||
s = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<gexf xmlns="http://www.gexf.net/1.2draft" version='1.2'>
|
||||
<graph mode="static" defaultedgetype="directed" name="">
|
||||
<nodes>
|
||||
<node id="0" label="Hello">
|
||||
<attvalues>
|
||||
<attvalue for='0' value='1'/>
|
||||
</attvalues>
|
||||
</node>
|
||||
<node id="1" label="Word" />
|
||||
</nodes>
|
||||
<edges>
|
||||
<edge id="0" source="0" target="1" type="undirected"/>
|
||||
</edges>
|
||||
</graph>
|
||||
</gexf>
|
||||
"""
|
||||
fh = io.BytesIO(s.encode("UTF-8"))
|
||||
pytest.raises(eg.EasyGraphError, eg.read_gexf, fh)
|
||||
|
||||
def test_relabel(self):
|
||||
s = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<gexf xmlns="http://www.gexf.net/1.2draft" version='1.2'>
|
||||
<graph mode="static" defaultedgetype="directed" name="">
|
||||
<nodes>
|
||||
<node id="0" label="Hello" />
|
||||
<node id="1" label="Word" />
|
||||
</nodes>
|
||||
<edges>
|
||||
<edge id="0" source="0" target="1"/>
|
||||
</edges>
|
||||
</graph>
|
||||
</gexf>
|
||||
"""
|
||||
fh = io.BytesIO(s.encode("UTF-8"))
|
||||
G = eg.read_gexf(fh, relabel=True)
|
||||
assert sorted(G.nodes) == ["Hello", "Word"]
|
||||
|
||||
def test_default_attribute(self):
|
||||
G = eg.Graph()
|
||||
G.add_node(1, label="1", color="green")
|
||||
eg.add_path(G, [0, 1, 2, 3])
|
||||
G.add_edge(1, 2, foo=3)
|
||||
G.graph["node_default"] = {"color": "yellow"}
|
||||
G.graph["edge_default"] = {"foo": 7}
|
||||
fh = io.BytesIO()
|
||||
eg.write_gexf(G, fh)
|
||||
fh.seek(0)
|
||||
H = eg.read_gexf(fh, node_type=int)
|
||||
assert sorted(G.nodes) == sorted(H.nodes)
|
||||
# Reading a gexf graph always sets mode attribute to either
|
||||
# 'static' or 'dynamic'. Remove the mode attribute from the
|
||||
# read graph for the sake of comparing remaining attributes.
|
||||
del H.graph["mode"]
|
||||
assert G.graph == H.graph
|
||||
|
||||
def test_serialize_ints_to_strings(self):
|
||||
G = eg.Graph()
|
||||
G.add_node(1, id=7, label=77)
|
||||
fh = io.BytesIO()
|
||||
eg.write_gexf(G, fh)
|
||||
fh.seek(0)
|
||||
H = eg.read_gexf(fh, node_type=int)
|
||||
assert list(H) == [7]
|
||||
assert H.nodes[7]["label"] == "77"
|
||||
|
||||
@pytest.mark.skipif(sys.version_info < (3, 8), reason="requires >= python3.8")
|
||||
def test_edge_id_construct(self):
|
||||
G = eg.Graph()
|
||||
G.add_edges_from([(0, 1, {"id": 0}), (1, 2, {"id": 2}), (2, 3)])
|
||||
|
||||
expected = f"""<gexf xmlns="http://www.gexf.net/1.2draft" xmlns:xsi\
|
||||
="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.\
|
||||
gexf.net/1.2draft http://www.gexf.net/1.2draft/gexf.xsd" version="1.2">
|
||||
<meta lastmodifieddate="{time.strftime('%Y-%m-%d')}">
|
||||
<creator>EasyGraph</creator>
|
||||
</meta>
|
||||
<graph defaultedgetype="undirected" mode="static" name="">
|
||||
<nodes>
|
||||
<node id="0" label="0" />
|
||||
<node id="1" label="1" />
|
||||
<node id="2" label="2" />
|
||||
<node id="3" label="3" />
|
||||
</nodes>
|
||||
<edges>
|
||||
<edge source="0" target="1" id="0" />
|
||||
<edge source="1" target="2" id="2" />
|
||||
<edge source="2" target="3" id="1" />
|
||||
</edges>
|
||||
</graph>
|
||||
</gexf>"""
|
||||
|
||||
obtained = "\n".join(eg.generate_gexf(G))
|
||||
assert expected == obtained
|
||||
|
||||
@pytest.mark.skipif(sys.version_info < (3, 8), reason="requires >= python3.8")
|
||||
def test_numpy_type(self):
|
||||
np = pytest.importorskip("numpy")
|
||||
G = eg.path_graph(4)
|
||||
eg.set_node_attributes(G, {n: n for n in np.arange(4)}, "number")
|
||||
G[0][1]["edge-number"] = np.float64(1.1)
|
||||
expected = f"""<gexf xmlns="http://www.gexf.net/1.2draft"\
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation\
|
||||
="http://www.gexf.net/1.2draft http://www.gexf.net/1.2draft/gexf.xsd"\
|
||||
version="1.2">
|
||||
<meta lastmodifieddate="{time.strftime('%Y-%m-%d')}">
|
||||
<creator>EasyGraph</creator>
|
||||
</meta>
|
||||
<graph defaultedgetype="undirected" mode="static" name="">
|
||||
<attributes mode="static" class="edge">
|
||||
<attribute id="1" title="edge-number" type="float" />
|
||||
</attributes>
|
||||
<attributes mode="static" class="node">
|
||||
<attribute id="0" title="number" type="int" />
|
||||
</attributes>
|
||||
<nodes>
|
||||
<node id="0" label="0">
|
||||
<attvalues>
|
||||
<attvalue for="0" value="0" />
|
||||
</attvalues>
|
||||
</node>
|
||||
<node id="1" label="1">
|
||||
<attvalues>
|
||||
<attvalue for="0" value="1" />
|
||||
</attvalues>
|
||||
</node>
|
||||
<node id="2" label="2">
|
||||
<attvalues>
|
||||
<attvalue for="0" value="2" />
|
||||
</attvalues>
|
||||
</node>
|
||||
<node id="3" label="3">
|
||||
<attvalues>
|
||||
<attvalue for="0" value="3" />
|
||||
</attvalues>
|
||||
</node>
|
||||
</nodes>
|
||||
<edges>
|
||||
<edge source="0" target="1" id="0">
|
||||
<attvalues>
|
||||
<attvalue for="1" value="1.1" />
|
||||
</attvalues>
|
||||
</edge>
|
||||
<edge source="1" target="2" id="1" />
|
||||
<edge source="2" target="3" id="2" />
|
||||
</edges>
|
||||
</graph>
|
||||
</gexf>"""
|
||||
obtained = "\n".join(eg.generate_gexf(G))
|
||||
assert expected == obtained
|
||||
|
||||
def test_bool(self):
|
||||
G = eg.Graph()
|
||||
G.add_node(1, testattr=True)
|
||||
fh = io.BytesIO()
|
||||
eg.write_gexf(G, fh)
|
||||
fh.seek(0)
|
||||
H = eg.read_gexf(fh, node_type=int)
|
||||
assert H.nodes[1]["testattr"]
|
||||
|
||||
def test_specials(self):
|
||||
from math import isnan
|
||||
|
||||
inf, nan = float("inf"), float("nan")
|
||||
G = eg.Graph()
|
||||
G.add_node(1, testattr=inf, strdata="inf", key="a")
|
||||
G.add_node(2, testattr=nan, strdata="nan", key="b")
|
||||
G.add_node(3, testattr=-inf, strdata="-inf", key="c")
|
||||
|
||||
fh = io.BytesIO()
|
||||
eg.write_gexf(G, fh)
|
||||
fh.seek(0)
|
||||
filetext = fh.read()
|
||||
fh.seek(0)
|
||||
H = eg.read_gexf(fh, node_type=int)
|
||||
|
||||
assert b"INF" in filetext
|
||||
assert b"NaN" in filetext
|
||||
assert b"-INF" in filetext
|
||||
|
||||
assert H.nodes[1]["testattr"] == inf
|
||||
assert isnan(H.nodes[2]["testattr"])
|
||||
assert H.nodes[3]["testattr"] == -inf
|
||||
|
||||
assert H.nodes[1]["strdata"] == "inf"
|
||||
assert H.nodes[2]["strdata"] == "nan"
|
||||
assert H.nodes[3]["strdata"] == "-inf"
|
||||
|
||||
assert H.nodes[1]["easygraph_key"] == "a"
|
||||
assert H.nodes[2]["easygraph_key"] == "b"
|
||||
assert H.nodes[3]["easygraph_key"] == "c"
|
||||
|
||||
def test_simple_list(self):
|
||||
G = eg.Graph()
|
||||
list_value = [(1, 2, 3), (9, 1, 2)]
|
||||
G.add_node(1, key=list_value)
|
||||
fh = io.BytesIO()
|
||||
eg.write_gexf(G, fh)
|
||||
fh.seek(0)
|
||||
H = eg.read_gexf(fh, node_type=int)
|
||||
assert H.nodes[1]["easygraph_key"] == list_value
|
||||
|
||||
def test_dynamic_mode(self):
|
||||
G = eg.Graph()
|
||||
G.add_node(1, label="1", color="green")
|
||||
G.graph["mode"] = "dynamic"
|
||||
fh = io.BytesIO()
|
||||
eg.write_gexf(G, fh)
|
||||
fh.seek(0)
|
||||
H = eg.read_gexf(fh, node_type=int)
|
||||
assert sorted(G.nodes) == sorted(H.nodes)
|
||||
assert sorted(sorted(e) for e in G.edges) == sorted(sorted(e) for e in H.edges)
|
||||
|
||||
def test_slice_and_spell(self):
|
||||
# Test spell first, so version = 1.2
|
||||
G = eg.Graph()
|
||||
G.add_node(0, label="1", color="green")
|
||||
G.nodes[0]["spells"] = [(1, 2)]
|
||||
fh = io.BytesIO()
|
||||
eg.write_gexf(G, fh)
|
||||
fh.seek(0)
|
||||
H = eg.read_gexf(fh, node_type=int)
|
||||
assert sorted(G.nodes) == sorted(H.nodes)
|
||||
assert sorted(sorted(e) for e in G.edges) == sorted(sorted(e) for e in H.edges)
|
||||
|
||||
G = eg.Graph()
|
||||
G.add_node(0, label="1", color="green")
|
||||
G.nodes[0]["slices"] = [(1, 2)]
|
||||
fh = io.BytesIO()
|
||||
eg.write_gexf(G, fh, version="1.1draft")
|
||||
fh.seek(0)
|
||||
H = eg.read_gexf(fh, node_type=int)
|
||||
assert sorted(G.nodes) == sorted(H.nodes)
|
||||
assert sorted(sorted(e) for e in G.edges) == sorted(sorted(e) for e in H.edges)
|
||||
|
||||
def test_add_parent(self):
|
||||
G = eg.Graph()
|
||||
G.add_node(0, label="1", color="green", parents=[1, 2])
|
||||
fh = io.BytesIO()
|
||||
eg.write_gexf(G, fh)
|
||||
fh.seek(0)
|
||||
H = eg.read_gexf(fh, node_type=int)
|
||||
assert sorted(G.nodes) == sorted(H.nodes)
|
||||
assert sorted(sorted(e) for e in G.edges) == sorted(sorted(e) for e in H.edges)
|
||||
@@ -0,0 +1,589 @@
|
||||
import codecs
|
||||
import io
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
from ast import literal_eval
|
||||
from contextlib import contextmanager
|
||||
from textwrap import dedent
|
||||
|
||||
import easygraph as eg
|
||||
import pytest
|
||||
|
||||
from easygraph.readwrite.gml import literal_destringizer
|
||||
from easygraph.readwrite.gml import 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"
|
||||
]
|
||||
]
|
||||
"""
|
||||
eg.parse_gml(cytoscape_example)
|
||||
|
||||
def test_parse_gml(self):
|
||||
G = eg.parse_gml(self.simple_data, label="label")
|
||||
assert sorted(G.nodes) == ["Node 1", "Node 2", "Node 3"]
|
||||
assert [e[:2] for e in sorted(G.edges)] == [
|
||||
("Node 1", "Node 2"),
|
||||
("Node 2", "Node 3"),
|
||||
("Node 3", "Node 1"),
|
||||
]
|
||||
|
||||
assert [e for e in sorted(G.edges)] == [
|
||||
(
|
||||
"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 = eg.read_gml(fname, label="label")
|
||||
G = eg.parse_gml(self.simple_data, label="label")
|
||||
assert sorted(G.nodes) == sorted(Gin.nodes)
|
||||
assert sorted(G.edges) == sorted(Gin.edges)
|
||||
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 = eg.Graph()
|
||||
G.add_node(1203)
|
||||
data = "\n".join(eg.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(eg.EasyGraphError, eg.read_gml, fh, label="label")
|
||||
|
||||
def test_quotes(self):
|
||||
G = eg.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()
|
||||
eg.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 = eg.Graph()
|
||||
G.add_node(node)
|
||||
fobj = tempfile.NamedTemporaryFile()
|
||||
eg.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 = eg.Graph()
|
||||
G.add_node(node)
|
||||
fobj = tempfile.NamedTemporaryFile()
|
||||
eg.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_name(self):
|
||||
G = eg.parse_gml('graph [ name "x" node [ id 0 label "x" ] ]')
|
||||
assert "x" == G.graph["name"]
|
||||
G = eg.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 = eg.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(eg.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 = eg.Graph()
|
||||
G.name = data
|
||||
G.graph["data"] = data
|
||||
G.add_node(0, int=-1, data=dict(data=data))
|
||||
G.add_edge(0, 0, float=-2.5, data=data)
|
||||
gml = "\n".join(eg.generate_gml(G, stringizer=literal_stringizer))
|
||||
G = eg.parse_gml(gml, destringizer=literal_destringizer)
|
||||
assert data == G.name
|
||||
assert {"name": data, "data": data} == G.graph
|
||||
assert G.nodes == {0: dict(int=-1, data=dict(data=data))}
|
||||
assert list(G.edges) == [(0, 0, dict(float=-2.5, data=data))]
|
||||
G = eg.Graph()
|
||||
G.graph["data"] = "frozenset([1, 2, 3])"
|
||||
G = eg.parse_gml(eg.generate_gml(G), destringizer=literal_eval)
|
||||
assert G.graph["data"] == "frozenset([1, 2, 3])"
|
||||
|
||||
def test_escape_unescape(self):
|
||||
gml = """graph [
|
||||
name "&"䑄��&unknown;"
|
||||
]"""
|
||||
G = eg.parse_gml(gml)
|
||||
assert (
|
||||
'&"\x0f' + chr(0x4444) + "��&unknown;"
|
||||
== G.name
|
||||
)
|
||||
gml = "\n".join(eg.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(eg.EasyGraphError, eg.read_gml, f)
|
||||
|
||||
def assert_parse_error(gml):
|
||||
pytest.raises(eg.EasyGraphError, eg.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 ] ]")
|
||||
eg.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 ] ]")
|
||||
eg.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 ] ]"
|
||||
)
|
||||
eg.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 ]"
|
||||
)
|
||||
eg.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 ]"
|
||||
)
|
||||
eg.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 ]"
|
||||
)
|
||||
eg.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
|
||||
eg.parse_gml("graph [edge [ source a target a ] node [ id a label b ] ]")
|
||||
eg.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(
|
||||
eg.EasyGraphError, lambda: list(eg.generate_gml(*args, **kwargs))
|
||||
)
|
||||
|
||||
G = eg.Graph()
|
||||
G.graph[3] = 3
|
||||
assert_generate_error(G)
|
||||
G = eg.Graph()
|
||||
G.graph["3"] = 3
|
||||
assert_generate_error(G)
|
||||
G = eg.Graph()
|
||||
G.graph["data"] = frozenset([1, 2, 3])
|
||||
assert_generate_error(G, stringizer=literal_stringizer)
|
||||
G = eg.Graph()
|
||||
G.graph["data"] = []
|
||||
assert_generate_error(G)
|
||||
assert_generate_error(G, stringizer=len)
|
||||
|
||||
def test_label_kwarg(self):
|
||||
G = eg.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 = eg.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 = eg.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:
|
||||
eg.write_gml(G, fname)
|
||||
# Check that the export wrote the nonfitting numbers as strings
|
||||
G2 = eg.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)
|
||||
|
||||
|
||||
@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 = eg.Graph()
|
||||
g.add_node("n1", properties=["element", 0, 1, 2.5, True, False])
|
||||
with byte_file() as f:
|
||||
eg.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 = eg.Graph()
|
||||
g.add_node("n1", properties=["element"])
|
||||
with byte_file() as f:
|
||||
eg.write_gml(g, f)
|
||||
result = f.read().decode()
|
||||
|
||||
assert result == dedent(
|
||||
"""\
|
||||
graph [
|
||||
node [
|
||||
id 0
|
||||
label "n1"
|
||||
properties "_easygraph_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 = eg.read_gml(f)
|
||||
assert graph.nodes["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 "_easygraph_list_start"
|
||||
properties "element"
|
||||
]
|
||||
]
|
||||
"""
|
||||
).encode("ascii")
|
||||
)
|
||||
f.seek(0)
|
||||
graph = eg.read_gml(f)
|
||||
assert graph.nodes["n1"] == {"properties": ["element"]}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,58 @@
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
pygraphviz = pytest.importorskip("pygraphviz")
|
||||
|
||||
import easygraph as eg
|
||||
|
||||
from easygraph.utils import edges_equal
|
||||
from easygraph.utils import nodes_equal
|
||||
|
||||
|
||||
class TestAGraph:
|
||||
def build_graph(self, G):
|
||||
edges = [("A", "B"), ("A", "C"), ("A", "C"), ("B", "C"), ("A", "D")]
|
||||
G.add_edges_from(edges)
|
||||
G.add_node("E")
|
||||
G.graph["metal"] = "bronze"
|
||||
return G
|
||||
|
||||
def assert_equal(self, G1, G2):
|
||||
assert nodes_equal(G1.nodes, G2.nodes)
|
||||
assert edges_equal(G1.edges, G2.edges)
|
||||
assert G1.graph["metal"] == G2.graph["metal"]
|
||||
|
||||
def agraph_checks(self, G):
|
||||
G = self.build_graph(G)
|
||||
A = eg.to_agraph(G)
|
||||
H = eg.from_agraph(A)
|
||||
self.assert_equal(G, H)
|
||||
|
||||
fd, fname = tempfile.mkstemp()
|
||||
eg.write_dot(H, fname)
|
||||
Hin = eg.read_dot(fname)
|
||||
self.assert_equal(H, Hin)
|
||||
os.close(fd)
|
||||
os.unlink(fname)
|
||||
|
||||
(fd, fname) = tempfile.mkstemp()
|
||||
with open(fname, "w") as fh:
|
||||
eg.write_dot(H, fh)
|
||||
|
||||
with open(fname) as fh:
|
||||
Hin = eg.read_dot(fh)
|
||||
os.close(fd)
|
||||
os.unlink(fname)
|
||||
self.assert_equal(H, Hin)
|
||||
|
||||
def test_from_agraph_name(self):
|
||||
G = eg.Graph(name="test")
|
||||
A = eg.to_agraph(G)
|
||||
H = eg.from_agraph(A)
|
||||
assert G.name == "test"
|
||||
|
||||
def test_undirected(self):
|
||||
self.agraph_checks(eg.Graph())
|
||||
@@ -0,0 +1,307 @@
|
||||
# # This file is part of the NetworkX distribution.
|
||||
|
||||
# NetworkX is distributed with the 3-clause BSD license.
|
||||
|
||||
|
||||
# ::
|
||||
# Copyright (C) 2004-2022, NetworkX Developers
|
||||
# Aric Hagberg <hagberg@lanl.gov>
|
||||
# Dan Schult <dschult@colgate.edu>
|
||||
# Pieter Swart <swart@lanl.gov>
|
||||
# All rights reserved.
|
||||
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following
|
||||
# disclaimer in the documentation and/or other materials provided
|
||||
# with the distribution.
|
||||
|
||||
# * Neither the name of the NetworkX Developers nor the names of its
|
||||
# contributors may be used to endorse or promote products derived
|
||||
# from this software without specific prior written permission.
|
||||
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
"""
|
||||
Pajek tests
|
||||
"""
|
||||
import easygraph as eg
|
||||
|
||||
|
||||
print(eg)
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
from easygraph.utils import edges_equal
|
||||
from easygraph.utils import nodes_equal
|
||||
|
||||
|
||||
# from rich import print
|
||||
|
||||
test_parse_pajek_edges = [
|
||||
(
|
||||
"A1",
|
||||
"A1",
|
||||
0,
|
||||
{
|
||||
"weight": 1.0,
|
||||
"h2": "0",
|
||||
"w": "3",
|
||||
"c": "Blue",
|
||||
"s": "3",
|
||||
"a1": "-130",
|
||||
"k1": "0.6",
|
||||
"a2": "-130",
|
||||
"k2": "0.6",
|
||||
"ap": "0.5",
|
||||
"l": "Bezier loop",
|
||||
"lc": "BlueViolet",
|
||||
"fos": "20",
|
||||
"lr": "58",
|
||||
"lp": "0.3",
|
||||
"la": "360",
|
||||
},
|
||||
),
|
||||
(
|
||||
"A1",
|
||||
"Bb",
|
||||
0,
|
||||
{
|
||||
"weight": 1.0,
|
||||
"h2": "0",
|
||||
"a1": "40",
|
||||
"k1": "2.8",
|
||||
"a2": "30",
|
||||
"k2": "0.8",
|
||||
"ap": "25",
|
||||
"l": "Bezier arc",
|
||||
"lphi": "90",
|
||||
"la": "0",
|
||||
"lp": "0.65",
|
||||
},
|
||||
),
|
||||
(
|
||||
"A1",
|
||||
"C",
|
||||
0,
|
||||
{
|
||||
"weight": 1.0,
|
||||
"p": "Dashed",
|
||||
"h2": "0",
|
||||
"w": "5",
|
||||
"k1": "-1",
|
||||
"k2": "-20",
|
||||
"ap": "25",
|
||||
"l": "Oval arc",
|
||||
"c": "Brown",
|
||||
"lc": "Black",
|
||||
},
|
||||
),
|
||||
(
|
||||
"Bb",
|
||||
"A1",
|
||||
0,
|
||||
{
|
||||
"weight": 1.0,
|
||||
"h2": "0",
|
||||
"a1": "120",
|
||||
"k1": "1.3",
|
||||
"a2": "-120",
|
||||
"k2": "0.3",
|
||||
"ap": "25",
|
||||
"l": "Bezier arc",
|
||||
"lphi": "270",
|
||||
"la": "180",
|
||||
"lr": "19",
|
||||
"lp": "0.5",
|
||||
},
|
||||
),
|
||||
(
|
||||
"C",
|
||||
"D2",
|
||||
0,
|
||||
{
|
||||
"weight": 1.0,
|
||||
"p": "Dashed",
|
||||
"h2": "0",
|
||||
"w": "2",
|
||||
"c": "OliveGreen",
|
||||
"ap": "25",
|
||||
"l": "Straight arc",
|
||||
"lc": "PineGreen",
|
||||
},
|
||||
),
|
||||
(
|
||||
"C",
|
||||
"C",
|
||||
0,
|
||||
{
|
||||
"weight": -1.0,
|
||||
"h1": "6",
|
||||
"w": "1",
|
||||
"h2": "12",
|
||||
"k1": "-2",
|
||||
"k2": "-15",
|
||||
"ap": "0.5",
|
||||
"l": "Circular loop",
|
||||
"c": "Red",
|
||||
"lc": "OrangeRed",
|
||||
"lphi": "270",
|
||||
"la": "180",
|
||||
},
|
||||
),
|
||||
(
|
||||
"D2",
|
||||
"Bb",
|
||||
0,
|
||||
{
|
||||
"weight": -1.0,
|
||||
"h2": "0",
|
||||
"w": "1",
|
||||
"k1": "-2",
|
||||
"k2": "250",
|
||||
"ap": "25",
|
||||
"l": "Circular arc",
|
||||
"c": "Red",
|
||||
"lc": "OrangeRed",
|
||||
},
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
class TestPajek:
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
cls.data = """*network Tralala\n*vertices 4\n 1 "A1" 0.0938 0.0896 ellipse x_fact 1 y_fact 1\n 2 "Bb" 0.8188 0.2458 ellipse x_fact 1 y_fact 1\n 3 "C" 0.3688 0.7792 ellipse x_fact 1\n 4 "D2" 0.9583 0.8563 ellipse x_fact 1\n*arcs\n1 1 1 h2 0 w 3 c Blue s 3 a1 -130 k1 0.6 a2 -130 k2 0.6 ap 0.5 l "Bezier loop" lc BlueViolet fos 20 lr 58 lp 0.3 la 360\n2 1 1 h2 0 a1 120 k1 1.3 a2 -120 k2 0.3 ap 25 l "Bezier arc" lphi 270 la 180 lr 19 lp 0.5\n1 2 1 h2 0 a1 40 k1 2.8 a2 30 k2 0.8 ap 25 l "Bezier arc" lphi 90 la 0 lp 0.65\n4 2 -1 h2 0 w 1 k1 -2 k2 250 ap 25 l "Circular arc" c Red lc OrangeRed\n3 4 1 p Dashed h2 0 w 2 c OliveGreen ap 25 l "Straight arc" lc PineGreen\n1 3 1 p Dashed h2 0 w 5 k1 -1 k2 -20 ap 25 l "Oval arc" c Brown lc Black\n3 3 -1 h1 6 w 1 h2 12 k1 -2 k2 -15 ap 0.5 l "Circular loop" c Red lc OrangeRed lphi 270 la 180"""
|
||||
cls.G = eg.MultiDiGraph()
|
||||
cls.G.add_nodes_from(["A1", "Bb", "C", "D2"])
|
||||
cls.G.add_edges_from(
|
||||
[
|
||||
("A1", "A1"),
|
||||
("A1", "Bb"),
|
||||
("A1", "C"),
|
||||
("Bb", "A1"),
|
||||
("C", "C"),
|
||||
("C", "D2"),
|
||||
("D2", "Bb"),
|
||||
]
|
||||
)
|
||||
|
||||
cls.G.graph["name"] = "Tralala"
|
||||
(fd, cls.fname) = tempfile.mkstemp()
|
||||
with os.fdopen(fd, "wb") as fh:
|
||||
fh.write(cls.data.encode("UTF-8"))
|
||||
|
||||
@classmethod
|
||||
def teardown_class(cls):
|
||||
os.unlink(cls.fname)
|
||||
|
||||
def test_parse_pajek_simple(self):
|
||||
# Example without node positions or shape
|
||||
data = """*Vertices 2\n1 "1"\n2 "2"\n*Edges\n1 2\n2 1"""
|
||||
G = eg.parse_pajek(data)
|
||||
assert sorted(G.nodes) == ["1", "2"]
|
||||
assert edges_equal(G.edges, [("1", "2", 0, {}), ("1", "2", 1, {})])
|
||||
|
||||
def test_parse_pajek(self):
|
||||
G = eg.parse_pajek(self.data)
|
||||
assert sorted(G.nodes) == ["A1", "Bb", "C", "D2"]
|
||||
# print(G.edges)
|
||||
assert edges_equal(G.edges, test_parse_pajek_edges)
|
||||
|
||||
def test_parse_pajek_mat(self):
|
||||
data = """*Vertices 3\n1 "one"\n2 "two"\n3 "three"\n*Matrix\n1 1 0\n0 1 0\n0 1 0\n"""
|
||||
G = eg.parse_pajek(data)
|
||||
assert set(G.nodes) == {"one", "two", "three"}
|
||||
assert G.nodes["two"] == {"id": "2"}
|
||||
assert edges_equal(
|
||||
# set(G.edges),
|
||||
G.edges,
|
||||
[
|
||||
("one", "one", {"weight": 1}),
|
||||
("one", "two", {"weight": 1}),
|
||||
("two", "two", {"weight": 1}),
|
||||
("three", "two", {"weight": 1}),
|
||||
],
|
||||
)
|
||||
|
||||
def test_read_pajek(self):
|
||||
G = eg.parse_pajek(self.data)
|
||||
Gin = eg.read_pajek(self.fname)
|
||||
assert sorted(G.nodes) == sorted(Gin.nodes)
|
||||
assert edges_equal(G.edges, Gin.edges)
|
||||
assert self.G.graph == Gin.graph
|
||||
for n in G:
|
||||
assert G.nodes[n] == Gin.nodes[n]
|
||||
|
||||
def test_write_pajek(self):
|
||||
import io
|
||||
|
||||
G = eg.parse_pajek(self.data)
|
||||
fh = io.BytesIO()
|
||||
eg.write_pajek(G, fh)
|
||||
fh.seek(0)
|
||||
H = eg.read_pajek(fh)
|
||||
assert nodes_equal(G.nodes, list(H))
|
||||
assert edges_equal(G.edges, list(H.edges))
|
||||
# Graph name is left out for now, therefore it is not tested.
|
||||
# assert_equal(G.graph, H.graph)
|
||||
|
||||
def test_ignored_attribute(self):
|
||||
import io
|
||||
|
||||
G = eg.Graph()
|
||||
fh = io.BytesIO()
|
||||
G.add_node(1, int_attr=1)
|
||||
G.add_node(2, empty_attr=" ")
|
||||
G.add_edge(1, 2, int_attr=2)
|
||||
G.add_edge(2, 3, empty_attr=" ")
|
||||
|
||||
import warnings
|
||||
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
eg.write_pajek(G, fh)
|
||||
assert len(w) == 4
|
||||
|
||||
def test_noname(self):
|
||||
# Make sure we can parse a line such as: *network
|
||||
# Issue #952
|
||||
line = "*network\n"
|
||||
other_lines = self.data.split("\n")[1:]
|
||||
data = line + "\n".join(other_lines)
|
||||
G = eg.parse_pajek(data)
|
||||
|
||||
def test_unicode(self):
|
||||
import io
|
||||
|
||||
G = eg.Graph()
|
||||
name1 = chr(2344) + chr(123) + chr(6543)
|
||||
name2 = chr(5543) + chr(1543) + chr(324)
|
||||
G.add_edge(name1, "Radiohead", foo=name2)
|
||||
fh = io.BytesIO()
|
||||
eg.write_pajek(G, fh)
|
||||
fh.seek(0)
|
||||
H = eg.read_pajek(fh)
|
||||
assert nodes_equal(list(G), list(H))
|
||||
# from icecream import ic
|
||||
# ic(G.edges)
|
||||
# ic(H.edges)
|
||||
# ic(G.graph)
|
||||
# ic(H.graph)
|
||||
# assert edges_equal(list(G.edges), list(H.edges))
|
||||
assert G.graph == H.graph
|
||||
@@ -0,0 +1,53 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
pickle read / write tests
|
||||
"""
|
||||
|
||||
import os
|
||||
import pickle
|
||||
import tempfile
|
||||
|
||||
import easygraph as eg
|
||||
|
||||
from easygraph.utils import edges_equal
|
||||
|
||||
|
||||
class TestPickle:
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
cls.data = """*network Tralala\n*vertices 4\n 1 "A1" 0.0938 0.0896 ellipse x_fact 1 y_fact 1\n 2 "Bb" 0.8188 0.2458 ellipse x_fact 1 y_fact 1\n 3 "C" 0.3688 0.7792 ellipse x_fact 1\n 4 "D2" 0.9583 0.8563 ellipse x_fact 1\n*arcs\n1 1 1 h2 0 w 3 c Blue s 3 a1 -130 k1 0.6 a2 -130 k2 0.6 ap 0.5 l "Bezier loop" lc BlueViolet fos 20 lr 58 lp 0.3 la 360\n2 1 1 h2 0 a1 120 k1 1.3 a2 -120 k2 0.3 ap 25 l "Bezier arc" lphi 270 la 180 lr 19 lp 0.5\n1 2 1 h2 0 a1 40 k1 2.8 a2 30 k2 0.8 ap 25 l "Bezier arc" lphi 90 la 0 lp 0.65\n4 2 -1 h2 0 w 1 k1 -2 k2 250 ap 25 l "Circular arc" c Red lc OrangeRed\n3 4 1 p Dashed h2 0 w 2 c OliveGreen ap 25 l "Straight arc" lc PineGreen\n1 3 1 p Dashed h2 0 w 5 k1 -1 k2 -20 ap 25 l "Oval arc" c Brown lc Black\n3 3 -1 h1 6 w 1 h2 12 k1 -2 k2 -15 ap 0.5 l "Circular loop" c Red lc OrangeRed lphi 270 la 180"""
|
||||
cls.G = eg.MultiDiGraph()
|
||||
cls.G.add_nodes_from(["A1", "Bb", "C", "D2"])
|
||||
cls.G.add_edges_from(
|
||||
[
|
||||
("A1", "A1"),
|
||||
("A1", "Bb"),
|
||||
("A1", "C"),
|
||||
("Bb", "A1"),
|
||||
("C", "C"),
|
||||
("C", "D2"),
|
||||
("D2", "Bb"),
|
||||
]
|
||||
)
|
||||
|
||||
cls.G.graph["name"] = "Tralala"
|
||||
(fd, cls.fname) = tempfile.mkstemp()
|
||||
with os.fdopen(fd, "wb") as fh:
|
||||
fh.write(pickle.dumps(cls.G))
|
||||
|
||||
@classmethod
|
||||
def teardown_class(cls):
|
||||
os.unlink(cls.fname)
|
||||
|
||||
def test_read_pickle(self):
|
||||
G = eg.read_pickle(self.fname)
|
||||
assert G.nodes == self.G.nodes
|
||||
assert G.edges == self.G.edges
|
||||
|
||||
def test_write_pickle(self):
|
||||
G = eg.parse_pajek(self.data)
|
||||
eg.write_pickle(self.fname, G)
|
||||
Gin = eg.read_pickle(self.fname)
|
||||
assert sorted(G.nodes) == sorted(Gin.nodes)
|
||||
assert edges_equal(G.edges, Gin.edges)
|
||||
assert self.G.graph == Gin.graph
|
||||
@@ -0,0 +1,340 @@
|
||||
"""
|
||||
UCINET tests
|
||||
"""
|
||||
|
||||
import io
|
||||
|
||||
import easygraph as eg
|
||||
|
||||
|
||||
# from nose import SkipTest
|
||||
# from nose.tools import *
|
||||
|
||||
|
||||
def filterEdges(edges):
|
||||
return [e[:3] for e in edges]
|
||||
|
||||
|
||||
class TestUcinet:
|
||||
@classmethod
|
||||
def setup_class(self):
|
||||
self.G = eg.MultiDiGraph()
|
||||
self.G.add_nodes_from(["a", "b", "c", "d", "e"])
|
||||
self.G.add_edges_from(
|
||||
[
|
||||
("a", "b"),
|
||||
("a", "c"),
|
||||
("a", "d"),
|
||||
("a", "e"),
|
||||
("b", "a"),
|
||||
("b", "c"),
|
||||
("b", "d"),
|
||||
("c", "a"),
|
||||
("c", "b"),
|
||||
("d", "a"),
|
||||
("d", "b"),
|
||||
("e", "a"),
|
||||
]
|
||||
)
|
||||
try:
|
||||
pass
|
||||
except ImportError:
|
||||
print("NumPy not available.")
|
||||
# raise SkipTest("NumPy not available.")
|
||||
|
||||
def test_generate_ucinet(self):
|
||||
Gout = eg.generate_ucinet(self.G)
|
||||
s = ""
|
||||
for line in Gout:
|
||||
s += line + "\n"
|
||||
G_generated = eg.parse_ucinet(s)
|
||||
|
||||
data = """\
|
||||
dl n=5 format=fullmatrix
|
||||
labels:
|
||||
a,b,c,d,e
|
||||
data:
|
||||
0 1 1 1 1
|
||||
1 0 1 1 0
|
||||
1 1 0 0 0
|
||||
1 1 0 0 0
|
||||
1 0 0 0 0"""
|
||||
G = eg.parse_ucinet(data)
|
||||
assert sorted(G.nodes) == sorted(G_generated.nodes)
|
||||
assert sorted(G.edges) == sorted(G_generated.edges)
|
||||
|
||||
def test_parse_ucinet(self):
|
||||
data = """
|
||||
DL N = 5
|
||||
Data:
|
||||
0 1 1 1 1
|
||||
1 0 1 0 0
|
||||
1 1 0 0 1
|
||||
1 0 0 0 0
|
||||
1 0 1 0 0
|
||||
"""
|
||||
graph = eg.MultiDiGraph()
|
||||
graph.add_nodes_from([0, 1, 2, 3, 4])
|
||||
graph.add_edges_from(
|
||||
[
|
||||
(0, 1),
|
||||
(0, 2),
|
||||
(0, 3),
|
||||
(0, 4),
|
||||
(1, 0),
|
||||
(1, 2),
|
||||
(2, 0),
|
||||
(2, 1),
|
||||
(2, 4),
|
||||
(3, 0),
|
||||
(4, 0),
|
||||
(4, 2),
|
||||
]
|
||||
)
|
||||
G = eg.parse_ucinet(data)
|
||||
assert sorted(G.nodes) == sorted(graph.nodes)
|
||||
assert sorted(filterEdges(G.edges)) == sorted(filterEdges(graph.edges))
|
||||
# print [n for n in G.nodes(data=True)]
|
||||
# print [e for e in G.edges]
|
||||
|
||||
def test_parse_ucinet_labels(self):
|
||||
"""
|
||||
Test parsing of labels : single line (data1), multiple lines (data2), embedded (data3)
|
||||
Labels must be separated by spaces, carriage returns, equal signs or commas.
|
||||
Labels with embedded spaces are not advisable, but can be entered by
|
||||
surrounding the label in quotes (e.g., "Humpty Dumpty").
|
||||
"""
|
||||
data1 = """
|
||||
dl n=5
|
||||
format = fullmatrix
|
||||
labels:
|
||||
barry,david,lin,pat,russ
|
||||
data:
|
||||
0 1 1 1 0
|
||||
1 0 0 0 1
|
||||
1 0 0 1 0
|
||||
1 0 1 0 1
|
||||
0 1 0 1 0
|
||||
"""
|
||||
data2 = """
|
||||
dl n=5
|
||||
format = fullmatrix
|
||||
labels:
|
||||
barry,david
|
||||
lin,pat
|
||||
russ
|
||||
data:
|
||||
0 1 1 1 0
|
||||
1 0 0 0 1
|
||||
1 0 0 1 0
|
||||
1 0 1 0 1
|
||||
0 1 0 1 0
|
||||
"""
|
||||
data3 = """\
|
||||
dl n=5
|
||||
format = fullmatrix
|
||||
labels embedded
|
||||
data:
|
||||
barry david lin pat russ
|
||||
Barry 0 1 1 1 0
|
||||
david 1 0 0 0 1
|
||||
Lin 1 0 0 1 0
|
||||
Pat 1 0 1 0 1
|
||||
Russ 0 1 0 1 0
|
||||
"""
|
||||
G = eg.MultiDiGraph()
|
||||
G.add_nodes_from(["russ", "barry", "lin", "pat", "david"])
|
||||
G.add_edges_from(
|
||||
[
|
||||
("russ", "pat"),
|
||||
("russ", "david"),
|
||||
("barry", "lin"),
|
||||
("barry", "pat"),
|
||||
("barry", "david"),
|
||||
("lin", "barry"),
|
||||
("lin", "pat"),
|
||||
("pat", "barry"),
|
||||
("pat", "lin"),
|
||||
("pat", "russ"),
|
||||
("david", "barry"),
|
||||
("david", "russ"),
|
||||
]
|
||||
)
|
||||
G1 = eg.parse_ucinet(data1)
|
||||
G2 = eg.parse_ucinet(data2)
|
||||
G3 = eg.parse_ucinet(data3)
|
||||
assert sorted(G1.nodes) == sorted(G.nodes)
|
||||
assert sorted(G2.nodes) == sorted(G.nodes)
|
||||
assert sorted(G3.nodes) == sorted(G.nodes)
|
||||
assert sorted(e[:3] for e in G1.edges) == sorted(e[:3] for e in G.edges)
|
||||
assert sorted(e[:3] for e in G2.edges) == sorted(e[:3] for e in G.edges)
|
||||
assert sorted(e[:3] for e in G3.edges) == sorted(e[:3] for e in G.edges)
|
||||
# print [n for n in G.nodes]
|
||||
# print [e for e in G.edges]
|
||||
|
||||
def test_parse_ucinet_nodelist1(self):
|
||||
data1 = """
|
||||
DL n=4
|
||||
format = nodelist1
|
||||
data:
|
||||
1 3 2 1
|
||||
4 1 4
|
||||
2 2 4 1
|
||||
"""
|
||||
data2 = """
|
||||
DL n=4
|
||||
format = nodelist1b
|
||||
data:
|
||||
3 1 2 3
|
||||
3 1 2 4
|
||||
0
|
||||
2 1 4
|
||||
"""
|
||||
G = eg.MultiDiGraph()
|
||||
G.add_nodes_from([0, 1, 2, 3])
|
||||
G.add_edges_from(
|
||||
[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 3), (3, 0), (3, 3)]
|
||||
)
|
||||
G1 = eg.parse_ucinet(data1)
|
||||
G2 = eg.parse_ucinet(data2)
|
||||
assert sorted(G1.nodes) == sorted(G.nodes)
|
||||
assert sorted(G2.nodes) == sorted(G.nodes)
|
||||
assert sorted(filterEdges(G1.edges)) == sorted(filterEdges(G.edges))
|
||||
assert sorted(filterEdges(G2.edges)) == sorted(filterEdges(G.edges))
|
||||
|
||||
def test_parse_ucinet_nodelist1_labels(self):
|
||||
data1 = """
|
||||
DL n=5
|
||||
format = nodelist1
|
||||
labels:
|
||||
george, sally, jim, billy, jane
|
||||
data:
|
||||
1 2 3
|
||||
2 3
|
||||
4 1
|
||||
5 3
|
||||
"""
|
||||
data2 = """
|
||||
DL n=5
|
||||
format = nodelist1
|
||||
labels embedded:
|
||||
data:
|
||||
george sally jim
|
||||
sally jim
|
||||
billy george
|
||||
jane jim
|
||||
"""
|
||||
G = eg.MultiDiGraph()
|
||||
G.add_nodes_from(["george", "sally", "jim", "billy", "jane"])
|
||||
G.add_edges_from(
|
||||
[
|
||||
("billy", "george"),
|
||||
("jane", "jim"),
|
||||
("sally", "jim"),
|
||||
("george", "jim"),
|
||||
("george", "sally"),
|
||||
]
|
||||
)
|
||||
G1 = eg.parse_ucinet(data1)
|
||||
G2 = eg.parse_ucinet(data2)
|
||||
assert sorted(G1.nodes) == sorted(G.nodes)
|
||||
assert sorted(G2.nodes) == sorted(G.nodes)
|
||||
assert sorted(G1.edges) == sorted(G.edges)
|
||||
assert sorted(G2.edges) == sorted(G.edges)
|
||||
|
||||
def test_read_ucinet(self):
|
||||
fh = io.BytesIO()
|
||||
data = """
|
||||
DL N = 5
|
||||
Data:
|
||||
0 1 1 1 1
|
||||
1 0 1 0 0
|
||||
1 1 0 0 1
|
||||
1 0 0 0 0
|
||||
1 0 1 0 0
|
||||
"""
|
||||
Gin = eg.parse_ucinet(data)
|
||||
fh.write(data.encode("UTF-8"))
|
||||
fh.seek(0)
|
||||
Gout = eg.read_ucinet(fh)
|
||||
assert sorted(Gout.nodes) == sorted(Gin.nodes)
|
||||
assert sorted(e[:3] for e in Gout.edges) == sorted(e[:3] for e in Gin.edges)
|
||||
|
||||
def test_write_ucinet(self):
|
||||
fh = io.BytesIO()
|
||||
data = """\
|
||||
dl n=5 format=fullmatrix
|
||||
data:
|
||||
0 1 1 1 1
|
||||
1 0 1 0 0
|
||||
1 1 0 0 1
|
||||
1 0 0 0 0
|
||||
1 0 1 0 0
|
||||
"""
|
||||
graph = eg.MultiDiGraph()
|
||||
graph.add_nodes_from([0, 1, 2, 3, 4])
|
||||
graph.add_edges_from(
|
||||
[
|
||||
(0, 1),
|
||||
(0, 2),
|
||||
(0, 3),
|
||||
(0, 4),
|
||||
(1, 0),
|
||||
(1, 2),
|
||||
(2, 0),
|
||||
(2, 1),
|
||||
(2, 4),
|
||||
(3, 0),
|
||||
(4, 0),
|
||||
(4, 2),
|
||||
]
|
||||
)
|
||||
|
||||
eg.write_ucinet(graph, fh)
|
||||
fh.seek(0)
|
||||
G = eg.parse_ucinet(fh.readlines())
|
||||
assert sorted(G.nodes) == sorted(graph.nodes)
|
||||
assert sorted(e[:3] for e in G.edges) == sorted(e[:3] for e in graph.edges)
|
||||
|
||||
def test_parse_ucinet_edgelist1(self):
|
||||
data1 = """
|
||||
DL n=5
|
||||
format = edgelist1
|
||||
labels:
|
||||
george, sally, jim, billy, jane
|
||||
data:
|
||||
1 2
|
||||
1 3
|
||||
2 3
|
||||
3 1
|
||||
5 4
|
||||
"""
|
||||
data2 = """
|
||||
DL n=5
|
||||
format = edgelist1
|
||||
labels embedded:
|
||||
data:
|
||||
george sally
|
||||
george jim
|
||||
sally jim
|
||||
jim george
|
||||
jane billy
|
||||
"""
|
||||
G = eg.MultiDiGraph()
|
||||
G.add_nodes_from(["george", "sally", "jim", "billy", "jane"])
|
||||
G.add_edges_from(
|
||||
[
|
||||
("jim", "george"),
|
||||
("jane", "billy"),
|
||||
("sally", "jim"),
|
||||
("george", "jim"),
|
||||
("george", "sally"),
|
||||
]
|
||||
)
|
||||
|
||||
G1 = eg.parse_ucinet(data1)
|
||||
G2 = eg.parse_ucinet(data2)
|
||||
assert sorted(G1.nodes) == sorted(G.nodes)
|
||||
assert sorted(G2.nodes) == sorted(G.nodes)
|
||||
assert sorted(G1.edges) == sorted(G.edges)
|
||||
assert sorted(G2.edges) == sorted(G.edges)
|
||||
Reference in New Issue
Block a user