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
9 changes: 6 additions & 3 deletions sr/robot3/robot.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from pathlib import Path
from socket import socket
from types import MappingProxyType
from typing import Mapping, Optional
from typing import Mapping, Optional, final

from . import game_specific, timeout
from ._version import __version__
Expand All @@ -23,12 +23,15 @@
from .raw_serial import RawSerial
from .servo_board import ServoBoard
from .simulator.time_server import TimeServer
from .utils import IN_SIMULATOR, ensure_atexit_on_term, obtain_lock, singular
from .utils import (
IN_SIMULATOR, FinalMeta, ensure_atexit_on_term, obtain_lock, singular,
)

logger = logging.getLogger(__name__)


class Robot:
@final
class Robot(metaclass=FinalMeta):
"""
The main robot class that provides access to all the boards.

Expand Down
14 changes: 13 additions & 1 deletion sr/robot3/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import socket
from abc import ABC, abstractmethod
from types import FrameType
from typing import Any, Mapping, NamedTuple, Optional, TypeVar
from typing import Any, Mapping, NamedTuple, Optional, Type, TypeVar

from serial.tools.list_ports import comports
from serial.tools.list_ports_common import ListPortInfo
Expand Down Expand Up @@ -77,6 +77,18 @@ def identify(self) -> BoardIdentity:
pass # pragma: no cover


class FinalMeta(type):
"""
A meta class which ensures a given class cannot be subclassed.

It's recommended to additionally use typing.final.
"""
def __new__(cls, name: str, bases: tuple, classdict: dict) -> Type:
if bases:
raise TypeError(f"{name} cannot be sub-classed.")
return super().__new__(cls, name, bases, dict(classdict))


def map_to_int(
x: float,
in_min: float,
Expand Down
6 changes: 6 additions & 0 deletions tests/test_sr_robot.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,3 +139,9 @@ def test_robot_discovery() -> None:
assert robot.power_board == robot.boards[power_asset_tag]
assert robot.servo_board == robot.boards[servo_asset_tag]
assert robot.motor_board == robot.boards[motor_asset_tag]


def test_cannot_subclass() -> None:
with pytest.raises(TypeError, match="Robot cannot be sub-classed"):
class MyRobot(Robot):
pass
Loading