diff options
author | Dan Schult <dschult@colgate.edu> | 2021-06-15 14:08:46 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-06-15 14:08:46 -0400 |
commit | 1ea2c797b230641ac4e673a822a5688f4b2c47d4 (patch) | |
tree | 3cd2ea6ea23c6f2526ce43bf329e815dbea3427f | |
parent | b442619bd60651574b733e06af79d8da17c93254 (diff) | |
download | networkx-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.rst | 5 | ||||
-rw-r--r-- | networkx/algorithms/tests/test_lowest_common_ancestors.py | 17 | ||||
-rw-r--r-- | networkx/classes/digraph.py | 12 | ||||
-rw-r--r-- | networkx/classes/graph.py | 14 | ||||
-rw-r--r-- | networkx/classes/multidigraph.py | 4 | ||||
-rw-r--r-- | networkx/classes/multigraph.py | 4 | ||||
-rw-r--r-- | networkx/classes/tests/test_function.py | 10 | ||||
-rw-r--r-- | networkx/classes/tests/test_graph.py | 11 |
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) |