diff --git a/api/src/opentrons/protocol_runner/create_simulating_runner.py b/api/src/opentrons/protocol_runner/create_simulating_runner.py index c6854662c06..392afa512f7 100644 --- a/api/src/opentrons/protocol_runner/create_simulating_runner.py +++ b/api/src/opentrons/protocol_runner/create_simulating_runner.py @@ -12,7 +12,7 @@ from opentrons_shared_data.robot.dev_types import RobotType -from .legacy_wrappers import LegacySimulatingContextCreator +from .python_protocol_wrappers import SimulatingContextCreator from .protocol_runner import create_protocol_runner, AbstractRunner @@ -62,7 +62,7 @@ async def create_simulating_runner( load_fixed_trash=should_load_fixed_trash(protocol_config), ) - simulating_legacy_context_creator = LegacySimulatingContextCreator( + simulating_context_creator = SimulatingContextCreator( hardware_api=simulating_hardware_api, protocol_engine=protocol_engine, ) @@ -71,7 +71,7 @@ async def create_simulating_runner( protocol_config=protocol_config, protocol_engine=protocol_engine, hardware_api=simulating_hardware_api, - legacy_context_creator=simulating_legacy_context_creator, + protocol_context_creator=simulating_context_creator, ) diff --git a/api/src/opentrons/protocol_runner/legacy_command_mapper.py b/api/src/opentrons/protocol_runner/legacy_command_mapper.py index 9243f50f70d..ababa892616 100644 --- a/api/src/opentrons/protocol_runner/legacy_command_mapper.py +++ b/api/src/opentrons/protocol_runner/legacy_command_mapper.py @@ -4,9 +4,24 @@ from datetime import datetime from typing import Dict, List, Optional, Tuple, Union +from opentrons.hardware_control.modules.types import ( + ModuleModel as HardwareModuleModel, + TemperatureModuleModel, + MagneticModuleModel, + ThermocyclerModuleModel, + HeaterShakerModuleModel, +) from opentrons_shared_data.pipette.dev_types import PipetteNameType from opentrons.types import MountType, DeckSlotName, Location from opentrons.legacy_commands import types as legacy_command_types +from opentrons.protocol_api import InstrumentContext +from opentrons.protocol_api.core.legacy.deck import FIXED_TRASH_ID +from opentrons.protocol_api.core.legacy.load_info import ( + LoadInfo as LegacyLoadInfo, + LabwareLoadInfo as LegacyLabwareLoadInfo, + InstrumentLoadInfo as LegacyInstrumentLoadInfo, + ModuleLoadInfo as LegacyModuleLoadInfo, +) from opentrons.protocol_engine import ( ProtocolEngineError, actions as pe_actions, @@ -19,22 +34,9 @@ ModuleDataProvider, pipette_data_provider, ) + from opentrons_shared_data.labware.labware_definition import LabwareDefinition from opentrons_shared_data.errors import ErrorCodes, EnumeratedError, PythonException -from opentrons.protocol_api.core.legacy.deck import FIXED_TRASH_ID - -from .legacy_wrappers import ( - LegacyLoadInfo, - LegacyInstrumentLoadInfo, - LegacyLabwareLoadInfo, - LegacyModuleLoadInfo, - LegacyPipetteContext, - LegacyModuleModel, - LegacyMagneticModuleModel, - LegacyTemperatureModuleModel, - LegacyThermocyclerModuleModel, - LegacyHeaterShakerModuleModel, -) class LegacyCommandParams(pe_commands.CustomParams): @@ -63,14 +65,14 @@ def __init__(self, wrapping_exc: BaseException) -> None: ) -_LEGACY_TO_PE_MODULE: Dict[LegacyModuleModel, pe_types.ModuleModel] = { - LegacyMagneticModuleModel.MAGNETIC_V1: pe_types.ModuleModel.MAGNETIC_MODULE_V1, - LegacyMagneticModuleModel.MAGNETIC_V2: pe_types.ModuleModel.MAGNETIC_MODULE_V2, - LegacyTemperatureModuleModel.TEMPERATURE_V1: pe_types.ModuleModel.TEMPERATURE_MODULE_V1, - LegacyTemperatureModuleModel.TEMPERATURE_V2: pe_types.ModuleModel.TEMPERATURE_MODULE_V2, - LegacyThermocyclerModuleModel.THERMOCYCLER_V1: pe_types.ModuleModel.THERMOCYCLER_MODULE_V1, - LegacyThermocyclerModuleModel.THERMOCYCLER_V2: pe_types.ModuleModel.THERMOCYCLER_MODULE_V2, - LegacyHeaterShakerModuleModel.HEATER_SHAKER_V1: pe_types.ModuleModel.HEATER_SHAKER_MODULE_V1, +_HARDWARE_TO_PE_MODULE: Dict[HardwareModuleModel, pe_types.ModuleModel] = { + MagneticModuleModel.MAGNETIC_V1: pe_types.ModuleModel.MAGNETIC_MODULE_V1, + MagneticModuleModel.MAGNETIC_V2: pe_types.ModuleModel.MAGNETIC_MODULE_V2, + TemperatureModuleModel.TEMPERATURE_V1: pe_types.ModuleModel.TEMPERATURE_MODULE_V1, + TemperatureModuleModel.TEMPERATURE_V2: pe_types.ModuleModel.TEMPERATURE_MODULE_V2, + ThermocyclerModuleModel.THERMOCYCLER_V1: pe_types.ModuleModel.THERMOCYCLER_MODULE_V1, + ThermocyclerModuleModel.THERMOCYCLER_V2: pe_types.ModuleModel.THERMOCYCLER_MODULE_V2, + HeaterShakerModuleModel.HEATER_SHAKER_V1: pe_types.ModuleModel.HEATER_SHAKER_MODULE_V1, } _HIGHER_ORDER_COMMAND_TYPES = { @@ -354,7 +356,7 @@ def _build_drop_tip( command_id: str, now: datetime, ) -> Tuple[pe_commands.CommandCreate, pe_commands.Command]: - pipette: LegacyPipetteContext = command["payload"]["instrument"] + pipette: InstrumentContext = command["payload"]["instrument"] well = command["payload"]["location"] mount = MountType(pipette.mount) # the following type checking suppression assumes the tiprack is not loaded on top of a module @@ -387,7 +389,7 @@ def _build_pick_up_tip( command_id: str, now: datetime, ) -> Tuple[pe_commands.CommandCreate, pe_commands.Command]: - pipette: LegacyPipetteContext = command["payload"]["instrument"] + pipette: InstrumentContext = command["payload"]["instrument"] location = command["payload"]["location"] well = location mount = MountType(pipette.mount) @@ -422,7 +424,7 @@ def _build_liquid_handling( command_id: str, now: datetime, ) -> Tuple[pe_commands.CommandCreate, pe_commands.Command]: - pipette: LegacyPipetteContext = command["payload"]["instrument"] + pipette: InstrumentContext = command["payload"]["instrument"] location = command["payload"]["location"] volume = command["payload"]["volume"] # TODO:(jr, 15.08.2022): aspirate and dispense commands with no specified labware @@ -533,7 +535,7 @@ def _build_blow_out( command_id: str, now: datetime, ) -> Tuple[pe_commands.CommandCreate, pe_commands.Command]: - pipette: LegacyPipetteContext = command["payload"]["instrument"] + pipette: InstrumentContext = command["payload"]["instrument"] location = command["payload"]["location"] flow_rate = pipette.flow_rate.blow_out # TODO:(jr, 15.08.2022): blow_out commands with no specified labware get filtered @@ -725,8 +727,8 @@ def _map_module_load( count = self._command_count["LOAD_MODULE"] command_id = f"commands.LOAD_MODULE-{count}" module_id = f"module-{count}" - requested_model = _LEGACY_TO_PE_MODULE[module_load_info.requested_model] - loaded_model = _LEGACY_TO_PE_MODULE[module_load_info.loaded_model] + requested_model = _HARDWARE_TO_PE_MODULE[module_load_info.requested_model] + loaded_model = _HARDWARE_TO_PE_MODULE[module_load_info.loaded_model] # This will fetch a V2 definition only. PAPI < v2.3 use V1 definitions. # When running a < v2.3 protocol, there will be a mismatch of definitions used diff --git a/api/src/opentrons/protocol_runner/legacy_context_plugin.py b/api/src/opentrons/protocol_runner/legacy_context_plugin.py index 7dd882f0fb7..baf6ccbc716 100644 --- a/api/src/opentrons/protocol_runner/legacy_context_plugin.py +++ b/api/src/opentrons/protocol_runner/legacy_context_plugin.py @@ -7,10 +7,10 @@ from opentrons.legacy_commands.types import CommandMessage as LegacyCommand from opentrons.legacy_broker import LegacyBroker +from opentrons.protocol_api.core.legacy.load_info import LoadInfo from opentrons.protocol_engine import AbstractPlugin, actions as pe_actions from opentrons.util.broker import ReadOnlyBroker -from .legacy_wrappers import LegacyLoadInfo from .legacy_command_mapper import LegacyCommandMapper from .thread_async_queue import ThreadAsyncQueue @@ -37,7 +37,7 @@ class LegacyContextPlugin(AbstractPlugin): def __init__( self, broker: LegacyBroker, - equipment_broker: ReadOnlyBroker[LegacyLoadInfo], + equipment_broker: ReadOnlyBroker[LoadInfo], legacy_command_mapper: Optional[LegacyCommandMapper] = None, ) -> None: """Initialize the plugin with its dependencies.""" @@ -122,15 +122,15 @@ def handle_action(self, action: pe_actions.Action) -> None: pass def _handle_legacy_command(self, command: LegacyCommand) -> None: - """Handle a command reported by the APIv2 protocol. + """Handle a command reported by the legacy APIv2 protocol. Used as a broker callback, so this will run in the APIv2 protocol's thread. """ pe_actions = self._legacy_command_mapper.map_command(command=command) self._actions_to_dispatch.put(pe_actions) - def _handle_equipment_loaded(self, load_info: LegacyLoadInfo) -> None: - """Handle an equipment load reported by the APIv2 protocol. + def _handle_equipment_loaded(self, load_info: LoadInfo) -> None: + """Handle an equipment load reported by the legacy APIv2 protocol. Used as a broker callback, so this will run in the APIv2 protocol's thread. """ diff --git a/api/src/opentrons/protocol_runner/protocol_runner.py b/api/src/opentrons/protocol_runner/protocol_runner.py index 9c097bbba2d..5a8b9134772 100644 --- a/api/src/opentrons/protocol_runner/protocol_runner.py +++ b/api/src/opentrons/protocol_runner/protocol_runner.py @@ -10,6 +10,7 @@ from opentrons import protocol_reader from opentrons.legacy_broker import LegacyBroker from opentrons.protocol_api import ParameterContext +from opentrons.protocol_api.core.legacy.load_info import LoadInfo from opentrons.protocol_reader import ( ProtocolSource, JsonProtocolConfig, @@ -28,13 +29,12 @@ from .json_file_reader import JsonFileReader from .json_translator import JsonTranslator from .legacy_context_plugin import LegacyContextPlugin -from .legacy_wrappers import ( +from .python_protocol_wrappers import ( LEGACY_PYTHON_API_VERSION_CUTOFF, LEGACY_JSON_SCHEMA_VERSION_CUTOFF, - LegacyFileReader, - LegacyContextCreator, - LegacyExecutor, - LegacyLoadInfo, + PythonAndLegacyFileReader, + ProtocolContextCreator, + PythonProtocolExecutor, ) from ..protocol_engine.errors import ProtocolCommandFailedError from ..protocol_engine.types import ( @@ -136,21 +136,26 @@ def __init__( protocol_engine: ProtocolEngine, hardware_api: HardwareControlAPI, task_queue: Optional[TaskQueue] = None, - legacy_file_reader: Optional[LegacyFileReader] = None, - legacy_context_creator: Optional[LegacyContextCreator] = None, - legacy_executor: Optional[LegacyExecutor] = None, + python_and_legacy_file_reader: Optional[PythonAndLegacyFileReader] = None, + protocol_context_creator: Optional[ProtocolContextCreator] = None, + python_protocol_executor: Optional[PythonProtocolExecutor] = None, post_run_hardware_state: PostRunHardwareState = PostRunHardwareState.HOME_AND_STAY_ENGAGED, drop_tips_after_run: bool = True, ) -> None: """Initialize the PythonAndLegacyRunner with its dependencies.""" super().__init__(protocol_engine) self._hardware_api = hardware_api - self._legacy_file_reader = legacy_file_reader or LegacyFileReader() - self._legacy_context_creator = legacy_context_creator or LegacyContextCreator( - hardware_api=hardware_api, - protocol_engine=protocol_engine, + self._protocol_file_reader = ( + python_and_legacy_file_reader or PythonAndLegacyFileReader() ) - self._legacy_executor = legacy_executor or LegacyExecutor() + self._protocol_context_creator = ( + protocol_context_creator + or ProtocolContextCreator( + hardware_api=hardware_api, + protocol_engine=protocol_engine, + ) + ) + self._protocol_executor = python_protocol_executor or PythonProtocolExecutor() # TODO(mc, 2022-01-11): replace task queue with specific implementations # of runner interface self._task_queue = task_queue or TaskQueue() @@ -185,14 +190,14 @@ async def load( # fixme(mm, 2022-12-23): This does I/O and compute-bound parsing that will block # the event loop. Jira RSS-165. - protocol = self._legacy_file_reader.read( + protocol = self._protocol_file_reader.read( protocol_source, labware_definitions, python_parse_mode ) self._parameter_context = ParameterContext(api_version=protocol.api_level) equipment_broker = None if protocol.api_level < LEGACY_PYTHON_API_VERSION_CUTOFF: - equipment_broker = Broker[LegacyLoadInfo]() + equipment_broker = Broker[LoadInfo]() self._protocol_engine.add_plugin( LegacyContextPlugin( broker=self._broker, equipment_broker=equipment_broker @@ -202,7 +207,7 @@ async def load( else: self._hardware_api.should_taskify_movement_execution(taskify=False) - context = self._legacy_context_creator.create( + context = self._protocol_context_creator.create( protocol=protocol, broker=self._broker, equipment_broker=equipment_broker, @@ -216,7 +221,7 @@ async def run_func() -> None: await self._protocol_engine.add_and_execute_command( request=initial_home_command ) - await self._legacy_executor.execute( + await self._protocol_executor.execute( protocol=protocol, context=context, parameter_context=self._parameter_context, @@ -417,9 +422,9 @@ def create_protocol_runner( task_queue: Optional[TaskQueue] = None, json_file_reader: Optional[JsonFileReader] = None, json_translator: Optional[JsonTranslator] = None, - legacy_file_reader: Optional[LegacyFileReader] = None, - legacy_context_creator: Optional[LegacyContextCreator] = None, - legacy_executor: Optional[LegacyExecutor] = None, + python_and_legacy_file_reader: Optional[PythonAndLegacyFileReader] = None, + protocol_context_creator: Optional[ProtocolContextCreator] = None, + python_protocol_executor: Optional[PythonProtocolExecutor] = None, post_run_hardware_state: PostRunHardwareState = PostRunHardwareState.HOME_AND_STAY_ENGAGED, drop_tips_after_run: bool = True, ) -> AnyRunner: @@ -443,9 +448,9 @@ def create_protocol_runner( protocol_engine=protocol_engine, hardware_api=hardware_api, task_queue=task_queue, - legacy_file_reader=legacy_file_reader, - legacy_context_creator=legacy_context_creator, - legacy_executor=legacy_executor, + python_and_legacy_file_reader=python_and_legacy_file_reader, + protocol_context_creator=protocol_context_creator, + python_protocol_executor=python_protocol_executor, post_run_hardware_state=post_run_hardware_state, drop_tips_after_run=drop_tips_after_run, ) diff --git a/api/src/opentrons/protocol_runner/legacy_wrappers.py b/api/src/opentrons/protocol_runner/python_protocol_wrappers.py similarity index 63% rename from api/src/opentrons/protocol_runner/legacy_wrappers.py rename to api/src/opentrons/protocol_runner/python_protocol_wrappers.py index 9783c877227..e0a345db0f0 100644 --- a/api/src/opentrons/protocol_runner/legacy_wrappers.py +++ b/api/src/opentrons/protocol_runner/python_protocol_wrappers.py @@ -1,23 +1,16 @@ -"""Wrappers for the legacy, Protocol API v2 execution pipeline.""" +"""Wrappers for Protocol API v2 execution pipeline.""" import asyncio from typing import Dict, Iterable, Optional, cast from anyio import to_thread from opentrons_shared_data.labware.dev_types import ( - LabwareDefinition as LegacyLabwareDefinition, + LabwareDefinition as LabwareDefinitionTypedDict, ) from opentrons_shared_data.labware.labware_definition import LabwareDefinition from opentrons.calibration_storage.helpers import uri_from_details from opentrons.hardware_control import HardwareControlAPI -from opentrons.hardware_control.modules.types import ( - ModuleModel as LegacyModuleModel, - TemperatureModuleModel as LegacyTemperatureModuleModel, - MagneticModuleModel as LegacyMagneticModuleModel, - ThermocyclerModuleModel as LegacyThermocyclerModuleModel, - HeaterShakerModuleModel as LegacyHeaterShakerModuleModel, -) from opentrons.legacy_broker import LegacyBroker from opentrons.protocol_engine import ProtocolEngine from opentrons.protocol_engine.types import RunTimeParamValuesType @@ -25,32 +18,20 @@ from opentrons.util.broker import Broker from opentrons.protocol_api import ( - ProtocolContext as LegacyProtocolContext, - InstrumentContext as LegacyPipetteContext, - ModuleContext as LegacyModuleContext, - Labware as LegacyLabware, - Well as LegacyWell, + ProtocolContext, ParameterContext, create_protocol_context, ) from opentrons.protocol_api.core.engine import ENGINE_CORE_API_VERSION -from opentrons.protocol_api.core.legacy.load_info import ( - LoadInfo as LegacyLoadInfo, - InstrumentLoadInfo as LegacyInstrumentLoadInfo, - LabwareLoadInfo as LegacyLabwareLoadInfo, - ModuleLoadInfo as LegacyModuleLoadInfo, -) +from opentrons.protocol_api.core.legacy.load_info import LoadInfo from opentrons.protocols.parse import PythonParseMode, parse from opentrons.protocols.execution.execute import run_protocol -from opentrons.protocols.types import ( - Protocol as LegacyProtocol, - JsonProtocol as LegacyJsonProtocol, - PythonProtocol as LegacyPythonProtocol, -) +from opentrons.protocols.types import Protocol, PythonProtocol + # The earliest Python Protocol API version ("apiLevel") where the protocol's simulation -# and execution will be handled by Protocol Engine, rather than the legacy machinery. +# and execution will be handled by Protocol Engine, rather than the previous direct hardware calls from protocol api. # # Note that even when simulation and execution are handled by the legacy machinery, # Protocol Engine still has some involvement for analyzing the simulation and @@ -63,7 +44,7 @@ LEGACY_JSON_SCHEMA_VERSION_CUTOFF = 6 -class LegacyFileReader: +class PythonAndLegacyFileReader: """Interface to read Protocol API v2 protocols prior to execution.""" @staticmethod @@ -71,16 +52,16 @@ def read( protocol_source: ProtocolSource, labware_definitions: Iterable[LabwareDefinition], python_parse_mode: PythonParseMode, - ) -> LegacyProtocol: + ) -> Protocol: """Read a PAPIv2 protocol into a data structure.""" protocol_file_path = protocol_source.main_file protocol_contents = protocol_file_path.read_text(encoding="utf-8") - legacy_labware_definitions: Dict[str, LegacyLabwareDefinition] = { + extra_labware: Dict[str, LabwareDefinitionTypedDict] = { uri_from_details( namespace=lw.namespace, load_name=lw.parameters.loadName, version=lw.version, - ): cast(LegacyLabwareDefinition, lw.dict(exclude_none=True)) + ): cast(LabwareDefinitionTypedDict, lw.dict(exclude_none=True)) for lw in labware_definitions } data_file_paths = [ @@ -92,7 +73,7 @@ def read( return parse( protocol_file=protocol_contents, filename=protocol_file_path.name, - extra_labware=legacy_labware_definitions, + extra_labware=extra_labware, extra_data={ data_path.name: data_path.read_bytes() for data_path in data_file_paths }, @@ -100,9 +81,7 @@ def read( ) -# TODO (spp, 2023-04-05): Remove 'legacy' wording since this is the context we are using -# for all python protocols. -class LegacyContextCreator: +class ProtocolContextCreator: """Interface to construct Protocol API v2 contexts.""" _USE_SIMULATING_CORE = False @@ -125,21 +104,17 @@ def __init__( def create( self, - protocol: LegacyProtocol, + protocol: Protocol, broker: Optional[LegacyBroker], - equipment_broker: Optional[Broker[LegacyLoadInfo]], - ) -> LegacyProtocolContext: + equipment_broker: Optional[Broker[LoadInfo]], + ) -> ProtocolContext: """Create a Protocol API v2 context.""" extra_labware = ( - protocol.extra_labware - if isinstance(protocol, LegacyPythonProtocol) - else None + protocol.extra_labware if isinstance(protocol, PythonProtocol) else None ) bundled_data = ( - protocol.bundled_data - if isinstance(protocol, LegacyPythonProtocol) - else None + protocol.bundled_data if isinstance(protocol, PythonProtocol) else None ) return create_protocol_context( @@ -156,7 +131,7 @@ def create( ) -class LegacySimulatingContextCreator(LegacyContextCreator): +class SimulatingContextCreator(ProtocolContextCreator): """Interface to construct PAPIv2 contexts using simulating implementations. Avoids some calls to the hardware API for performance. @@ -166,13 +141,13 @@ class LegacySimulatingContextCreator(LegacyContextCreator): _USE_SIMULATING_CORE = True -class LegacyExecutor: +class PythonProtocolExecutor: """Interface to execute Protocol API v2 protocols in a child thread.""" @staticmethod async def execute( - protocol: LegacyProtocol, - context: LegacyProtocolContext, + protocol: Protocol, + context: ProtocolContext, parameter_context: Optional[ParameterContext], run_time_param_values: Optional[RunTimeParamValuesType], ) -> None: @@ -180,29 +155,3 @@ async def execute( await to_thread.run_sync( run_protocol, protocol, context, parameter_context, run_time_param_values ) - - -__all__ = [ - # Re-exports of user-facing Python Protocol APIv2 stuff: - # TODO(mc, 2022-08-22): remove, no longer "legacy", so re-exports unnecessary - "LegacyProtocolContext", - "LegacyLabware", - "LegacyWell", - "LegacyPipetteContext", - "LegacyModuleContext", - # Re-exports of internal stuff: - "LegacyProtocol", - "LegacyJsonProtocol", - "LegacyPythonProtocol", - "LegacyLoadInfo", - "LegacyLabwareLoadInfo", - "LegacyInstrumentLoadInfo", - "LegacyModuleLoadInfo", - "LegacyModuleModel", - "LegacyMagneticModuleModel", - "LegacyTemperatureModuleModel", - "LegacyThermocyclerModuleModel", - "LegacyHeaterShakerModuleModel", - # legacy typed dicts - "LegacyLabwareDefinition", -] diff --git a/api/tests/opentrons/protocol_runner/test_legacy_command_mapper.py b/api/tests/opentrons/protocol_runner/test_legacy_command_mapper.py index a0581001a82..ada52714ee6 100644 --- a/api/tests/opentrons/protocol_runner/test_legacy_command_mapper.py +++ b/api/tests/opentrons/protocol_runner/test_legacy_command_mapper.py @@ -7,7 +7,13 @@ from decoy import matchers, Decoy from opentrons.hardware_control.dev_types import PipetteDict +from opentrons.hardware_control.modules.types import TemperatureModuleModel from opentrons.legacy_commands.types import CommentMessage, PauseMessage, CommandMessage +from opentrons.protocol_api.core.legacy.load_info import ( + LabwareLoadInfo as LegacyLabwareLoadInfo, + InstrumentLoadInfo as LegacyInstrumentLoadInfo, + ModuleLoadInfo as LegacyModuleLoadInfo, +) from opentrons.protocol_engine import ( DeckSlotLocation, ModuleLocation, @@ -29,12 +35,6 @@ LegacyCommandMapper, LegacyCommandParams, ) -from opentrons.protocol_runner.legacy_wrappers import ( - LegacyInstrumentLoadInfo, - LegacyLabwareLoadInfo, - LegacyModuleLoadInfo, - LegacyTemperatureModuleModel, -) from opentrons_shared_data.labware.dev_types import LabwareDefinition from opentrons_shared_data.module.dev_types import ModuleDefinitionV3 from opentrons_shared_data.pipette.dev_types import PipetteNameType @@ -396,8 +396,8 @@ def test_map_module_load( """It should correctly map a module load.""" test_definition = ModuleDefinition.parse_obj(minimal_module_def) input = LegacyModuleLoadInfo( - requested_model=LegacyTemperatureModuleModel.TEMPERATURE_V1, - loaded_model=LegacyTemperatureModuleModel.TEMPERATURE_V2, + requested_model=TemperatureModuleModel.TEMPERATURE_V1, + loaded_model=TemperatureModuleModel.TEMPERATURE_V2, deck_slot=DeckSlotName.SLOT_1, configuration="conf", module_serial="module-serial", diff --git a/api/tests/opentrons/protocol_runner/test_legacy_context_plugin.py b/api/tests/opentrons/protocol_runner/test_legacy_context_plugin.py index f11676bcd37..368a34a297f 100644 --- a/api/tests/opentrons/protocol_runner/test_legacy_context_plugin.py +++ b/api/tests/opentrons/protocol_runner/test_legacy_context_plugin.py @@ -17,12 +17,10 @@ from opentrons.legacy_broker import LegacyBroker from opentrons.util.broker import ReadOnlyBroker +from opentrons.protocol_api.core.legacy.load_info import LoadInfo, LabwareLoadInfo + from opentrons.protocol_runner.legacy_command_mapper import LegacyCommandMapper from opentrons.protocol_runner.legacy_context_plugin import LegacyContextPlugin -from opentrons.protocol_runner.legacy_wrappers import ( - LegacyLoadInfo, - LegacyLabwareLoadInfo, -) from opentrons.types import DeckSlotName @@ -38,9 +36,9 @@ def mock_legacy_broker(decoy: Decoy) -> LegacyBroker: @pytest.fixture -def mock_equipment_broker(decoy: Decoy) -> ReadOnlyBroker[LegacyLoadInfo]: +def mock_equipment_broker(decoy: Decoy) -> ReadOnlyBroker[LoadInfo]: """Get a mocked out `equipment_broker: Broker` dependency.""" - return decoy.mock(cls=ReadOnlyBroker[LegacyLoadInfo]) + return decoy.mock(cls=ReadOnlyBroker[LoadInfo]) @pytest.fixture @@ -64,7 +62,7 @@ def mock_action_dispatcher(decoy: Decoy) -> pe_actions.ActionDispatcher: @pytest.fixture def subject( mock_legacy_broker: LegacyBroker, - mock_equipment_broker: ReadOnlyBroker[LegacyLoadInfo], + mock_equipment_broker: ReadOnlyBroker[LoadInfo], mock_legacy_command_mapper: LegacyCommandMapper, mock_state_view: StateView, mock_action_dispatcher: pe_actions.ActionDispatcher, @@ -92,7 +90,7 @@ def __exit__(self, type: object, value: object, traceback: object) -> None: async def test_broker_subscribe_unsubscribe( decoy: Decoy, mock_legacy_broker: LegacyBroker, - mock_equipment_broker: ReadOnlyBroker[LegacyLoadInfo], + mock_equipment_broker: ReadOnlyBroker[LoadInfo], subject: LegacyContextPlugin, ) -> None: """It should subscribe to the brokers on setup and unsubscribe on teardown.""" @@ -125,7 +123,7 @@ async def test_broker_subscribe_unsubscribe( async def test_command_broker_messages( decoy: Decoy, mock_legacy_broker: LegacyBroker, - mock_equipment_broker: ReadOnlyBroker[LegacyLoadInfo], + mock_equipment_broker: ReadOnlyBroker[LoadInfo], mock_legacy_command_mapper: LegacyCommandMapper, mock_action_dispatcher: pe_actions.ActionDispatcher, subject: LegacyContextPlugin, @@ -181,7 +179,7 @@ async def test_command_broker_messages( async def test_equipment_broker_messages( decoy: Decoy, mock_legacy_broker: LegacyBroker, - mock_equipment_broker: ReadOnlyBroker[LegacyLoadInfo], + mock_equipment_broker: ReadOnlyBroker[LoadInfo], mock_legacy_command_mapper: LegacyCommandMapper, mock_action_dispatcher: pe_actions.ActionDispatcher, subject: LegacyContextPlugin, @@ -199,9 +197,9 @@ async def test_equipment_broker_messages( subject.setup() - handler: Callable[[LegacyLabwareLoadInfo], None] = labware_handler_captor.value + handler: Callable[[LabwareLoadInfo], None] = labware_handler_captor.value - load_info = LegacyLabwareLoadInfo( + load_info = LabwareLoadInfo( labware_definition=minimal_labware_def, labware_namespace="some_namespace", labware_load_name="some_load_name", diff --git a/api/tests/opentrons/protocol_runner/test_protocol_runner.py b/api/tests/opentrons/protocol_runner/test_protocol_runner.py index 68e215bf3dd..6aa790bee98 100644 --- a/api/tests/opentrons/protocol_runner/test_protocol_runner.py +++ b/api/tests/opentrons/protocol_runner/test_protocol_runner.py @@ -8,15 +8,20 @@ from typing import List, cast, Optional, Union, Type from opentrons_shared_data.labware.labware_definition import LabwareDefinition +from opentrons_shared_data.labware.dev_types import ( + LabwareDefinition as LabwareDefinitionTypedDict, +) from opentrons_shared_data.protocol.models import ProtocolSchemaV6, ProtocolSchemaV7 from opentrons_shared_data.protocol.dev_types import ( JsonProtocol as LegacyJsonProtocolDict, ) from opentrons.hardware_control import API as HardwareAPI from opentrons.legacy_broker import LegacyBroker +from opentrons.protocol_api import ProtocolContext from opentrons.protocol_engine.types import PostRunHardwareState from opentrons.protocols.api_support.types import APIVersion from opentrons.protocols.parse import PythonParseMode +from opentrons.protocols.types import PythonProtocol, JsonProtocol from opentrons.util.broker import Broker from opentrons import protocol_reader @@ -42,14 +47,10 @@ from opentrons.protocol_runner.json_file_reader import JsonFileReader from opentrons.protocol_runner.json_translator import JsonTranslator from opentrons.protocol_runner.legacy_context_plugin import LegacyContextPlugin -from opentrons.protocol_runner.legacy_wrappers import ( - LegacyFileReader, - LegacyContextCreator, - LegacyExecutor, - LegacyPythonProtocol, - LegacyJsonProtocol, - LegacyProtocolContext, - LegacyLabwareDefinition, +from opentrons.protocol_runner.python_protocol_wrappers import ( + PythonAndLegacyFileReader, + ProtocolContextCreator, + PythonProtocolExecutor, ) @@ -84,21 +85,21 @@ def json_translator(decoy: Decoy) -> JsonTranslator: @pytest.fixture -def legacy_file_reader(decoy: Decoy) -> LegacyFileReader: - """Get a mocked out LegacyFileReader dependency.""" - return decoy.mock(cls=LegacyFileReader) +def python_and_legacy_file_reader(decoy: Decoy) -> PythonAndLegacyFileReader: + """Get a mocked out PythonAndLegacyFileReader dependency.""" + return decoy.mock(cls=PythonAndLegacyFileReader) @pytest.fixture -def legacy_context_creator(decoy: Decoy) -> LegacyContextCreator: - """Get a mocked out LegacyContextCreator dependency.""" - return decoy.mock(cls=LegacyContextCreator) +def protocol_context_creator(decoy: Decoy) -> ProtocolContextCreator: + """Get a mocked out ProtocolContextCreator dependency.""" + return decoy.mock(cls=ProtocolContextCreator) @pytest.fixture -def legacy_executor(decoy: Decoy) -> LegacyExecutor: - """Get a mocked out LegacyExecutor dependency.""" - return decoy.mock(cls=LegacyExecutor) +def python_protocol_executor(decoy: Decoy) -> PythonProtocolExecutor: + """Get a mocked out PythonProtocolExecutor dependency.""" + return decoy.mock(cls=PythonProtocolExecutor) @pytest.fixture(autouse=True) @@ -132,22 +133,22 @@ def json_runner_subject( @pytest.fixture -def legacy_python_runner_subject( +def python_runner_subject( protocol_engine: ProtocolEngine, hardware_api: HardwareAPI, task_queue: TaskQueue, - legacy_file_reader: LegacyFileReader, - legacy_context_creator: LegacyContextCreator, - legacy_executor: LegacyExecutor, + python_and_legacy_file_reader: PythonAndLegacyFileReader, + protocol_context_creator: ProtocolContextCreator, + python_protocol_executor: PythonProtocolExecutor, ) -> PythonAndLegacyRunner: """Get a PythonAndLegacyRunner test subject with mocked dependencies.""" return PythonAndLegacyRunner( protocol_engine=protocol_engine, hardware_api=hardware_api, task_queue=task_queue, - legacy_file_reader=legacy_file_reader, - legacy_context_creator=legacy_context_creator, - legacy_executor=legacy_executor, + python_and_legacy_file_reader=python_and_legacy_file_reader, + protocol_context_creator=protocol_context_creator, + python_protocol_executor=python_protocol_executor, ) @@ -182,9 +183,9 @@ def test_create_protocol_runner( task_queue: TaskQueue, json_file_reader: JsonFileReader, json_translator: JsonTranslator, - legacy_file_reader: LegacyFileReader, - legacy_context_creator: LegacyContextCreator, - legacy_executor: LegacyExecutor, + python_and_legacy_file_reader: PythonAndLegacyFileReader, + protocol_context_creator: ProtocolContextCreator, + python_protocol_executor: PythonProtocolExecutor, config: Optional[Union[JsonProtocolConfig, PythonProtocolConfig]], runner_type: Type[AnyRunner], ) -> None: @@ -206,7 +207,7 @@ def test_create_protocol_runner( "subject", [ (lazy_fixture("json_runner_subject")), - (lazy_fixture("legacy_python_runner_subject")), + (lazy_fixture("python_runner_subject")), (lazy_fixture("live_runner_subject")), ], ) @@ -226,7 +227,7 @@ def test_play_starts_run( "subject", [ (lazy_fixture("json_runner_subject")), - (lazy_fixture("legacy_python_runner_subject")), + (lazy_fixture("python_runner_subject")), (lazy_fixture("live_runner_subject")), ], ) @@ -245,7 +246,7 @@ def test_pause( "subject", [ (lazy_fixture("json_runner_subject")), - (lazy_fixture("legacy_python_runner_subject")), + (lazy_fixture("python_runner_subject")), (lazy_fixture("live_runner_subject")), ], ) @@ -268,7 +269,7 @@ async def test_stop( "subject", [ (lazy_fixture("json_runner_subject")), - (lazy_fixture("legacy_python_runner_subject")), + (lazy_fixture("python_runner_subject")), (lazy_fixture("live_runner_subject")), ], ) @@ -297,7 +298,7 @@ async def test_stop_when_run_never_started( "subject", [ (lazy_fixture("json_runner_subject")), - (lazy_fixture("legacy_python_runner_subject")), + (lazy_fixture("python_runner_subject")), (lazy_fixture("live_runner_subject")), ], ) @@ -521,12 +522,12 @@ async def test_load_json_runner( async def test_load_legacy_python( decoy: Decoy, - legacy_file_reader: LegacyFileReader, - legacy_context_creator: LegacyContextCreator, - legacy_executor: LegacyExecutor, + python_and_legacy_file_reader: PythonAndLegacyFileReader, + protocol_context_creator: ProtocolContextCreator, + python_protocol_executor: PythonProtocolExecutor, task_queue: TaskQueue, protocol_engine: ProtocolEngine, - legacy_python_runner_subject: PythonAndLegacyRunner, + python_runner_subject: PythonAndLegacyRunner, ) -> None: """It should load a legacy context-based Python protocol.""" labware_definition = LabwareDefinition.construct() # type: ignore[call-arg] @@ -541,9 +542,9 @@ async def test_load_legacy_python( content_hash="abc123", ) - extra_labware = {"definition-uri": cast(LegacyLabwareDefinition, {})} + extra_labware = {"definition-uri": cast(LabwareDefinitionTypedDict, {})} - legacy_protocol = LegacyPythonProtocol( + legacy_protocol = PythonProtocol( text="", contents="", filename="protocol.py", @@ -556,13 +557,13 @@ async def test_load_legacy_python( extra_labware=extra_labware, ) - legacy_context = decoy.mock(cls=LegacyProtocolContext) + protocol_context = decoy.mock(cls=ProtocolContext) decoy.when( await protocol_reader.extract_labware_definitions(legacy_protocol_source) ).then_return([labware_definition]) decoy.when( - legacy_file_reader.read( + python_and_legacy_file_reader.read( protocol_source=legacy_protocol_source, labware_definitions=[labware_definition], python_parse_mode=PythonParseMode.ALLOW_LEGACY_METADATA_AND_REQUIREMENTS, @@ -570,14 +571,14 @@ async def test_load_legacy_python( ).then_return(legacy_protocol) broker_captor = matchers.Captor() decoy.when( - legacy_context_creator.create( + protocol_context_creator.create( protocol=legacy_protocol, broker=broker_captor, equipment_broker=matchers.IsA(Broker), ) - ).then_return(legacy_context) + ).then_return(protocol_context) - await legacy_python_runner_subject.load( + await python_runner_subject.load( legacy_protocol_source, python_parse_mode=PythonParseMode.ALLOW_LEGACY_METADATA_AND_REQUIREMENTS, run_time_param_values=None, @@ -591,7 +592,7 @@ async def test_load_legacy_python( task_queue.set_run_func(run_func_captor), ) - assert broker_captor.value is legacy_python_runner_subject.broker + assert broker_captor.value is python_runner_subject.broker # Verify that the run func calls the right things: run_func = run_func_captor.value @@ -600,10 +601,10 @@ async def test_load_legacy_python( await protocol_engine.add_and_execute_command( request=pe_commands.HomeCreate(params=pe_commands.HomeParams(axes=None)) ), - await legacy_executor.execute( + await python_protocol_executor.execute( protocol=legacy_protocol, - context=legacy_context, - parameter_context=legacy_python_runner_subject._parameter_context, + context=protocol_context, + parameter_context=python_runner_subject._parameter_context, run_time_param_values=None, ), ) @@ -611,13 +612,13 @@ async def test_load_legacy_python( async def test_load_python_with_pe_papi_core( decoy: Decoy, - legacy_file_reader: LegacyFileReader, - legacy_context_creator: LegacyContextCreator, + python_and_legacy_file_reader: PythonAndLegacyFileReader, + protocol_context_creator: ProtocolContextCreator, protocol_engine: ProtocolEngine, - legacy_python_runner_subject: PythonAndLegacyRunner, + python_runner_subject: PythonAndLegacyRunner, ) -> None: """It should load a legacy context-based Python protocol.""" - legacy_protocol_source = ProtocolSource( + protocol_source = ProtocolSource( directory=Path("/dev/null"), main_file=Path("/dev/null/abc.py"), files=[], @@ -627,7 +628,7 @@ async def test_load_python_with_pe_papi_core( content_hash="abc123", ) - legacy_protocol = LegacyPythonProtocol( + protocol = PythonProtocol( text="", contents="", filename="protocol.py", @@ -640,43 +641,43 @@ async def test_load_python_with_pe_papi_core( extra_labware=None, ) - legacy_context = decoy.mock(cls=LegacyProtocolContext) + protocol_context = decoy.mock(cls=ProtocolContext) decoy.when( - await protocol_reader.extract_labware_definitions(legacy_protocol_source) + await protocol_reader.extract_labware_definitions(protocol_source) ).then_return([]) decoy.when( - legacy_file_reader.read( - protocol_source=legacy_protocol_source, + python_and_legacy_file_reader.read( + protocol_source=protocol_source, labware_definitions=[], python_parse_mode=PythonParseMode.ALLOW_LEGACY_METADATA_AND_REQUIREMENTS, ) - ).then_return(legacy_protocol) + ).then_return(protocol) broker_captor = matchers.Captor() decoy.when( - legacy_context_creator.create( - protocol=legacy_protocol, broker=broker_captor, equipment_broker=None + protocol_context_creator.create( + protocol=protocol, broker=broker_captor, equipment_broker=None ) - ).then_return(legacy_context) + ).then_return(protocol_context) - await legacy_python_runner_subject.load( - legacy_protocol_source, + await python_runner_subject.load( + protocol_source, python_parse_mode=PythonParseMode.ALLOW_LEGACY_METADATA_AND_REQUIREMENTS, run_time_param_values=None, ) decoy.verify(protocol_engine.add_plugin(matchers.IsA(LegacyContextPlugin)), times=0) - assert broker_captor.value is legacy_python_runner_subject.broker + assert broker_captor.value is python_runner_subject.broker async def test_load_legacy_json( decoy: Decoy, - legacy_file_reader: LegacyFileReader, - legacy_context_creator: LegacyContextCreator, - legacy_executor: LegacyExecutor, + python_and_legacy_file_reader: PythonAndLegacyFileReader, + protocol_context_creator: ProtocolContextCreator, + python_protocol_executor: PythonProtocolExecutor, task_queue: TaskQueue, protocol_engine: ProtocolEngine, - legacy_python_runner_subject: PythonAndLegacyRunner, + python_runner_subject: PythonAndLegacyRunner, ) -> None: """It should load a legacy context-based JSON protocol.""" labware_definition = LabwareDefinition.construct() # type: ignore[call-arg] @@ -691,7 +692,7 @@ async def test_load_legacy_json( content_hash="abc123", ) - legacy_protocol = LegacyJsonProtocol( + legacy_protocol = JsonProtocol( text="{}", contents=cast(LegacyJsonProtocolDict, {}), filename="protocol.json", @@ -701,27 +702,27 @@ async def test_load_legacy_json( metadata={"protocolName": "A Very Impressive Protocol"}, ) - legacy_context = decoy.mock(cls=LegacyProtocolContext) + protocol_context = decoy.mock(cls=ProtocolContext) decoy.when( await protocol_reader.extract_labware_definitions(legacy_protocol_source) ).then_return([labware_definition]) decoy.when( - legacy_file_reader.read( + python_and_legacy_file_reader.read( protocol_source=legacy_protocol_source, labware_definitions=[labware_definition], python_parse_mode=PythonParseMode.ALLOW_LEGACY_METADATA_AND_REQUIREMENTS, ) ).then_return(legacy_protocol) decoy.when( - legacy_context_creator.create( + protocol_context_creator.create( legacy_protocol, broker=matchers.IsA(LegacyBroker), equipment_broker=matchers.IsA(Broker), ) - ).then_return(legacy_context) + ).then_return(protocol_context) - await legacy_python_runner_subject.load( + await python_runner_subject.load( legacy_protocol_source, python_parse_mode=PythonParseMode.ALLOW_LEGACY_METADATA_AND_REQUIREMENTS, run_time_param_values=None, @@ -742,10 +743,10 @@ async def test_load_legacy_json( await protocol_engine.add_and_execute_command( request=pe_commands.HomeCreate(params=pe_commands.HomeParams(axes=None)) ), - await legacy_executor.execute( + await python_protocol_executor.execute( protocol=legacy_protocol, - context=legacy_context, - parameter_context=legacy_python_runner_subject._parameter_context, + context=protocol_context, + parameter_context=python_runner_subject._parameter_context, run_time_param_values=None, ), ) @@ -756,16 +757,16 @@ async def test_run_python_runner( hardware_api: HardwareAPI, protocol_engine: ProtocolEngine, task_queue: TaskQueue, - legacy_python_runner_subject: PythonAndLegacyRunner, + python_runner_subject: PythonAndLegacyRunner, ) -> None: """It should run a protocol to completion.""" decoy.when(protocol_engine.state_view.commands.has_been_played()).then_return( False, True ) - assert legacy_python_runner_subject.was_started() is False - await legacy_python_runner_subject.run(deck_configuration=[]) - assert legacy_python_runner_subject.was_started() is True + assert python_runner_subject.was_started() is False + await python_runner_subject.run(deck_configuration=[]) + assert python_runner_subject.was_started() is True decoy.verify( protocol_engine.play(deck_configuration=[]),