diff --git a/src/pgraph/PGraph.py b/src/pgraph/PGraph.py index cfc7255d..d44b815a 100644 --- a/src/pgraph/PGraph.py +++ b/src/pgraph/PGraph.py @@ -25,6 +25,7 @@ class BaseGraph(ABC): def __init__( self, + *, metric: Callable[[NDArray], float] | str | None = None, heuristic: Callable[[NDArray], float] | str | None = None, verbose: bool = False, @@ -60,6 +61,14 @@ def __init__( This is the common base class of :class:`UGraph` and :class:`DGraph` and should not be instantiated directly. + All arguments are keyword-only, so ``UGraph(dim=6)`` is correct and + ``UGraph(6)`` raises :exc:`TypeError`. + + .. versionchanged:: 1.0 + Arguments are keyword-only. In 0.6.5 the first positional + argument was accepted and ignored, so ``UGraph(6)`` silently did + nothing. + ``vertex_class`` and ``edge_class`` let user subclasses be built by the graph, so they never need to be constructed by hand:: diff --git a/tests/test_constructor_keywords.py b/tests/test_constructor_keywords.py new file mode 100644 index 00000000..c8d042b3 --- /dev/null +++ b/tests/test_constructor_keywords.py @@ -0,0 +1,40 @@ +"""Graph constructor arguments are keyword-only. + +In 0.6.5 the first positional argument was accepted and ignored, so +``UGraph(6)`` looked as though it set a dimension but did nothing. +""" + +import unittest + +from pgraph import DGraph, UGraph + + +class _Keywords: + cls: type + + def test_positional_rejected(self): + for arg in (6, "L2", None, True): + with self.subTest(arg=arg): + with self.assertRaises(TypeError): + self.cls(arg) + + def test_keywords_accepted(self): + g = self.cls(dim=6, metric="L1", heuristic="L2", verbose=False) + self.assertEqual(g.n, 0) + with self.assertRaises(ValueError): + g.add_vertex([1, 2]) + + def test_no_arguments(self): + self.assertEqual(self.cls().n, 0) + + +class TestUGraphKeywords(_Keywords, unittest.TestCase): + cls = UGraph + + +class TestDGraphKeywords(_Keywords, unittest.TestCase): + cls = DGraph + + +if __name__ == "__main__": + unittest.main()