Repository: networkx/networkx

Issue:
repeatedly reading and writing dot files does not converge on the same dot file
Steps to reproduce
---

Suppose the following Python command:

    python3 -c 'import networkx as nx, sys; nx.nx_agraph.write_dot(nx.nx_agraph.read_dot(sys.stdin), sys.stdout)'

Essentially this is just writing the same dot graph to stdout that it read from stdin.

And then also this trivial dot input:

```dot
digraph G {
	graph ["edge"="{}",
		"graph"="{}",
		name=G,
		"node"="{}"
	];
}
```

Now I'm executing:

    command < graph1.dot > graph2.dot
    command < graph2.dot > graph3.dot
    command < graph3.dot > graph4.dot
    ...

Expected behaviour
---

Since the command is writing out the same graph it is reading without modifying it, it should not matter how often I read in and write out the same dot file. At least after the first invocation, what networkx writes out should remain the same over the remaining invocations. That is, the file should converge to a stable version that doesn't change independent how often the script is executed on it.

Reality
---

The `graph` attribute gets more and more populated. The more often this is executed the bigger the file becomes.

`graph2.dot`:

```dot
digraph G {
	graph ["edge"="{}",
		"graph"="{'edge': '{}', 'graph': '{}', 'name': 'G', 'node': '{}'}",
		name=G,
		"node"="{}"
	];
}
```

`graph3.dot`:
```dot
digraph G {
	graph ["edge"="{}",
		"graph"="{'edge': '{}', 'graph': \"{'edge': '{}', 'graph': '{}', 'name': 'G', 'node': '{}'}\", 'name': 'G', 'node': '{}'}",
		name=G,
		"node"="{}"
	];
}
```

`graph4.dot`:

```dot
digraph G {                   
	graph ["edge"="{}",
		"graph"="{'edge': '{}', 'graph': '{\'edge\': \'{}\', \'graph\': \"{\'edge\': \'{}\', \'graph\': \'{}\', \'name\': \'G\', \'node\': \'{}\'}\", \'\
name\': \'G\', \'node\': \'{}\'}', 'name': 'G', 'node': '{}'}",
		name=G,
		"node"="{}"
	];
}
```


Discussion:
This looks like it is putting the entire graph into the ```graph``` attribute.  
Your initial graph shouldn't use the special attribute ```graph```. Am I right?
Does that create a subgraph within the bigger graph? I;m not sure why you have it there.

I certainly messes up reading and writing as you demonstrate.  What if it isn't there? Does the same thing happen one iteration later?

Do you have suggestions for how to handle this?
If my initial graph doesn't have the `graph` attribute, then it will be put in by networkx and the issue is the same. Try this initial graph instead:

```dot
digraph G {
}
```

After passing this through the command, it becomes:

```dot
digraph G {
	graph ["edge"="{}",
		"graph"="{}",
		name=G,
		"node"="{}"
	];
}
```

It doesn't matter why *I* have it there. As you can see, it is put in by networkx itself and not by me. I noticed this issue specifically because I didn't put the attribute there but networkx did.

So yes, if it isn't there, networkx will put it and we have the same problem again.

Did you not try to reproduce the issue yourself?

Resolving patch:
diff --git a/networkx/drawing/nx_agraph.py b/networkx/drawing/nx_agraph.py
--- a/networkx/drawing/nx_agraph.py
+++ b/networkx/drawing/nx_agraph.py
@@ -145,7 +145,8 @@ def to_agraph(N):
     A.node_attr.update(N.graph.get('node', {}))
     A.edge_attr.update(N.graph.get('edge', {}))
 
-    A.graph_attr.update(N.graph)
+    A.graph_attr.update((k, v) for k, v in N.graph.items()
+                        if k not in ('graph', 'node', 'edge'))
 
     # add nodes
     for n, nodedata in N.nodes(data=True):


Test patch:
diff --git a/networkx/drawing/tests/test_agraph.py b/networkx/drawing/tests/test_agraph.py
--- a/networkx/drawing/tests/test_agraph.py
+++ b/networkx/drawing/tests/test_agraph.py
@@ -3,7 +3,8 @@
 import tempfile
 from nose import SkipTest
 from nose.tools import assert_true, assert_equal, assert_raises
-from networkx.testing import assert_edges_equal, assert_nodes_equal
+from networkx.testing import assert_edges_equal, assert_nodes_equal, \
+        assert_graphs_equal
 
 import networkx as nx
 
@@ -90,3 +91,16 @@ def test_graph_with_reserved_keywords(self):
         G.edges[('A', 'B')]['u'] = 'keyword'
         G.edges[('A', 'B')]['v'] = 'keyword'
         A = nx.nx_agraph.to_agraph(G)
+
+    def test_round_trip(self):
+        G = nx.Graph()
+        A = nx.nx_agraph.to_agraph(G)
+        H = nx.nx_agraph.from_agraph(A)
+        #assert_graphs_equal(G, H)
+        AA = nx.nx_agraph.to_agraph(H)
+        HH = nx.nx_agraph.from_agraph(AA)
+        assert_graphs_equal(H, HH)
+        G.graph['graph'] = {}
+        G.graph['node'] = {}
+        G.graph['edge'] = {}
+        assert_graphs_equal(G, HH)