Repository: networkx/networkx

Issue:
Issue with networkx.has_eulerian_path()
This issue adds to #3976 

There appears to be a problem with the `has_eulerian_path` function. It returns the wrong answer on this example:
```
test_graph = nx.DiGraph()
test_graph.add_edges_from([(0, 1), (1,2), (0,2)])
print(nx.has_eulerian_path(test_graph))
print(list(nx.eulerian_path(test_graph)))
```
Output:
```
True
[(0, 0), (0, 1), (1, 2)]
```

This directed graph does not have a Eulerian path, and, adding to #3976, the path returned by `eulerian_path` is not an actual Eulerian path, since it (1) does not include the edge `(0,2)` and (2) includes a self-loop `(0,0)` that is not in the graph.

My hunch is that the problem is in this line: https://github.com/networkx/networkx/blob/3351206a3ce5b3a39bb2fc451e93ef545b96c95b/networkx/algorithms/euler.py#L256

I think changing that `2` to be `semibalanced_ins+semibalanced_outs` may do the trick, since the definition is
>"...every _other_ vertex has equal in\_degree and out\_degree..." 

Going to see about fixing and adding this test. Appreciate any clues, ideas or tips.



Discussion:
This does seem to be giving an incorrect result for the directed graph you mention. The thing that jumps out to me is that the description for directed graphs in the docstring does not seem to match the implementation for directed graphs.

Resolving patch:
diff --git a/networkx/algorithms/euler.py b/networkx/algorithms/euler.py
--- a/networkx/algorithms/euler.py
+++ b/networkx/algorithms/euler.py
@@ -224,7 +224,7 @@ def has_eulerian_path(G):
         - at most one vertex has in_degree - out_degree = 1,
         - every other vertex has equal in_degree and out_degree,
         - and all of its vertices with nonzero degree belong to a
-        - single connected component of the underlying undirected graph.
+          single connected component of the underlying undirected graph.
 
     An undirected graph has an Eulerian path iff:
         - exactly zero or two vertices have odd degree,
@@ -246,16 +246,28 @@ def has_eulerian_path(G):
     eulerian_path
     """
     if G.is_directed():
+        # Remove isolated nodes (if any) without altering the input graph
+        nodes_remove = [v for v in G if G.in_degree[v] == 0 and G.out_degree[v] == 0]
+        if nodes_remove:
+            G = G.copy()
+            G.remove_nodes_from(nodes_remove)
+
         ins = G.in_degree
         outs = G.out_degree
-        semibalanced_ins = sum(ins(v) - outs(v) == 1 for v in G)
-        semibalanced_outs = sum(outs(v) - ins(v) == 1 for v in G)
+        unbalanced_ins = 0
+        unbalanced_outs = 0
+        for v in G:
+            if ins[v] - outs[v] == 1:
+                unbalanced_ins += 1
+            elif outs[v] - ins[v] == 1:
+                unbalanced_outs += 1
+            elif ins[v] != outs[v]:
+                return False
+
         return (
-            semibalanced_ins <= 1
-            and semibalanced_outs <= 1
-            and sum(G.in_degree(v) != G.out_degree(v) for v in G) <= 2
-            and nx.is_weakly_connected(G)
+            unbalanced_ins <= 1 and unbalanced_outs <= 1 and nx.is_weakly_connected(G)
         )
+
     else:
         return sum(d % 2 == 1 for v, d in G.degree()) in (0, 2) and nx.is_connected(G)
 


Test patch:
diff --git a/networkx/algorithms/tests/test_euler.py b/networkx/algorithms/tests/test_euler.py
--- a/networkx/algorithms/tests/test_euler.py
+++ b/networkx/algorithms/tests/test_euler.py
@@ -134,6 +134,22 @@ def test_has_eulerian_path_non_cyclic(self):
         G = nx.path_graph(6, create_using=nx.DiGraph)
         assert nx.has_eulerian_path(G)
 
+    def test_has_eulerian_path_directed_graph(self):
+        # Test directed graphs and returns False
+        G = nx.DiGraph()
+        G.add_edges_from([(0, 1), (1, 2), (0, 2)])
+        assert not nx.has_eulerian_path(G)
+
+    def test_has_eulerian_path_isolated_node(self):
+        # Test directed graphs without isolated node returns True
+        G = nx.DiGraph()
+        G.add_edges_from([(0, 1), (1, 2), (2, 0)])
+        assert nx.has_eulerian_path(G)
+
+        # Test directed graphs with isolated node returns True
+        G.add_node(3)
+        assert nx.has_eulerian_path(G)
+
 
 class TestFindPathStart:
     def testfind_path_start(self):