summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Schult <dschult@colgate.edu>2021-06-15 14:08:46 -0400
committerGitHub <noreply@github.com>2021-06-15 14:08:46 -0400
commit1ea2c797b230641ac4e673a822a5688f4b2c47d4 (patch)
tree3cd2ea6ea23c6f2526ce43bf329e815dbea3427f
parentb442619bd60651574b733e06af79d8da17c93254 (diff)
downloadnetworkx-1ea2c797b230641ac4e673a822a5688f4b2c47d4.tar.gz
Raise ValueError if None is added as a node. (#4892)
* Raise ValueError if None is added as a node. Removed some tests that checked that errors raised when None was a node. * update tutorial to make a stronger statement about None
-rw-r--r--doc/tutorial.rst5
-rw-r--r--networkx/algorithms/tests/test_lowest_common_ancestors.py17
-rw-r--r--networkx/classes/digraph.py12
-rw-r--r--networkx/classes/graph.py14
-rw-r--r--networkx/classes/multidigraph.py4
-rw-r--r--networkx/classes/multigraph.py4
-rw-r--r--networkx/classes/tests/test_function.py10
-rw-r--r--networkx/classes/tests/test_graph.py11
8 files changed, 55 insertions, 22 deletions
diff --git a/doc/tutorial.rst b/doc/tutorial.rst
index 4e28dafa..5f8ada5c 100644
--- a/doc/tutorial.rst
+++ b/doc/tutorial.rst
@@ -20,8 +20,9 @@ identified pairs of nodes (called edges, links, etc). In NetworkX, nodes can
be any :py:term:`hashable` object e.g., a text string, an image, an XML object,
another Graph, a customized node object, etc.
-.. note:: Python's ``None`` object should not be used as a node as it determines
- whether optional function arguments have been assigned in many functions.
+.. note:: Python's ``None`` object is not allowed to be used as a node. It
+ determines whether optional function arguments have been assigned in many
+ functions. And it can be used as a sentinel object meaning "not a node".
Nodes
-----
diff --git a/networkx/algorithms/tests/test_lowest_common_ancestors.py b/networkx/algorithms/tests/test_lowest_common_ancestors.py
index fb09e1d6..4724f383 100644
--- a/networkx/algorithms/tests/test_lowest_common_ancestors.py
+++ b/networkx/algorithms/tests/test_lowest_common_ancestors.py
@@ -123,14 +123,9 @@ class TestTreeLCA:
"""Test that pairs not in the graph raises error."""
lca = tree_all_pairs_lca(self.DG, 0, [(-1, -1)])
pytest.raises(nx.NodeNotFound, list, lca)
-
- def test_tree_all_pairs_lowest_common_ancestor11(self):
- """Test that None as a node in the graph raises an error."""
- G = nx.DiGraph([(None, 3)])
- pytest.raises(nx.NetworkXError, list, tree_all_pairs_lca(G))
- pytest.raises(
- nx.NodeNotFound, list, tree_all_pairs_lca(self.DG, pairs=G.edges())
- )
+ # check if node is None
+ lca = tree_all_pairs_lca(self.DG, None, [(-1, -1)])
+ pytest.raises(nx.NodeNotFound, list, lca)
def test_tree_all_pairs_lowest_common_ancestor12(self):
"""Test that tree routine bails on DAGs."""
@@ -292,12 +287,6 @@ class TestDAGLCA:
ans = list(all_pairs_lca(G))
assert ans == [((3, 3), 3)]
- def test_all_pairs_lowest_common_ancestor10(self):
- """Test that it bails on None as a node."""
- G = nx.DiGraph([(None, 3)])
- pytest.raises(nx.NetworkXError, all_pairs_lca, G)
- pytest.raises(nx.NodeNotFound, all_pairs_lca, self.DG, pairs=G.edges())
-
def test_lowest_common_ancestor1(self):
"""Test that the one-pair function works on default."""
G = nx.DiGraph([(0, 1), (2, 1)])
diff --git a/networkx/classes/digraph.py b/networkx/classes/digraph.py
index 14da0b3f..30b6a393 100644
--- a/networkx/classes/digraph.py
+++ b/networkx/classes/digraph.py
@@ -416,6 +416,8 @@ class DiGraph(Graph):
doesn't change on mutables.
"""
if node_for_adding not in self._succ:
+ if node_for_adding is None:
+ raise ValueError("None cannot be a node")
self._succ[node_for_adding] = self.adjlist_inner_dict_factory()
self._pred[node_for_adding] = self.adjlist_inner_dict_factory()
attr_dict = self._node[node_for_adding] = self.node_attr_dict_factory()
@@ -477,6 +479,8 @@ class DiGraph(Graph):
newdict = attr.copy()
newdict.update(ndict)
if newnode:
+ if n is None:
+ raise ValueError("None cannot be a node")
self._succ[n] = self.adjlist_inner_dict_factory()
self._pred[n] = self.adjlist_inner_dict_factory()
self._node[n] = self.node_attr_dict_factory()
@@ -614,10 +618,14 @@ class DiGraph(Graph):
u, v = u_of_edge, v_of_edge
# add nodes
if u not in self._succ:
+ if u is None:
+ raise ValueError("None cannot be a node")
self._succ[u] = self.adjlist_inner_dict_factory()
self._pred[u] = self.adjlist_inner_dict_factory()
self._node[u] = self.node_attr_dict_factory()
if v not in self._succ:
+ if v is None:
+ raise ValueError("None cannot be a node")
self._succ[v] = self.adjlist_inner_dict_factory()
self._pred[v] = self.adjlist_inner_dict_factory()
self._node[v] = self.node_attr_dict_factory()
@@ -675,10 +683,14 @@ class DiGraph(Graph):
else:
raise NetworkXError(f"Edge tuple {e} must be a 2-tuple or 3-tuple.")
if u not in self._succ:
+ if u is None:
+ raise ValueError("None cannot be a node")
self._succ[u] = self.adjlist_inner_dict_factory()
self._pred[u] = self.adjlist_inner_dict_factory()
self._node[u] = self.node_attr_dict_factory()
if v not in self._succ:
+ if v is None:
+ raise ValueError("None cannot be a node")
self._succ[v] = self.adjlist_inner_dict_factory()
self._pred[v] = self.adjlist_inner_dict_factory()
self._node[v] = self.node_attr_dict_factory()
diff --git a/networkx/classes/graph.py b/networkx/classes/graph.py
index 6b0fb748..26586779 100644
--- a/networkx/classes/graph.py
+++ b/networkx/classes/graph.py
@@ -28,7 +28,7 @@ class Graph:
(parallel) edges are not.
Nodes can be arbitrary (hashable) Python objects with optional
- key/value attributes. By convention `None` is not used as a node.
+ key/value attributes, except that `None` is not allowed as a node.
Edges are represented as links between nodes with optional
key/value attributes.
@@ -515,6 +515,8 @@ class Graph:
doesn't change on mutables.
"""
if node_for_adding not in self._node:
+ if node_for_adding is None:
+ raise ValueError("None cannot be a node")
self._adj[node_for_adding] = self.adjlist_inner_dict_factory()
attr_dict = self._node[node_for_adding] = self.node_attr_dict_factory()
attr_dict.update(attr)
@@ -575,6 +577,8 @@ class Graph:
newdict = attr.copy()
newdict.update(ndict)
if newnode:
+ if n is None:
+ raise ValueError("None cannot be a node")
self._adj[n] = self.adjlist_inner_dict_factory()
self._node[n] = self.node_attr_dict_factory()
self._node[n].update(newdict)
@@ -873,9 +877,13 @@ class Graph:
u, v = u_of_edge, v_of_edge
# add nodes
if u not in self._node:
+ if u is None:
+ raise ValueError("None cannot be a node")
self._adj[u] = self.adjlist_inner_dict_factory()
self._node[u] = self.node_attr_dict_factory()
if v not in self._node:
+ if v is None:
+ raise ValueError("None cannot be a node")
self._adj[v] = self.adjlist_inner_dict_factory()
self._node[v] = self.node_attr_dict_factory()
# add the edge
@@ -932,9 +940,13 @@ class Graph:
else:
raise NetworkXError(f"Edge tuple {e} must be a 2-tuple or 3-tuple.")
if u not in self._node:
+ if u is None:
+ raise ValueError("None cannot be a node")
self._adj[u] = self.adjlist_inner_dict_factory()
self._node[u] = self.node_attr_dict_factory()
if v not in self._node:
+ if v is None:
+ raise ValueError("None cannot be a node")
self._adj[v] = self.adjlist_inner_dict_factory()
self._node[v] = self.node_attr_dict_factory()
datadict = self._adj[u].get(v, self.edge_attr_dict_factory())
diff --git a/networkx/classes/multidigraph.py b/networkx/classes/multidigraph.py
index 51e590fe..6da147f9 100644
--- a/networkx/classes/multidigraph.py
+++ b/networkx/classes/multidigraph.py
@@ -464,10 +464,14 @@ class MultiDiGraph(MultiGraph, DiGraph):
u, v = u_for_edge, v_for_edge
# add nodes
if u not in self._succ:
+ if u is None:
+ raise ValueError("None cannot be a node")
self._succ[u] = self.adjlist_inner_dict_factory()
self._pred[u] = self.adjlist_inner_dict_factory()
self._node[u] = self.node_attr_dict_factory()
if v not in self._succ:
+ if v is None:
+ raise ValueError("None cannot be a node")
self._succ[v] = self.adjlist_inner_dict_factory()
self._pred[v] = self.adjlist_inner_dict_factory()
self._node[v] = self.node_attr_dict_factory()
diff --git a/networkx/classes/multigraph.py b/networkx/classes/multigraph.py
index b2e69561..90c50620 100644
--- a/networkx/classes/multigraph.py
+++ b/networkx/classes/multigraph.py
@@ -469,9 +469,13 @@ class MultiGraph(Graph):
u, v = u_for_edge, v_for_edge
# add nodes
if u not in self._adj:
+ if u is None:
+ raise ValueError("None cannot be a node")
self._adj[u] = self.adjlist_inner_dict_factory()
self._node[u] = self.node_attr_dict_factory()
if v not in self._adj:
+ if v is None:
+ raise ValueError("None cannot be a node")
self._adj[v] = self.adjlist_inner_dict_factory()
self._node[v] = self.node_attr_dict_factory()
if key is None:
diff --git a/networkx/classes/tests/test_function.py b/networkx/classes/tests/test_function.py
index 5c3fbe95..0eeb6c20 100644
--- a/networkx/classes/tests/test_function.py
+++ b/networkx/classes/tests/test_function.py
@@ -107,16 +107,16 @@ class TestFunction:
)
G = self.G.copy()
- nlist = [None]
+ nlist = ["node"]
nx.add_path(G, nlist)
assert edges_equal(G.edges(nlist), [])
- assert nodes_equal(G, list(self.G) + [None])
+ assert nodes_equal(G, list(self.G) + ["node"])
G = self.G.copy()
- nlist = iter([None])
+ nlist = iter(["node"])
nx.add_path(G, nlist)
- assert edges_equal(G.edges([None]), [])
- assert nodes_equal(G, list(self.G) + [None])
+ assert edges_equal(G.edges(["node"]), [])
+ assert nodes_equal(G, list(self.G) + ["node"])
G = self.G.copy()
nlist = [12]
diff --git a/networkx/classes/tests/test_graph.py b/networkx/classes/tests/test_graph.py
index 86744403..288a7efb 100644
--- a/networkx/classes/tests/test_graph.py
+++ b/networkx/classes/tests/test_graph.py
@@ -29,6 +29,17 @@ class BaseGraphTester:
assert sorted(G.nodes()) == self.k3nodes
assert sorted(G.nodes(data=True)) == [(0, {}), (1, {}), (2, {})]
+ def test_none_node(self):
+ G = self.Graph()
+ with pytest.raises(ValueError):
+ G.add_node(None)
+ with pytest.raises(ValueError):
+ G.add_nodes_from([None])
+ with pytest.raises(ValueError):
+ G.add_edge(0, None)
+ with pytest.raises(ValueError):
+ G.add_edges_from([(0, None)])
+
def test_has_node(self):
G = self.K3
assert G.has_node(1)