Repository: networkx/networkx

Issue:
Documentation of networkx.traversal module
The functions in <code>networkx.traversal</code> in 1.8 have some counter intuitive behavior, e.g. no return value of <code>dfs_edges</code>, and inconsistent use of terms, e.g. "return" and "produce" (What means "produce"? Is the return value backup up by the graph's node/edge list?) could be used to distungish between functions with return values and those without, at least there shouldn't be a mix.



Discussion:
Some of those functions, like `dfs_edges` return Python generators.  This is often the most efficient way to iterate over the edges.  f you want to expand the result to a list you can call `list(dfs_edges)`.

The type of object returned is documented in the "Returns" section of the function.

Produce is maybe not the best choice of verb for what this does.  We could alternatively simply use "generate" since the function returns a "generator".

OK... from this the following issues arise:
- <code>dfs_edges</code> return a list of edge tuples (source x target) -> Do I have to construct a DiGraph to get a node list for the path? -> please add some use cases, explain, give examples (it is again not intuitive that the return value is a list of tuple, is a DFS/BFS one expects a node list -> that doesn't hurt, but I strongly recommend to document)
- <code>dfs_tree</code> returns a graph -> How is this graph related to doing a DFS (Do I get the path from <code>DiGraph.node_iter()</code>, <code>DiGraph.nodes()</code>, <code>networkx.magic_function(G)</code>?)

Thanks for your hints. You don't have to answer the questions here (they're rather rhetorical and should serve as orientation to improve the docs) :)

This is about [`algorithms/traversal/depth_first_search.py`](https://github.com/networkx/networkx/blob/master/networkx/algorithms/traversal/depth_first_search.py). I has changed a little since Jun 2014, but not the docs.

I think it's easy to expand on `dfs_edges` by showing how a graph could be rebuild using the edges or by showing how one could get nodes from the result.

However I don't understand the proposal for `dfs_tree`: Why should the function work on a list of nodes (as is the case for `DiGraph.nodes()` or `DiGraph.node_iter()`)?

The docs look ok to me.  And there are examples.  Do they make sense to
you?  If so perhaps we can close this.

On Wednesday, March 2, 2016, Michael E. Rose notifications@github.com
wrote:

> This is about algorithms/traversal/depth_first_search.py
> https://github.com/networkx/networkx/blob/master/networkx/algorithms/traversal/depth_first_search.py.
> I has changed a little since Jun 2014, but not the docs.
> 
> I think it's easy to expand on dfs_edges by showing how a graph could be
> rebuild using the edges or by showing how one could get nodes from the
> result.
> 
> However I don't understand the proposal for dfs_tree: Why should the
> function work on a list of nodes (as is the case for DiGraph.nodes() or
> DiGraph.node_iter())?
> 
> —
> Reply to this email directly or view it on GitHub
> https://github.com/networkx/networkx/issues/1195#issuecomment-191454823.

The documentation has been approved apparently. I didn't check for completeness of docs.


Resolving patch:
diff --git a/networkx/algorithms/traversal/breadth_first_search.py b/networkx/algorithms/traversal/breadth_first_search.py
--- a/networkx/algorithms/traversal/breadth_first_search.py
+++ b/networkx/algorithms/traversal/breadth_first_search.py
@@ -21,7 +21,7 @@
 
 
 def generic_bfs_edges(G, source, neighbors=None):
-    """Iterates over edges in a breadth-first search.
+    """Iterate over edges in a breadth-first search.
 
     The breadth-first search begins at `source` and enqueues the
     neighbors of newly visited nodes specified by the `neighbors`
@@ -78,7 +78,7 @@ def generic_bfs_edges(G, source, neighbors=None):
 
 
 def bfs_edges(G, source, reverse=False):
-    """Produce edges in a breadth-first-search starting at source.
+    """Iterate over edges in a breadth-first-search starting at source.
 
     Parameters
     ----------
diff --git a/networkx/algorithms/traversal/depth_first_search.py b/networkx/algorithms/traversal/depth_first_search.py
--- a/networkx/algorithms/traversal/depth_first_search.py
+++ b/networkx/algorithms/traversal/depth_first_search.py
@@ -20,11 +20,12 @@
 
 __all__ = ['dfs_edges', 'dfs_tree',
            'dfs_predecessors', 'dfs_successors',
-           'dfs_preorder_nodes','dfs_postorder_nodes',
+           'dfs_preorder_nodes', 'dfs_postorder_nodes',
            'dfs_labeled_edges']
 
+
 def dfs_edges(G, source=None):
-    """Produce edges in a depth-first-search (DFS).
+    """Iterate over edges in a depth-first-search (DFS).
 
     Parameters
     ----------
@@ -42,7 +43,7 @@ def dfs_edges(G, source=None):
     Examples
     --------
     >>> G = nx.path_graph(3)
-    >>> print(list(nx.dfs_edges(G,0)))
+    >>> print(list(nx.dfs_edges(G, 0)))
     [(0, 1), (1, 2)]
 
     Notes
@@ -52,30 +53,37 @@ def dfs_edges(G, source=None):
 
     If a source is not specified then a source is chosen arbitrarily and
     repeatedly until all components in the graph are searched.
+
+    See Also
+    --------
+    dfs_preorder_nodes
+    dfs_postorder_nodes
+    dfs_labeled_edges
     """
     if source is None:
-        # produce edges for all components
+        # edges for all components
         nodes = G
     else:
-        # produce edges for components with source
+        # edges for components with source
         nodes = [source]
-    visited=set()
+    visited = set()
     for start in nodes:
         if start in visited:
             continue
         visited.add(start)
-        stack = [(start,iter(G[start]))]
+        stack = [(start, iter(G[start]))]
         while stack:
-            parent,children = stack[-1]
+            parent, children = stack[-1]
             try:
                 child = next(children)
                 if child not in visited:
-                    yield parent,child
+                    yield parent, child
                     visited.add(child)
-                    stack.append((child,iter(G[child])))
+                    stack.append((child, iter(G[child])))
             except StopIteration:
                 stack.pop()
 
+
 def dfs_tree(G, source=None):
     """Return oriented tree constructed from a depth-first-search from source.
 
@@ -94,7 +102,7 @@ def dfs_tree(G, source=None):
     Examples
     --------
     >>> G = nx.path_graph(3)
-    >>> T = nx.dfs_tree(G,0)
+    >>> T = nx.dfs_tree(G, 0)
     >>> print(list(T.edges()))
     [(0, 1), (1, 2)]
     """
@@ -103,9 +111,10 @@ def dfs_tree(G, source=None):
         T.add_nodes_from(G)
     else:
         T.add_node(source)
-    T.add_edges_from(dfs_edges(G,source))
+    T.add_edges_from(dfs_edges(G, source))
     return T
 
+
 def dfs_predecessors(G, source=None):
     """Return dictionary of predecessors in depth-first-search from source.
 
@@ -125,7 +134,7 @@ def dfs_predecessors(G, source=None):
     Examples
     --------
     >>> G = nx.path_graph(3)
-    >>> print(nx.dfs_predecessors(G,0))
+    >>> print(nx.dfs_predecessors(G, 0))
     {1: 0, 2: 1}
 
     Notes
@@ -136,7 +145,7 @@ def dfs_predecessors(G, source=None):
     If a source is not specified then a source is chosen arbitrarily and
     repeatedly until all components in the graph are searched.
     """
-    return dict((t,s) for s,t in dfs_edges(G,source=source))
+    return dict((t, s) for s, t in dfs_edges(G, source=source))
 
 
 def dfs_successors(G, source=None):
@@ -158,7 +167,7 @@ def dfs_successors(G, source=None):
     Examples
     --------
     >>> G = nx.path_graph(3)
-    >>> print(nx.dfs_successors(G,0))
+    >>> print(nx.dfs_successors(G, 0))
     {0: [1], 1: [2]}
 
     Notes
@@ -170,13 +179,13 @@ def dfs_successors(G, source=None):
     repeatedly until all components in the graph are searched.
     """
     d = defaultdict(list)
-    for s,t in dfs_edges(G,source=source):
+    for s, t in dfs_edges(G, source=source):
         d[s].append(t)
     return dict(d)
 
 
-def dfs_postorder_nodes(G,source=None):
-    """Produce nodes in a depth-first-search post-ordering starting
+def dfs_postorder_nodes(G, source=None):
+    """Iterate over nodes in a depth-first-search post-ordering starting
     from source.
 
     Parameters
@@ -195,7 +204,7 @@ def dfs_postorder_nodes(G,source=None):
     Examples
     --------
     >>> G = nx.path_graph(3)
-    >>> print(list(nx.dfs_postorder_nodes(G,0)))
+    >>> print(list(nx.dfs_postorder_nodes(G, 0)))
     [2, 1, 0]
 
     Notes
@@ -205,16 +214,21 @@ def dfs_postorder_nodes(G,source=None):
 
     If a source is not specified then a source is chosen arbitrarily and
     repeatedly until all components in the graph are searched.
+    See Also
+    --------
+    dfs_edges
+    dfs_preorder_nodes
+    dfs_labeled_edges
     """
     post = (v for u, v, d in nx.dfs_labeled_edges(G, source=source)
             if d == 'reverse')
     # potential modification: chain source to end of post-ordering
-    # return chain(post,[source])
+    # return chain(post, [source])
     return post
 
 
 def dfs_preorder_nodes(G, source=None):
-    """Produce nodes in a depth-first-search pre-ordering starting
+    """Iterate over nodes in a depth-first-search pre-ordering starting
     from source.
 
     Parameters
@@ -233,7 +247,7 @@ def dfs_preorder_nodes(G, source=None):
     Examples
     --------
     >>> G = nx.path_graph(3)
-    >>> print(list(nx.dfs_preorder_nodes(G,0)))
+    >>> print(list(nx.dfs_preorder_nodes(G, 0)))
     [0, 1, 2]
 
     Notes
@@ -243,16 +257,22 @@ def dfs_preorder_nodes(G, source=None):
 
     If a source is not specified then a source is chosen arbitrarily and
     repeatedly until all components in the graph are searched.
+
+    See Also
+    --------
+    dfs_edges
+    dfs_postorder_nodes
+    dfs_labeled_edges
     """
     pre = (v for u, v, d in nx.dfs_labeled_edges(G, source=source)
            if d == 'forward')
     # potential modification: chain source to beginning of pre-ordering
-    # return chain([source],pre)
+    # return chain([source], pre)
     return pre
 
 
 def dfs_labeled_edges(G, source=None):
-    """Produce edges in a depth-first-search (DFS) labeled by type.
+    """Iterate over edges in a depth-first-search (DFS) labeled by type.
 
     Parameters
     ----------
@@ -300,14 +320,19 @@ def dfs_labeled_edges(G, source=None):
     If a source is not specified then a source is chosen arbitrarily and
     repeatedly until all components in the graph are searched.
 
+    See Also
+    --------
+    dfs_edges
+    dfs_preorder_nodes
+    dfs_postorder_nodes
     """
     # Based on http://www.ics.uci.edu/~eppstein/PADS/DFS.py
     # by D. Eppstein, July 2004.
     if source is None:
-        # produce edges for all components
+        # edges for all components
         nodes = G
     else:
-        # produce edges for components with source
+        # edges for components with source
         nodes = [source]
     visited = set()
     for start in nodes:
@@ -315,9 +340,9 @@ def dfs_labeled_edges(G, source=None):
             continue
         yield start, start, 'forward'
         visited.add(start)
-        stack = [(start,iter(G[start]))]
+        stack = [(start, iter(G[start]))]
         while stack:
-            parent,children = stack[-1]
+            parent, children = stack[-1]
             try:
                 child = next(children)
                 if child in visited:
@@ -325,7 +350,7 @@ def dfs_labeled_edges(G, source=None):
                 else:
                     yield parent, child, 'forward'
                     visited.add(child)
-                    stack.append((child,iter(G[child])))
+                    stack.append((child, iter(G[child])))
             except StopIteration:
                 stack.pop()
                 if stack:


Test patch: