Skip to content

Commit 453cd47

Browse files
authoredNov 22, 2024··
fix(shared-data): Fix migrating mutable pipette configs (#16962)
1 parent 6ae2f38 commit 453cd47

File tree

2 files changed

+163
-4
lines changed

2 files changed

+163
-4
lines changed
 

‎shared-data/python/opentrons_shared_data/pipette/mutable_configurations.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
LIQUID_CLASS = LiquidClasses.default
4242

4343

44-
def _edit_non_quirk(
44+
def _edit_non_quirk( # noqa: C901
4545
mutable_config_key: str, new_mutable_value: MutableConfig, base_dict: Dict[str, Any]
4646
) -> None:
4747
def _do_edit_non_quirk(
@@ -58,6 +58,9 @@ def _do_edit_non_quirk(
5858
elif thiskey == "##EACHTIPTYPE##":
5959
for key in existing.keys():
6060
_do_edit_non_quirk(new_value, existing[key], restkeys)
61+
elif thiskey == "##EACHTIP##":
62+
for key in existing.keys():
63+
_do_edit_non_quirk(new_value, existing[key], restkeys)
6164
else:
6265
_do_edit_non_quirk(new_value, existing[thiskey], restkeys)
6366
else:

‎shared-data/python/tests/pipette/test_mutable_configurations.py

+159-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import json
2+
import logging
23
import os
34
from pathlib import Path
45
from typing import Dict, Any, cast, Union, Generator
@@ -68,9 +69,8 @@ def test_load_old_overrides_regression(
6869
"type": "float",
6970
"default": 0.1,
7071
}
71-
json.dump(
72-
TMPFILE_DATA, open(override_configuration_path / "P20SV222021040709.json", "w")
73-
)
72+
with open(override_configuration_path / "P20SV222021040709.json", "w") as f:
73+
json.dump(TMPFILE_DATA, f)
7474
configs = mutable_configurations.load_with_mutable_configurations(
7575
pipette_definition.PipetteModelVersionType(
7676
pipette_type=types.PipetteModelType.p20,
@@ -305,3 +305,159 @@ def test_build_mutable_config_using_old_units() -> None:
305305
assert (
306306
types.MutableConfig.build(**old_units_config, name="dropTipSpeed") is not None # type: ignore
307307
)
308+
309+
310+
@pytest.mark.parametrize(
311+
("filename", "type", "channels", "version", "file_contents"),
312+
# From https://opentrons.atlassian.net/browse/RQA-3676.
313+
# These could probably be pared down.
314+
[
315+
(
316+
"P20MV202020121412.json",
317+
types.PipetteModelType.p20,
318+
types.PipetteChannelType.EIGHT_CHANNEL,
319+
types.PipetteVersionType(2, 0),
320+
'{"model": "p20_multi_v2.0"}',
321+
),
322+
(
323+
"P3HSV1318071638.json",
324+
types.PipetteModelType.p300,
325+
types.PipetteChannelType.SINGLE_CHANNEL,
326+
types.PipetteVersionType(1, 3),
327+
'{"dropTipShake": true, "model": "p300_single_v1.3", "quirks": {"dropTipShake": true}, "top": {"value": 30.0, "default": 19.5, "units": "mm", "type": "float", "min": -20, "max": 30}, "pickUpPresses": {"value": 3.0, "default": 3, "units": "presses", "type": "int", "min": 0, "max": 15}}',
328+
),
329+
(
330+
"P3HMV212021040004.json",
331+
types.PipetteModelType.p300,
332+
types.PipetteChannelType.EIGHT_CHANNEL,
333+
types.PipetteVersionType(2, 1),
334+
'{"needsUnstick": true, "model": "p300_multi_v2.1"}',
335+
),
336+
(
337+
"P20SV202020032604.json",
338+
types.PipetteModelType.p20,
339+
types.PipetteChannelType.SINGLE_CHANNEL,
340+
types.PipetteVersionType(2, 0),
341+
'{"model": "p20_single_v2.0"}',
342+
),
343+
(
344+
"P1KSV202019072441.json",
345+
types.PipetteModelType.p1000,
346+
types.PipetteChannelType.SINGLE_CHANNEL,
347+
types.PipetteVersionType(2, 0),
348+
'{"pickupTipShake": true, "model": "p1000_single_v2.0", "quirks": {"pickupTipShake": true}}',
349+
),
350+
(
351+
"P3HMV202021011105.json",
352+
types.PipetteModelType.p300,
353+
types.PipetteChannelType.EIGHT_CHANNEL,
354+
types.PipetteVersionType(2, 0),
355+
'{"needsUnstick": true, "model": "p300_multi_v2.0"}',
356+
),
357+
(
358+
"P20SV202019072527.json",
359+
types.PipetteModelType.p20,
360+
types.PipetteChannelType.SINGLE_CHANNEL,
361+
types.PipetteVersionType(2, 0),
362+
'{"model": "p20_single_v2.0"}',
363+
),
364+
(
365+
"P3HSV202021042602.json",
366+
types.PipetteModelType.p300,
367+
types.PipetteChannelType.SINGLE_CHANNEL,
368+
types.PipetteVersionType(2, 0),
369+
'{"model": "p300_single_v2.0"}',
370+
),
371+
(
372+
"P20SV222021030914.json",
373+
types.PipetteModelType.p20,
374+
types.PipetteChannelType.SINGLE_CHANNEL,
375+
types.PipetteVersionType(2, 2),
376+
'{"model": "p20_single_v2.2", "quirks": {}, "pickUpPresses": {"value": 3.0, "default": 1, "units": "presses", "type": "int", "min": 0, "max": 15}}',
377+
),
378+
(
379+
"P3HMV202020040801.json",
380+
types.PipetteModelType.p300,
381+
types.PipetteChannelType.EIGHT_CHANNEL,
382+
types.PipetteVersionType(2, 0),
383+
'{"needsUnstick": true, "model": "p300_multi_v2.0", "quirks": {"needsUnstick": true}, "top": {"value": 20.0, "default": 19.5, "units": "mm", "type": "float", "min": -20, "max": 30}, "bottom": {"value": -14.0, "default": -14.5, "units": "mm", "type": "float", "min": -20, "max": 30}, "blowout": {"value": -18.5, "default": -19.0, "units": "mm", "type": "float", "min": -20, "max": 30}, "dropTip": {"value": -20.0, "default": -33.4, "units": "mm", "type": "float", "min": -20, "max": 30}, "pickUpCurrent": {"value": 0.9, "default": 0.8, "units": "amps", "type": "float", "min": 0.1, "max": 2.0}, "pickUpDistance": {"value": 10.0, "default": 11.0, "units": "mm", "type": "float", "min": 0, "max": 10}, "pickUpIncrement": {"value": 0.1, "default": 0.0, "units": "mm", "type": "int", "min": 0, "max": 10}, "pickUpPresses": {"value": 4.0, "default": 1, "units": "presses", "type": "int", "min": 0, "max": 15}, "pickUpSpeed": {"value": 11.0, "default": 10.0, "units": "mm/s", "type": "float", "min": 0.01, "max": 30}, "plungerCurrent": {"value": 1.1, "default": 1.0, "units": "amps", "type": "float", "min": 0.1, "max": 2.0}, "dropTipCurrent": {"value": 1.22, "default": 1.25, "units": "amps", "type": "float", "min": 0.1, "max": 2.0}, "dropTipSpeed": {"value": 8.0, "default": 7.5, "units": "mm/s", "type": "float", "min": 0.01, "max": 30}, "tipLength": {"value": 52.0, "default": 51.0, "units": "mm", "type": "float", "min": 0, "max": 100}}',
384+
),
385+
(
386+
"P3HMV212021040002.json",
387+
types.PipetteModelType.p300,
388+
types.PipetteChannelType.EIGHT_CHANNEL,
389+
types.PipetteVersionType(2, 1),
390+
'{"needsUnstick": true, "model": "p300_multi_v2.1"}',
391+
),
392+
(
393+
"P3HMV1318072625.json",
394+
types.PipetteModelType.p300,
395+
types.PipetteChannelType.EIGHT_CHANNEL,
396+
types.PipetteVersionType(1, 3),
397+
'{"dropTipShake": true, "model": "p300_multi_v1.3", "quirks": {"dropTipShake": true}, "pickUpPresses": {"value": 4.0, "default": 3, "units": "presses", "type": "int", "min": 0, "max": 15}}',
398+
),
399+
(
400+
"P50MV1519091757.json",
401+
types.PipetteModelType.p50,
402+
types.PipetteChannelType.EIGHT_CHANNEL,
403+
types.PipetteVersionType(1, 5),
404+
'{"dropTipShake": true, "doubleDropTip": true, "model": "p50_multi_v1.5", "quirks": {"doubleDropTip": true, "dropTipShake": true}}',
405+
),
406+
(
407+
"P3HSV202019072224.json",
408+
types.PipetteModelType.p300,
409+
types.PipetteChannelType.SINGLE_CHANNEL,
410+
types.PipetteVersionType(2, 0),
411+
'{"model": "p300_single_v2.0", "quirks": {}}',
412+
),
413+
(
414+
"P20MV202019112708.json",
415+
types.PipetteModelType.p20,
416+
types.PipetteChannelType.EIGHT_CHANNEL,
417+
types.PipetteVersionType(2, 0),
418+
'{"model": "p20_multi_v2.0", "quirks": {}}',
419+
),
420+
(
421+
"P3HSV202021031503.json",
422+
types.PipetteModelType.p300,
423+
types.PipetteChannelType.SINGLE_CHANNEL,
424+
types.PipetteVersionType(2, 1),
425+
'{"model": "p300_single_v2.1"}',
426+
),
427+
(
428+
"P1KSV202020060206.json",
429+
types.PipetteModelType.p1000,
430+
types.PipetteChannelType.SINGLE_CHANNEL,
431+
types.PipetteVersionType(2, 0),
432+
'{"pickupTipShake": true, "model": "p1000_single_v2.0"}',
433+
),
434+
(
435+
"P3HMV202021010906.json",
436+
types.PipetteModelType.p300,
437+
types.PipetteChannelType.EIGHT_CHANNEL,
438+
types.PipetteVersionType(2, 0),
439+
'{"needsUnstick": true, "model": "p300_multi_v2.0"}',
440+
),
441+
],
442+
)
443+
def test_loading_does_not_log_warnings(
444+
filename: str,
445+
type: types.PipetteModelType,
446+
channels: types.PipetteChannelType,
447+
version: types.PipetteVersionType,
448+
file_contents: str,
449+
caplog: pytest.LogCaptureFixture,
450+
override_configuration_path: Path,
451+
) -> None:
452+
"""Make sure load_with_mutable_configurations() doesn't log any exceptions.
453+
454+
load_with_mutable_configurations() suppresses and logs internal exceptions to
455+
protect its caller, but those are still bugs, and we still want tests to catch them.
456+
"""
457+
model = pipette_definition.PipetteModelVersionType(type, channels, version)
458+
(override_configuration_path / filename).write_text(file_contents)
459+
with caplog.at_level(logging.WARNING):
460+
mutable_configurations.load_with_mutable_configurations(
461+
model, override_configuration_path, Path(filename).stem
462+
)
463+
assert len(caplog.records) == 0

0 commit comments

Comments
 (0)
Please sign in to comment.