#!/usr/bin/env python import io import sys import time import pytest import networkx as nx class TestGEXF(object): @classmethod def setup_class(cls): _ = pytest.importorskip("xml.etree.ElementTree") cls.simple_directed_data = """ """ cls.simple_directed_graph = nx.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 = """\ Gephi.org A Web network true """ cls.attribute_graph = nx.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') 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 = """ """ cls.simple_undirected_graph = nx.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 = nx.read_gexf(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) def test_write_read_simple_directed_graphml(self): G = self.simple_directed_graph fh = io.BytesIO() nx.write_gexf(G, fh) fh.seek(0) H = nx.read_gexf(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_read_simple_undirected_graphml(self): G = self.simple_undirected_graph H = nx.read_gexf(self.simple_undirected_fh) assert sorted(G.nodes()) == sorted(H.nodes()) assert ( sorted(sorted(e) for e in G.edges()) == sorted(sorted(e) for e in H.edges())) self.simple_undirected_fh.seek(0) def test_read_attribute_graphml(self): G = self.attribute_graph H = nx.read_gexf(self.attribute_fh) assert sorted(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) def test_directed_edge_in_undirected(self): s = """ """ fh = io.BytesIO(s.encode('UTF-8')) pytest.raises(nx.NetworkXError, nx.read_gexf, fh) def test_undirected_edge_in_directed(self): s = """ """ fh = io.BytesIO(s.encode('UTF-8')) pytest.raises(nx.NetworkXError, nx.read_gexf, fh) def test_key_raises(self): s = """ """ fh = io.BytesIO(s.encode('UTF-8')) pytest.raises(nx.NetworkXError, nx.read_gexf, fh) def test_relabel(self): s = """ """ fh = io.BytesIO(s.encode('UTF-8')) G = nx.read_gexf(fh, relabel=True) assert sorted(G.nodes()) == ["Hello", "Word"] def test_default_attribute(self): G = nx.Graph() G.add_node(1, label='1', color='green') nx.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() nx.write_gexf(G, fh) fh.seek(0) H = nx.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())) # 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 = nx.Graph() G.add_node(1, id=7, label=77) fh = io.BytesIO() nx.write_gexf(G, fh) fh.seek(0) H = nx.read_gexf(fh, node_type=int) assert list(H) == [7] assert H.nodes[7]['label'] == '77' # FIXME: We should test xml without caring about their order This is causing a # problem b/c of a change in Python 3.8 # # "Prior to Python 3.8, the serialisation order of the XML attributes of # elements was artificially made predictable by sorting the attributes by their # name. Based on the now guaranteed ordering of dicts, this arbitrary # reordering was removed in Python 3.8 to preserve the order in which # attributes were originally parsed or created by user code." # # https://docs.python.org/3.8/library/xml.etree.elementtree.html # https://bugs.python.org/issue34160 def test_write_with_node_attributes(self): # Addresses #673. G = nx.OrderedGraph() G.add_edges_from([(0, 1), (1, 2), (2, 3)]) for i in range(4): G.nodes[i]['id'] = i G.nodes[i]['label'] = i G.nodes[i]['pid'] = i G.nodes[i]['start'] = i G.nodes[i]['end'] = i + 1 if sys.version_info < (3, 8): expected = """ NetworkX {} """.format(time.strftime('%Y-%m-%d'), nx.__version__) else: expected = """ NetworkX {} """.format(time.strftime('%Y-%m-%d'), nx.__version__) obtained = '\n'.join(nx.generate_gexf(G)) assert expected == obtained def test_edge_id_construct(self): G = nx.Graph() G.add_edges_from([(0, 1, {'id': 0}), (1, 2, {'id': 2}), (2, 3)]) if sys.version_info < (3, 8): expected = """ NetworkX {} """.format(time.strftime('%Y-%m-%d'), nx.__version__) else: expected = """ NetworkX {} """.format(time.strftime('%Y-%m-%d'), nx.__version__) obtained = '\n'.join(nx.generate_gexf(G)) assert expected == obtained def test_numpy_type(self): G = nx.path_graph(4) try: import numpy except ImportError: return nx.set_node_attributes(G, {n: n for n in numpy.arange(4)}, 'number') G[0][1]['edge-number'] = numpy.float64(1.1) expected = """ NetworkX {} """.format(time.strftime('%Y-%m-%d'), nx.__version__) obtained = '\n'.join(nx.generate_gexf(G)) assert expected == obtained def test_bool(self): G = nx.Graph() G.add_node(1, testattr=True) fh = io.BytesIO() nx.write_gexf(G, fh) fh.seek(0) H = nx.read_gexf(fh, node_type=int) assert H.nodes[1]['testattr'] == True # Test for NaN, INF and -INF def test_specials(self): from math import isnan inf, nan = float('inf'), float('nan') G = nx.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() nx.write_gexf(G, fh) fh.seek(0) filetext = fh.read() fh.seek(0) H = nx.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]['networkx_key'] == 'a' assert H.nodes[2]['networkx_key'] == 'b' assert H.nodes[3]['networkx_key'] == 'c'