#!/usr/bin/env python
import pytest
import networkx as nx
from networkx.testing.utils import *
import io
import tempfile
import os
from networkx.testing import almost_equal
class BaseGraphML(object):
@classmethod
def setup_class(cls):
cls.simple_directed_data = """
"""
cls.simple_directed_graph = nx.DiGraph()
cls.simple_directed_graph.add_node('n10')
cls.simple_directed_graph.add_edge('n0', 'n2', id='foo')
cls.simple_directed_graph.add_edges_from([('n1', 'n2'),
('n2', 'n3'),
('n3', 'n5'),
('n3', 'n4'),
('n4', 'n6'),
('n6', 'n5'),
('n5', 'n7'),
('n6', 'n8'),
('n8', 'n7'),
('n8', 'n9'),
])
cls.simple_directed_fh = \
io.BytesIO(cls.simple_directed_data.encode('UTF-8'))
cls.attribute_data = """
yellow
green
blue
red
turquoise
1.0
1.0
2.0
1.1
"""
cls.attribute_graph = nx.DiGraph(id='G')
cls.attribute_graph.graph['node_default'] = {'color': 'yellow'}
cls.attribute_graph.add_node('n0', color='green')
cls.attribute_graph.add_node('n2', color='blue')
cls.attribute_graph.add_node('n3', color='red')
cls.attribute_graph.add_node('n4')
cls.attribute_graph.add_node('n5', color='turquoise')
cls.attribute_graph.add_edge('n0', 'n2', id='e0', weight=1.0)
cls.attribute_graph.add_edge('n0', 'n1', id='e1', weight=1.0)
cls.attribute_graph.add_edge('n1', 'n3', id='e2', weight=2.0)
cls.attribute_graph.add_edge('n3', 'n2', id='e3')
cls.attribute_graph.add_edge('n2', 'n4', id='e4')
cls.attribute_graph.add_edge('n3', 'n5', id='e5')
cls.attribute_graph.add_edge('n5', 'n4', id='e6', weight=1.1)
cls.attribute_fh = io.BytesIO(cls.attribute_data.encode('UTF-8'))
cls.attribute_numeric_type_data = """
1
2.0
1
k
1.0
"""
cls.attribute_numeric_type_graph = nx.DiGraph()
cls.attribute_numeric_type_graph.add_node('n0', weight=1)
cls.attribute_numeric_type_graph.add_node('n1', weight=2.0)
cls.attribute_numeric_type_graph.add_edge('n0', 'n1', weight=1)
cls.attribute_numeric_type_graph.add_edge('n1', 'n1', weight=1.0)
fh = io.BytesIO(cls.attribute_numeric_type_data.encode('UTF-8'))
cls.attribute_numeric_type_fh = fh
cls.simple_undirected_data = """
"""
#
cls.simple_undirected_graph = nx.Graph()
cls.simple_undirected_graph.add_node('n10')
cls.simple_undirected_graph.add_edge('n0', 'n2', id='foo')
cls.simple_undirected_graph.add_edges_from([('n1', 'n2'),
('n2', 'n3'),
])
fh = io.BytesIO(cls.simple_undirected_data.encode('UTF-8'))
cls.simple_undirected_fh = fh
class TestReadGraphML(BaseGraphML):
def test_read_simple_directed_graphml(self):
G = self.simple_directed_graph
H = nx.read_graphml(self.simple_directed_fh)
assert sorted(G.nodes()) == sorted(H.nodes())
assert sorted(G.edges()) == sorted(H.edges())
assert (sorted(G.edges(data=True)) ==
sorted(H.edges(data=True)))
self.simple_directed_fh.seek(0)
I = nx.parse_graphml(self.simple_directed_data)
assert sorted(G.nodes()) == sorted(I.nodes())
assert sorted(G.edges()) == sorted(I.edges())
assert (sorted(G.edges(data=True)) ==
sorted(I.edges(data=True)))
def test_read_simple_undirected_graphml(self):
G = self.simple_undirected_graph
H = nx.read_graphml(self.simple_undirected_fh)
assert_nodes_equal(G.nodes(), H.nodes())
assert_edges_equal(G.edges(), H.edges())
self.simple_undirected_fh.seek(0)
I = nx.parse_graphml(self.simple_undirected_data)
assert_nodes_equal(G.nodes(), I.nodes())
assert_edges_equal(G.edges(), I.edges())
def test_read_attribute_graphml(self):
G = self.attribute_graph
H = nx.read_graphml(self.attribute_fh)
assert_nodes_equal(G.nodes(True), sorted(H.nodes(data=True)))
ge = sorted(G.edges(data=True))
he = sorted(H.edges(data=True))
for a, b in zip(ge, he):
assert a == b
self.attribute_fh.seek(0)
I = nx.parse_graphml(self.attribute_data)
assert sorted(G.nodes(True)) == sorted(I.nodes(data=True))
ge = sorted(G.edges(data=True))
he = sorted(I.edges(data=True))
for a, b in zip(ge, he):
assert a == b
def test_directed_edge_in_undirected(self):
s = """
"""
fh = io.BytesIO(s.encode('UTF-8'))
pytest.raises(nx.NetworkXError, nx.read_graphml, fh)
pytest.raises(nx.NetworkXError, nx.parse_graphml, s)
def test_undirected_edge_in_directed(self):
s = """
"""
fh = io.BytesIO(s.encode('UTF-8'))
pytest.raises(nx.NetworkXError, nx.read_graphml, fh)
pytest.raises(nx.NetworkXError, nx.parse_graphml, s)
def test_key_raise(self):
s = """
yellow
green
blue
1.0
"""
fh = io.BytesIO(s.encode('UTF-8'))
pytest.raises(nx.NetworkXError, nx.read_graphml, fh)
pytest.raises(nx.NetworkXError, nx.parse_graphml, s)
def test_hyperedge_raise(self):
s = """
yellow
green
blue
"""
fh = io.BytesIO(s.encode('UTF-8'))
pytest.raises(nx.NetworkXError, nx.read_graphml, fh)
pytest.raises(nx.NetworkXError, nx.parse_graphml, s)
def test_multigraph_keys(self):
# Test that reading multigraphs uses edge id attributes as keys
s = """
"""
fh = io.BytesIO(s.encode('UTF-8'))
G = nx.read_graphml(fh)
expected = [("n0", "n1", "e0"), ("n0", "n1", "e1")]
assert sorted(G.edges(keys=True)) == expected
fh.seek(0)
H = nx.parse_graphml(s)
assert sorted(H.edges(keys=True)) == expected
def test_preserve_multi_edge_data(self):
"""
Test that data and keys of edges are preserved on consequent
write and reads
"""
G = nx.MultiGraph()
G.add_node(1)
G.add_node(2)
G.add_edges_from([
# edges with no data, no keys:
(1, 2),
# edges with only data:
(1, 2, dict(key='data_key1')),
(1, 2, dict(id='data_id2')),
(1, 2, dict(key='data_key3', id='data_id3')),
# edges with both data and keys:
(1, 2, 103, dict(key='data_key4')),
(1, 2, 104, dict(id='data_id5')),
(1, 2, 105, dict(key='data_key6', id='data_id7')),
])
fh = io.BytesIO()
nx.write_graphml(G, fh)
fh.seek(0)
H = nx.read_graphml(fh, node_type=int)
assert_edges_equal(
G.edges(data=True, keys=True), H.edges(data=True, keys=True)
)
assert G._adj == H._adj
def test_yfiles_extension(self):
data = """
1
2
"""
fh = io.BytesIO(data.encode('UTF-8'))
G = nx.read_graphml(fh)
assert list(G.edges()) == [('n0', 'n1')]
assert G['n0']['n1']['id'] == 'e0'
assert G.nodes['n0']['label'] == '1'
assert G.nodes['n1']['label'] == '2'
H = nx.parse_graphml(data)
assert list(H.edges()) == [('n0', 'n1')]
assert H['n0']['n1']['id'] == 'e0'
assert H.nodes['n0']['label'] == '1'
assert H.nodes['n1']['label'] == '2'
def test_bool(self):
s = """
false
true
false
FaLsE
True
0
1
"""
fh = io.BytesIO(s.encode('UTF-8'))
G = nx.read_graphml(fh)
H = nx.parse_graphml(s)
for graph in [G, H]:
assert graph.nodes['n0']['test'] == True
assert graph.nodes['n2']['test'] == False
assert graph.nodes['n3']['test'] == False
assert graph.nodes['n4']['test'] == True
assert graph.nodes['n5']['test'] == False
assert graph.nodes['n6']['test'] == True
def test_graphml_header_line(self):
good = """
false
true
"""
bad = """
false
true
"""
ugly = """
false
true
"""
for s in (good, bad):
fh = io.BytesIO(s.encode('UTF-8'))
G = nx.read_graphml(fh)
H = nx.parse_graphml(s)
for graph in [G, H]:
assert graph.nodes['n0']['test'] == True
fh = io.BytesIO(ugly.encode('UTF-8'))
pytest.raises(nx.NetworkXError, nx.read_graphml, fh)
pytest.raises(nx.NetworkXError, nx.parse_graphml, ugly)
def test_read_attributes_with_groups(self):
data = """\
2
Group 3
Folder 3
Group 1
Folder 1
1
3
Group 2
Folder 2
5
6
9
"""
# verify that nodes / attributes are correctly read when part of a group
fh = io.BytesIO(data.encode('UTF-8'))
G = nx.read_graphml(fh)
data = [x for _, x in G.nodes(data=True)]
assert len(data) == 9
for node_data in data:
assert node_data['CustomProperty'] != ''
class TestWriteGraphML(BaseGraphML):
writer = staticmethod(nx.write_graphml_lxml)
@classmethod
def setup_class(cls):
BaseGraphML.setup_class()
_ = pytest.importorskip("lxml.etree")
def test_write_interface(self):
try:
import lxml.etree
assert nx.write_graphml == nx.write_graphml_lxml
except ImportError:
assert nx.write_graphml == nx.write_graphml_xml
def test_write_read_simple_directed_graphml(self):
G = self.simple_directed_graph
G.graph['hi'] = 'there'
fh = io.BytesIO()
self.writer(G, fh)
fh.seek(0)
H = nx.read_graphml(fh)
assert sorted(G.nodes()) == sorted(H.nodes())
assert sorted(G.edges()) == sorted(H.edges())
assert sorted(G.edges(data=True)) == sorted(H.edges(data=True))
self.simple_directed_fh.seek(0)
def test_write_read_attribute_numeric_type_graphml(self):
from xml.etree.ElementTree import parse
G = self.attribute_numeric_type_graph
fh = io.BytesIO()
self.writer(G, fh, infer_numeric_types=True)
fh.seek(0)
H = nx.read_graphml(fh)
fh.seek(0)
assert_nodes_equal(G.nodes(), H.nodes())
assert_edges_equal(G.edges(), H.edges())
assert_edges_equal(G.edges(data=True), H.edges(data=True))
self.attribute_numeric_type_fh.seek(0)
xml = parse(fh)
# Children are the key elements, and the graph element
children = xml.getroot().getchildren()
assert len(children) == 3
keys = [child.items() for child in children[:2]]
assert len(keys) == 2
assert ('attr.type', 'double') in keys[0]
assert ('attr.type', 'double') in keys[1]
def test_more_multigraph_keys(self):
"""Writing keys as edge id attributes means keys become strings.
The original keys are stored as data, so read them back in
if `make_str(key) == edge_id`
This allows the adjacency to remain the same.
"""
G = nx.MultiGraph()
G.add_edges_from([('a', 'b', 2), ('a', 'b', 3)])
fd, fname = tempfile.mkstemp()
self.writer(G, fname)
H = nx.read_graphml(fname)
assert H.is_multigraph()
assert_edges_equal(G.edges(keys=True), H.edges(keys=True))
assert G._adj == H._adj
os.close(fd)
os.unlink(fname)
def test_default_attribute(self):
G = nx.Graph(name="Fred")
G.add_node(1, label=1, color='green')
nx.add_path(G, [0, 1, 2, 3])
G.add_edge(1, 2, weight=3)
G.graph['node_default'] = {'color': 'yellow'}
G.graph['edge_default'] = {'weight': 7}
fh = io.BytesIO()
self.writer(G, fh)
fh.seek(0)
H = nx.read_graphml(fh, node_type=int)
assert_nodes_equal(G.nodes(), H.nodes())
assert_edges_equal(G.edges(), H.edges())
assert G.graph == H.graph
def test_mixed_type_attributes(self):
G = nx.MultiGraph()
G.add_node('n0', special=False)
G.add_node('n1', special=0)
G.add_edge('n0', 'n1', special=False)
G.add_edge('n0', 'n1', special=0)
fh = io.BytesIO()
self.writer(G, fh)
fh.seek(0)
H = nx.read_graphml(fh)
assert H.nodes['n0']['special'] is False
assert H.nodes['n1']['special'] is 0
assert H.edges['n0','n1',0]['special'] is False
assert H.edges['n0','n1',1]['special'] is 0
def test_multigraph_to_graph(self):
# test converting multigraph to graph if no parallel edges found
G = nx.MultiGraph()
G.add_edges_from([('a', 'b', 2), ('b', 'c', 3)]) # no multiedges
fd, fname = tempfile.mkstemp()
self.writer(G, fname)
H = nx.read_graphml(fname)
assert not H.is_multigraph()
os.close(fd)
os.unlink(fname)
def test_numpy_float(self):
try:
import numpy as np
except:
return
wt = np.float(3.4)
G = nx.Graph([(1, 2, {'weight': wt})])
fd, fname = tempfile.mkstemp()
self.writer(G, fname)
H = nx.read_graphml(fname, node_type=int)
assert G._adj == H._adj
os.close(fd)
os.unlink(fname)
def test_numpy_float64(self):
try:
import numpy as np
except:
return
wt = np.float64(3.4)
G = nx.Graph([(1, 2, {'weight': wt})])
fd, fname = tempfile.mkstemp()
self.writer(G, fname)
H = nx.read_graphml(fname, node_type=int)
assert G.edges == H.edges
wtG = G[1][2]['weight']
wtH = H[1][2]['weight']
assert almost_equal(wtG, wtH, places=6)
assert type(wtG) == np.float64
assert type(wtH) == float
os.close(fd)
os.unlink(fname)
def test_numpy_float32(self):
try:
import numpy as np
except:
return
wt = np.float32(3.4)
G = nx.Graph([(1, 2, {'weight': wt})])
fd, fname = tempfile.mkstemp()
self.writer(G, fname)
H = nx.read_graphml(fname, node_type=int)
assert G.edges == H.edges
wtG = G[1][2]['weight']
wtH = H[1][2]['weight']
assert almost_equal(wtG, wtH, places=6)
assert type(wtG) == np.float32
assert type(wtH) == float
os.close(fd)
os.unlink(fname)
def test_unicode_attributes(self):
G = nx.Graph()
try: # Python 3.x
name1 = chr(2344) + chr(123) + chr(6543)
name2 = chr(5543) + chr(1543) + chr(324)
node_type = str
except ValueError: # Python 2.6+
name1 = unichr(2344) + unichr(123) + unichr(6543)
name2 = unichr(5543) + unichr(1543) + unichr(324)
node_type = unicode
G.add_edge(name1, 'Radiohead', foo=name2)
fd, fname = tempfile.mkstemp()
self.writer(G, fname)
H = nx.read_graphml(fname, node_type=node_type)
assert G._adj == H._adj
os.close(fd)
os.unlink(fname)
def test_unicode_escape(self):
# test for handling json escaped stings in python 2 Issue #1880
import json
a = dict(a='{"a": "123"}') # an object with many chars to escape
try: # Python 3.x
chr(2344)
sa = json.dumps(a)
except ValueError: # Python 2.6+
sa = unicode(json.dumps(a))
G = nx.Graph()
G.graph['test'] = sa
fh = io.BytesIO()
self.writer(G, fh)
fh.seek(0)
H = nx.read_graphml(fh)
assert G.graph['test'] == H.graph['test']
class TestXMLGraphML(TestWriteGraphML):
writer = staticmethod(nx.write_graphml_xml)
@classmethod
def setup_class(cls):
TestWriteGraphML.setup_class()
_ = pytest.importorskip("xml.etree.ElementTree")