Repository: networkx/networkx

Issue:
Write pajek error
When rewriting a network who has been read from pajek there is an error:
'float' object has no attribute 'strip'
apparently coordinates (x,y) don't get translated back to string
(Networkx 2.1)


Discussion:

Resolving patch:
diff --git a/networkx/readwrite/pajek.py b/networkx/readwrite/pajek.py
--- a/networkx/readwrite/pajek.py
+++ b/networkx/readwrite/pajek.py
@@ -22,6 +22,8 @@
 
 """
 
+import warnings
+
 import networkx as nx
 from networkx.utils import is_string_like, open_file, make_str
 
@@ -55,16 +57,24 @@ def generate_pajek(G):
     # make dictionary mapping nodes to integers
     nodenumber = dict(zip(nodes, range(1, len(nodes) + 1)))
     for n in nodes:
-        na = G.nodes.get(n, {})
-        x = na.get('x', 0.0)
-        y = na.get('y', 0.0)
-        id = int(na.get('id', nodenumber[n]))
+        # copy node attributes and pop mandatory attributes
+        # to avoid duplication.
+        na = G.nodes.get(n, {}).copy()
+        x = na.pop('x', 0.0)
+        y = na.pop('y', 0.0)
+        id = int(na.pop('id', nodenumber[n]))
         nodenumber[n] = id
-        shape = na.get('shape', 'ellipse')
+        shape = na.pop('shape', 'ellipse')
         s = ' '.join(map(make_qstr, (id, n, x, y, shape)))
+        # only optional attributes are left in na.
         for k, v in na.items():
-            if v.strip() != '':
+            if is_string_like(v) and v.strip() != '':
                 s += ' %s %s' % (make_qstr(k), make_qstr(v))
+            else:
+                warnings.warn('Node attribute %s is not processed. %s.' %
+                              (k,
+                               'Empty attribute' if is_string_like(v) else
+                               'Non-string attribute'))
         yield s
 
     # write edges with attributes
@@ -77,8 +87,13 @@ def generate_pajek(G):
         value = d.pop('weight', 1.0)  # use 1 as default edge value
         s = ' '.join(map(make_qstr, (nodenumber[u], nodenumber[v], value)))
         for k, v in d.items():
-            if v.strip() != '':
+            if is_string_like(v) and v.strip() != '':
                 s += ' %s %s' % (make_qstr(k), make_qstr(v))
+            else:
+                warnings.warn('Edge attribute %s is not processed. %s.' %
+                              (k,
+                               'Empty attribute' if is_string_like(v) else
+                               'Non-string attribute'))
         yield s
 
 
@@ -99,6 +114,12 @@ def write_pajek(G, path, encoding='UTF-8'):
     >>> G=nx.path_graph(4)
     >>> nx.write_pajek(G, "test.net")
 
+    Warnings
+    --------
+    Optional node attributes and edge attributes must be non-empty strings.
+    Otherwise it will not be written into the file. You will need to
+    convert those attributes to strings if you want to keep them.
+
     References
     ----------
     See http://vlado.fmf.uni-lj.si/pub/networks/pajek/doc/draweps.htm


Test patch:
diff --git a/networkx/readwrite/tests/test_pajek.py b/networkx/readwrite/tests/test_pajek.py
--- a/networkx/readwrite/tests/test_pajek.py
+++ b/networkx/readwrite/tests/test_pajek.py
@@ -57,6 +57,32 @@ def test_read_pajek(self):
         for n in G:
             assert_equal(G.nodes[n], Gin.nodes[n])
 
+    def test_write_pajek(self):
+        import io
+        G = parse_pajek(self.data)
+        fh = io.BytesIO()
+        nx.write_pajek(G,fh)
+        fh.seek(0)
+        H = nx.read_pajek(fh)
+        assert_nodes_equal(list(G), list(H))
+        assert_edges_equal(list(G.edges()), list(H.edges()))
+        # Graph name is left out for now, therefore it is not tested.
+        # assert_equal(G.graph, H.graph)
+
+    def test_ignored_attribute(self):
+        import io
+        G = nx.Graph()
+        fh = io.BytesIO()
+        G.add_node(1, int_attr=1)
+        G.add_node(2, empty_attr='  ')
+        G.add_edge(1, 2, int_attr=2)
+        G.add_edge(2, 3, empty_attr='  ')
+
+        import warnings
+        with warnings.catch_warnings(record=True) as w:
+            nx.write_pajek(G,fh)
+            assert_equal(len(w), 4)
+
     def test_noname(self):
         # Make sure we can parse a line such as:  *network
         # Issue #952