Repository: networkx/networkx

Issue:
Add a node when add_path involves no edge.
`add_path` is convenient for adding edges without adding nodes explicitly.
Consider a case where there exists no edge to be added:

```
G = nx.Graph()
nx.add_path(G, [0])
```
If one wants to test the neighbors of a node in a graph, `G.neighbors(0)` will throw networkx.exception.NetworkXError: The node 0 is not in the graph.

Obviously, one can check whether the argument for `add_path` is a singleton and explicitly do

```
path = [0]  # or something else
G = nx.Graph()
if len(path) == 1:
    G.add_node(path[0]) 

nx.add_path(G, path)
G.neighbors(0)
```

Should adding a zero-edge path with a single node add a node in the graph if the node isn’t already in the graph?
This also applies to `add_star`.
Add a node when add_path involves no edge.
`add_path` is convenient for adding edges without adding nodes explicitly.
Consider a case where there exists no edge to be added:

```
G = nx.Graph()
nx.add_path(G, [0])
```
If one wants to test the neighbors of a node in a graph, `G.neighbors(0)` will throw networkx.exception.NetworkXError: The node 0 is not in the graph.

Obviously, one can check whether the argument for `add_path` is a singleton and explicitly do

```
path = [0]  # or something else
G = nx.Graph()
if len(path) == 1:
    G.add_node(path[0]) 

nx.add_path(G, path)
G.neighbors(0)
```

Should adding a zero-edge path with a single node add a node in the graph if the node isn’t already in the graph?
This also applies to `add_star`.


Discussion:
I think I have to agree that a path of length zero (and a star with no leaves) should be included.  Would you like to create a PR with this fix?
I think I have to agree that a path of length zero (and a star with no leaves) should be included.  Would you like to create a PR with this fix?

Resolving patch:
diff --git a/networkx/classes/function.py b/networkx/classes/function.py
--- a/networkx/classes/function.py
+++ b/networkx/classes/function.py
@@ -258,7 +258,13 @@ def add_path(G, nodes, **attr):
     >>> nx.add_path(G, [0, 1, 2, 3])
     >>> nx.add_path(G, [10, 11, 12], weight=7)
     """
-    G.add_edges_from(pairwise(nodes), **attr)
+    nlist = iter(nodes)
+    try:
+        first_node = next(nlist)
+    except StopIteration:
+        return
+    G.add_node(first_node)
+    G.add_edges_from(pairwise(chain((first_node,), nlist)), **attr)
 
 
 def add_cycle(G, nodes, **attr):


Test patch:
diff --git a/networkx/classes/tests/test_function.py b/networkx/classes/tests/test_function.py
--- a/networkx/classes/tests/test_function.py
+++ b/networkx/classes/tests/test_function.py
@@ -81,6 +81,42 @@ def test_add_path(self):
                             (13, 14, {'weight': 2.}),
                             (14, 15, {'weight': 2.})])
 
+        G = self.G.copy()
+        nlist = [None]
+        nx.add_path(G, nlist)
+        assert_edges_equal(G.edges(nlist), [])
+        assert_nodes_equal(G, list(self.G) + [None])
+
+        G = self.G.copy()
+        nlist = iter([None])
+        nx.add_path(G, nlist)
+        assert_edges_equal(G.edges([None]), [])
+        assert_nodes_equal(G, list(self.G) + [None])
+
+        G = self.G.copy()
+        nlist = [12]
+        nx.add_path(G, nlist)
+        assert_edges_equal(G.edges(nlist), [])
+        assert_nodes_equal(G, list(self.G) + [12])
+
+        G = self.G.copy()
+        nlist = iter([12])
+        nx.add_path(G, nlist)
+        assert_edges_equal(G.edges([12]), [])
+        assert_nodes_equal(G, list(self.G) + [12])
+
+        G = self.G.copy()
+        nlist = []
+        nx.add_path(G, nlist)
+        assert_edges_equal(G.edges, self.G.edges)
+        assert_nodes_equal(G, list(self.G))
+
+        G = self.G.copy()
+        nlist = iter([])
+        nx.add_path(G, nlist)
+        assert_edges_equal(G.edges, self.G.edges)
+        assert_nodes_equal(G, list(self.G))
+
     def test_add_cycle(self):
         G = self.G.copy()
         nlist = [12, 13, 14, 15]