From 629ac675efa272abf75ccc795086fa69dc06dfc1 Mon Sep 17 00:00:00 2001 From: Frank Sinapi Date: Wed, 6 Dec 2023 14:57:34 -0500 Subject: [PATCH] Modifed `get_robot_type` to be parameterized on some singleton class types for robot models --- api/src/opentrons/hardware_control/api.py | 5 +--- api/src/opentrons/hardware_control/ot3api.py | 5 +--- .../hardware_control/protocols/__init__.py | 28 +++++++++++++++---- .../protocols/identifiable.py | 7 +++-- .../hardware_control/protocols/types.py | 14 +++++++++- .../resources/ot3_validation.py | 12 ++++---- .../opentrons/protocol_engine/conftest.py | 8 ++++-- .../resources/test_ot3_validation.py | 6 ++-- robot-server/tests/instruments/test_router.py | 6 ++-- 9 files changed, 58 insertions(+), 33 deletions(-) diff --git a/api/src/opentrons/hardware_control/api.py b/api/src/opentrons/hardware_control/api.py index 068a2248076..92971e43d27 100644 --- a/api/src/opentrons/hardware_control/api.py +++ b/api/src/opentrons/hardware_control/api.py @@ -28,7 +28,7 @@ pipette_load_name_conversions as pipette_load_name, ) from opentrons_shared_data.pipette.dev_types import PipetteName -from opentrons_shared_data.robot.dev_types import RobotType, RobotTypeEnum +from opentrons_shared_data.robot.dev_types import RobotType from opentrons import types as top_types from opentrons.config import robot_configs from opentrons.config.types import RobotConfig, OT3Config @@ -302,9 +302,6 @@ async def build_hardware_simulator( def __repr__(self) -> str: return "<{} using backend {}>".format(type(self), type(self._backend)) - def get_robot_type(self) -> RobotTypeEnum: - return RobotTypeEnum.OT2 - async def get_serial_number(self) -> Optional[str]: return await self._backend.get_serial_number() diff --git a/api/src/opentrons/hardware_control/ot3api.py b/api/src/opentrons/hardware_control/ot3api.py index 40a7967d6c5..b99cdadbb26 100644 --- a/api/src/opentrons/hardware_control/ot3api.py +++ b/api/src/opentrons/hardware_control/ot3api.py @@ -33,7 +33,7 @@ from opentrons_shared_data.pipette import ( pipette_load_name_conversions as pipette_load_name, ) -from opentrons_shared_data.robot.dev_types import RobotType, RobotTypeEnum +from opentrons_shared_data.robot.dev_types import RobotType from opentrons_shared_data.errors.exceptions import ( StallOrCollisionDetectedError, ) @@ -291,9 +291,6 @@ async def set_gantry_load(self, gantry_load: GantryLoad) -> None: ) await self._backend.update_to_default_current_settings(gantry_load) - def get_robot_type(self) -> RobotTypeEnum: - return RobotTypeEnum.FLEX - async def get_serial_number(self) -> Optional[str]: return await self._backend.get_serial_number() diff --git a/api/src/opentrons/hardware_control/protocols/__init__.py b/api/src/opentrons/hardware_control/protocols/__init__.py index f1532b608d1..4b85140f9ba 100644 --- a/api/src/opentrons/hardware_control/protocols/__init__.py +++ b/api/src/opentrons/hardware_control/protocols/__init__.py @@ -1,5 +1,5 @@ """Typing protocols describing a hardware controller.""" -from typing_extensions import Protocol +from typing_extensions import Protocol, Type from .module_provider import ModuleProvider from .hardware_manager import HardwareManager @@ -19,7 +19,13 @@ from .flex_calibratable import FlexCalibratable from .flex_instrument_configurer import FlexInstrumentConfigurer -from .types import CalibrationType, MountArgType, ConfigType +from .types import ( + CalibrationType, + MountArgType, + ConfigType, + OT2RobotType, + FlexRobotType, +) class HardwareControlInterface( @@ -31,7 +37,7 @@ class HardwareControlInterface( AsyncioConfigurable, Stoppable, Simulatable, - Identifiable, + Identifiable[Type[OT2RobotType]], Protocol[CalibrationType, MountArgType, ConfigType], ): """A mypy protocol for a hardware controller. @@ -48,14 +54,23 @@ class HardwareControlInterface( however, they can satisfy protocols. """ - ... + def get_robot_type(self) -> Type[OT2RobotType]: + return OT2RobotType class FlexHardwareControlInterface( - HardwareControlInterface[CalibrationType, MountArgType, ConfigType], + ModuleProvider, + ExecutionControllable, + LiquidHandler[CalibrationType, MountArgType, ConfigType], + ChassisAccessoryManager, + HardwareManager, + AsyncioConfigurable, + Stoppable, + Simulatable, GripperController, FlexCalibratable, FlexInstrumentConfigurer[MountArgType], + Identifiable[Type[FlexRobotType]], Protocol[CalibrationType, MountArgType, ConfigType], ): """A mypy protocol for a hardware controller with Flex-specific extensions. @@ -64,7 +79,8 @@ class FlexHardwareControlInterface( with some additional functionality and parameterization not supported on the OT-2. """ - ... + def get_robot_type(self) -> Type[FlexRobotType]: + return FlexRobotType __all__ = [ diff --git a/api/src/opentrons/hardware_control/protocols/identifiable.py b/api/src/opentrons/hardware_control/protocols/identifiable.py index 5e7f35cfe93..4e964f5633f 100644 --- a/api/src/opentrons/hardware_control/protocols/identifiable.py +++ b/api/src/opentrons/hardware_control/protocols/identifiable.py @@ -1,11 +1,12 @@ from typing_extensions import Protocol -from opentrons_shared_data.robot.dev_types import RobotTypeEnum +from .types import ProtocolRobotType -class Identifiable(Protocol): + +class Identifiable(Protocol[ProtocolRobotType]): """Protocol specifying support for hardware identification.""" - def get_robot_type(self) -> RobotTypeEnum: + def get_robot_type(self) -> ProtocolRobotType: """Return the enumerated robot type that this API controls. When a caller needs to determine whether an API function is expected diff --git a/api/src/opentrons/hardware_control/protocols/types.py b/api/src/opentrons/hardware_control/protocols/types.py index 3f7d088ec6c..bdd4a3799f4 100644 --- a/api/src/opentrons/hardware_control/protocols/types.py +++ b/api/src/opentrons/hardware_control/protocols/types.py @@ -1,11 +1,19 @@ """Types that are common across protocols.""" -from typing import TypeVar, Union +from typing import TypeVar, Union, Type from opentrons.hardware_control.types import OT3Mount from opentrons.types import Mount from opentrons.config.types import RobotConfig, OT3Config +class OT2RobotType: + pass + + +class FlexRobotType: + pass + + CalibrationType = TypeVar("CalibrationType") MountArgType = TypeVar( @@ -13,3 +21,7 @@ ) ConfigType = TypeVar("ConfigType", RobotConfig, OT3Config) + +ProtocolRobotType = TypeVar( + "ProtocolRobotType", Type[FlexRobotType], Type[OT2RobotType], covariant=True +) diff --git a/api/src/opentrons/protocol_engine/resources/ot3_validation.py b/api/src/opentrons/protocol_engine/resources/ot3_validation.py index 0f6b99cd19d..870594fc641 100644 --- a/api/src/opentrons/protocol_engine/resources/ot3_validation.py +++ b/api/src/opentrons/protocol_engine/resources/ot3_validation.py @@ -3,7 +3,7 @@ from typing import Optional, TYPE_CHECKING from opentrons.protocol_engine.errors import HardwareNotSupportedError -from opentrons_shared_data.robot.dev_types import RobotTypeEnum +from opentrons.hardware_control.protocols.types import FlexRobotType if TYPE_CHECKING: from opentrons.hardware_control import HardwareControlAPI, OT3HardwareControlAPI @@ -14,9 +14,9 @@ def ensure_ot3_hardware( error_msg: Optional[str] = None, ) -> OT3HardwareControlAPI: """Validate that the HardwareControlAPI is of OT-3 instance.""" - if hardware_api.get_robot_type() != RobotTypeEnum.FLEX: - raise HardwareNotSupportedError( - error_msg or "This command is supported by OT-3 only." - ) + if hardware_api.get_robot_type() == FlexRobotType: + return hardware_api # type: ignore - return hardware_api # type: ignore + raise HardwareNotSupportedError( + error_msg or "This command is supported by OT-3 only." + ) diff --git a/api/tests/opentrons/protocol_engine/conftest.py b/api/tests/opentrons/protocol_engine/conftest.py index d4b6ccbff61..d703a964078 100644 --- a/api/tests/opentrons/protocol_engine/conftest.py +++ b/api/tests/opentrons/protocol_engine/conftest.py @@ -20,7 +20,7 @@ from opentrons.hardware_control import HardwareControlAPI, OT2HardwareControlAPI from opentrons.hardware_control.api import API -from opentrons_shared_data.robot.dev_types import RobotTypeEnum +from opentrons.hardware_control.protocols.types import FlexRobotType, OT2RobotType if TYPE_CHECKING: from opentrons.hardware_control.ot3api import OT3API @@ -35,7 +35,9 @@ def hardware_api(decoy: Decoy) -> HardwareControlAPI: @pytest.fixture def ot2_hardware_api(decoy: Decoy) -> API: """Get a mocked out OT-2 hardware API.""" - return decoy.mock(cls=API) + mock = decoy.mock(cls=API) + decoy.when(mock.get_robot_type()).then_return(OT2RobotType) + return mock @pytest.mark.ot3_only @@ -46,7 +48,7 @@ def ot3_hardware_api(decoy: Decoy) -> OT3API: from opentrons.hardware_control.ot3api import OT3API mock = decoy.mock(cls=OT3API) - decoy.when(mock.get_robot_type()).then_return(RobotTypeEnum.FLEX) + decoy.when(mock.get_robot_type()).then_return(FlexRobotType) return mock except ImportError: # TODO (tz, 9-23-22) Figure out a better way to use this fixture with OT-3 api only. diff --git a/api/tests/opentrons/protocol_engine/resources/test_ot3_validation.py b/api/tests/opentrons/protocol_engine/resources/test_ot3_validation.py index 0c575f96c25..327daf82129 100644 --- a/api/tests/opentrons/protocol_engine/resources/test_ot3_validation.py +++ b/api/tests/opentrons/protocol_engine/resources/test_ot3_validation.py @@ -6,7 +6,7 @@ from opentrons.protocol_engine.errors.exceptions import HardwareNotSupportedError from opentrons.hardware_control.api import API -from opentrons_shared_data.robot.dev_types import RobotTypeEnum +from opentrons.hardware_control.protocols.types import FlexRobotType, OT2RobotType @pytest.mark.ot3_only @@ -17,7 +17,7 @@ def test_ensure_ot3_hardware(decoy: Decoy) -> None: from opentrons.hardware_control.ot3api import OT3API ot_3_hardware_api = decoy.mock(cls=OT3API) - decoy.when(ot_3_hardware_api.get_robot_type()).then_return(RobotTypeEnum.FLEX) + decoy.when(ot_3_hardware_api.get_robot_type()).then_return(FlexRobotType) result = ensure_ot3_hardware( ot_3_hardware_api, ) @@ -30,7 +30,7 @@ def test_ensure_ot3_hardware(decoy: Decoy) -> None: def test_ensure_ot3_hardware_raises_error(decoy: Decoy) -> None: """Should raise a HardwareNotSupportedError exception.""" ot_2_hardware_api = decoy.mock(cls=API) - decoy.when(ot_2_hardware_api.get_robot_type()).then_return(RobotTypeEnum.OT2) + decoy.when(ot_2_hardware_api.get_robot_type()).then_return(OT2RobotType) with pytest.raises(HardwareNotSupportedError): ensure_ot3_hardware( ot_2_hardware_api, diff --git a/robot-server/tests/instruments/test_router.py b/robot-server/tests/instruments/test_router.py index e629a545a49..b67f24a14cd 100644 --- a/robot-server/tests/instruments/test_router.py +++ b/robot-server/tests/instruments/test_router.py @@ -26,7 +26,7 @@ GripperModel, ) from opentrons_shared_data.pipette.dev_types import PipetteName, PipetteModel -from opentrons_shared_data.robot.dev_types import RobotTypeEnum +from opentrons.hardware_control.protocols.types import FlexRobotType, OT2RobotType from robot_server.instruments.instrument_models import ( Gripper, @@ -52,7 +52,7 @@ def ot2_hardware_api(decoy: Decoy) -> HardwareControlAPI: """Get a mock hardware control API.""" mock = decoy.mock(cls=API) - decoy.when(mock.get_robot_type()).then_return(RobotTypeEnum.OT2) + decoy.when(mock.get_robot_type()).then_return(OT2RobotType) return mock @@ -81,7 +81,7 @@ def ot3_hardware_api(decoy: Decoy) -> HardwareControlAPI: from opentrons.hardware_control.ot3api import OT3API mock = decoy.mock(cls=OT3API) - decoy.when(mock.get_robot_type()).then_return(RobotTypeEnum.FLEX) + decoy.when(mock.get_robot_type()).then_return(FlexRobotType) return mock except ImportError: return None # type: ignore[return-value]