Repository: networkx/networkx

Issue:
Gephi writer outputs static graph even when graph is dynamic
This pertains to serializing a networkx graph to the Gephi `.gexf` format.  Calling `write_gexf` on a networkx graph will always output the graph `mode` as `static`, which is an issue when the graph mode is actually `dynamic`.

The implementation of `write_gexf` (`networkx/networkx/readwrite/gexf.py`) minus any comments is:

``` python
def write_gexf(G, path, encoding='utf-8',prettyprint=True,version='1.1draft'):                                                                                                                                                                                                                                                                        
    writer = GEXFWriter(encoding=encoding,prettyprint=prettyprint,                                                                                                                       
                        version=version)                                                                                                                                                 
    writer.add_graph(G)                                                                                                                                                                  
    writer.write(path) 
```

`GEXFWriter`'s `__init__` has an optional graph mode parameter; see the method signature below:

``` python
    def __init__(self, graph=None, encoding="utf-8",                                                                                                                                     
                 mode='static',prettyprint=True,                                                                                                                                         
                 version='1.1draft'):   
```

`write_gexf` does not specify the mode when instantiating `GEXFWriter` though and so the default value of `static` is always used, and, `__init__` sets `self.mode=mode`.  The following call to `writer.add_graph(G)` ultimately uses `self.mode` to determine what gets written to the output XML, and so the `mode` attribute of the graph always shows as `static` in the output.

A quick fix would be to `i)` add a `mode` parameter to `write_gexf` allowing the user to specify the graph type and `ii)` pass the argument through to the instantiation of `GEXFWriter`.  The problem with this is that it leaves it up to the user to supply the correct graph type at the time `write_gexf` is called.  A more appropriate fix is probably to store the mode as an attribute of the networkx graph and use this to determine the value of `mode` as it appears in the output XML.  This also means `mode` should be removed as a parameter to `GEXFWriter's` `__init__` call.



Discussion:

Resolving patch:
diff --git a/networkx/readwrite/gexf.py b/networkx/readwrite/gexf.py
--- a/networkx/readwrite/gexf.py
+++ b/networkx/readwrite/gexf.py
@@ -236,8 +236,7 @@ def set_version(self,version):
 class GEXFWriter(GEXF):
     # class for writing GEXF format files
     # use write_gexf() function
-    def __init__(self, graph=None, encoding="utf-8",
-                 mode='static',prettyprint=True,
+    def __init__(self, graph=None, encoding="utf-8", prettyprint=True,
                  version='1.1draft'):
         try:
             import xml.etree.ElementTree
@@ -245,7 +244,6 @@ def __init__(self, graph=None, encoding="utf-8",
              raise ImportError('GEXF writer requires '
                                'xml.elementtree.ElementTree')
         self.prettyprint=prettyprint
-        self.mode=mode
         self.encoding = encoding
         self.set_version(version)
         self.xml = Element("gexf",
@@ -277,12 +275,18 @@ def __str__(self):
         return s
 
     def add_graph(self, G):
+        # set graph attributes
+        if G.graph.get('mode')=='dynamic':
+            mode='dynamic'
+        else:
+            mode='static'
+
         # Add a graph element to the XML
         if G.is_directed():
             default='directed'
         else:
             default='undirected'
-        graph_element = Element("graph",defaultedgetype=default,mode=self.mode)
+        graph_element = Element("graph",defaultedgetype=default,mode=mode)
         self.graph_element=graph_element
         self.add_nodes(G,graph_element)
         self.add_edges(G,graph_element)
@@ -575,10 +579,6 @@ def __call__(self, stream):
 
 
     def make_graph(self, graph_xml):
-        # mode is "static" or "dynamic"
-        graph_mode = graph_xml.get("mode", "")
-        self.dynamic=(graph_mode=='dynamic')
-
         # start with empty DiGraph or MultiDiGraph
         edgedefault = graph_xml.get("defaultedgetype", None)
         if edgedefault=='directed':
@@ -593,6 +593,11 @@ def make_graph(self, graph_xml):
         graph_end=graph_xml.get('end')
         if graph_end is not None:
             G.graph['end']=graph_end
+        graph_mode=graph_xml.get("mode", "")
+        if graph_mode=='dynamic':
+            G.graph['mode']='dynamic'
+        else:
+            G.graph['mode']='static'
 
         # node and edge attributes
         attributes_elements=graph_xml.findall("{%s}attributes"%self.NS_GEXF)


Test patch:
diff --git a/networkx/readwrite/tests/test_gexf.py b/networkx/readwrite/tests/test_gexf.py
--- a/networkx/readwrite/tests/test_gexf.py
+++ b/networkx/readwrite/tests/test_gexf.py
@@ -265,6 +265,10 @@ def test_default_attribute(self):
         assert_equal(
             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_equal(G.graph,H.graph)
 
     def test_serialize_ints_to_strings(self):