Repository: networkx/networkx

Issue:
(Di)Graph.add_nodes_from broken under IronPython
It seems that #1314 breaks (Di)Graph.add_nodes_from in IronPython on Travis pretty badly (see https://travis-ci.org/networkx/networkx/jobs/46474504#L3452 for example). I think that I fixed those functions before. IIRC, that was due to `<dict> in <dict>` returning `false` instead of raising `TypeError` in IronPython. #1314 apparently [reintroduced the same logic](https://github.com/networkx/networkx/pull/1314/files#diff-4fe234273eebd1a251430097d68e9854R403). I am not sure if that has been fixed in IronPython, or if .travis.yml is picking up the correct release.



Discussion:
Sorry for not checking the Travis error output for #1314. 

So--do I understand correctly that inside the try/except clause CPython throws a TypeError for the "if n not in self.succ" while IronPython throws a TypeError on the next line: "self.succ[n]={}"???

Wow--- that's totally off my radar (and a good example of why try-clauses should be kept as short as possible.  You can't tell what error is being caught for which reason. :)

Would a quick fix in add_nodes_from along with some comments to describe why we have so much in the try-clause be the solution? 

Supposedly that bug ([CodePlex issue](https://ironpython.codeplex.com/workitem/35348)) has been fixed (IronLanguages/main#221, more specifically IronLanguages/main@2e9b42b667f94f247d54f912230d4e83968d05e6) and released in IronPython 2.7.5. .travis.yml apparently is picking up that release.

You can use the following to work around the issue:

``` python
        for n in nodes:
            try:
                if n not in self.succ:
                    self.succ[n] = self.adjlist_dict_factory()
                    self.pred[n] = self.adjlist_dict_factory()
                    self.node[n] = attr.copy()
                else:
                    self.node[n].update(attr)
            except TypeError:
                nn,ndict = n
                if nn not in self.succ:
                    self.succ[nn] = self.adjlist_dict_factory()
                    self.pred[nn] = self.adjlist_dict_factory()
                    newdict = attr.copy()
                    newdict.update(ndict)
                    self.node[nn] = newdict
                else:
                    olddict = self.node[nn]
                    olddict.update(attr)
                    olddict.update(ndict)
```

Even if you manage to get past `n not in self.succ` you will still be caught by `self.succ[n]`.

Yes, that makes good sense.  I usually don't like to catch two different
exceptions with the same exception type, but I'm not used to protecting
against two different pythons. :)

Welcome back!  And Congratulations on you new job!

On Fri, Jan 9, 2015 at 8:40 PM, ysitu notifications@github.com wrote:

> Supposedly that bug (CodePlex issue
> https://ironpython.codeplex.com/workitem/35348) has been fixed (
> IronLanguages/main#221 https://github.com/IronLanguages/main/pull/221,
> more specifically IronLanguages/main@2e9b42b
> https://github.com/IronLanguages/main/commit/2e9b42b667f94f247d54f912230d4e83968d05e6)
> and released in IronPython 2.7.5. .travis.yml apparently is picking up that
> release.
> 
> You can use the following to work around the issue:
> 
> ```
>     for n in nodes:
>         try:
>             if n not in self.succ:
>                 self.succ[n] = self.adjlist_dict_factory()
>                 self.pred[n] = self.adjlist_dict_factory()
>                 self.node[n] = attr.copy()
>             else:
>                 self.node[n].update(attr)
>         except TypeError:
>             nn,ndict = n
>             if nn not in self.succ:
>                 self.succ[nn] = self.adjlist_dict_factory()
>                 self.pred[nn] = self.adjlist_dict_factory()
>                 newdict = attr.copy()
>                 newdict.update(ndict)
>                 self.node[nn] = newdict
>             else:
>                 olddict = self.node[nn]
>                 olddict.update(attr)
>                 olddict.update(ndict)
> ```
> 
> Even if you manage to get past n not in self.succ you will still be
> caught by self.succ[n].
> 
> —
> Reply to this email directly or view it on GitHub
> https://github.com/networkx/networkx/issues/1327#issuecomment-69432211.


Resolving patch:
diff --git a/networkx/classes/digraph.py b/networkx/classes/digraph.py
--- a/networkx/classes/digraph.py
+++ b/networkx/classes/digraph.py
@@ -399,8 +399,16 @@ def add_nodes_from(self, nodes, **attr):
 
         """
         for n in nodes:
+            # keep all this inside try/except because
+            # CPython throws TypeError on n not in self.succ,
+            # while pre-2.7.5 ironpython throws on self.succ[n] 
             try:
-                newnode=n not in self.succ
+                if n not in self.succ:
+                    self.succ[n] = self.adjlist_dict_factory()
+                    self.pred[n] = self.adjlist_dict_factory()
+                    self.node[n] = attr.copy()
+                else:
+                    self.node[n].update(attr)
             except TypeError:
                 nn,ndict = n
                 if nn not in self.succ:
@@ -413,13 +421,6 @@ def add_nodes_from(self, nodes, **attr):
                     olddict = self.node[nn]
                     olddict.update(attr)
                     olddict.update(ndict)
-                continue
-            if newnode:
-                self.succ[n] = self.adjlist_dict_factory()
-                self.pred[n] = self.adjlist_dict_factory()
-                self.node[n] = attr.copy()
-            else:
-                self.node[n].update(attr)
 
     def remove_node(self, n):
         """Remove node n.
diff --git a/networkx/classes/graph.py b/networkx/classes/graph.py
--- a/networkx/classes/graph.py
+++ b/networkx/classes/graph.py
@@ -507,8 +507,15 @@ def add_nodes_from(self, nodes, **attr):
 
         """
         for n in nodes:
+            # keep all this inside try/except because
+            # CPython throws TypeError on n not in self.succ,
+            # while pre-2.7.5 ironpython throws on self.succ[n] 
             try:
-                newnode=n not in self.node
+                if n not in self.node:
+                    self.adj[n] = self.adjlist_dict_factory()
+                    self.node[n] = attr.copy()
+                else:
+                    self.node[n].update(attr)
             except TypeError:
                 nn,ndict = n
                 if nn not in self.node:
@@ -520,12 +527,6 @@ def add_nodes_from(self, nodes, **attr):
                     olddict = self.node[nn]
                     olddict.update(attr)
                     olddict.update(ndict)
-                continue
-            if newnode:
-                self.adj[n] = self.adjlist_dict_factory()
-                self.node[n] = attr.copy()
-            else:
-                self.node[n].update(attr)
 
     def remove_node(self,n):
         """Remove node n.


Test patch: