Skip to content

Commit 528a825

Browse files
committed
shared-data: deprecate json_encoder and use v2 serializer
1 parent 16b69eb commit 528a825

File tree

6 files changed

+76
-27
lines changed

6 files changed

+76
-27
lines changed

shared-data/python/opentrons_shared_data/labware/models.py

+2-6
Original file line numberDiff line numberDiff line change
@@ -42,17 +42,13 @@ def __add__(self, other: Any) -> OffsetVector:
4242
"""Adds two vectors together."""
4343
if not isinstance(other, OffsetVector):
4444
return NotImplemented
45-
return OffsetVector(
46-
x=self.x + other.x, y=self.y + other.y, z=self.z + other.z
47-
)
45+
return OffsetVector(x=self.x + other.x, y=self.y + other.y, z=self.z + other.z)
4846

4947
def __sub__(self, other: Any) -> OffsetVector:
5048
"""Subtracts two vectors."""
5149
if not isinstance(other, OffsetVector):
5250
return NotImplemented
53-
return OffsetVector(
54-
x=self.x - other.x, y=self.y - other.y, z=self.z - other.z
55-
)
51+
return OffsetVector(x=self.x - other.x, y=self.y - other.y, z=self.z - other.z)
5652

5753

5854
class GripperOffsets(BaseModel):

shared-data/python/opentrons_shared_data/pipette/pipette_definition.py

+17-18
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
import re
2-
from typing import List, Dict, Tuple, Optional, Annotated
3-
from pydantic import field_validator, ConfigDict, BaseModel, Field, BeforeValidator
4-
from typing_extensions import Literal
2+
from typing import List, Dict, Tuple, Optional, Annotated, Literal, TypeVar, Generic
3+
from pydantic import (
4+
field_validator,
5+
ConfigDict,
6+
BaseModel,
7+
Field,
8+
BeforeValidator,
9+
PlainSerializer,
10+
)
511
from dataclasses import dataclass
612

713
from . import types as pip_types, dev_types
@@ -21,6 +27,10 @@ def validate_opentrons_tiprack(v: str) -> str:
2127
return v
2228

2329

30+
EnumType = TypeVar("EnumType")
31+
EnumSerializer = Annotated[EnumType, PlainSerializer(lambda v: v.value)]
32+
33+
2434
# TODO (lc 12-5-2022) Ideally we can deprecate this
2535
# at somepoint once we load pipettes by channels and type
2636
@dataclass
@@ -286,12 +296,12 @@ class PipettePhysicalPropertiesDefinition(BaseModel):
286296
description="A list of pipette names that are compatible with this pipette.",
287297
alias="backCompatNames",
288298
)
289-
pipette_type: pip_types.PipetteModelType = Field(
299+
pipette_type: EnumSerializer[pip_types.PipetteModelType] = Field(
290300
...,
291301
description="The pipette model type (related to number of channels).",
292302
alias="model",
293303
)
294-
display_category: pip_types.PipetteGenerationType = Field(
304+
display_category: EnumSerializer[pip_types.PipetteGenerationType] = Field(
295305
..., description="The product model of the pipette.", alias="displayCategory"
296306
)
297307
pick_up_tip_configurations: PickUpTipConfigurations = Field(
@@ -313,7 +323,7 @@ class PipettePhysicalPropertiesDefinition(BaseModel):
313323
partial_tip_configurations: PartialTipDefinition = Field(
314324
..., alias="partialTipConfigurations"
315325
)
316-
channels: pip_types.PipetteChannelType = Field(
326+
channels: EnumSerializer[pip_types.PipetteChannelType] = Field(
317327
..., description="The maximum number of channels on the pipette."
318328
)
319329
shaft_diameter: float = Field(
@@ -329,7 +339,7 @@ class PipettePhysicalPropertiesDefinition(BaseModel):
329339
description="The distance of backlash on the plunger motor.",
330340
alias="backlashDistance",
331341
)
332-
quirks: List[pip_types.Quirks] = Field(
342+
quirks: List[EnumSerializer[pip_types.Quirks]] = Field(
333343
..., description="The list of quirks available for the loaded configuration"
334344
)
335345
tip_presence_check_distance_mm: float = Field(
@@ -373,17 +383,6 @@ def convert_plunger_positions(
373383
) -> Dict[pip_types.LiquidClasses, PlungerPositions]:
374384
return {pip_types.LiquidClasses[key]: value for key, value in v.items()}
375385

376-
# TODO[pydantic]: The following keys were removed: `json_encoders`.
377-
# Check https://docs.pydantic.dev/dev-v2/migration/#changes-to-config for more information.
378-
model_config = ConfigDict(
379-
json_encoders={
380-
pip_types.PipetteChannelType: lambda v: v.value,
381-
pip_types.PipetteModelType: lambda v: v.value,
382-
pip_types.PipetteGenerationType: lambda v: v.value,
383-
pip_types.Quirks: lambda v: v.value,
384-
}
385-
)
386-
387386

388387
class PipetteRowDefinition(BaseModel):
389388
key: str

shared-data/python/opentrons_shared_data/pipette/types.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import enum
22
from dataclasses import dataclass
3-
from typing import Union, Dict, Mapping, Tuple, cast
3+
from typing import Union, Dict, Mapping, Tuple, TypeVar, cast
44
from typing_extensions import Literal
55

66
"""Pipette Definition V2 Types"""

shared-data/python/opentrons_shared_data/protocol/models/protocol_schema_v6.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
from pydantic import ConfigDict, BaseModel, Field, model_validator, field_validator, TypeAdapter
1+
from pydantic import (
2+
ConfigDict,
3+
BaseModel,
4+
Field,
5+
model_validator,
6+
field_validator,
7+
TypeAdapter,
8+
)
29
from typing import Any, List, Optional, Dict, Union
310
from typing_extensions import Literal
411
from opentrons_shared_data.labware.models import LabwareDefinition

shared-data/python/tests/pipette/test_load_data.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ def test_update_pipette_configuration(
9090
base_configurations, v1_configuration_changes, liquid_class
9191
)
9292

93-
updated_configurations_dict = updated_configurations.dict()
93+
updated_configurations_dict = updated_configurations.model_dump()
9494
for k, v in v1_configuration_changes.items():
9595
if k == "tip_length":
9696
for i in updated_configurations_dict["liquid_properties"][liquid_class][

shared-data/python/tests/pipette/test_validate_schema.py

+47
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import os
2+
from typing import List
23
from opentrons_shared_data import get_shared_data_root
34

45
from opentrons_shared_data.pipette.pipette_definition import PipetteConfigurations
56
from opentrons_shared_data.pipette.load_data import load_definition
67
from opentrons_shared_data.pipette.pipette_load_name_conversions import (
78
convert_pipette_model,
89
)
10+
from opentrons_shared_data.pipette import types
911
from opentrons_shared_data.pipette.dev_types import PipetteModel
1012

1113

@@ -68,3 +70,48 @@ def test_pick_up_configs_tip_count_keys() -> None:
6870
== pick_up_tip_configs.speed_by_tip_count.keys()
6971
== pick_up_tip_configs.current_by_tip_count.keys()
7072
)
73+
74+
75+
def test_serializer() -> None:
76+
"""Verify that the serializer works as expected."""
77+
78+
loaded_model = load_definition(
79+
types.PipetteModelType.p1000,
80+
types.PipetteChannelType.NINETY_SIX_CHANNEL,
81+
types.PipetteVersionType(3, 3),
82+
)
83+
quirk_0 = types.Quirks.pickupTipShake
84+
quirk_1 = types.Quirks.dropTipShake
85+
loaded_model.quirks = [quirk_0, quirk_1]
86+
87+
assert loaded_model.pipette_type == types.PipetteModelType.p1000
88+
assert loaded_model.display_category == types.PipetteGenerationType.FLEX
89+
assert loaded_model.channels == types.PipetteChannelType.NINETY_SIX_CHANNEL
90+
91+
model_dict = loaded_model.model_dump()
92+
# each field should be the value of the enum class
93+
assert (
94+
isinstance(model_dict["pipette_type"], str)
95+
and model_dict["pipette_type"] == loaded_model.pipette_type.value
96+
)
97+
assert (
98+
isinstance(model_dict["display_category"], str)
99+
and model_dict["display_category"] == loaded_model.display_category.value
100+
)
101+
assert (
102+
isinstance(model_dict["channels"], int)
103+
and model_dict["channels"] == loaded_model.channels.value
104+
)
105+
106+
assert len(model_dict["quirks"]) == 2
107+
dict_quirk_0 = model_dict["quirks"][0]
108+
dict_quirk_1 = model_dict["quirks"][1]
109+
assert isinstance(dict_quirk_0, str) and dict_quirk_0 == quirk_0.value
110+
assert isinstance(dict_quirk_1, str) and dict_quirk_1 == quirk_1.value
111+
112+
113+
# TODO: (AA, 4/9/2024) we should add a test to validate the dumped json to make
114+
# sure we can re-load it as the BaseModel class. But we haven't added serializer
115+
# for other enums yet, such as LiquidClass, and since we haven't been
116+
# creating the definition files using model_dump/model_dump_json, it is okay to
117+
# skip this for now.

0 commit comments

Comments
 (0)