Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 16 additions & 7 deletions src/pgraph/PGraph.py
Original file line number Diff line number Diff line change
Expand Up @@ -915,16 +915,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::

Expand All @@ -933,12 +938,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]:
"""
Expand Down
68 changes: 68 additions & 0 deletions tests/test_getitem.py
Original file line number Diff line number Diff line change
@@ -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()
Loading