comparison env/lib/python3.9/site-packages/networkx/readwrite/tests/test_gml.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 from ast import literal_eval
2 import codecs
3 from contextlib import contextmanager
4 import io
5 import pytest
6 import networkx as nx
7 from networkx.readwrite.gml import literal_stringizer, literal_destringizer
8 import os
9 import tempfile
10 from textwrap import dedent
11
12
13 class TestGraph:
14 @classmethod
15 def setup_class(cls):
16 cls.simple_data = """Creator "me"
17 Version "xx"
18 graph [
19 comment "This is a sample graph"
20 directed 1
21 IsPlanar 1
22 pos [ x 0 y 1 ]
23 node [
24 id 1
25 label "Node 1"
26 pos [ x 1 y 1 ]
27 ]
28 node [
29 id 2
30 pos [ x 1 y 2 ]
31 label "Node 2"
32 ]
33 node [
34 id 3
35 label "Node 3"
36 pos [ x 1 y 3 ]
37 ]
38 edge [
39 source 1
40 target 2
41 label "Edge from node 1 to node 2"
42 color [line "blue" thickness 3]
43
44 ]
45 edge [
46 source 2
47 target 3
48 label "Edge from node 2 to node 3"
49 ]
50 edge [
51 source 3
52 target 1
53 label "Edge from node 3 to node 1"
54 ]
55 ]
56 """
57
58 def test_parse_gml_cytoscape_bug(self):
59 # example from issue #321, originally #324 in trac
60 cytoscape_example = """
61 Creator "Cytoscape"
62 Version 1.0
63 graph [
64 node [
65 root_index -3
66 id -3
67 graphics [
68 x -96.0
69 y -67.0
70 w 40.0
71 h 40.0
72 fill "#ff9999"
73 type "ellipse"
74 outline "#666666"
75 outline_width 1.5
76 ]
77 label "node2"
78 ]
79 node [
80 root_index -2
81 id -2
82 graphics [
83 x 63.0
84 y 37.0
85 w 40.0
86 h 40.0
87 fill "#ff9999"
88 type "ellipse"
89 outline "#666666"
90 outline_width 1.5
91 ]
92 label "node1"
93 ]
94 node [
95 root_index -1
96 id -1
97 graphics [
98 x -31.0
99 y -17.0
100 w 40.0
101 h 40.0
102 fill "#ff9999"
103 type "ellipse"
104 outline "#666666"
105 outline_width 1.5
106 ]
107 label "node0"
108 ]
109 edge [
110 root_index -2
111 target -2
112 source -1
113 graphics [
114 width 1.5
115 fill "#0000ff"
116 type "line"
117 Line [
118 ]
119 source_arrow 0
120 target_arrow 3
121 ]
122 label "DirectedEdge"
123 ]
124 edge [
125 root_index -1
126 target -1
127 source -3
128 graphics [
129 width 1.5
130 fill "#0000ff"
131 type "line"
132 Line [
133 ]
134 source_arrow 0
135 target_arrow 3
136 ]
137 label "DirectedEdge"
138 ]
139 ]
140 """
141 nx.parse_gml(cytoscape_example)
142
143 def test_parse_gml(self):
144 G = nx.parse_gml(self.simple_data, label="label")
145 assert sorted(G.nodes()) == ["Node 1", "Node 2", "Node 3"]
146 assert [e for e in sorted(G.edges())] == [
147 ("Node 1", "Node 2"),
148 ("Node 2", "Node 3"),
149 ("Node 3", "Node 1"),
150 ]
151
152 assert [e for e in sorted(G.edges(data=True))] == [
153 (
154 "Node 1",
155 "Node 2",
156 {
157 "color": {"line": "blue", "thickness": 3},
158 "label": "Edge from node 1 to node 2",
159 },
160 ),
161 ("Node 2", "Node 3", {"label": "Edge from node 2 to node 3"}),
162 ("Node 3", "Node 1", {"label": "Edge from node 3 to node 1"}),
163 ]
164
165 def test_read_gml(self):
166 (fd, fname) = tempfile.mkstemp()
167 fh = open(fname, "w")
168 fh.write(self.simple_data)
169 fh.close()
170 Gin = nx.read_gml(fname, label="label")
171 G = nx.parse_gml(self.simple_data, label="label")
172 assert sorted(G.nodes(data=True)) == sorted(Gin.nodes(data=True))
173 assert sorted(G.edges(data=True)) == sorted(Gin.edges(data=True))
174 os.close(fd)
175 os.unlink(fname)
176
177 def test_labels_are_strings(self):
178 # GML requires labels to be strings (i.e., in quotes)
179 answer = """graph [
180 node [
181 id 0
182 label "1203"
183 ]
184 ]"""
185 G = nx.Graph()
186 G.add_node(1203)
187 data = "\n".join(nx.generate_gml(G, stringizer=literal_stringizer))
188 assert data == answer
189
190 def test_relabel_duplicate(self):
191 data = """
192 graph
193 [
194 label ""
195 directed 1
196 node
197 [
198 id 0
199 label "same"
200 ]
201 node
202 [
203 id 1
204 label "same"
205 ]
206 ]
207 """
208 fh = io.BytesIO(data.encode("UTF-8"))
209 fh.seek(0)
210 pytest.raises(nx.NetworkXError, nx.read_gml, fh, label="label")
211
212 def test_tuplelabels(self):
213 # https://github.com/networkx/networkx/pull/1048
214 # Writing tuple labels to GML failed.
215 G = nx.OrderedGraph()
216 G.add_edge((0, 1), (1, 0))
217 data = "\n".join(nx.generate_gml(G, stringizer=literal_stringizer))
218 answer = """graph [
219 node [
220 id 0
221 label "(0,1)"
222 ]
223 node [
224 id 1
225 label "(1,0)"
226 ]
227 edge [
228 source 0
229 target 1
230 ]
231 ]"""
232 assert data == answer
233
234 def test_quotes(self):
235 # https://github.com/networkx/networkx/issues/1061
236 # Encoding quotes as HTML entities.
237 G = nx.path_graph(1)
238 G.name = "path_graph(1)"
239 attr = 'This is "quoted" and this is a copyright: ' + chr(169)
240 G.nodes[0]["demo"] = attr
241 fobj = tempfile.NamedTemporaryFile()
242 nx.write_gml(G, fobj)
243 fobj.seek(0)
244 # Should be bytes in 2.x and 3.x
245 data = fobj.read().strip().decode("ascii")
246 answer = """graph [
247 name "path_graph(1)"
248 node [
249 id 0
250 label "0"
251 demo "This is "quoted" and this is a copyright: ©"
252 ]
253 ]"""
254 assert data == answer
255
256 def test_unicode_node(self):
257 node = "node" + chr(169)
258 G = nx.Graph()
259 G.add_node(node)
260 fobj = tempfile.NamedTemporaryFile()
261 nx.write_gml(G, fobj)
262 fobj.seek(0)
263 # Should be bytes in 2.x and 3.x
264 data = fobj.read().strip().decode("ascii")
265 answer = """graph [
266 node [
267 id 0
268 label "node©"
269 ]
270 ]"""
271 assert data == answer
272
273 def test_float_label(self):
274 node = 1.0
275 G = nx.Graph()
276 G.add_node(node)
277 fobj = tempfile.NamedTemporaryFile()
278 nx.write_gml(G, fobj)
279 fobj.seek(0)
280 # Should be bytes in 2.x and 3.x
281 data = fobj.read().strip().decode("ascii")
282 answer = """graph [
283 node [
284 id 0
285 label "1.0"
286 ]
287 ]"""
288 assert data == answer
289
290 def test_name(self):
291 G = nx.parse_gml('graph [ name "x" node [ id 0 label "x" ] ]')
292 assert "x" == G.graph["name"]
293 G = nx.parse_gml('graph [ node [ id 0 label "x" ] ]')
294 assert "" == G.name
295 assert "name" not in G.graph
296
297 def test_graph_types(self):
298 for directed in [None, False, True]:
299 for multigraph in [None, False, True]:
300 gml = "graph ["
301 if directed is not None:
302 gml += " directed " + str(int(directed))
303 if multigraph is not None:
304 gml += " multigraph " + str(int(multigraph))
305 gml += ' node [ id 0 label "0" ]'
306 gml += " edge [ source 0 target 0 ]"
307 gml += " ]"
308 G = nx.parse_gml(gml)
309 assert bool(directed) == G.is_directed()
310 assert bool(multigraph) == G.is_multigraph()
311 gml = "graph [\n"
312 if directed is True:
313 gml += " directed 1\n"
314 if multigraph is True:
315 gml += " multigraph 1\n"
316 gml += """ node [
317 id 0
318 label "0"
319 ]
320 edge [
321 source 0
322 target 0
323 """
324 if multigraph:
325 gml += " key 0\n"
326 gml += " ]\n]"
327 assert gml == "\n".join(nx.generate_gml(G))
328
329 def test_data_types(self):
330 data = [
331 True,
332 False,
333 10 ** 20,
334 -2e33,
335 "'",
336 '"&&&""',
337 [{(b"\xfd",): "\x7f", chr(0x4444): (1, 2)}, (2, "3")],
338 ]
339 try: # fails under IronPython
340 data.append(chr(0x14444))
341 except ValueError:
342 data.append(chr(0x1444))
343 data.append(literal_eval("{2.3j, 1 - 2.3j, ()}"))
344 G = nx.Graph()
345 G.name = data
346 G.graph["data"] = data
347 G.add_node(0, int=-1, data=dict(data=data))
348 G.add_edge(0, 0, float=-2.5, data=data)
349 gml = "\n".join(nx.generate_gml(G, stringizer=literal_stringizer))
350 G = nx.parse_gml(gml, destringizer=literal_destringizer)
351 assert data == G.name
352 assert {"name": data, "data": data} == G.graph
353 assert list(G.nodes(data=True)) == [(0, dict(int=-1, data=dict(data=data)))]
354 assert list(G.edges(data=True)) == [(0, 0, dict(float=-2.5, data=data))]
355 G = nx.Graph()
356 G.graph["data"] = "frozenset([1, 2, 3])"
357 G = nx.parse_gml(nx.generate_gml(G), destringizer=literal_eval)
358 assert G.graph["data"] == "frozenset([1, 2, 3])"
359
360 def test_escape_unescape(self):
361 gml = """graph [
362 name "&"䑄��&unknown;"
363 ]"""
364 G = nx.parse_gml(gml)
365 assert (
366 '&"\x0f' + chr(0x4444) + "��&unknown;"
367 == G.name
368 )
369 gml = "\n".join(nx.generate_gml(G))
370 alnu = "#1234567890;&#x1234567890abcdef"
371 answer = (
372 """graph [
373 name "&"䑄&"""
374 + alnu
375 + """;&unknown;"
376 ]"""
377 )
378 assert answer == gml
379
380 def test_exceptions(self):
381 pytest.raises(ValueError, literal_destringizer, "(")
382 pytest.raises(ValueError, literal_destringizer, "frozenset([1, 2, 3])")
383 pytest.raises(ValueError, literal_destringizer, literal_destringizer)
384 pytest.raises(ValueError, literal_stringizer, frozenset([1, 2, 3]))
385 pytest.raises(ValueError, literal_stringizer, literal_stringizer)
386 with tempfile.TemporaryFile() as f:
387 f.write(codecs.BOM_UTF8 + b"graph[]")
388 f.seek(0)
389 pytest.raises(nx.NetworkXError, nx.read_gml, f)
390
391 def assert_parse_error(gml):
392 pytest.raises(nx.NetworkXError, nx.parse_gml, gml)
393
394 assert_parse_error(["graph [\n\n", "]"])
395 assert_parse_error("")
396 assert_parse_error('Creator ""')
397 assert_parse_error("0")
398 assert_parse_error("graph ]")
399 assert_parse_error("graph [ 1 ]")
400 assert_parse_error("graph [ 1.E+2 ]")
401 assert_parse_error('graph [ "A" ]')
402 assert_parse_error("graph [ ] graph ]")
403 assert_parse_error("graph [ ] graph [ ]")
404 assert_parse_error("graph [ data [1, 2, 3] ]")
405 assert_parse_error("graph [ node [ ] ]")
406 assert_parse_error("graph [ node [ id 0 ] ]")
407 nx.parse_gml('graph [ node [ id "a" ] ]', label="id")
408 assert_parse_error("graph [ node [ id 0 label 0 ] node [ id 0 label 1 ] ]")
409 assert_parse_error("graph [ node [ id 0 label 0 ] node [ id 1 label 0 ] ]")
410 assert_parse_error("graph [ node [ id 0 label 0 ] edge [ ] ]")
411 assert_parse_error("graph [ node [ id 0 label 0 ] edge [ source 0 ] ]")
412 nx.parse_gml("graph [edge [ source 0 target 0 ] node [ id 0 label 0 ] ]")
413 assert_parse_error("graph [ node [ id 0 label 0 ] edge [ source 1 target 0 ] ]")
414 assert_parse_error("graph [ node [ id 0 label 0 ] edge [ source 0 target 1 ] ]")
415 assert_parse_error(
416 "graph [ node [ id 0 label 0 ] node [ id 1 label 1 ] "
417 "edge [ source 0 target 1 ] edge [ source 1 target 0 ] ]"
418 )
419 nx.parse_gml(
420 "graph [ node [ id 0 label 0 ] node [ id 1 label 1 ] "
421 "edge [ source 0 target 1 ] edge [ source 1 target 0 ] "
422 "directed 1 ]"
423 )
424 nx.parse_gml(
425 "graph [ node [ id 0 label 0 ] node [ id 1 label 1 ] "
426 "edge [ source 0 target 1 ] edge [ source 0 target 1 ]"
427 "multigraph 1 ]"
428 )
429 nx.parse_gml(
430 "graph [ node [ id 0 label 0 ] node [ id 1 label 1 ] "
431 "edge [ source 0 target 1 key 0 ] edge [ source 0 target 1 ]"
432 "multigraph 1 ]"
433 )
434 assert_parse_error(
435 "graph [ node [ id 0 label 0 ] node [ id 1 label 1 ] "
436 "edge [ source 0 target 1 key 0 ] edge [ source 0 target 1 key 0 ]"
437 "multigraph 1 ]"
438 )
439 nx.parse_gml(
440 "graph [ node [ id 0 label 0 ] node [ id 1 label 1 ] "
441 "edge [ source 0 target 1 key 0 ] edge [ source 1 target 0 key 0 ]"
442 "directed 1 multigraph 1 ]"
443 )
444
445 # Tests for string convertable alphanumeric id and label values
446 nx.parse_gml("graph [edge [ source a target a ] node [ id a label b ] ]")
447 nx.parse_gml(
448 "graph [ node [ id n42 label 0 ] node [ id x43 label 1 ]"
449 "edge [ source n42 target x43 key 0 ]"
450 "edge [ source x43 target n42 key 0 ]"
451 "directed 1 multigraph 1 ]"
452 )
453 assert_parse_error(
454 "graph [edge [ source u'u\4200' target u'u\4200' ] "
455 + "node [ id u'u\4200' label b ] ]"
456 )
457
458 def assert_generate_error(*args, **kwargs):
459 pytest.raises(
460 nx.NetworkXError, lambda: list(nx.generate_gml(*args, **kwargs))
461 )
462
463 G = nx.Graph()
464 G.graph[3] = 3
465 assert_generate_error(G)
466 G = nx.Graph()
467 G.graph["3"] = 3
468 assert_generate_error(G)
469 G = nx.Graph()
470 G.graph["data"] = frozenset([1, 2, 3])
471 assert_generate_error(G, stringizer=literal_stringizer)
472 G = nx.Graph()
473 G.graph["data"] = []
474 assert_generate_error(G)
475 assert_generate_error(G, stringizer=len)
476
477 def test_label_kwarg(self):
478 G = nx.parse_gml(self.simple_data, label="id")
479 assert sorted(G.nodes) == [1, 2, 3]
480 labels = [G.nodes[n]["label"] for n in sorted(G.nodes)]
481 assert labels == ["Node 1", "Node 2", "Node 3"]
482
483 G = nx.parse_gml(self.simple_data, label=None)
484 assert sorted(G.nodes) == [1, 2, 3]
485 labels = [G.nodes[n]["label"] for n in sorted(G.nodes)]
486 assert labels == ["Node 1", "Node 2", "Node 3"]
487
488 def test_outofrange_integers(self):
489 # GML restricts integers to 32 signed bits.
490 # Check that we honor this restriction on export
491 G = nx.Graph()
492 # Test export for numbers that barely fit or don't fit into 32 bits,
493 # and 3 numbers in the middle
494 numbers = {
495 "toosmall": (-(2 ** 31)) - 1,
496 "small": -(2 ** 31),
497 "med1": -4,
498 "med2": 0,
499 "med3": 17,
500 "big": (2 ** 31) - 1,
501 "toobig": 2 ** 31,
502 }
503 G.add_node("Node", **numbers)
504
505 fd, fname = tempfile.mkstemp()
506 try:
507 nx.write_gml(G, fname)
508 # Check that the export wrote the nonfitting numbers as strings
509 G2 = nx.read_gml(fname)
510 for attr, value in G2.nodes["Node"].items():
511 if attr == "toosmall" or attr == "toobig":
512 assert type(value) == str
513 else:
514 assert type(value) == int
515 finally:
516 os.close(fd)
517 os.unlink(fname)
518
519
520 @contextmanager
521 def byte_file():
522 _file_handle = io.BytesIO()
523 yield _file_handle
524 _file_handle.seek(0)
525
526
527 class TestPropertyLists:
528 def test_writing_graph_with_multi_element_property_list(self):
529 g = nx.Graph()
530 g.add_node("n1", properties=["element", 0, 1, 2.5, True, False])
531 with byte_file() as f:
532 nx.write_gml(g, f)
533 result = f.read().decode()
534
535 assert result == dedent(
536 """\
537 graph [
538 node [
539 id 0
540 label "n1"
541 properties "element"
542 properties 0
543 properties 1
544 properties 2.5
545 properties 1
546 properties 0
547 ]
548 ]
549 """
550 )
551
552 def test_writing_graph_with_one_element_property_list(self):
553 g = nx.Graph()
554 g.add_node("n1", properties=["element"])
555 with byte_file() as f:
556 nx.write_gml(g, f)
557 result = f.read().decode()
558
559 assert result == dedent(
560 """\
561 graph [
562 node [
563 id 0
564 label "n1"
565 properties "_networkx_list_start"
566 properties "element"
567 ]
568 ]
569 """
570 )
571
572 def test_reading_graph_with_list_property(self):
573 with byte_file() as f:
574 f.write(
575 dedent(
576 """
577 graph [
578 node [
579 id 0
580 label "n1"
581 properties "element"
582 properties 0
583 properties 1
584 properties 2.5
585 ]
586 ]
587 """
588 ).encode("ascii")
589 )
590 f.seek(0)
591 graph = nx.read_gml(f)
592 assert graph.nodes(data=True)["n1"] == {"properties": ["element", 0, 1, 2.5]}
593
594 def test_reading_graph_with_single_element_list_property(self):
595 with byte_file() as f:
596 f.write(
597 dedent(
598 """
599 graph [
600 node [
601 id 0
602 label "n1"
603 properties "_networkx_list_start"
604 properties "element"
605 ]
606 ]
607 """
608 ).encode("ascii")
609 )
610 f.seek(0)
611 graph = nx.read_gml(f)
612 assert graph.nodes(data=True)["n1"] == {"properties": ["element"]}