From d03b828e4ad354f5b52a14b73e56d465728ef48d Mon Sep 17 00:00:00 2001 From: Peter Corke Date: Sun, 11 Oct 2026 11:11:03 +1000 Subject: [PATCH 1/2] test: graph[x] resolves int/name/vertex and raises otherwise Co-Authored-By: Claude Sonnet 5.5 --- tests/test_getitem.py | 68 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 tests/test_getitem.py diff --git a/tests/test_getitem.py b/tests/test_getitem.py new file mode 100644 index 00000000..5c0ad8ac --- /dev/null +++ b/tests/test_getitem.py @@ -0,0 +1,68 @@ +""" +``graph[x]`` resolves an index, a name or a vertex, and says so clearly when it +cannot -- it must never quietly hand back ``None``. +""" + +import unittest + +import numpy as np + +from pgraph import DGraph, UGraph + + +class _GetItem: + Graph: type + + def setUp(self): + self.g = self.Graph() + self.a = self.g.add_vertex([0, 0], name="a") + self.b = self.g.add_vertex([1, 0], name="b") + self.c = self.g.add_vertex([2, 0], name="c") + + def test_int(self): + self.assertIs(self.g[0], self.a) + self.assertIs(self.g[2], self.c) + self.assertIs(self.g[-1], self.c) + + def test_numpy_integer(self): + # e.g. the result of np.argmin(), a very common way to get an index + self.assertIs(self.g[np.int64(1)], self.b) + self.assertIs(self.g[np.argmin([3.0, 1.0, 2.0])], self.b) + + def test_name(self): + self.assertIs(self.g["b"], self.b) + + def test_vertex(self): + self.assertIs(self.g[self.c], self.c) + + def test_unknown_name(self): + with self.assertRaises(KeyError): + self.g["zz"] + + def test_index_out_of_range(self): + with self.assertRaises(IndexError): + self.g[3] + with self.assertRaises(IndexError): + self.g[-4] + + def test_wrong_type(self): + for bad in (1.5, None, [0], (0,), slice(0, 2), 1 + 2j): + with self.subTest(bad=bad): + with self.assertRaises(TypeError): + self.g[bad] + + def test_membership_and_iteration_unaffected(self): + self.assertIn("a", self.g) + self.assertEqual([v.name for v in self.g], ["a", "b", "c"]) + + +class TestUGraphGetItem(_GetItem, unittest.TestCase): + Graph = UGraph + + +class TestDGraphGetItem(_GetItem, unittest.TestCase): + Graph = DGraph + + +if __name__ == "__main__": + unittest.main() From 26937cffcf62f30496b0982d036f8e74268eb1a9 Mon Sep 17 00:00:00 2001 From: Peter Corke Date: Sun, 11 Oct 2026 11:11:12 +1000 Subject: [PATCH 2/2] fix: graph[x] raised nothing for NumPy integers or other types __getitem__ fell off the end and returned None for anything that was not a Python int, str or vertex -- including np.int64, so g[np.argmin(d)] quietly gave None. NumPy integers are now accepted, and any other type raises TypeError. Exceptions are documented. Co-Authored-By: Claude Sonnet 5.5 --- src/pgraph/PGraph.py | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/pgraph/PGraph.py b/src/pgraph/PGraph.py index f92d00de..6d8e630b 100644 --- a/src/pgraph/PGraph.py +++ b/src/pgraph/PGraph.py @@ -852,16 +852,21 @@ def __getitem__(self, i: int | str | BaseVertex) -> BaseVertex: Get vertex (base class method) :param i: vertex description - :type i: int or str + :type i: int, str or BaseVertex subclass + :raises IndexError: ``i`` is an integer outside the range of vertices + :raises KeyError: ``i`` is a name that is not in the graph + :raises TypeError: ``i`` is none of the types above :return: the referenced vertex :rtype: BaseVertex subclass Retrieve a vertex by index or name: - -``g[i]`` is the i'th vertex in the graph. This reflects the order of - addition to the graph. - -``g[s]`` is vertex named ``s`` - -``g[v]`` is ``v`` where ``v`` is a ``BaseVertex`` subclass + - ``g[i]`` is the i'th vertex in the graph. This reflects the order of + addition to the graph. ``i`` can be any integer, including a NumPy + integer such as the result of ``np.argmin()``, and a negative + ``i`` counts from the last vertex. + - ``g[s]`` is vertex named ``s`` + - ``g[v]`` is ``v`` where ``v`` is a ``BaseVertex`` subclass This method also supports iteration over the vertices in a graph:: @@ -870,12 +875,16 @@ def __getitem__(self, i: int | str | BaseVertex) -> BaseVertex: will iterate over all the vertices. """ - if isinstance(i, int): - return self._vertexlist[i] + if isinstance(i, (int, np.integer)): + return self._vertexlist[int(i)] elif isinstance(i, str): return self._vertexdict[i] elif isinstance(i, BaseVertex): return i + else: + raise TypeError( + f"vertex must be an int, str or BaseVertex, got {type(i).__name__}" + ) def __iter__(self) -> Iterator[BaseVertex]: """