comparison env/lib/python3.9/site-packages/networkx/readwrite/tests/test_gexf.py @ 0:4f3585e2f14b draft default tip

"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
author shellac
date Mon, 22 Mar 2021 18:12:50 +0000
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:4f3585e2f14b
1 import io
2 import sys
3 import time
4 import pytest
5
6 import networkx as nx
7
8
9 class TestGEXF:
10 @classmethod
11 def setup_class(cls):
12 _ = pytest.importorskip("xml.etree.ElementTree")
13
14 cls.simple_directed_data = """<?xml version="1.0" encoding="UTF-8"?>
15 <gexf xmlns="http://www.gexf.net/1.2draft" version="1.2">
16 <graph mode="static" defaultedgetype="directed">
17 <nodes>
18 <node id="0" label="Hello" />
19 <node id="1" label="Word" />
20 </nodes>
21 <edges>
22 <edge id="0" source="0" target="1" />
23 </edges>
24 </graph>
25 </gexf>
26 """
27 cls.simple_directed_graph = nx.DiGraph()
28 cls.simple_directed_graph.add_node("0", label="Hello")
29 cls.simple_directed_graph.add_node("1", label="World")
30 cls.simple_directed_graph.add_edge("0", "1", id="0")
31
32 cls.simple_directed_fh = io.BytesIO(cls.simple_directed_data.encode("UTF-8"))
33
34 cls.attribute_data = """<?xml version="1.0" encoding="UTF-8"?>\
35 <gexf xmlns="http://www.gexf.net/1.2draft" xmlns:xsi="http://www.w3.\
36 org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.gexf.net/\
37 1.2draft http://www.gexf.net/1.2draft/gexf.xsd" version="1.2">
38 <meta lastmodifieddate="2009-03-20">
39 <creator>Gephi.org</creator>
40 <description>A Web network</description>
41 </meta>
42 <graph defaultedgetype="directed">
43 <attributes class="node">
44 <attribute id="0" title="url" type="string"/>
45 <attribute id="1" title="indegree" type="integer"/>
46 <attribute id="2" title="frog" type="boolean">
47 <default>true</default>
48 </attribute>
49 </attributes>
50 <nodes>
51 <node id="0" label="Gephi">
52 <attvalues>
53 <attvalue for="0" value="https://gephi.org"/>
54 <attvalue for="1" value="1"/>
55 <attvalue for="2" value="false"/>
56 </attvalues>
57 </node>
58 <node id="1" label="Webatlas">
59 <attvalues>
60 <attvalue for="0" value="http://webatlas.fr"/>
61 <attvalue for="1" value="2"/>
62 <attvalue for="2" value="false"/>
63 </attvalues>
64 </node>
65 <node id="2" label="RTGI">
66 <attvalues>
67 <attvalue for="0" value="http://rtgi.fr"/>
68 <attvalue for="1" value="1"/>
69 <attvalue for="2" value="true"/>
70 </attvalues>
71 </node>
72 <node id="3" label="BarabasiLab">
73 <attvalues>
74 <attvalue for="0" value="http://barabasilab.com"/>
75 <attvalue for="1" value="1"/>
76 <attvalue for="2" value="true"/>
77 </attvalues>
78 </node>
79 </nodes>
80 <edges>
81 <edge id="0" source="0" target="1" label="foo"/>
82 <edge id="1" source="0" target="2"/>
83 <edge id="2" source="1" target="0"/>
84 <edge id="3" source="2" target="1"/>
85 <edge id="4" source="0" target="3"/>
86 </edges>
87 </graph>
88 </gexf>
89 """
90 cls.attribute_graph = nx.DiGraph()
91 cls.attribute_graph.graph["node_default"] = {"frog": True}
92 cls.attribute_graph.add_node(
93 "0", label="Gephi", url="https://gephi.org", indegree=1, frog=False
94 )
95 cls.attribute_graph.add_node(
96 "1", label="Webatlas", url="http://webatlas.fr", indegree=2, frog=False
97 )
98 cls.attribute_graph.add_node(
99 "2", label="RTGI", url="http://rtgi.fr", indegree=1, frog=True
100 )
101 cls.attribute_graph.add_node(
102 "3",
103 label="BarabasiLab",
104 url="http://barabasilab.com",
105 indegree=1,
106 frog=True,
107 )
108 cls.attribute_graph.add_edge("0", "1", id="0", label="foo")
109 cls.attribute_graph.add_edge("0", "2", id="1")
110 cls.attribute_graph.add_edge("1", "0", id="2")
111 cls.attribute_graph.add_edge("2", "1", id="3")
112 cls.attribute_graph.add_edge("0", "3", id="4")
113 cls.attribute_fh = io.BytesIO(cls.attribute_data.encode("UTF-8"))
114
115 cls.simple_undirected_data = """<?xml version="1.0" encoding="UTF-8"?>
116 <gexf xmlns="http://www.gexf.net/1.2draft" version="1.2">
117 <graph mode="static" defaultedgetype="undirected">
118 <nodes>
119 <node id="0" label="Hello" />
120 <node id="1" label="Word" />
121 </nodes>
122 <edges>
123 <edge id="0" source="0" target="1" />
124 </edges>
125 </graph>
126 </gexf>
127 """
128 cls.simple_undirected_graph = nx.Graph()
129 cls.simple_undirected_graph.add_node("0", label="Hello")
130 cls.simple_undirected_graph.add_node("1", label="World")
131 cls.simple_undirected_graph.add_edge("0", "1", id="0")
132
133 cls.simple_undirected_fh = io.BytesIO(
134 cls.simple_undirected_data.encode("UTF-8")
135 )
136
137 def test_read_simple_directed_graphml(self):
138 G = self.simple_directed_graph
139 H = nx.read_gexf(self.simple_directed_fh)
140 assert sorted(G.nodes()) == sorted(H.nodes())
141 assert sorted(G.edges()) == sorted(H.edges())
142 assert sorted(G.edges(data=True)) == sorted(H.edges(data=True))
143 self.simple_directed_fh.seek(0)
144
145 def test_write_read_simple_directed_graphml(self):
146 G = self.simple_directed_graph
147 fh = io.BytesIO()
148 nx.write_gexf(G, fh)
149 fh.seek(0)
150 H = nx.read_gexf(fh)
151 assert sorted(G.nodes()) == sorted(H.nodes())
152 assert sorted(G.edges()) == sorted(H.edges())
153 assert sorted(G.edges(data=True)) == sorted(H.edges(data=True))
154 self.simple_directed_fh.seek(0)
155
156 def test_read_simple_undirected_graphml(self):
157 G = self.simple_undirected_graph
158 H = nx.read_gexf(self.simple_undirected_fh)
159 assert sorted(G.nodes()) == sorted(H.nodes())
160 assert sorted(sorted(e) for e in G.edges()) == sorted(
161 sorted(e) for e in H.edges()
162 )
163 self.simple_undirected_fh.seek(0)
164
165 def test_read_attribute_graphml(self):
166 G = self.attribute_graph
167 H = nx.read_gexf(self.attribute_fh)
168 assert sorted(G.nodes(True)) == sorted(H.nodes(data=True))
169 ge = sorted(G.edges(data=True))
170 he = sorted(H.edges(data=True))
171 for a, b in zip(ge, he):
172 assert a == b
173 self.attribute_fh.seek(0)
174
175 def test_directed_edge_in_undirected(self):
176 s = """<?xml version="1.0" encoding="UTF-8"?>
177 <gexf xmlns="http://www.gexf.net/1.2draft" version='1.2'>
178 <graph mode="static" defaultedgetype="undirected" name="">
179 <nodes>
180 <node id="0" label="Hello" />
181 <node id="1" label="Word" />
182 </nodes>
183 <edges>
184 <edge id="0" source="0" target="1" type="directed"/>
185 </edges>
186 </graph>
187 </gexf>
188 """
189 fh = io.BytesIO(s.encode("UTF-8"))
190 pytest.raises(nx.NetworkXError, nx.read_gexf, fh)
191
192 def test_undirected_edge_in_directed(self):
193 s = """<?xml version="1.0" encoding="UTF-8"?>
194 <gexf xmlns="http://www.gexf.net/1.2draft" version='1.2'>
195 <graph mode="static" defaultedgetype="directed" name="">
196 <nodes>
197 <node id="0" label="Hello" />
198 <node id="1" label="Word" />
199 </nodes>
200 <edges>
201 <edge id="0" source="0" target="1" type="undirected"/>
202 </edges>
203 </graph>
204 </gexf>
205 """
206 fh = io.BytesIO(s.encode("UTF-8"))
207 pytest.raises(nx.NetworkXError, nx.read_gexf, fh)
208
209 def test_key_raises(self):
210 s = """<?xml version="1.0" encoding="UTF-8"?>
211 <gexf xmlns="http://www.gexf.net/1.2draft" version='1.2'>
212 <graph mode="static" defaultedgetype="directed" name="">
213 <nodes>
214 <node id="0" label="Hello">
215 <attvalues>
216 <attvalue for='0' value='1'/>
217 </attvalues>
218 </node>
219 <node id="1" label="Word" />
220 </nodes>
221 <edges>
222 <edge id="0" source="0" target="1" type="undirected"/>
223 </edges>
224 </graph>
225 </gexf>
226 """
227 fh = io.BytesIO(s.encode("UTF-8"))
228 pytest.raises(nx.NetworkXError, nx.read_gexf, fh)
229
230 def test_relabel(self):
231 s = """<?xml version="1.0" encoding="UTF-8"?>
232 <gexf xmlns="http://www.gexf.net/1.2draft" version='1.2'>
233 <graph mode="static" defaultedgetype="directed" name="">
234 <nodes>
235 <node id="0" label="Hello" />
236 <node id="1" label="Word" />
237 </nodes>
238 <edges>
239 <edge id="0" source="0" target="1"/>
240 </edges>
241 </graph>
242 </gexf>
243 """
244 fh = io.BytesIO(s.encode("UTF-8"))
245 G = nx.read_gexf(fh, relabel=True)
246 assert sorted(G.nodes()) == ["Hello", "Word"]
247
248 def test_default_attribute(self):
249 G = nx.Graph()
250 G.add_node(1, label="1", color="green")
251 nx.add_path(G, [0, 1, 2, 3])
252 G.add_edge(1, 2, foo=3)
253 G.graph["node_default"] = {"color": "yellow"}
254 G.graph["edge_default"] = {"foo": 7}
255 fh = io.BytesIO()
256 nx.write_gexf(G, fh)
257 fh.seek(0)
258 H = nx.read_gexf(fh, node_type=int)
259 assert sorted(G.nodes()) == sorted(H.nodes())
260 assert sorted(sorted(e) for e in G.edges()) == sorted(
261 sorted(e) for e in H.edges()
262 )
263 # Reading a gexf graph always sets mode attribute to either
264 # 'static' or 'dynamic'. Remove the mode attribute from the
265 # read graph for the sake of comparing remaining attributes.
266 del H.graph["mode"]
267 assert G.graph == H.graph
268
269 def test_serialize_ints_to_strings(self):
270 G = nx.Graph()
271 G.add_node(1, id=7, label=77)
272 fh = io.BytesIO()
273 nx.write_gexf(G, fh)
274 fh.seek(0)
275 H = nx.read_gexf(fh, node_type=int)
276 assert list(H) == [7]
277 assert H.nodes[7]["label"] == "77"
278
279 # FIXME: We should test xml without caring about their order This is causing a
280 # problem b/c of a change in Python 3.8
281 #
282 # "Prior to Python 3.8, the serialisation order of the XML attributes of
283 # elements was artificially made predictable by sorting the attributes by their
284 # name. Based on the now guaranteed ordering of dicts, this arbitrary
285 # reordering was removed in Python 3.8 to preserve the order in which
286 # attributes were originally parsed or created by user code."
287 #
288 # https://docs.python.org/3.8/library/xml.etree.elementtree.html
289 # https://bugs.python.org/issue34160
290
291 def test_write_with_node_attributes(self):
292 # Addresses #673.
293 G = nx.OrderedGraph()
294 G.add_edges_from([(0, 1), (1, 2), (2, 3)])
295 for i in range(4):
296 G.nodes[i]["id"] = i
297 G.nodes[i]["label"] = i
298 G.nodes[i]["pid"] = i
299 G.nodes[i]["start"] = i
300 G.nodes[i]["end"] = i + 1
301
302 if sys.version_info < (3, 8):
303 expected = f"""<gexf version="1.2" xmlns="http://www.gexf.net/1.2\
304 draft" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:\
305 schemaLocation="http://www.gexf.net/1.2draft http://www.gexf.net/1.2draft/\
306 gexf.xsd">
307 <meta lastmodifieddate="{time.strftime('%Y-%m-%d')}">
308 <creator>NetworkX {nx.__version__}</creator>
309 </meta>
310 <graph defaultedgetype="undirected" mode="dynamic" name="" timeformat="long">
311 <nodes>
312 <node end="1" id="0" label="0" pid="0" start="0" />
313 <node end="2" id="1" label="1" pid="1" start="1" />
314 <node end="3" id="2" label="2" pid="2" start="2" />
315 <node end="4" id="3" label="3" pid="3" start="3" />
316 </nodes>
317 <edges>
318 <edge id="0" source="0" target="1" />
319 <edge id="1" source="1" target="2" />
320 <edge id="2" source="2" target="3" />
321 </edges>
322 </graph>
323 </gexf>"""
324 else:
325 expected = f"""<gexf xmlns="http://www.gexf.net/1.2draft" xmlns:xsi\
326 ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=\
327 "http://www.gexf.net/1.2draft http://www.gexf.net/1.2draft/\
328 gexf.xsd" version="1.2">
329 <meta lastmodifieddate="{time.strftime('%Y-%m-%d')}">
330 <creator>NetworkX {nx.__version__}</creator>
331 </meta>
332 <graph defaultedgetype="undirected" mode="dynamic" name="" timeformat="long">
333 <nodes>
334 <node id="0" label="0" pid="0" start="0" end="1" />
335 <node id="1" label="1" pid="1" start="1" end="2" />
336 <node id="2" label="2" pid="2" start="2" end="3" />
337 <node id="3" label="3" pid="3" start="3" end="4" />
338 </nodes>
339 <edges>
340 <edge source="0" target="1" id="0" />
341 <edge source="1" target="2" id="1" />
342 <edge source="2" target="3" id="2" />
343 </edges>
344 </graph>
345 </gexf>"""
346 obtained = "\n".join(nx.generate_gexf(G))
347 assert expected == obtained
348
349 def test_edge_id_construct(self):
350 G = nx.Graph()
351 G.add_edges_from([(0, 1, {"id": 0}), (1, 2, {"id": 2}), (2, 3)])
352
353 if sys.version_info < (3, 8):
354 expected = f"""<gexf version="1.2" xmlns="http://www.gexf.net/\
355 1.2draft" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:\
356 schemaLocation="http://www.gexf.net/1.2draft http://www.gexf.net/1.2draft/\
357 gexf.xsd">
358 <meta lastmodifieddate="{time.strftime('%Y-%m-%d')}">
359 <creator>NetworkX {nx.__version__}</creator>
360 </meta>
361 <graph defaultedgetype="undirected" mode="static" name="">
362 <nodes>
363 <node id="0" label="0" />
364 <node id="1" label="1" />
365 <node id="2" label="2" />
366 <node id="3" label="3" />
367 </nodes>
368 <edges>
369 <edge id="0" source="0" target="1" />
370 <edge id="2" source="1" target="2" />
371 <edge id="1" source="2" target="3" />
372 </edges>
373 </graph>
374 </gexf>"""
375 else:
376 expected = f"""<gexf xmlns="http://www.gexf.net/1.2draft" xmlns:xsi\
377 ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.\
378 gexf.net/1.2draft http://www.gexf.net/1.2draft/gexf.xsd" version="1.2">
379 <meta lastmodifieddate="{time.strftime('%Y-%m-%d')}">
380 <creator>NetworkX {nx.__version__}</creator>
381 </meta>
382 <graph defaultedgetype="undirected" mode="static" name="">
383 <nodes>
384 <node id="0" label="0" />
385 <node id="1" label="1" />
386 <node id="2" label="2" />
387 <node id="3" label="3" />
388 </nodes>
389 <edges>
390 <edge source="0" target="1" id="0" />
391 <edge source="1" target="2" id="2" />
392 <edge source="2" target="3" id="1" />
393 </edges>
394 </graph>
395 </gexf>"""
396
397 obtained = "\n".join(nx.generate_gexf(G))
398 assert expected == obtained
399
400 def test_numpy_type(self):
401 G = nx.path_graph(4)
402 try:
403 import numpy
404 except ImportError:
405 return
406 nx.set_node_attributes(G, {n: n for n in numpy.arange(4)}, "number")
407 G[0][1]["edge-number"] = numpy.float64(1.1)
408
409 if sys.version_info < (3, 8):
410 expected = f"""<gexf version="1.2" xmlns="http://www.gexf.net/1.2draft"\
411 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation\
412 ="http://www.gexf.net/1.2draft http://www.gexf.net/1.2draft/gexf.xsd">
413 <meta lastmodifieddate="{time.strftime('%Y-%m-%d')}">
414 <creator>NetworkX {nx.__version__}</creator>
415 </meta>
416 <graph defaultedgetype="undirected" mode="static" name="">
417 <attributes class="edge" mode="static">
418 <attribute id="1" title="edge-number" type="float" />
419 </attributes>
420 <attributes class="node" mode="static">
421 <attribute id="0" title="number" type="int" />
422 </attributes>
423 <nodes>
424 <node id="0" label="0">
425 <attvalues>
426 <attvalue for="0" value="0" />
427 </attvalues>
428 </node>
429 <node id="1" label="1">
430 <attvalues>
431 <attvalue for="0" value="1" />
432 </attvalues>
433 </node>
434 <node id="2" label="2">
435 <attvalues>
436 <attvalue for="0" value="2" />
437 </attvalues>
438 </node>
439 <node id="3" label="3">
440 <attvalues>
441 <attvalue for="0" value="3" />
442 </attvalues>
443 </node>
444 </nodes>
445 <edges>
446 <edge id="0" source="0" target="1">
447 <attvalues>
448 <attvalue for="1" value="1.1" />
449 </attvalues>
450 </edge>
451 <edge id="1" source="1" target="2" />
452 <edge id="2" source="2" target="3" />
453 </edges>
454 </graph>
455 </gexf>"""
456 else:
457 expected = f"""<gexf xmlns="http://www.gexf.net/1.2draft"\
458 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation\
459 ="http://www.gexf.net/1.2draft http://www.gexf.net/1.2draft/gexf.xsd"\
460 version="1.2">
461 <meta lastmodifieddate="{time.strftime('%Y-%m-%d')}">
462 <creator>NetworkX {nx.__version__}</creator>
463 </meta>
464 <graph defaultedgetype="undirected" mode="static" name="">
465 <attributes mode="static" class="edge">
466 <attribute id="1" title="edge-number" type="float" />
467 </attributes>
468 <attributes mode="static" class="node">
469 <attribute id="0" title="number" type="int" />
470 </attributes>
471 <nodes>
472 <node id="0" label="0">
473 <attvalues>
474 <attvalue for="0" value="0" />
475 </attvalues>
476 </node>
477 <node id="1" label="1">
478 <attvalues>
479 <attvalue for="0" value="1" />
480 </attvalues>
481 </node>
482 <node id="2" label="2">
483 <attvalues>
484 <attvalue for="0" value="2" />
485 </attvalues>
486 </node>
487 <node id="3" label="3">
488 <attvalues>
489 <attvalue for="0" value="3" />
490 </attvalues>
491 </node>
492 </nodes>
493 <edges>
494 <edge source="0" target="1" id="0">
495 <attvalues>
496 <attvalue for="1" value="1.1" />
497 </attvalues>
498 </edge>
499 <edge source="1" target="2" id="1" />
500 <edge source="2" target="3" id="2" />
501 </edges>
502 </graph>
503 </gexf>"""
504 obtained = "\n".join(nx.generate_gexf(G))
505 assert expected == obtained
506
507 def test_bool(self):
508 G = nx.Graph()
509 G.add_node(1, testattr=True)
510 fh = io.BytesIO()
511 nx.write_gexf(G, fh)
512 fh.seek(0)
513 H = nx.read_gexf(fh, node_type=int)
514 assert H.nodes[1]["testattr"]
515
516 # Test for NaN, INF and -INF
517 def test_specials(self):
518 from math import isnan
519
520 inf, nan = float("inf"), float("nan")
521 G = nx.Graph()
522 G.add_node(1, testattr=inf, strdata="inf", key="a")
523 G.add_node(2, testattr=nan, strdata="nan", key="b")
524 G.add_node(3, testattr=-inf, strdata="-inf", key="c")
525
526 fh = io.BytesIO()
527 nx.write_gexf(G, fh)
528 fh.seek(0)
529 filetext = fh.read()
530 fh.seek(0)
531 H = nx.read_gexf(fh, node_type=int)
532
533 assert b"INF" in filetext
534 assert b"NaN" in filetext
535 assert b"-INF" in filetext
536
537 assert H.nodes[1]["testattr"] == inf
538 assert isnan(H.nodes[2]["testattr"])
539 assert H.nodes[3]["testattr"] == -inf
540
541 assert H.nodes[1]["strdata"] == "inf"
542 assert H.nodes[2]["strdata"] == "nan"
543 assert H.nodes[3]["strdata"] == "-inf"
544
545 assert H.nodes[1]["networkx_key"] == "a"
546 assert H.nodes[2]["networkx_key"] == "b"
547 assert H.nodes[3]["networkx_key"] == "c"
548
549 def test_simple_list(self):
550 G = nx.Graph()
551 list_value = [(1, 2, 3), (9, 1, 2)]
552 G.add_node(1, key=list_value)
553 fh = io.BytesIO()
554 nx.write_gexf(G, fh)
555 fh.seek(0)
556 H = nx.read_gexf(fh, node_type=int)
557 assert H.nodes[1]["networkx_key"] == list_value
558
559 def test_dynamic_mode(self):
560 G = nx.Graph()
561 G.add_node(1, label="1", color="green")
562 G.graph["mode"] = "dynamic"
563 fh = io.BytesIO()
564 nx.write_gexf(G, fh)
565 fh.seek(0)
566 H = nx.read_gexf(fh, node_type=int)
567 assert sorted(G.nodes()) == sorted(H.nodes())
568 assert sorted(sorted(e) for e in G.edges()) == sorted(
569 sorted(e) for e in H.edges()
570 )
571
572 def test_multigraph_with_missing_attributes(self):
573 G = nx.MultiGraph()
574 G.add_node(0, label="1", color="green")
575 G.add_node(1, label="2", color="green")
576 G.add_edge(0, 1, id="0", wight=3, type="undirected", start=0, end=1)
577 G.add_edge(0, 1, id="1", label="foo", start=0, end=1)
578 G.add_edge(0, 1)
579 fh = io.BytesIO()
580 nx.write_gexf(G, fh)
581 fh.seek(0)
582 H = nx.read_gexf(fh, node_type=int)
583 assert sorted(G.nodes()) == sorted(H.nodes())
584 assert sorted(sorted(e) for e in G.edges()) == sorted(
585 sorted(e) for e in H.edges()
586 )
587
588 def test_missing_viz_attributes(self):
589 G = nx.Graph()
590 G.add_node(0, label="1", color="green")
591 G.nodes[0]["viz"] = {"size": 54}
592 G.nodes[0]["viz"]["position"] = {"x": 0, "y": 1, "z": 0}
593 G.nodes[0]["viz"]["color"] = {"r": 0, "g": 0, "b": 256}
594 G.nodes[0]["viz"]["shape"] = "http://random.url"
595 G.nodes[0]["viz"]["thickness"] = 2
596 fh = io.BytesIO()
597 nx.write_gexf(G, fh, version="1.1draft")
598 fh.seek(0)
599 H = nx.read_gexf(fh, node_type=int)
600 assert sorted(G.nodes()) == sorted(H.nodes())
601 assert sorted(sorted(e) for e in G.edges()) == sorted(
602 sorted(e) for e in H.edges()
603 )
604
605 # Second graph for the other branch
606 G = nx.Graph()
607 G.add_node(0, label="1", color="green")
608 G.nodes[0]["viz"] = {"size": 54}
609 G.nodes[0]["viz"]["position"] = {"x": 0, "y": 1, "z": 0}
610 G.nodes[0]["viz"]["color"] = {"r": 0, "g": 0, "b": 256, "a": 0.5}
611 G.nodes[0]["viz"]["shape"] = "ftp://random.url"
612 G.nodes[0]["viz"]["thickness"] = 2
613 fh = io.BytesIO()
614 nx.write_gexf(G, fh)
615 fh.seek(0)
616 H = nx.read_gexf(fh, node_type=int)
617 assert sorted(G.nodes()) == sorted(H.nodes())
618 assert sorted(sorted(e) for e in G.edges()) == sorted(
619 sorted(e) for e in H.edges()
620 )
621
622 def test_slice_and_spell(self):
623 # Test spell first, so version = 1.2
624 G = nx.Graph()
625 G.add_node(0, label="1", color="green")
626 G.nodes[0]["spells"] = [(1, 2)]
627 fh = io.BytesIO()
628 nx.write_gexf(G, fh)
629 fh.seek(0)
630 H = nx.read_gexf(fh, node_type=int)
631 assert sorted(G.nodes()) == sorted(H.nodes())
632 assert sorted(sorted(e) for e in G.edges()) == sorted(
633 sorted(e) for e in H.edges()
634 )
635
636 G = nx.Graph()
637 G.add_node(0, label="1", color="green")
638 G.nodes[0]["slices"] = [(1, 2)]
639 fh = io.BytesIO()
640 nx.write_gexf(G, fh, version="1.1draft")
641 fh.seek(0)
642 H = nx.read_gexf(fh, node_type=int)
643 assert sorted(G.nodes()) == sorted(H.nodes())
644 assert sorted(sorted(e) for e in G.edges()) == sorted(
645 sorted(e) for e in H.edges()
646 )
647
648 def test_add_parent(self):
649 G = nx.Graph()
650 G.add_node(0, label="1", color="green", parents=[1, 2])
651 fh = io.BytesIO()
652 nx.write_gexf(G, fh)
653 fh.seek(0)
654 H = nx.read_gexf(fh, node_type=int)
655 assert sorted(G.nodes()) == sorted(H.nodes())
656 assert sorted(sorted(e) for e in G.edges()) == sorted(
657 sorted(e) for e in H.edges()
658 )