From fa268d0d37cfad1a9b55f2a526ab85c787e6c988 Mon Sep 17 00:00:00 2001 From: caila-marashaj Date: Thu, 9 Jan 2025 16:30:24 -0500 Subject: [PATCH 1/3] find default new labware value if api level > 2.21 --- .../core/engine/load_labware_params.py | 44 ++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/api/src/opentrons/protocol_api/core/engine/load_labware_params.py b/api/src/opentrons/protocol_api/core/engine/load_labware_params.py index ab853bb55ce..1cf26985c71 100644 --- a/api/src/opentrons/protocol_api/core/engine/load_labware_params.py +++ b/api/src/opentrons/protocol_api/core/engine/load_labware_params.py @@ -34,6 +34,42 @@ "corning_24_wellplate_3.4ml_flat": 2, } +_APILEVEL_2_21_OT_DEFAULT_VERSIONS: Dict[str, int] = { + "opentrons_10_tuberack_nest_4x50ml_6x15ml_conical": 2, + "opentrons_24_tuberack_nest_2ml_screwcap": 2, + "opentrons_24_tuberack_nest_1.5ml_screwcap": 2, + "nest_1_reservoir_290ml": 2, + "opentrons_24_tuberack_nest_2ml_snapcap": 2, + "nest_96_wellplate_2ml_deep": 3, + "opentrons_24_tuberack_nest_1.5ml_snapcap": 2, + "nest_12_reservoir_15ml": 2, + "nest_1_reservoir_195ml": 3, + "opentrons_24_tuberack_nest_0.5ml_screwcap": 2, + "opentrons_96_wellplate_200ul_pcr_full_skirt": 3, + "nest_96_wellplate_100ul_pcr_full_skirt": 3, + "nest_96_wellplate_200ul_flat": 3, + "opentrons_10_tuberack_falcon_4x50ml_6x15ml_conical": 2, + "usascientific_12_reservoir_22ml": 2, + "thermoscientificnunc_96_wellplate_2000ul": 2, + "usascientific_96_wellplate_2.4ml_deep": 2, + "agilent_1_reservoir_290ml": 2, + "opentrons_24_tuberack_eppendorf_1.5ml_safelock_snapcap": 2, + "thermoscientificnunc_96_wellplate_1300ul": 2, + "corning_12_wellplate_6.9ml_flat": 3, + "corning_24_wellplate_3.4ml_flat": 3, + "corning_6_wellplate_16.8ml_flat": 3, + "corning_48_wellplate_1.6ml_flat": 3, + "biorad_96_wellplate_200ul_pcr": 3, + "axygen_1_reservoir_90ml": 2, + "corning_384_wellplate_112ul_flat": 3, + "corning_96_wellplate_360ul_flat": 3, + "biorad_384_wellplate_50ul": 3, + "appliedbiosystemsmicroamp_384_wellplate_40ul": 2, + "opentrons_24_tuberack_eppendorf_2ml_safelock_snapcap": 2, + "opentrons_10_tuberack_nest_4x50ml_6x15ml_conical": 2, + "opentrons_10_tuberack_falcon_4x50ml_6x15ml_conical": 2, +} + class AmbiguousLoadLabwareParamsError(RuntimeError): """Error raised when specific labware parameters cannot be found due to multiple matching labware definitions.""" @@ -99,7 +135,13 @@ def matches_params(custom_params: LabwareLoadParams) -> bool: return resolved_namespace, resolved_version -def _get_default_version_for_standard_labware(load_name: str) -> int: +def _get_default_version_for_standard_labware( + load_name: str, api_level: Tuple[int, int] = (2, 14) +) -> int: # We know the protocol is running at least apiLevel 2.14 by this point because # apiLevel 2.13 and below has its own separate code path for resolving labware. + if api_level >= (2, 21): + found_version = _APILEVEL_2_21_OT_DEFAULT_VERSIONS.get(load_name, None) + if found_version: + return found_version return _APILEVEL_2_14_OT_DEFAULT_VERSIONS.get(load_name, 1) From 6b59349badf41ce1308817f43611800c24f42a77 Mon Sep 17 00:00:00 2001 From: caila-marashaj Date: Thu, 9 Jan 2025 17:17:27 -0500 Subject: [PATCH 2/3] require request to get schema 3 default version --- .../core/engine/load_labware_params.py | 18 +++++++++++++++--- .../protocol_api/core/engine/protocol.py | 10 ++++++++-- .../core/legacy/legacy_protocol_core.py | 3 ++- .../opentrons/protocol_api/core/protocol.py | 3 ++- api/src/opentrons/protocol_api/labware.py | 2 ++ 5 files changed, 29 insertions(+), 7 deletions(-) diff --git a/api/src/opentrons/protocol_api/core/engine/load_labware_params.py b/api/src/opentrons/protocol_api/core/engine/load_labware_params.py index 1cf26985c71..549c5a861ea 100644 --- a/api/src/opentrons/protocol_api/core/engine/load_labware_params.py +++ b/api/src/opentrons/protocol_api/core/engine/load_labware_params.py @@ -80,6 +80,8 @@ def resolve( namespace: Optional[str], version: Optional[int], custom_load_labware_params: List[LabwareLoadParams], + api_level: Optional[Tuple[int, int]] = None, + schema_version: Optional[int] = 2, ) -> Tuple[str, int]: """Resolve the load labware parameters that best matches any custom labware, or default to opentrons standards @@ -89,6 +91,8 @@ def resolve( version: Optionally provided labware definition version custom_load_labware_params: List of load labware parameters associated with custom labware that match given parameters + api_level: Current api level. + schema_version: The desired labware schema version to draw a definition from. Returns: A tuple of the resolved namespace and version @@ -115,10 +119,16 @@ def matches_params(custom_params: LabwareLoadParams) -> bool: # custom labware matching that namespace, so we will always take this path in # that case. resolved_namespace = namespace if namespace is not None else OPENTRONS_NAMESPACE + api_level = api_level if api_level else (2, 14) + schema_version = schema_version if schema_version else 2 resolved_version = ( version if version is not None - else _get_default_version_for_standard_labware(load_name=load_name) + else _get_default_version_for_standard_labware( + load_name=load_name, + current_api_level=api_level, + schema_version=schema_version, + ) ) elif len(filtered_custom_params) > 1: @@ -136,11 +146,13 @@ def matches_params(custom_params: LabwareLoadParams) -> bool: def _get_default_version_for_standard_labware( - load_name: str, api_level: Tuple[int, int] = (2, 14) + load_name: str, + current_api_level: Tuple[int, int] = (2, 14), + schema_version: int = 2, ) -> int: # We know the protocol is running at least apiLevel 2.14 by this point because # apiLevel 2.13 and below has its own separate code path for resolving labware. - if api_level >= (2, 21): + if current_api_level >= (2, 21) and schema_version == 3: found_version = _APILEVEL_2_21_OT_DEFAULT_VERSIONS.get(load_name, None) if found_version: return found_version diff --git a/api/src/opentrons/protocol_api/core/engine/protocol.py b/api/src/opentrons/protocol_api/core/engine/protocol.py index ece431b0d1e..07b417594cb 100644 --- a/api/src/opentrons/protocol_api/core/engine/protocol.py +++ b/api/src/opentrons/protocol_api/core/engine/protocol.py @@ -210,7 +210,8 @@ def load_labware( ], label: Optional[str], namespace: Optional[str], - version: Optional[int], + version: Optional[int] = None, + schema: Optional[int] = 2, ) -> LabwareCore: """Load a labware using its identifying parameters.""" load_location = self._convert_labware_location(location=location) @@ -219,7 +220,12 @@ def load_labware( self._engine_client.state.labware.find_custom_labware_load_params() ) namespace, version = load_labware_params.resolve( - load_name, namespace, version, custom_labware_params + load_name, + namespace, + version, + custom_labware_params, + self._api_version, + schema, ) load_result = self._engine_client.execute_command_without_recovery( diff --git a/api/src/opentrons/protocol_api/core/legacy/legacy_protocol_core.py b/api/src/opentrons/protocol_api/core/legacy/legacy_protocol_core.py index 8adadbe1ecf..4de0b96dd3a 100644 --- a/api/src/opentrons/protocol_api/core/legacy/legacy_protocol_core.py +++ b/api/src/opentrons/protocol_api/core/legacy/legacy_protocol_core.py @@ -175,7 +175,8 @@ def load_labware( ], label: Optional[str], namespace: Optional[str], - version: Optional[int], + version: Optional[int] = None, + schema: Optional[int] = 2, ) -> LegacyLabwareCore: """Load a labware using its identifying parameters.""" if isinstance(location, OffDeckType): diff --git a/api/src/opentrons/protocol_api/core/protocol.py b/api/src/opentrons/protocol_api/core/protocol.py index 27d41b921b0..44d47eb2e19 100644 --- a/api/src/opentrons/protocol_api/core/protocol.py +++ b/api/src/opentrons/protocol_api/core/protocol.py @@ -84,7 +84,8 @@ def load_labware( ], label: Optional[str], namespace: Optional[str], - version: Optional[int], + version: Optional[int] = None, + schema: Optional[int] = 2, ) -> LabwareCoreType: """Load a labware using its identifying parameters.""" ... diff --git a/api/src/opentrons/protocol_api/labware.py b/api/src/opentrons/protocol_api/labware.py index bb8a094e4c2..8b12e410b30 100644 --- a/api/src/opentrons/protocol_api/labware.py +++ b/api/src/opentrons/protocol_api/labware.py @@ -547,6 +547,7 @@ def load_labware( lid: Optional[str] = None, namespace: Optional[str] = None, version: Optional[int] = None, + schema: Optional[int] = None, ) -> Labware: """Load a compatible labware onto the labware using its load parameters. @@ -563,6 +564,7 @@ def load_labware( namespace=namespace, version=version, location=self._core, + schema=schema, ) labware = Labware( From 2d4bb284239b1e9c02986fa063519faa355d4772 Mon Sep 17 00:00:00 2001 From: caila-marashaj Date: Thu, 9 Jan 2025 18:36:32 -0500 Subject: [PATCH 3/3] test fixes --- api/docs/v2/deck_slots.rst | 2 +- .../protocol_api/core/engine/protocol.py | 4 ++-- .../core/legacy/legacy_protocol_core.py | 4 ++-- .../opentrons/protocol_api/core/protocol.py | 4 ++-- .../opentrons/protocol_api/module_contexts.py | 2 ++ .../protocol_api/protocol_context.py | 4 ++++ .../core/engine/test_protocol_core.py | 22 +++++++++++++++++++ .../test_protocol_context_implementation.py | 5 +++++ .../opentrons/protocol_api/test_labware.py | 3 +++ .../protocol_api/test_module_context.py | 3 +++ .../protocol_api/test_protocol_context.py | 7 ++++++ 11 files changed, 53 insertions(+), 7 deletions(-) diff --git a/api/docs/v2/deck_slots.rst b/api/docs/v2/deck_slots.rst index 6441ab0d562..097bf03547a 100644 --- a/api/docs/v2/deck_slots.rst +++ b/api/docs/v2/deck_slots.rst @@ -36,7 +36,7 @@ For example, these two ``load_labware()`` commands are equivalent: .. code-block:: python - protocol.load_labware("nest_96_wellplate_200ul_flat", "A1") + protocol.load_labware("nest_96_wellplate_200ul_flat", "A1", None) .. versionadded:: 2.15 diff --git a/api/src/opentrons/protocol_api/core/engine/protocol.py b/api/src/opentrons/protocol_api/core/engine/protocol.py index 07b417594cb..ba8ac7d69f6 100644 --- a/api/src/opentrons/protocol_api/core/engine/protocol.py +++ b/api/src/opentrons/protocol_api/core/engine/protocol.py @@ -210,8 +210,8 @@ def load_labware( ], label: Optional[str], namespace: Optional[str], - version: Optional[int] = None, - schema: Optional[int] = 2, + version: Optional[int], + schema: Optional[int], ) -> LabwareCore: """Load a labware using its identifying parameters.""" load_location = self._convert_labware_location(location=location) diff --git a/api/src/opentrons/protocol_api/core/legacy/legacy_protocol_core.py b/api/src/opentrons/protocol_api/core/legacy/legacy_protocol_core.py index 4de0b96dd3a..30f5db2d992 100644 --- a/api/src/opentrons/protocol_api/core/legacy/legacy_protocol_core.py +++ b/api/src/opentrons/protocol_api/core/legacy/legacy_protocol_core.py @@ -175,8 +175,8 @@ def load_labware( ], label: Optional[str], namespace: Optional[str], - version: Optional[int] = None, - schema: Optional[int] = 2, + version: Optional[int], + schema: Optional[int], ) -> LegacyLabwareCore: """Load a labware using its identifying parameters.""" if isinstance(location, OffDeckType): diff --git a/api/src/opentrons/protocol_api/core/protocol.py b/api/src/opentrons/protocol_api/core/protocol.py index 44d47eb2e19..10c8ca992dc 100644 --- a/api/src/opentrons/protocol_api/core/protocol.py +++ b/api/src/opentrons/protocol_api/core/protocol.py @@ -84,8 +84,8 @@ def load_labware( ], label: Optional[str], namespace: Optional[str], - version: Optional[int] = None, - schema: Optional[int] = 2, + version: Optional[int], + schema: Optional[int], ) -> LabwareCoreType: """Load a labware using its identifying parameters.""" ... diff --git a/api/src/opentrons/protocol_api/module_contexts.py b/api/src/opentrons/protocol_api/module_contexts.py index 614bb4f53c7..bb9ad2bfbdd 100644 --- a/api/src/opentrons/protocol_api/module_contexts.py +++ b/api/src/opentrons/protocol_api/module_contexts.py @@ -126,6 +126,7 @@ def load_labware( version: Optional[int] = None, adapter: Optional[str] = None, lid: Optional[str] = None, + schema: Optional[int] = None, ) -> Labware: """Load a labware onto the module using its load parameters. @@ -180,6 +181,7 @@ def load_labware( namespace=namespace, version=version, location=load_location, + schema=schema, ) if lid is not None: if self._api_version < validation.LID_STACK_VERSION_GATE: diff --git a/api/src/opentrons/protocol_api/protocol_context.py b/api/src/opentrons/protocol_api/protocol_context.py index b9f96e4d536..b0c815dde4f 100644 --- a/api/src/opentrons/protocol_api/protocol_context.py +++ b/api/src/opentrons/protocol_api/protocol_context.py @@ -400,6 +400,7 @@ def load_labware( version: Optional[int] = None, adapter: Optional[str] = None, lid: Optional[str] = None, + schema: Optional[int] = None, ) -> Labware: """Load a labware onto a location. @@ -448,6 +449,8 @@ def load_labware( values as the ``load_name`` parameter of :py:meth:`.load_lid_stack`. The lid will use the same namespace as the labware, and the API will choose the lid's version automatically. + :param schema: If specified, the schema version that will correspond to the + labware definition to load by default. .. versionadded:: 2.15 """ @@ -486,6 +489,7 @@ def load_labware( label=label, namespace=namespace, version=version, + schema=schema, ) if lid is not None: diff --git a/api/tests/opentrons/protocol_api/core/engine/test_protocol_core.py b/api/tests/opentrons/protocol_api/core/engine/test_protocol_core.py index 2889a47cea9..cf7f2a2526d 100644 --- a/api/tests/opentrons/protocol_api/core/engine/test_protocol_core.py +++ b/api/tests/opentrons/protocol_api/core/engine/test_protocol_core.py @@ -331,6 +331,7 @@ def test_load_labware( decoy: Decoy, mock_engine_client: EngineClient, subject: ProtocolCore, + api_version: APIVersion, ) -> None: """It should issue a LoadLabware command.""" decoy.when( @@ -343,6 +344,8 @@ def test_load_labware( "a_namespace", 456, [EngineLabwareLoadParams("hello", "world", 654)], + api_version, + 2, ) ).then_return(("some_namespace", 9001)) @@ -374,6 +377,7 @@ def test_load_labware( label="some_display_name", # maps to optional display name namespace="a_namespace", version=456, + schema=2, ) assert isinstance(result, LabwareCore) @@ -405,6 +409,7 @@ def test_load_labware_on_staging_slot( decoy: Decoy, mock_engine_client: EngineClient, subject: ProtocolCore, + api_version: APIVersion, ) -> None: """It should issue a LoadLabware command for a labware on a staging slot.""" decoy.when( @@ -417,6 +422,8 @@ def test_load_labware_on_staging_slot( "a_namespace", 456, [EngineLabwareLoadParams("hello", "world", 654)], + api_version, + 3, ) ).then_return(("some_namespace", 9001)) @@ -448,6 +455,7 @@ def test_load_labware_on_staging_slot( label="some_display_name", # maps to optional display name namespace="a_namespace", version=456, + schema=3, ) assert isinstance(result, LabwareCore) @@ -479,6 +487,7 @@ def test_load_labware_on_labware( decoy: Decoy, mock_engine_client: EngineClient, subject: ProtocolCore, + api_version: APIVersion, ) -> None: """It should issue a LoadLabware command onto an OnLabware location.""" mock_labware_core = decoy.mock(cls=LabwareCore) @@ -494,6 +503,8 @@ def test_load_labware_on_labware( "a_namespace", 456, [EngineLabwareLoadParams("hello", "world", 654)], + api_version, + None, ) ).then_return(("some_namespace", 9001)) @@ -529,6 +540,7 @@ def test_load_labware_on_labware( label="some_display_name", namespace="a_namespace", version=456, + schema=None, ) assert isinstance(result, LabwareCore) @@ -552,6 +564,7 @@ def test_load_labware_off_deck( decoy: Decoy, mock_engine_client: EngineClient, subject: ProtocolCore, + api_version: APIVersion, ) -> None: """It should issue a LoadLabware off deck command.""" decoy.when( @@ -564,6 +577,8 @@ def test_load_labware_off_deck( "a_namespace", 456, [EngineLabwareLoadParams("hello", "world", 654)], + api_version, + None, ) ).then_return(("some_namespace", 9001)) @@ -595,6 +610,7 @@ def test_load_labware_off_deck( label="some_display_name", # maps to optional display name namespace="a_namespace", version=456, + schema=None, ) assert isinstance(result, LabwareCore) @@ -1189,6 +1205,8 @@ def test_load_labware_on_module( "a_namespace", 456, [EngineLabwareLoadParams("hello", "world", 654)], + api_version, + None, ) ).then_return(("some_namespace", 9001)) @@ -1227,6 +1245,7 @@ def test_load_labware_on_module( label="some_display_name", # maps to optional display name namespace="a_namespace", version=456, + schema=None, ) assert isinstance(result, LabwareCore) @@ -1266,6 +1285,8 @@ def test_load_labware_on_non_connected_module( "a_namespace", 456, [EngineLabwareLoadParams("hello", "world", 654)], + api_version, + 2, ) ).then_return(("some_namespace", 9001)) @@ -1303,6 +1324,7 @@ def test_load_labware_on_non_connected_module( label="some_display_name", # maps to optional display name namespace="a_namespace", version=456, + schema=2, ) assert isinstance(result, LabwareCore) diff --git a/api/tests/opentrons/protocol_api/core/legacy/test_protocol_context_implementation.py b/api/tests/opentrons/protocol_api/core/legacy/test_protocol_context_implementation.py index d9fcfa8e29b..bb643c610dd 100644 --- a/api/tests/opentrons/protocol_api/core/legacy/test_protocol_context_implementation.py +++ b/api/tests/opentrons/protocol_api/core/legacy/test_protocol_context_implementation.py @@ -176,6 +176,7 @@ def test_load_labware_off_deck_raises( label="cool label", namespace="cool namespace", version=1337, + schema=None, ) @@ -190,6 +191,7 @@ def test_load_labware_on_staging_slot_raises( label="cool label", namespace="cool namespace", version=1337, + schema=None, ) @@ -246,6 +248,7 @@ def test_load_labware( label="cool label", namespace="cool namespace", version=1337, + schema=None, ) assert isinstance(result, LegacyLabwareCore) @@ -347,6 +350,7 @@ def test_load_labware_on_module( label="cool label", namespace="cool namespace", version=1337, + schema=None, ) assert isinstance(result, LegacyLabwareCore) @@ -384,6 +388,7 @@ def test_load_labware_on_labware_raises( label="cool label", namespace="cool namespace", version=1337, + schema=None, ) diff --git a/api/tests/opentrons/protocol_api/test_labware.py b/api/tests/opentrons/protocol_api/test_labware.py index 5e49cd29947..c4337f310fb 100644 --- a/api/tests/opentrons/protocol_api/test_labware.py +++ b/api/tests/opentrons/protocol_api/test_labware.py @@ -132,6 +132,7 @@ def test_load_labware( namespace="a-namespace", version=123, location=mock_labware_core, + schema=None, ) ).then_return(new_mock_core) decoy.when(new_mock_core.get_well_columns()).then_return([]) @@ -141,6 +142,7 @@ def test_load_labware( label="a label", namespace="a-namespace", version=123, + schema=None, ) assert isinstance(result, Labware) @@ -175,6 +177,7 @@ def test_load_labware_from_definition( version=1337, label="a label", location=mock_labware_core, + schema=None, ) ).then_return(new_mock_core) diff --git a/api/tests/opentrons/protocol_api/test_module_context.py b/api/tests/opentrons/protocol_api/test_module_context.py index 1fb5132b59c..127b2097bbb 100644 --- a/api/tests/opentrons/protocol_api/test_module_context.py +++ b/api/tests/opentrons/protocol_api/test_module_context.py @@ -101,6 +101,7 @@ def test_load_labware( namespace="ideal", version=101, location=mock_core, + schema=None, ) ).then_return(mock_labware_core) @@ -149,6 +150,7 @@ def test_load_labware_from_definition( version=1337, label="Some Display Name", location=mock_core, + schema=None, ) ).then_return(mock_labware_core) @@ -265,6 +267,7 @@ def test_load_labware_with_adapter( namespace="ideal", version=101, location=mock_adapter_core, + schema=None, ) ).then_return(mock_labware_core) diff --git a/api/tests/opentrons/protocol_api/test_protocol_context.py b/api/tests/opentrons/protocol_api/test_protocol_context.py index 80728b7820c..514d8dcbd35 100644 --- a/api/tests/opentrons/protocol_api/test_protocol_context.py +++ b/api/tests/opentrons/protocol_api/test_protocol_context.py @@ -431,6 +431,7 @@ def test_load_labware( label="some_display_name", namespace="some_namespace", version=1337, + schema=2, ) ).then_return(mock_labware_core) @@ -444,6 +445,7 @@ def test_load_labware( label="some_display_name", namespace="some_namespace", version=1337, + schema=2, ) assert isinstance(result, Labware) @@ -472,6 +474,7 @@ def test_load_labware_off_deck( label="some_display_name", namespace="some_namespace", version=1337, + schema=None, ) ).then_return(mock_labware_core) @@ -533,6 +536,7 @@ def test_load_labware_on_staging_slot( label="some_display_name", namespace="some_namespace", version=1337, + schema=None, ) ).then_return(mock_labware_core) @@ -585,6 +589,7 @@ def test_load_labware_from_definition( version=1337, location=DeckSlotName.SLOT_1, label="Some Display Name", + schema=None, ) ).then_return(mock_labware_core) @@ -720,6 +725,7 @@ def test_load_labware_on_adapter( label="some_display_name", namespace="some_namespace", version=1337, + schema=None, ) ).then_return(mock_labware_core) @@ -773,6 +779,7 @@ def test_load_labware_with_lid( label="some_display_name", namespace="some_namespace", version=1337, + schema=None, ) ).then_return(mock_labware_core) decoy.when(mock_lid_core.get_well_columns()).then_return([])