Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(shared-data): allow default version finding for labware schema v3 #17233

Draft
wants to merge 3 commits into
base: edge
Choose a base branch
from
Draft
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 api/docs/v2/deck_slots.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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."""
Expand All @@ -44,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

Expand All @@ -53,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
Expand All @@ -79,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:
Expand All @@ -99,7 +145,15 @@ 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,
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 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
return _APILEVEL_2_14_OT_DEFAULT_VERSIONS.get(load_name, 1)
8 changes: 7 additions & 1 deletion api/src/opentrons/protocol_api/core/engine/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ def load_labware(
label: Optional[str],
namespace: Optional[str],
version: Optional[int],
schema: Optional[int],
) -> LabwareCore:
"""Load a labware using its identifying parameters."""
load_location = self._convert_labware_location(location=location)
Expand All @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ def load_labware(
label: Optional[str],
namespace: Optional[str],
version: Optional[int],
schema: Optional[int],
) -> LegacyLabwareCore:
"""Load a labware using its identifying parameters."""
if isinstance(location, OffDeckType):
Expand Down
1 change: 1 addition & 0 deletions api/src/opentrons/protocol_api/core/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ def load_labware(
label: Optional[str],
namespace: Optional[str],
version: Optional[int],
schema: Optional[int],
) -> LabwareCoreType:
"""Load a labware using its identifying parameters."""
...
Expand Down
2 changes: 2 additions & 0 deletions api/src/opentrons/protocol_api/labware.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -563,6 +564,7 @@ def load_labware(
namespace=namespace,
version=version,
location=self._core,
schema=schema,
)

labware = Labware(
Expand Down
2 changes: 2 additions & 0 deletions api/src/opentrons/protocol_api/module_contexts.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down Expand Up @@ -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:
Expand Down
4 changes: 4 additions & 0 deletions api/src/opentrons/protocol_api/protocol_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down Expand Up @@ -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
"""
Expand Down Expand Up @@ -486,6 +489,7 @@ def load_labware(
label=label,
namespace=namespace,
version=version,
schema=schema,
)

if lid is not None:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -343,6 +344,8 @@ def test_load_labware(
"a_namespace",
456,
[EngineLabwareLoadParams("hello", "world", 654)],
api_version,
2,
)
).then_return(("some_namespace", 9001))

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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(
Expand All @@ -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))

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand All @@ -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))

Expand Down Expand Up @@ -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)
Expand All @@ -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(
Expand All @@ -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))

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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))

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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))

Expand Down Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ def test_load_labware_off_deck_raises(
label="cool label",
namespace="cool namespace",
version=1337,
schema=None,
)


Expand All @@ -190,6 +191,7 @@ def test_load_labware_on_staging_slot_raises(
label="cool label",
namespace="cool namespace",
version=1337,
schema=None,
)


Expand Down Expand Up @@ -246,6 +248,7 @@ def test_load_labware(
label="cool label",
namespace="cool namespace",
version=1337,
schema=None,
)

assert isinstance(result, LegacyLabwareCore)
Expand Down Expand Up @@ -347,6 +350,7 @@ def test_load_labware_on_module(
label="cool label",
namespace="cool namespace",
version=1337,
schema=None,
)

assert isinstance(result, LegacyLabwareCore)
Expand Down Expand Up @@ -384,6 +388,7 @@ def test_load_labware_on_labware_raises(
label="cool label",
namespace="cool namespace",
version=1337,
schema=None,
)


Expand Down
3 changes: 3 additions & 0 deletions api/tests/opentrons/protocol_api/test_labware.py
Original file line number Diff line number Diff line change
Expand Up @@ -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([])
Expand All @@ -141,6 +142,7 @@ def test_load_labware(
label="a label",
namespace="a-namespace",
version=123,
schema=None,
)

assert isinstance(result, Labware)
Expand Down Expand Up @@ -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)

Expand Down
Loading
Loading