chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:36:30 +08:00
commit 55ab4e4a73
473 changed files with 72932 additions and 0 deletions
+318
View File
@@ -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)
+447
View File
@@ -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)
+589
View File
@@ -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 &#34;quoted&#34; and this is a copyright: &#169;"
]
]"""
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&#169;"
]
]"""
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,
"'",
'"&&amp;&&#34;"',
[{(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 "&amp;&#34;&#xf;&#x4444;&#1234567890;&#x1234567890abcdef;&unknown;"
]"""
G = eg.parse_gml(gml)
assert (
'&"\x0f' + chr(0x4444) + "&#1234567890;&#x1234567890abcdef;&unknown;"
== G.name
)
gml = "\n".join(eg.generate_gml(G))
alnu = "#1234567890;&#38;#x1234567890abcdef"
answer = (
"""graph [
name "&#38;&#34;&#15;&#17476;&#38;"""
+ alnu
+ """;&#38;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())
+307
View File
@@ -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
+53
View File
@@ -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
+340
View File
@@ -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)