Skip to content
Open
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
2 changes: 1 addition & 1 deletion docs/source/arm_backend.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
16 changes: 7 additions & 9 deletions src/roboticstoolbox/backends/Connector.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
8 changes: 6 additions & 2 deletions src/roboticstoolbox/backends/swift/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
35 changes: 5 additions & 30 deletions tests/test_pyplot_backend_fixes.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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):
Expand Down
Loading