diff --git a/docs/source/arm_backend.rst b/docs/source/arm_backend.rst index 88c6c0f6e..8c32a5184 100644 --- a/docs/source/arm_backend.rst +++ b/docs/source/arm_backend.rst @@ -72,7 +72,7 @@ The backend holds a *scene*: the robots and other objects that it has been asked The contract ------------ -This is written down as the :py:class:`~roboticstoolbox.backends.Connector.Connector` protocol. A backend is *any* object that has these methods and flags, and does not have to inherit from it: Swift does not, since it does not depend on the Toolbox. The PyPlot backends inherit from it, which supplies the default values of the flags and makes a missing method an error when the backend is made. The arguments differ from one backend to another, so the methods are declared with ``*args, **kwargs``, and the details are in each backend. +This is written down as the :py:class:`~roboticstoolbox.backends.Connector.Connector` abstract class. A backend inherits from it, which supplies the default values of the flags and makes a missing method an error when the backend is made. The arguments differ from one backend to another, so the methods are declared with ``*args, **kwargs``, and the details are in each backend. .. autosummary:: :nosignatures: diff --git a/src/roboticstoolbox/backends/Connector.py b/src/roboticstoolbox/backends/Connector.py index f6e810e62..1f457760d 100644 --- a/src/roboticstoolbox/backends/Connector.py +++ b/src/roboticstoolbox/backends/Connector.py @@ -5,20 +5,18 @@ from __future__ import annotations -from abc import abstractmethod -from typing import Any, Protocol, runtime_checkable +from abc import ABC, abstractmethod +from typing import Any -@runtime_checkable -class Connector(Protocol): +class Connector(ABC): """ What RTB needs of a graphical backend - A backend is any object that has these methods and flags: it does not have to - inherit from this class. Swift does not, since it does not depend on RTB. - The PyPlot backends inherit from it explicitly, which supplies the default - values of the flags and of :meth:`_add_teach_panel`, and makes a missing - method an error at construction. + A backend inherits from this class, which supplies the default values of the + flags and of :meth:`_add_teach_panel`, and makes a missing method an error + when the backend is made. A backend from outside the Toolbox inherits from + it too. The methods take different arguments in different backends, so they are declared with ``*args, **kwargs``; see the backend for the details. diff --git a/src/roboticstoolbox/backends/swift/__init__.py b/src/roboticstoolbox/backends/swift/__init__.py index 4017ba22e..0a39fc738 100644 --- a/src/roboticstoolbox/backends/swift/__init__.py +++ b/src/roboticstoolbox/backends/swift/__init__.py @@ -15,14 +15,18 @@ ) from swift.Swift import Swift as _SwiftBase +from roboticstoolbox.backends.Connector import Connector -class Swift(_SwiftBase): + +class Swift(_SwiftBase, Connector): """Swift backend with RTB capability flags.""" supports_teach: bool = True supports_ellipse: bool = False - def _add_teach_panel(self, robot: Any, q: Any, handle: Any, block: Any) -> Any: + def _add_teach_panel( + self, robot: Any, q: Any, handle: Any = None, block: bool = True + ) -> Any: """ Add a joint-slider teach panel plus a live end-effector pose readout, and wire the sliders to drive ``handle`` (the diff --git a/tests/test_pyplot_backend_fixes.py b/tests/test_pyplot_backend_fixes.py index 6724f5d90..c2be3b4d1 100644 --- a/tests/test_pyplot_backend_fixes.py +++ b/tests/test_pyplot_backend_fixes.py @@ -82,31 +82,16 @@ def test_animation_without_a_window_manager(self): self.assertIn("Robotics Toolbox", title.call_args.args[1]) -class TestConnectorProtocol(unittest.TestCase): +class TestConnector(unittest.TestCase): def test_backends_are_connectors(self): self.assertIsInstance(PyPlot(), Connector) self.assertIsInstance(PyPlot2(), Connector) - def test_a_class_need_not_inherit(self): - # Swift does not depend on RTB, so it cannot inherit from Connector + def test_a_class_that_does_not_inherit_is_not_a_connector(self): class Standalone: - supports_teach = False - supports_ellipse = False - def launch(self, *a, **k): ... - def step(self, *a, **k): ... - def reset(self, *a, **k): ... - def restart(self, *a, **k): ... - def close(self, *a, **k): ... - def add(self, *a, **k): ... - def remove(self, *a, **k): ... - def hold(self, *a, **k): ... - def _add_teach_panel(self, *a, **k): ... - - self.assertIsInstance(Standalone(), Connector) - def test_an_object_without_the_methods_is_not(self): - self.assertNotIsInstance(object(), Connector) + self.assertNotIsInstance(Standalone(), Connector) def test_inheriting_gets_the_defaults(self): class Minimal(Connector): @@ -132,23 +117,13 @@ def launch(self, *a, **k): ... with self.assertRaises(TypeError): Incomplete() - def test_swift_satisfies_the_protocol(self): + def test_swift_is_a_connector(self): swift = __import__("importlib").util.find_spec("swift") if swift is None: self.skipTest("swift is not installed") from roboticstoolbox.backends.swift import Swift - for name in ( - "launch", - "step", - "reset", - "restart", - "close", - "add", - "remove", - "hold", - ): - self.assertTrue(callable(getattr(Swift, name, None)), name) + self.assertTrue(issubclass(Swift, Connector)) class TestShapePlot(unittest.TestCase):