diff --git a/robot-server/robot_server/commands/router.py b/robot-server/robot_server/commands/router.py index 0d617e38a5a..2b0067afb4d 100644 --- a/robot-server/robot_server/commands/router.py +++ b/robot-server/robot_server/commands/router.py @@ -115,7 +115,7 @@ async def create_command( response_data = cast(StatelessCommand, engine.state_view.commands.get(command.id)) return await PydanticResponse.create( - content=SimpleBody.construct(data=response_data), + content=SimpleBody.model_construct(data=response_data), status_code=status.HTTP_201_CREATED, ) @@ -160,7 +160,7 @@ async def get_commands_list( meta = MultiBodyMeta(cursor=cmd_slice.cursor, totalLength=cmd_slice.total_length) return await PydanticResponse.create( - content=SimpleMultiBody.construct(data=commands, meta=meta), + content=SimpleMultiBody.model_construct(data=commands, meta=meta), status_code=status.HTTP_200_OK, ) @@ -196,6 +196,6 @@ async def get_command( raise CommandNotFound.from_exc(e).as_error(status.HTTP_404_NOT_FOUND) from e return await PydanticResponse.create( - content=SimpleBody.construct(data=cast(StatelessCommand, command)), + content=SimpleBody.model_construct(data=cast(StatelessCommand, command)), status_code=status.HTTP_200_OK, ) diff --git a/robot-server/robot_server/deck_configuration/defaults.py b/robot-server/robot_server/deck_configuration/defaults.py index a591e9798df..bccc04ed461 100644 --- a/robot-server/robot_server/deck_configuration/defaults.py +++ b/robot-server/robot_server/deck_configuration/defaults.py @@ -4,84 +4,84 @@ from . import models -_for_flex = models.DeckConfigurationRequest.construct( +_for_flex = models.DeckConfigurationRequest.model_construct( cutoutFixtures=[ - models.CutoutFixture.construct( + models.CutoutFixture.model_construct( cutoutId="cutoutA1", cutoutFixtureId="singleLeftSlot" ), - models.CutoutFixture.construct( + models.CutoutFixture.model_construct( cutoutId="cutoutB1", cutoutFixtureId="singleLeftSlot" ), - models.CutoutFixture.construct( + models.CutoutFixture.model_construct( cutoutId="cutoutC1", cutoutFixtureId="singleLeftSlot" ), - models.CutoutFixture.construct( + models.CutoutFixture.model_construct( cutoutId="cutoutD1", cutoutFixtureId="singleLeftSlot" ), - models.CutoutFixture.construct( + models.CutoutFixture.model_construct( cutoutId="cutoutA2", cutoutFixtureId="singleCenterSlot" ), - models.CutoutFixture.construct( + models.CutoutFixture.model_construct( cutoutId="cutoutB2", cutoutFixtureId="singleCenterSlot" ), - models.CutoutFixture.construct( + models.CutoutFixture.model_construct( cutoutId="cutoutC2", cutoutFixtureId="singleCenterSlot" ), - models.CutoutFixture.construct( + models.CutoutFixture.model_construct( cutoutId="cutoutD2", cutoutFixtureId="singleCenterSlot" ), - models.CutoutFixture.construct( + models.CutoutFixture.model_construct( cutoutId="cutoutA3", cutoutFixtureId="trashBinAdapter" ), - models.CutoutFixture.construct( + models.CutoutFixture.model_construct( cutoutId="cutoutB3", cutoutFixtureId="singleRightSlot" ), - models.CutoutFixture.construct( + models.CutoutFixture.model_construct( cutoutId="cutoutC3", cutoutFixtureId="singleRightSlot" ), - models.CutoutFixture.construct( + models.CutoutFixture.model_construct( cutoutId="cutoutD3", cutoutFixtureId="singleRightSlot" ), ] ) -_for_ot2 = models.DeckConfigurationRequest.construct( +_for_ot2 = models.DeckConfigurationRequest.model_construct( cutoutFixtures=[ - models.CutoutFixture.construct( + models.CutoutFixture.model_construct( cutoutId="cutout1", cutoutFixtureId="singleStandardSlot" ), - models.CutoutFixture.construct( + models.CutoutFixture.model_construct( cutoutId="cutout2", cutoutFixtureId="singleStandardSlot" ), - models.CutoutFixture.construct( + models.CutoutFixture.model_construct( cutoutId="cutout3", cutoutFixtureId="singleStandardSlot" ), - models.CutoutFixture.construct( + models.CutoutFixture.model_construct( cutoutId="cutout4", cutoutFixtureId="singleStandardSlot" ), - models.CutoutFixture.construct( + models.CutoutFixture.model_construct( cutoutId="cutout5", cutoutFixtureId="singleStandardSlot" ), - models.CutoutFixture.construct( + models.CutoutFixture.model_construct( cutoutId="cutout6", cutoutFixtureId="singleStandardSlot" ), - models.CutoutFixture.construct( + models.CutoutFixture.model_construct( cutoutId="cutout7", cutoutFixtureId="singleStandardSlot" ), - models.CutoutFixture.construct( + models.CutoutFixture.model_construct( cutoutId="cutout8", cutoutFixtureId="singleStandardSlot" ), - models.CutoutFixture.construct( + models.CutoutFixture.model_construct( cutoutId="cutout9", cutoutFixtureId="singleStandardSlot" ), - models.CutoutFixture.construct( + models.CutoutFixture.model_construct( cutoutId="cutout10", cutoutFixtureId="singleStandardSlot" ), - models.CutoutFixture.construct( + models.CutoutFixture.model_construct( cutoutId="cutout11", cutoutFixtureId="singleStandardSlot" ), - models.CutoutFixture.construct( + models.CutoutFixture.model_construct( cutoutId="cutout12", cutoutFixtureId="fixedTrashSlot" ), ] diff --git a/robot-server/robot_server/deck_configuration/router.py b/robot-server/robot_server/deck_configuration/router.py index 4e00a3d707e..04ba198ced4 100644 --- a/robot-server/robot_server/deck_configuration/router.py +++ b/robot-server/robot_server/deck_configuration/router.py @@ -76,12 +76,12 @@ async def put_deck_configuration( # noqa: D103 if len(validation_errors) == 0: success_data = await store.set(request=request_body.data, last_modified_at=now) return await PydanticResponse.create( - content=SimpleBody.construct(data=success_data) + content=SimpleBody.model_construct(data=success_data) ) else: error_data = validation_mapping.map_out(validation_errors) return await PydanticResponse.create( - content=ErrorBody.construct(errors=error_data), + content=ErrorBody.model_construct(errors=error_data), status_code=HTTP_422_UNPROCESSABLE_ENTITY, ) @@ -107,5 +107,5 @@ async def get_deck_configuration( # noqa: D103 store: DeckConfigurationStore = fastapi.Depends(get_deck_configuration_store), ) -> PydanticResponse[SimpleBody[models.DeckConfigurationResponse]]: return await PydanticResponse.create( - content=SimpleBody.construct(data=await store.get()) + content=SimpleBody.model_construct(data=await store.get()) ) diff --git a/robot-server/robot_server/deck_configuration/store.py b/robot-server/robot_server/deck_configuration/store.py index feffa539ec0..98e26aa7429 100644 --- a/robot-server/robot_server/deck_configuration/store.py +++ b/robot-server/robot_server/deck_configuration/store.py @@ -90,7 +90,7 @@ async def _get_assuming_locked(self) -> models.DeckConfigurationResponse: # 5XX errors. We think falling back to an arbitrary default is safe because users # of the Opentrons App will always have an opportunity to view and confirm their robot's # deck configuration before running a protocol. - return models.DeckConfigurationResponse.construct( + return models.DeckConfigurationResponse.model_construct( cutoutFixtures=defaults.for_deck_definition( self._deck_type.value ).cutoutFixtures, @@ -99,13 +99,13 @@ async def _get_assuming_locked(self) -> models.DeckConfigurationResponse: else: cutout_fixtures_from_storage, last_modified_at = from_storage cutout_fixtures = [ - models.CutoutFixture.construct( + models.CutoutFixture.model_construct( cutoutFixtureId=e.cutout_fixture_id, cutoutId=e.cutout_id, ) for e in cutout_fixtures_from_storage ] - return models.DeckConfigurationResponse.construct( + return models.DeckConfigurationResponse.model_construct( cutoutFixtures=cutout_fixtures, lastModifiedAt=last_modified_at, ) diff --git a/robot-server/robot_server/instruments/router.py b/robot-server/robot_server/instruments/router.py index 28c756528a9..4e8d992e201 100644 --- a/robot-server/robot_server/instruments/router.py +++ b/robot-server/robot_server/instruments/router.py @@ -63,7 +63,7 @@ def _pipette_dict_to_pipette_res( """Convert PipetteDict to Pipette response model.""" if pipette_dict: calibration_data = pipette_offset - return Pipette.construct( + return Pipette.model_construct( firmwareVersion=str(fw_version) if fw_version else None, ok=True, mount=MountType.from_hw_mount(mount).value, @@ -75,7 +75,7 @@ def _pipette_dict_to_pipette_res( channels=pipette_dict["channels"], min_volume=pipette_dict["min_volume"], max_volume=pipette_dict["max_volume"], - calibratedOffset=InstrumentCalibrationData.construct( + calibratedOffset=InstrumentCalibrationData.model_construct( offset=Vec3f( x=calibration_data.offset.x, y=calibration_data.offset.y, @@ -84,9 +84,9 @@ def _pipette_dict_to_pipette_res( source=calibration_data.source, last_modified=calibration_data.last_modified, reasonability_check_failures=[ - InconsistentCalibrationFailure.construct( + InconsistentCalibrationFailure.model_construct( offsets={ - k.name: Vec3f.construct(x=v.x, y=v.y, z=v.z) + k.name: Vec3f.model_construct(x=v.x, y=v.y, z=v.z) for k, v in failure.offsets.items() }, limit=failure.limit, @@ -106,7 +106,7 @@ def _gripper_dict_to_gripper_res( ) -> Gripper: """Convert GripperDict to Gripper response model.""" calibration_data = gripper_dict["calibration_offset"] - return Gripper.construct( + return Gripper.model_construct( firmwareVersion=str(fw_version) if fw_version else None, ok=True, mount=MountType.EXTENSION.value, @@ -115,7 +115,7 @@ def _gripper_dict_to_gripper_res( subsystem=SubSystem.from_hw(HWSubSystem.of_mount(OT3Mount.GRIPPER)), data=GripperData( jawState=gripper_dict["state"].name.lower(), - calibratedOffset=InstrumentCalibrationData.construct( + calibratedOffset=InstrumentCalibrationData.model_construct( offset=Vec3f( x=calibration_data.offset.x, y=calibration_data.offset.y, @@ -219,7 +219,7 @@ async def _get_attached_instruments_ot3( await hardware.cache_instruments(skip_if_would_block=True) response_data = await _get_instrument_data(hardware) return await PydanticResponse.create( - content=SimpleMultiBody.construct( + content=SimpleMultiBody.model_construct( data=response_data, meta=MultiBodyMeta(cursor=0, totalLength=len(response_data)), ), @@ -243,7 +243,7 @@ async def _get_attached_instruments_ot2( if pipette_dict ] return await PydanticResponse.create( - content=SimpleMultiBody.construct( + content=SimpleMultiBody.model_construct( data=response_data, meta=MultiBodyMeta(cursor=0, totalLength=len(response_data)), ), diff --git a/robot-server/robot_server/maintenance_runs/maintenance_run_data_manager.py b/robot-server/robot_server/maintenance_runs/maintenance_run_data_manager.py index 084a7552a3a..130f19c7276 100644 --- a/robot-server/robot_server/maintenance_runs/maintenance_run_data_manager.py +++ b/robot-server/robot_server/maintenance_runs/maintenance_run_data_manager.py @@ -24,7 +24,7 @@ def _build_run( created_at: datetime, state_summary: Optional[StateSummary], ) -> MaintenanceRun: - state_summary = state_summary or StateSummary.construct( + state_summary = state_summary or StateSummary.model_construct( status=EngineStatus.IDLE, errors=[], labware=[], @@ -33,7 +33,7 @@ def _build_run( modules=[], liquids=[], ) - return MaintenanceRun.construct( + return MaintenanceRun.model_construct( id=run_id, createdAt=created_at, status=state_summary.status, diff --git a/robot-server/robot_server/maintenance_runs/router/base_router.py b/robot-server/robot_server/maintenance_runs/router/base_router.py index c115d46509f..fc329f853ee 100644 --- a/robot-server/robot_server/maintenance_runs/router/base_router.py +++ b/robot-server/robot_server/maintenance_runs/router/base_router.py @@ -188,7 +188,7 @@ async def create_run( log.info(f'Created an empty run "{run_id}"".') return await PydanticResponse.create( - content=SimpleBody.construct(data=run_data), + content=SimpleBody.model_construct(data=run_data), status_code=status.HTTP_201_CREATED, ) @@ -221,11 +221,11 @@ async def get_current_run( data = run_data_manager.get(current_run_id) links = AllRunsLinks( - current=ResourceLink.construct(href=f"/maintenance_runs/{current_run_id}") + current=ResourceLink.model_construct(href=f"/maintenance_runs/{current_run_id}") ) return await PydanticResponse.create( - content=Body.construct(data=data, links=links), + content=Body.model_construct(data=data, links=links), status_code=status.HTTP_200_OK, ) @@ -249,7 +249,7 @@ async def get_run( run_data: Data of the run specified in the runId url parameter. """ return await PydanticResponse.create( - content=SimpleBody.construct(data=run_data), + content=SimpleBody.model_construct(data=run_data), status_code=status.HTTP_200_OK, ) @@ -285,6 +285,6 @@ async def remove_run( raise RunNotFound(detail=str(e)).as_error(status.HTTP_404_NOT_FOUND) from e return await PydanticResponse.create( - content=SimpleEmptyBody.construct(), + content=SimpleEmptyBody.model_construct(), status_code=status.HTTP_200_OK, ) diff --git a/robot-server/robot_server/maintenance_runs/router/commands_router.py b/robot-server/robot_server/maintenance_runs/router/commands_router.py index f90cf2dc171..751c914343c 100644 --- a/robot-server/robot_server/maintenance_runs/router/commands_router.py +++ b/robot-server/robot_server/maintenance_runs/router/commands_router.py @@ -199,7 +199,7 @@ async def create_run_command( response_data = protocol_engine.state_view.commands.get(command.id) return await PydanticResponse.create( - content=SimpleBody.construct(data=response_data), + content=SimpleBody.model_construct(data=response_data), status_code=status.HTTP_201_CREATED, ) @@ -259,7 +259,7 @@ async def get_run_commands( current_command = run_data_manager.get_current_command(run_id=runId) data = [ - MaintenanceRunCommandSummary.construct( + MaintenanceRunCommandSummary.model_construct( id=c.id, key=c.key, commandType=c.commandType, @@ -294,7 +294,7 @@ async def get_run_commands( ) return await PydanticResponse.create( - content=MultiBody.construct(data=data, meta=meta, links=links), + content=MultiBody.model_construct(data=data, meta=meta, links=links), status_code=status.HTTP_200_OK, ) @@ -336,6 +336,6 @@ async def get_run_command( raise CommandNotFound(detail=str(e)).as_error(status.HTTP_404_NOT_FOUND) from e return await PydanticResponse.create( - content=SimpleBody.construct(data=command), + content=SimpleBody.model_construct(data=command), status_code=status.HTTP_200_OK, ) diff --git a/robot-server/robot_server/maintenance_runs/router/labware_router.py b/robot-server/robot_server/maintenance_runs/router/labware_router.py index 95e1c01f9bc..4e8ffdb3900 100644 --- a/robot-server/robot_server/maintenance_runs/router/labware_router.py +++ b/robot-server/robot_server/maintenance_runs/router/labware_router.py @@ -51,7 +51,7 @@ async def add_labware_offset( log.info(f'Added labware offset "{added_offset.id}"' f' to run "{run.id}".') return await PydanticResponse.create( - content=SimpleBody.construct(data=added_offset), + content=SimpleBody.model_construct(data=added_offset), status_code=status.HTTP_201_CREATED, ) @@ -88,8 +88,8 @@ async def add_labware_definition( log.info(f'Added labware definition "{uri}"' f' to run "{run.id}".') return PydanticResponse( - content=SimpleBody.construct( - data=LabwareDefinitionSummary.construct(definitionUri=uri) + content=SimpleBody.model_construct( + data=LabwareDefinitionSummary.model_construct(definitionUri=uri) ), status_code=status.HTTP_201_CREATED, ) diff --git a/robot-server/robot_server/modules/router.py b/robot-server/robot_server/modules/router.py index 8155a88c4a6..fbe3b408593 100644 --- a/robot-server/robot_server/modules/router.py +++ b/robot-server/robot_server/modules/router.py @@ -67,7 +67,7 @@ async def get_attached_modules( module_identity=module_identity, live_data=mod.live_data, usb_port=mod.usb_port, - module_offset=ModuleCalibrationData.construct( + module_offset=ModuleCalibrationData.model_construct( offset=Vec3f( x=calibrated.offset.x, y=calibrated.offset.y, @@ -83,7 +83,7 @@ async def get_attached_modules( ) return await PydanticResponse.create( - content=SimpleMultiBody.construct( + content=SimpleMultiBody.model_construct( data=response_data, meta=MultiBodyMeta(cursor=0, totalLength=len(response_data)), ), diff --git a/robot-server/robot_server/persistence/pydantic.py b/robot-server/robot_server/persistence/pydantic.py index c3486394ad4..5fc38aea22f 100644 --- a/robot-server/robot_server/persistence/pydantic.py +++ b/robot-server/robot_server/persistence/pydantic.py @@ -1,7 +1,7 @@ """Store Pydantic objects in the SQL database.""" from typing import Type, TypeVar -from pydantic import BaseModel, parse_raw_as +from pydantic import BaseModel _BaseModelT = TypeVar("_BaseModelT", bound=BaseModel) @@ -19,4 +19,4 @@ def pydantic_to_json(obj: BaseModel) -> str: def json_to_pydantic(model: Type[_BaseModelT], json: str) -> _BaseModelT: """Parse a Pydantic object stored in the SQL database.""" - return parse_raw_as(model, json) + return model.model_validate_json(json) diff --git a/robot-server/robot_server/protocols/analysis_store.py b/robot-server/robot_server/protocols/analysis_store.py index b0ea474ec07..f4ca483a3d5 100644 --- a/robot-server/robot_server/protocols/analysis_store.py +++ b/robot-server/robot_server/protocols/analysis_store.py @@ -176,7 +176,7 @@ async def update( else: result = AnalysisResult.OK - completed_analysis = CompletedAnalysis.construct( + completed_analysis = CompletedAnalysis.model_construct( id=analysis_id, result=result, robotType=robot_type, @@ -246,7 +246,9 @@ def get_summaries_by_protocol(self, protocol_id: str) -> List[AnalysisSummary]: protocol_id=protocol_id ) completed_analysis_summaries = [ - AnalysisSummary.construct(id=analysis_id, status=AnalysisStatus.COMPLETED) + AnalysisSummary.model_construct( + id=analysis_id, status=AnalysisStatus.COMPLETED + ) for analysis_id in completed_analysis_ids ] @@ -379,7 +381,7 @@ def add(self, protocol_id: str, analysis_id: str) -> PendingAnalysis: protocol_id not in self._analysis_ids_by_protocol_id ), "Protocol must not already have a pending analysis." - new_pending_analysis = PendingAnalysis.construct(id=analysis_id) + new_pending_analysis = PendingAnalysis.model_construct(id=analysis_id) self._analyses_by_id[analysis_id] = new_pending_analysis self._analysis_ids_by_protocol_id[protocol_id] = analysis_id diff --git a/robot-server/robot_server/protocols/completed_analysis_store.py b/robot-server/robot_server/protocols/completed_analysis_store.py index 58017e4398a..0fade79fbc1 100644 --- a/robot-server/robot_server/protocols/completed_analysis_store.py +++ b/robot-server/robot_server/protocols/completed_analysis_store.py @@ -9,7 +9,7 @@ import sqlalchemy import anyio -from pydantic import parse_raw_as +from pydantic import TypeAdapter from robot_server.persistence.database import sqlite_rowid from robot_server.persistence.tables import analysis_table @@ -22,6 +22,9 @@ _log = getLogger(__name__) +RtpAdapter = TypeAdapter(Dict[str, RunTimeParameterAnalysisData]) + + @dataclass class CompletedAnalysisResource: """A protocol analysis that's been completed, storable in a SQL database. @@ -123,14 +126,7 @@ async def get_run_time_parameter_values_and_defaults( def parse_rtp_dict() -> Dict[str, RunTimeParameterAnalysisData]: rtp_contents = sql_row.run_time_parameter_values_and_defaults - return ( - parse_raw_as( - Dict[str, RunTimeParameterAnalysisData], - sql_row.run_time_parameter_values_and_defaults, - ) - if rtp_contents - else {} - ) + return RtpAdapter.validate_python(rtp_contents) if rtp_contents else {} # In most cases, this parsing should be quite quick but theoretically # there could be an unexpectedly large number of run time params. diff --git a/robot-server/robot_server/protocols/protocol_models.py b/robot-server/robot_server/protocols/protocol_models.py index 7d5697388a7..a24ce0a0cbe 100644 --- a/robot-server/robot_server/protocols/protocol_models.py +++ b/robot-server/robot_server/protocols/protocol_models.py @@ -39,6 +39,7 @@ class Metadata(BaseModel): protocols define their `apiLevel` inside their metadata, but this should be considered an exception to the rule. """ + model_config = ConfigDict(extra="allow") diff --git a/robot-server/robot_server/protocols/router.py b/robot-server/robot_server/protocols/router.py index 204f2563824..b10ec613e51 100644 --- a/robot-server/robot_server/protocols/router.py +++ b/robot-server/robot_server/protocols/router.py @@ -257,7 +257,7 @@ async def create_protocol( status.HTTP_503_SERVICE_UNAVAILABLE ) from error - data = Protocol.construct( + data = Protocol.model_construct( id=cached_protocol_id, createdAt=resource.created_at, protocolType=resource.source.config.protocol_type, @@ -277,7 +277,7 @@ async def create_protocol( ) return await PydanticResponse.create( - content=SimpleBody.construct(data=data), + content=SimpleBody.model_construct(data=data), # not returning a 201 because we're not actually creating a new resource status_code=status.HTTP_200_OK, ) @@ -337,7 +337,7 @@ async def create_protocol( log.info(f'Created protocol "{protocol_id}" and started analysis "{analysis_id}".') return await PydanticResponse.create( - content=SimpleBody.construct(data=data), + content=SimpleBody.model_construct(data=data), status_code=status.HTTP_201_CREATED, ) @@ -408,7 +408,7 @@ async def get_protocols( """ protocol_resources = protocol_store.get_all() data = [ - Protocol.construct( + Protocol.model_construct( id=r.protocol_id, createdAt=r.created_at, protocolType=r.source.config.protocol_type, @@ -423,7 +423,7 @@ async def get_protocols( meta = MultiBodyMeta(cursor=0, totalLength=len(data)) return await PydanticResponse.create( - content=SimpleMultiBody.construct(data=data, meta=meta), + content=SimpleMultiBody.model_construct(data=data, meta=meta), status_code=status.HTTP_200_OK, ) @@ -454,7 +454,7 @@ async def get_protocol_ids( meta = MultiBodyMeta(cursor=0, totalLength=len(protocol_ids)) return await PydanticResponse.create( - content=SimpleMultiBody.construct(data=protocol_ids, meta=meta) + content=SimpleMultiBody.model_construct(data=protocol_ids, meta=meta) ) @@ -487,7 +487,7 @@ async def get_protocol_by_id( analyses = analysis_store.get_summaries_by_protocol(protocol_id=protocolId) referencing_run_ids = protocol_store.get_referencing_run_ids(protocolId) - data = Protocol.construct( + data = Protocol.model_construct( id=protocolId, createdAt=resource.created_at, protocolType=resource.source.config.protocol_type, @@ -500,15 +500,15 @@ async def get_protocol_by_id( ], ) - links = ProtocolLinks.construct( + links = ProtocolLinks.model_construct( referencingRuns=[ - RunLink.construct(id=run_id, href=f"/runs/{run_id}") + RunLink.model_construct(id=run_id, href=f"/runs/{run_id}") for run_id in referencing_run_ids ] ) return await PydanticResponse.create( - content=Body.construct( + content=Body.model_construct( data=data, links=links, ), @@ -546,7 +546,7 @@ async def delete_protocol_by_id( raise ProtocolUsedByRun(detail=str(e)).as_error(status.HTTP_409_CONFLICT) from e return await PydanticResponse.create( - content=SimpleEmptyBody.construct(), + content=SimpleEmptyBody.model_construct(), status_code=status.HTTP_200_OK, ) @@ -613,7 +613,7 @@ async def create_protocol_analysis( status.HTTP_503_SERVICE_UNAVAILABLE ) from error return await PydanticResponse.create( - content=SimpleMultiBody.construct( + content=SimpleMultiBody.model_construct( data=analysis_summaries, meta=MultiBodyMeta(cursor=0, totalLength=len(analysis_summaries)), ), @@ -654,7 +654,7 @@ async def get_protocol_analyses( analyses = await analysis_store.get_by_protocol(protocolId) return await PydanticResponse.create( - content=SimpleMultiBody.construct( + content=SimpleMultiBody.model_construct( data=analyses, meta=MultiBodyMeta(cursor=0, totalLength=len(analyses)), ) @@ -700,7 +700,9 @@ async def get_protocol_analysis_by_id( status.HTTP_404_NOT_FOUND ) from error - return await PydanticResponse.create(content=SimpleBody.construct(data=analysis)) + return await PydanticResponse.create( + content=SimpleBody.model_construct(data=analysis) + ) @protocols_router.get( diff --git a/robot-server/robot_server/robot/calibration/check/models.py b/robot-server/robot_server/robot/calibration/check/models.py index 6a3f0769578..d301b413606 100644 --- a/robot-server/robot_server/robot/calibration/check/models.py +++ b/robot-server/robot_server/robot/calibration/check/models.py @@ -123,32 +123,35 @@ class CalibrationCheckSessionStatus(BaseModel): supportedCommands: List[str] = Field( ..., description="A list of supported commands for this user flow" ) - model_config = ConfigDict(arbitrary_types_allowed=True, json_schema_extra={ - "examples": [ - { - "instruments": [ - { - "model": "p300_single_v1.5", - "name": "p300_single", - "tip_length": 51.7, - "mount": "left", - "id": "P3HS12123041", + model_config = ConfigDict( + arbitrary_types_allowed=True, + json_schema_extra={ + "examples": [ + { + "instruments": [ + { + "model": "p300_single_v1.5", + "name": "p300_single", + "tip_length": 51.7, + "mount": "left", + "id": "P3HS12123041", + }, + { + "model": None, + "name": None, + "tip_length": None, + "mount": "right", + "id": None, + }, + ], + "currentStep": "sessionStarted", + "comparisonsByPipette": { + "comparingFirstPipetteHeight": { + "differenceVector": [1, 0, 0], + "exceedsThreshold": False, + } }, - { - "model": None, - "name": None, - "tip_length": None, - "mount": "right", - "id": None, - }, - ], - "currentStep": "sessionStarted", - "comparisonsByPipette": { - "comparingFirstPipetteHeight": { - "differenceVector": [1, 0, 0], - "exceedsThreshold": False, - } - }, - } - ] - }) + } + ] + }, + ) diff --git a/robot-server/robot_server/robot/calibration/deck/models.py b/robot-server/robot_server/robot/calibration/deck/models.py index b5a4a1a9b89..7e1f1fa5fee 100644 --- a/robot-server/robot_server/robot/calibration/deck/models.py +++ b/robot-server/robot_server/robot/calibration/deck/models.py @@ -15,27 +15,29 @@ class DeckCalibrationSessionStatus(BaseModel): supportedCommands: List[str] = Field( ..., description="A list of supported commands for this user flow" ) - model_config = ConfigDict(json_schema_extra={ - "example": [ - { - "instrument": { - "model": "p300_single_v1.5", - "name": "p300_single", - "tip_length": 42, - "mount": "right", - "serial": "P3HS12123041", - }, - "currentStep": "sessionStarted", - "labware": [ - { - "slot": "8", - "loadName": "opentrons_96_tiprack_300ul", - "namespace": "opentrons", - "version": 1, - "isTiprack": "true", - "definition": {"ordering": "the ordering section..."}, - } - ], - } - ] - }) + model_config = ConfigDict( + json_schema_extra={ + "example": [ + { + "instrument": { + "model": "p300_single_v1.5", + "name": "p300_single", + "tip_length": 42, + "mount": "right", + "serial": "P3HS12123041", + }, + "currentStep": "sessionStarted", + "labware": [ + { + "slot": "8", + "loadName": "opentrons_96_tiprack_300ul", + "namespace": "opentrons", + "version": 1, + "isTiprack": "true", + "definition": {"ordering": "the ordering section..."}, + } + ], + } + ] + } + ) diff --git a/robot-server/robot_server/robot/calibration/pipette_offset/models.py b/robot-server/robot_server/robot/calibration/pipette_offset/models.py index 5d25c6313d5..26d70046d1f 100644 --- a/robot-server/robot_server/robot/calibration/pipette_offset/models.py +++ b/robot-server/robot_server/robot/calibration/pipette_offset/models.py @@ -23,29 +23,31 @@ class PipetteOffsetCalibrationSessionStatus(BaseModel): nextSteps: Optional[NextSteps] = Field( None, description="Next Available Steps in Session" ) - model_config = ConfigDict(json_schema_extra={ - "examples": [ - { - "instrument": { - "model": "p300_single_v1.5", - "name": "p300_single", - "tip_length": 51.7, - "mount": "left", - "serial": "P3HS12123041", - }, - "currentStep": "sessionStarted", - "nextSteps": {"links": {"loadLabware": {"url": "", "params": {}}}}, - "labware": [ - { - "slot": "8", - "loadName": "tiprack_loadname", - "namespace": "opentrons", - "version": "1", - "isTiprack": "true", - "definition": {"ordering": "the ordering section..."}, + model_config = ConfigDict( + json_schema_extra={ + "examples": [ + { + "instrument": { + "model": "p300_single_v1.5", + "name": "p300_single", + "tip_length": 51.7, + "mount": "left", + "serial": "P3HS12123041", }, - ], - "shouldPerformTipLength": True, - } - ] - }) + "currentStep": "sessionStarted", + "nextSteps": {"links": {"loadLabware": {"url": "", "params": {}}}}, + "labware": [ + { + "slot": "8", + "loadName": "tiprack_loadname", + "namespace": "opentrons", + "version": "1", + "isTiprack": "true", + "definition": {"ordering": "the ordering section..."}, + }, + ], + "shouldPerformTipLength": True, + } + ] + } + ) diff --git a/robot-server/robot_server/robot/calibration/tip_length/models.py b/robot-server/robot_server/robot/calibration/tip_length/models.py index 5ea06b42d27..30d698abb5d 100644 --- a/robot-server/robot_server/robot/calibration/tip_length/models.py +++ b/robot-server/robot_server/robot/calibration/tip_length/models.py @@ -18,36 +18,38 @@ class TipCalibrationSessionStatus(BaseModel): supportedCommands: List[str] = Field( ..., description="A list of supported commands for this user flow" ) - model_config = ConfigDict(json_schema_extra={ - "examples": [ - { - "instrument": { - "model": "p300_single_v1.5", - "name": "p300_single", - "tip_length": 51.7, - "mount": "left", - "serial": "P3HS12123041", - }, - "currentStep": "sessionStarted", - "nextSteps": {"links": {"loadLabware": {"url": "", "params": {}}}}, - "labware": [ - { - "slot": "8", - "loadName": "tiprack_loadname", - "namespace": "opentrons", - "version": "1", - "isTiprack": "true", - "definition": {"ordering": "the ordering section..."}, + model_config = ConfigDict( + json_schema_extra={ + "examples": [ + { + "instrument": { + "model": "p300_single_v1.5", + "name": "p300_single", + "tip_length": 51.7, + "mount": "left", + "serial": "P3HS12123041", }, - { - "slot": "3", - "loadName": "cal_block_loadname", - "namespace": "opentrons", - "version": "1", - "isTiprack": "false", - "definition": {"ordering": "the ordering section..."}, - }, - ], - } - ] - }) + "currentStep": "sessionStarted", + "nextSteps": {"links": {"loadLabware": {"url": "", "params": {}}}}, + "labware": [ + { + "slot": "8", + "loadName": "tiprack_loadname", + "namespace": "opentrons", + "version": "1", + "isTiprack": "true", + "definition": {"ordering": "the ordering section..."}, + }, + { + "slot": "3", + "loadName": "cal_block_loadname", + "namespace": "opentrons", + "version": "1", + "isTiprack": "false", + "definition": {"ordering": "the ordering section..."}, + }, + ], + } + ] + } + ) diff --git a/robot-server/robot_server/robot/control/router.py b/robot-server/robot_server/robot/control/router.py index 012d9d63997..313aa088b06 100644 --- a/robot-server/robot_server/robot/control/router.py +++ b/robot-server/robot_server/robot/control/router.py @@ -29,12 +29,12 @@ async def _get_estop_status_response( estop_handler: EstopHandler, ) -> PydanticResponse[SimpleBody[EstopStatusModel]]: """Helper to generate the current Estop Status as a response model.""" - data = EstopStatusModel.construct( + data = EstopStatusModel.model_construct( status=estop_handler.get_state(), leftEstopPhysicalStatus=estop_handler.get_left_physical_status(), rightEstopPhysicalStatus=estop_handler.get_right_physical_status(), ) - return await PydanticResponse.create(content=SimpleBody.construct(data=data)) + return await PydanticResponse.create(content=SimpleBody.model_construct(data=data)) @PydanticResponse.wrap_route( @@ -89,8 +89,8 @@ async def get_door_status( door_required: bool = Depends(get_door_switch_required), ) -> PydanticResponse[SimpleBody[DoorStatusModel]]: return await PydanticResponse.create( - content=SimpleBody.construct( - data=DoorStatusModel.construct( + content=SimpleBody.model_construct( + data=DoorStatusModel.model_construct( status=DoorState.from_hw_physical_status(hardware.door_state), doorRequiredClosedForProtocol=door_required, ) diff --git a/robot-server/robot_server/runs/router/actions_router.py b/robot-server/robot_server/runs/router/actions_router.py index b662d59f554..bb054f79fbb 100644 --- a/robot-server/robot_server/runs/router/actions_router.py +++ b/robot-server/robot_server/runs/router/actions_router.py @@ -140,6 +140,6 @@ async def create_run_action( raise RunNotFound.from_exc(e).as_error(status.HTTP_404_NOT_FOUND) from e return await PydanticResponse.create( - content=SimpleBody.construct(data=action), + content=SimpleBody.model_construct(data=action), status_code=status.HTTP_201_CREATED, ) diff --git a/robot-server/robot_server/runs/router/base_router.py b/robot-server/robot_server/runs/router/base_router.py index 728966823fb..3c4b76ab773 100644 --- a/robot-server/robot_server/runs/router/base_router.py +++ b/robot-server/robot_server/runs/router/base_router.py @@ -200,7 +200,7 @@ async def create_run( log.info(f'Created protocol run "{run_id}" from protocol "{protocol_id}".') return await PydanticResponse.create( - content=SimpleBody.construct(data=run_data), + content=SimpleBody.model_construct(data=run_data), status_code=status.HTTP_201_CREATED, ) @@ -238,13 +238,13 @@ async def get_runs( current_run_id = run_data_manager.current_run_id meta = MultiBodyMeta(cursor=0, totalLength=len(data)) links = AllRunsLinks( - current=ResourceLink.construct(href=f"/runs/{current_run_id}") + current=ResourceLink.model_construct(href=f"/runs/{current_run_id}") if current_run_id is not None else None ) return await PydanticResponse.create( - content=MultiBody.construct(data=data, links=links, meta=meta), + content=MultiBody.model_construct(data=data, links=links, meta=meta), status_code=status.HTTP_200_OK, ) @@ -268,7 +268,7 @@ async def get_run( run_data: Data of the run specified in the runId url parameter. """ return await PydanticResponse.create( - content=SimpleBody.construct(data=run_data), + content=SimpleBody.model_construct(data=run_data), status_code=status.HTTP_200_OK, ) @@ -303,7 +303,7 @@ async def remove_run( raise RunNotFound(detail=str(e)).as_error(status.HTTP_404_NOT_FOUND) from e return await PydanticResponse.create( - content=SimpleEmptyBody.construct(), + content=SimpleEmptyBody.model_construct(), status_code=status.HTTP_200_OK, ) @@ -343,6 +343,6 @@ async def update_run( raise RunNotFound(detail=str(e)).as_error(status.HTTP_404_NOT_FOUND) from e return await PydanticResponse.create( - content=SimpleBody.construct(data=run_data), + content=SimpleBody.model_construct(data=run_data), status_code=status.HTTP_200_OK, ) diff --git a/robot-server/robot_server/runs/router/commands_router.py b/robot-server/robot_server/runs/router/commands_router.py index 734d1a26066..ff3dc937ab9 100644 --- a/robot-server/robot_server/runs/router/commands_router.py +++ b/robot-server/robot_server/runs/router/commands_router.py @@ -224,7 +224,7 @@ async def create_run_command( response_data = protocol_engine.state_view.commands.get(command.id) return await PydanticResponse.create( - content=SimpleBody.construct(data=response_data), + content=SimpleBody.model_construct(data=response_data), status_code=status.HTTP_201_CREATED, ) @@ -285,7 +285,7 @@ async def get_run_commands( current_command = run_data_manager.get_current_command(run_id=runId) data = [ - RunCommandSummary.construct( + RunCommandSummary.model_construct( id=c.id, key=c.key, commandType=c.commandType, @@ -321,7 +321,7 @@ async def get_run_commands( ) return await PydanticResponse.create( - content=MultiBody.construct(data=data, meta=meta, links=links), + content=MultiBody.model_construct(data=data, meta=meta, links=links), status_code=status.HTTP_200_OK, ) @@ -361,6 +361,6 @@ async def get_run_command( raise CommandNotFound.from_exc(e).as_error(status.HTTP_404_NOT_FOUND) from e return await PydanticResponse.create( - content=SimpleBody.construct(data=command), + content=SimpleBody.model_construct(data=command), status_code=status.HTTP_200_OK, ) diff --git a/robot-server/robot_server/runs/router/labware_router.py b/robot-server/robot_server/runs/router/labware_router.py index b54742f837a..3f0509b17a6 100644 --- a/robot-server/robot_server/runs/router/labware_router.py +++ b/robot-server/robot_server/runs/router/labware_router.py @@ -68,7 +68,7 @@ async def add_labware_offset( log.info(f'Added labware offset "{added_offset.id}"' f' to run "{run.id}".') return await PydanticResponse.create( - content=SimpleBody.construct(data=added_offset), + content=SimpleBody.model_construct(data=added_offset), status_code=status.HTTP_201_CREATED, ) @@ -110,8 +110,8 @@ async def add_labware_definition( log.info(f'Added labware definition "{uri}"' f' to run "{run.id}".') return PydanticResponse( - content=SimpleBody.construct( - data=LabwareDefinitionSummary.construct(definitionUri=uri) + content=SimpleBody.model_construct( + data=LabwareDefinitionSummary.model_construct(definitionUri=uri) ), status_code=status.HTTP_201_CREATED, ) @@ -151,8 +151,8 @@ async def get_run_loaded_labware_definitions( except RunNotCurrentError as e: raise RunStopped(detail=str(e)).as_error(status.HTTP_409_CONFLICT) from e - labware_definitions_result = ResponseList.construct(__root__=labware_definitions) + labware_definitions_result = ResponseList(root=labware_definitions) return await PydanticResponse.create( - content=SimpleBody.construct(data=labware_definitions_result), + content=SimpleBody.model_construct(data=labware_definitions_result), status_code=status.HTTP_200_OK, ) diff --git a/robot-server/robot_server/runs/run_data_manager.py b/robot-server/robot_server/runs/run_data_manager.py index 650d9ad7253..59826d5c37a 100644 --- a/robot-server/robot_server/runs/run_data_manager.py +++ b/robot-server/robot_server/runs/run_data_manager.py @@ -34,7 +34,7 @@ def _build_run( # such that this default summary object is not needed if run_resource.ok and isinstance(state_summary, StateSummary): - return Run.construct( + return Run.model_construct( id=run_resource.run_id, protocolId=run_resource.protocol_id, createdAt=run_resource.created_at, @@ -53,7 +53,7 @@ def _build_run( errors: List[EnumeratedError] = [] if isinstance(state_summary, BadStateSummary): - state = StateSummary.construct( + state = StateSummary.model_construct( status=EngineStatus.STOPPED, errors=[], labware=[], @@ -86,7 +86,7 @@ def _build_run( AssertionError("Logic error in parsing invalid run.") ) - return BadRun.construct( + return BadRun.model_construct( dataError=run_loading_error, id=run_resource.run_id, protocolId=run_resource.protocol_id, diff --git a/robot-server/robot_server/service/json_api/response.py b/robot-server/robot_server/service/json_api/response.py index f11ebc78d0d..51bd097fe08 100644 --- a/robot-server/robot_server/service/json_api/response.py +++ b/robot-server/robot_server/service/json_api/response.py @@ -11,8 +11,8 @@ ParamSpec, Callable, ) -from pydantic import Field, BaseModel -from pydantic.typing import get_args +from typing_extensions import get_args +from pydantic import Field, BaseModel, RootModel from fastapi.responses import JSONResponse from fastapi.dependencies.utils import get_typed_return_annotation from .resource_links import ResourceLinks as DeprecatedResourceLinks @@ -115,7 +115,8 @@ class SimpleMultiBody(BaseResponseBody, BaseModel, Generic[ResponseDataT]): class MultiBody( BaseResponseBody, - BaseModel, Generic[ResponseDataT, ResponseLinksT], + BaseModel, + Generic[ResponseDataT, ResponseLinksT], ): """A response that returns multiple resources and stateful links.""" @@ -257,7 +258,8 @@ class DeprecatedResponseModel(BaseModel, Generic[ResponseDataT]): # TODO(mc, 2021-12-09): remove this model class DeprecatedMultiResponseModel( - BaseModel, Generic[ResponseDataT], + BaseModel, + Generic[ResponseDataT], ): """A response that returns multiple resources and stateful links. @@ -275,10 +277,10 @@ class DeprecatedMultiResponseModel( ) -class ResponseList(BaseModel, Generic[ResponseDataT]): +class ResponseList(RootModel[List[ResponseDataT]], Generic[ResponseDataT]): """A response that returns a list resource.""" - __root__: List[ResponseDataT] + root: List[ResponseDataT] class NotifyRefetchBody(BaseResponseBody): diff --git a/robot-server/robot_server/service/labware/models.py b/robot-server/robot_server/service/labware/models.py index 411fa67821e..89d140ced61 100644 --- a/robot-server/robot_server/service/labware/models.py +++ b/robot-server/robot_server/service/labware/models.py @@ -73,42 +73,44 @@ class LabwareCalibration(DeprecatedResponseDataModel): definitionHash: str = Field( ..., description="The sha256 hash of key labware definition details" ) - model_config = ConfigDict(json_schema_extra={ - "examples": [ - { - "calibrationData": { - "tipLength": { - "value": 10, - "lastModified": "2020-07-10T12:50:47.156321", - }, - "offset": { - "value": [1, -2, 10], - "lastModified": "2020-07-10T12:40:17.05", + model_config = ConfigDict( + json_schema_extra={ + "examples": [ + { + "calibrationData": { + "tipLength": { + "value": 10, + "lastModified": "2020-07-10T12:50:47.156321", + }, + "offset": { + "value": [1, -2, 10], + "lastModified": "2020-07-10T12:40:17.05", + }, }, + "version": "1", + "parent": "", + "namespace": "opentrons", + "loadName": "opentrons_96_tiprack_300ul", }, - "version": "1", - "parent": "", - "namespace": "opentrons", - "loadName": "opentrons_96_tiprack_300ul", - }, - { - "calibrationData": { - "tipLength": { - "value": 10, - "lastModified": "2020-07-10T12:50:47.156321", - }, - "offset": { - "value": [1, -2, 10], - "lastModified": "2020-07-10T12:40:17.05", + { + "calibrationData": { + "tipLength": { + "value": 10, + "lastModified": "2020-07-10T12:50:47.156321", + }, + "offset": { + "value": [1, -2, 10], + "lastModified": "2020-07-10T12:40:17.05", + }, }, + "version": "1", + "parent": "temperatureModuleV2", + "namespace": "opentrons", + "loadName": "corning_96_wellPlate_384ul", }, - "version": "1", - "parent": "temperatureModuleV2", - "namespace": "opentrons", - "loadName": "corning_96_wellPlate_384ul", - }, - ] - }) + ] + } + ) MultipleCalibrationsResponse = DeprecatedMultiResponseModel[LabwareCalibration] diff --git a/robot-server/robot_server/service/legacy/models/control.py b/robot-server/robot_server/service/legacy/models/control.py index c59186b74c7..ba94c121fa6 100644 --- a/robot-server/robot_server/service/legacy/models/control.py +++ b/robot-server/robot_server/service/legacy/models/control.py @@ -51,18 +51,20 @@ class RobotPositions(BaseModel): class RobotPositionsResponse(BaseModel): positions: RobotPositions - model_config = ConfigDict(json_schema_extra={ - "example": { - "positions": { - "change_pipette": { - "target": "mount", - "left": [325, 40, 30], - "right": [65, 40, 30], - }, - "attach_tip": {"target": "pipette", "point": [200, 90, 150]}, + model_config = ConfigDict( + json_schema_extra={ + "example": { + "positions": { + "change_pipette": { + "target": "mount", + "left": [325, 40, 30], + "right": [65, 40, 30], + }, + "attach_tip": {"target": "pipette", "point": [200, 90, 150]}, + } } } - }) + ) class Mount(str, Enum): @@ -106,21 +108,24 @@ def root_validator(cls, values): " mount movement must be >= 30" ) return values - model_config = ConfigDict(json_schema_extra={ - "examples": [ - { - "target": "mount", - "point": [100, 100, 80], - "mount": "left", - }, - { - "target": "pipette", - "mount": "right", - "model": "p300_single", - "point": [25, 25, 50], - }, - ] - }) + + model_config = ConfigDict( + json_schema_extra={ + "examples": [ + { + "target": "mount", + "point": [100, 100, 80], + "mount": "left", + }, + { + "target": "pipette", + "mount": "right", + "model": "p300_single", + "point": [25, 25, 50], + }, + ] + } + ) class RobotHomeTarget(BaseModel): @@ -145,9 +150,12 @@ def root_validate(cls, values): if values.get("target") == HomeTarget.pipette.value and not values.get("mount"): raise ValueError("mount must be specified if target is pipette") return values - model_config = ConfigDict(json_schema_extra={ - "examples": [{"target": "robot"}, {"target": "pipette", "mount": "right"}] - }) + + model_config = ConfigDict( + json_schema_extra={ + "examples": [{"target": "robot"}, {"target": "pipette", "mount": "right"}] + } + ) class RobotLightState(BaseModel): diff --git a/robot-server/robot_server/service/legacy/models/deck_calibration.py b/robot-server/robot_server/service/legacy/models/deck_calibration.py index a1db8eef866..f424ee4fb07 100644 --- a/robot-server/robot_server/service/legacy/models/deck_calibration.py +++ b/robot-server/robot_server/service/legacy/models/deck_calibration.py @@ -31,19 +31,19 @@ class InstrumentOffset(BaseModel): single: Offset = Field( ..., - deprecated=True, description=( "This will always be `[0, 0, 0]`." " Use the `GET /calibration/pipette_offset` endpoint instead." ), + json_schema_extra={"deprecated": True}, ) multi: Offset = Field( ..., - deprecated=True, description=( "This will always be `[0, 0, 0]`." " Use the `GET /calibration/pipette_offset` endpoint instead." ), + json_schema_extra={"deprecated": True}, ) @@ -78,7 +78,7 @@ class DeckCalibrationData(BaseModel): " was used in this calibration." " This is deprecated because it was prone to bugs where semantically identical" " definitions had different hashes.", - deprecated=True, + json_schema_extra={"deprecated": True}, ) source: typing.Optional[SourceType] = Field( None, description="The calibration source" diff --git a/robot-server/robot_server/service/legacy/models/modules.py b/robot-server/robot_server/service/legacy/models/modules.py index 986409cb48d..e15419b7397 100644 --- a/robot-server/robot_server/service/legacy/models/modules.py +++ b/robot-server/robot_server/service/legacy/models/modules.py @@ -168,106 +168,108 @@ class Modules(BaseModel): """A list of all attached modules and the status of each one""" modules: typing.List[Module] - model_config = ConfigDict(json_schema_extra={ - "examples": [ - {"modules": []}, - { - "modules": [ - { - "name": "magdeck", - "displayName": "Magnetic Module", - "moduleModel": "magneticModuleV1", - "port": "tty01_magdeck", - "serial": "MDV2313121", - "model": "mag_deck_v4.0", - "revision": "mag_deck_v4.0", - "fwVersion": "2.1.3", - "status": "engaged", - "hasAvailableUpdate": True, - "data": {"engaged": True, "height": 10}, - } - ] - }, - { - "modules": [ - { - "name": "tempdeck", - "displayName": "Temperature Module", - "moduleModel": "temperatureModuleV1", - "revision": "temp_deck_v10", - "port": "tty2_tempdeck", - "serial": "TDV10231231", - "model": "temp_deck_v10", - "hasAvailableUpdate": False, - "fwVersion": "1.2.0", - "status": "cooling", - "data": {"currentTemp": 25, "targetTemp": 10}, - } - ] - }, - { - "modules": [ - { - "name": "thermocycler", - "displayName": "Thermocycler", - "revision": "thermocycler_v10", - "moduleModel": "thermocyclerModuleV1", - "port": "tty3_thermocycler", - "serial": "TCV1006052018", - "model": "thermocycler_v10", - "hasAvailableUpdate": True, - "fwVersion": "1.0.0", - "status": "cooling", - "data": { - "lid": "closed", - "lidTarget": 10, - "lidTemp": 15, - "currentTemp": 20, - "targetTemp": 10, - "holdTime": None, - "rampRate": 10, - "currentCycleIndex": None, - "totalCycleCount": None, - "currentStepIndex": None, - "totalStepCount": None, - }, - } - ] - }, - { - "modules": [ - { - "name": "heatershaker", - "displayName": "heatershaker", - "fwVersion": "0.0.1", - "hasAvailableUpdate": True, - "model": "heater-shaker_v10", - "moduleModel": "heaterShakerModuleV1", - "port": "/dev/ot_module_heatershaker1", - "usbPort": { - "hub": False, - "port": 1, - "portGroup": "unknown", - "hubPort": None, - }, - "revision": "heater-shaker_v10", - "serial": "HSnnnnnn", - "status": "running", - "data": { - "temperatureStatus": "heating", - "speedStatus": "holding at target", - "labwareLatchStatus": "closed", - "currentTemp": 25.5, - "targetTemp": 50, - "currentSpeed": 10, - "targetSpeed": 300, - "errorDetails": None, - }, - } - ] - }, - ] - }) + model_config = ConfigDict( + json_schema_extra={ + "examples": [ + {"modules": []}, + { + "modules": [ + { + "name": "magdeck", + "displayName": "Magnetic Module", + "moduleModel": "magneticModuleV1", + "port": "tty01_magdeck", + "serial": "MDV2313121", + "model": "mag_deck_v4.0", + "revision": "mag_deck_v4.0", + "fwVersion": "2.1.3", + "status": "engaged", + "hasAvailableUpdate": True, + "data": {"engaged": True, "height": 10}, + } + ] + }, + { + "modules": [ + { + "name": "tempdeck", + "displayName": "Temperature Module", + "moduleModel": "temperatureModuleV1", + "revision": "temp_deck_v10", + "port": "tty2_tempdeck", + "serial": "TDV10231231", + "model": "temp_deck_v10", + "hasAvailableUpdate": False, + "fwVersion": "1.2.0", + "status": "cooling", + "data": {"currentTemp": 25, "targetTemp": 10}, + } + ] + }, + { + "modules": [ + { + "name": "thermocycler", + "displayName": "Thermocycler", + "revision": "thermocycler_v10", + "moduleModel": "thermocyclerModuleV1", + "port": "tty3_thermocycler", + "serial": "TCV1006052018", + "model": "thermocycler_v10", + "hasAvailableUpdate": True, + "fwVersion": "1.0.0", + "status": "cooling", + "data": { + "lid": "closed", + "lidTarget": 10, + "lidTemp": 15, + "currentTemp": 20, + "targetTemp": 10, + "holdTime": None, + "rampRate": 10, + "currentCycleIndex": None, + "totalCycleCount": None, + "currentStepIndex": None, + "totalStepCount": None, + }, + } + ] + }, + { + "modules": [ + { + "name": "heatershaker", + "displayName": "heatershaker", + "fwVersion": "0.0.1", + "hasAvailableUpdate": True, + "model": "heater-shaker_v10", + "moduleModel": "heaterShakerModuleV1", + "port": "/dev/ot_module_heatershaker1", + "usbPort": { + "hub": False, + "port": 1, + "portGroup": "unknown", + "hubPort": None, + }, + "revision": "heater-shaker_v10", + "serial": "HSnnnnnn", + "status": "running", + "data": { + "temperatureStatus": "heating", + "speedStatus": "holding at target", + "labwareLatchStatus": "closed", + "currentTemp": 25.5, + "targetTemp": 50, + "currentSpeed": 10, + "targetSpeed": 300, + "errorDetails": None, + }, + } + ] + }, + ] + } + ) class ModuleSerial(BaseModel): @@ -286,7 +288,11 @@ class SerialCommand(BaseModel): args: typing.Optional[typing.List[typing.Any]] = Field( None, description="The ordered args list for the call" ) - model_config = ConfigDict(json_schema_extra={"examples": [{"command_type": "set_Temperature", "args": [60]}]}) + model_config = ConfigDict( + json_schema_extra={ + "examples": [{"command_type": "set_Temperature", "args": [60]}] + } + ) class SerialCommandResponse(BaseModel): @@ -296,4 +302,6 @@ class SerialCommandResponse(BaseModel): returnValue: typing.Optional[str] = Field( None, description="The return value from the call" ) - model_config = ConfigDict(json_schema_extra={"examples": [{"message": "Success", "returnValue": None}]}) + model_config = ConfigDict( + json_schema_extra={"examples": [{"message": "Success", "returnValue": None}]} + ) diff --git a/robot-server/robot_server/service/legacy/models/motors.py b/robot-server/robot_server/service/legacy/models/motors.py index c2063ce98d6..2242185d6cc 100644 --- a/robot-server/robot_server/service/legacy/models/motors.py +++ b/robot-server/robot_server/service/legacy/models/motors.py @@ -38,7 +38,9 @@ class EngagedMotors(BaseModel): z_r: EngagedMotor p_l: EngagedMotor p_r: EngagedMotor - q: typing.Optional[EngagedMotor] = None # Optional since OT2 doesn't have these axes + q: typing.Optional[ + EngagedMotor + ] = None # Optional since OT2 doesn't have these axes g: typing.Optional[EngagedMotor] = None diff --git a/robot-server/robot_server/service/legacy/models/networking.py b/robot-server/robot_server/service/legacy/models/networking.py index 53002b4bc19..53a666719f7 100644 --- a/robot-server/robot_server/service/legacy/models/networking.py +++ b/robot-server/robot_server/service/legacy/models/networking.py @@ -1,7 +1,14 @@ import typing from enum import Enum -from pydantic import field_validator, model_validator, ConfigDict, BaseModel, Field, SecretStr +from pydantic import ( + field_validator, + model_validator, + ConfigDict, + BaseModel, + Field, + SecretStr, +) from opentrons.system import wifi @@ -53,27 +60,29 @@ class NetworkingStatus(BaseModel): description="Per-interface networking status. Properties are " "named for network interfaces", ) - model_config = ConfigDict(json_schema_extra={ - "example": { - "status": "full", - "interfaces": { - "wlan0": { - "ipAddress": "192.168.43.97/24", - "macAddress": "B8:27:EB:6C:95:CF", - "gatewayAddress": "192.168.43.161", - "state": "connected", - "type": "wifi", + model_config = ConfigDict( + json_schema_extra={ + "example": { + "status": "full", + "interfaces": { + "wlan0": { + "ipAddress": "192.168.43.97/24", + "macAddress": "B8:27:EB:6C:95:CF", + "gatewayAddress": "192.168.43.161", + "state": "connected", + "type": "wifi", + }, + "eth0": { + "ipAddress": "169.254.229.173/16", + "macAddress": "B8:27:EB:39:C0:9A", + "gatewayAddress": None, + "state": "connected", + "type": "ethernet", + }, }, - "eth0": { - "ipAddress": "169.254.229.173/16", - "macAddress": "B8:27:EB:39:C0:9A", - "gatewayAddress": None, - "state": "connected", - "type": "ethernet", - }, - }, + } } - }) + ) class NetworkingSecurityType(str, Enum): @@ -109,19 +118,21 @@ class WifiNetworks(BaseModel): """The list of networks""" list: typing.List[WifiNetworkFull] - model_config = ConfigDict(json_schema_extra={ - "example": { - "list": [ - { - "ssid": "linksys", - "signal": 50, - "active": False, - "security": "WPA2 802.1X", - "securityType": "wpa-eap", - } - ] + model_config = ConfigDict( + json_schema_extra={ + "example": { + "list": [ + { + "ssid": "linksys", + "signal": 50, + "active": False, + "security": "WPA2 802.1X", + "securityType": "wpa-eap", + } + ] + } } - }) + ) class WifiConfiguration(BaseModel): @@ -156,7 +167,7 @@ class WifiConfiguration(BaseModel): 'and it may also have `"anonymousIdentity"` and ' '`"caCert"` properties, both of which are identified' " as present but not required.", - required=["eapType"], + json_schema_extra={"required": ["eapType"]}, ) @field_validator("eapConfig") @@ -196,31 +207,34 @@ def validate_configuration(cls, values): elif security_type == NetworkingSecurityType.wpa_eap and not eapconfig: raise ValueError("If securityType is wpa-eap, eapConfig must be specified") return values - model_config = ConfigDict(json_schema_extra={ - "examples": [ - {"ssid": "linksys"}, - { - "ssid": "linksys", - "securityType": "wpa-psk", - "psk": "psksrock", - }, - { - "ssid": "cantseeme", - "securityType": "wpa-psk", - "psk": "letmein", - "hidden": True, - }, - { - "ssid": "Eduroam", - "securityType": "wpa-eap", - "eapConfig": { - "eapType": "peap/mschapv2", - "identity": "scientist@biology.org", - "password": "leeuwenhoek", + + model_config = ConfigDict( + json_schema_extra={ + "examples": [ + {"ssid": "linksys"}, + { + "ssid": "linksys", + "securityType": "wpa-psk", + "psk": "psksrock", + }, + { + "ssid": "cantseeme", + "securityType": "wpa-psk", + "psk": "letmein", + "hidden": True, }, - }, - ] - }) + { + "ssid": "Eduroam", + "securityType": "wpa-eap", + "eapConfig": { + "eapType": "peap/mschapv2", + "identity": "scientist@biology.org", + "password": "leeuwenhoek", + }, + }, + ] + } + ) class WifiConfigurationResponse(BaseModel): @@ -262,17 +276,19 @@ class WifiKeyFiles(BaseModel): wifi_keys: typing.List[WifiKeyFile] = Field( [], alias="keys", description="A list of keys in the system" ) - model_config = ConfigDict(json_schema_extra={ - "example": { - "keys": [ - { - "uri": "/wifi/keys/abda234a234", - "id": "abda234a234", - "name": "client.pem", - } - ] + model_config = ConfigDict( + json_schema_extra={ + "example": { + "keys": [ + { + "uri": "/wifi/keys/abda234a234", + "id": "abda234a234", + "name": "client.pem", + } + ] + } } - }) + ) class EapConfigOptionType(str, Enum): @@ -321,39 +337,41 @@ class EapOptions(BaseModel): """An object describing all supported EAP variants and their parameters""" options: typing.List[EapVariant] - model_config = ConfigDict(json_schema_extra={ - "example": { - "options": [ - { - "name": "peap/mschapv2", - "displayName": "PEAP/MS-CHAP v2", - "options": [ - { - "name": "identity", - "displayName": "Username", - "required": True, - "type": "string", - }, - { - "name": "anonymousIdentity", - "displayName": "Anonymous Identity", - "required": False, - "type": "string", - }, - { - "name": "caCert", - "displayName": "CA Certificate File", - "required": False, - "type": "file", - }, - { - "name": "password", - "displayName": "password", - "required": True, - "type": "password", - }, - ], - } - ] + model_config = ConfigDict( + json_schema_extra={ + "example": { + "options": [ + { + "name": "peap/mschapv2", + "displayName": "PEAP/MS-CHAP v2", + "options": [ + { + "name": "identity", + "displayName": "Username", + "required": True, + "type": "string", + }, + { + "name": "anonymousIdentity", + "displayName": "Anonymous Identity", + "required": False, + "type": "string", + }, + { + "name": "caCert", + "displayName": "CA Certificate File", + "required": False, + "type": "file", + }, + { + "name": "password", + "displayName": "password", + "required": True, + "type": "password", + }, + ], + } + ] + } } - }) + ) diff --git a/robot-server/robot_server/service/legacy/models/pipettes.py b/robot-server/robot_server/service/legacy/models/pipettes.py index 0849852e880..cff8c01307f 100644 --- a/robot-server/robot_server/service/legacy/models/pipettes.py +++ b/robot-server/robot_server/service/legacy/models/pipettes.py @@ -36,23 +36,25 @@ class PipettesByMount(BaseModel): left: AttachedPipette right: AttachedPipette - model_config = ConfigDict(json_schema_extra={ - "example": { - "left": { - "model": "p300_single_v1.5", - "name": "p300_single", - "tip_length": 51.7, - "mount_axis": "z", - "plunger_axis": "b", - "id": "P3HS12123041", - }, - "right": { - "model": None, - "name": None, - "tip_length": None, - "mount_axis": "a", - "plunger_axis": "c", - "id": None, - }, + model_config = ConfigDict( + json_schema_extra={ + "example": { + "left": { + "model": "p300_single_v1.5", + "name": "p300_single", + "tip_length": 51.7, + "mount_axis": "z", + "plunger_axis": "b", + "id": "P3HS12123041", + }, + "right": { + "model": None, + "name": None, + "tip_length": None, + "mount_axis": "a", + "plunger_axis": "c", + "id": None, + }, + } } - }) + ) diff --git a/robot-server/robot_server/service/legacy/models/settings.py b/robot-server/robot_server/service/legacy/models/settings.py index 2169a167f15..980610f8ef5 100644 --- a/robot-server/robot_server/service/legacy/models/settings.py +++ b/robot-server/robot_server/service/legacy/models/settings.py @@ -3,7 +3,7 @@ from typing import Optional, List, Dict, Any, Union -from pydantic import field_validator, ConfigDict, BaseModel, Field, create_model +from pydantic import field_validator, BaseModel, Field, create_model from opentrons_shared_data.pipette import model_constants from opentrons.config.reset import ResetOptionId @@ -17,7 +17,7 @@ class AdvancedSetting(BaseModel): ..., description="The ID by which the property used to be known; not" " useful now and may contain spaces or hyphens", - deprecated=True, + json_schema_extra={"deprecated": True}, ) title: str = Field( ..., @@ -185,11 +185,9 @@ class BasePipetteSettingFields(BaseModel): class PipetteSettings(BaseModel): + info: PipetteSettingsInfo - setting_fields: PipetteSettingsFields # type: ignore - # TODO[pydantic]: The following keys were removed: `fields`. - # Check https://docs.pydantic.dev/dev-v2/migration/#changes-to-config for more information. - model_config = ConfigDict(fields={"setting_fields": "fields"}) + setting_fields: PipetteSettingsFields = Field(..., alias="fields") # type: ignore MultiPipetteSettings = Dict[str, PipetteSettings] diff --git a/robot-server/robot_server/service/legacy/routers/settings.py b/robot-server/robot_server/service/legacy/routers/settings.py index 16a732ff97f..235f5ba8190 100644 --- a/robot-server/robot_server/service/legacy/routers/settings.py +++ b/robot-server/robot_server/service/legacy/routers/settings.py @@ -404,7 +404,7 @@ def _pipette_settings_from_mutable_configs( # TODO(mc, 2020-09-17): s/fields/setting_fields (?) # need model and name? - return PipetteSettings( # type: ignore[call-arg] + return PipetteSettings( info=PipetteSettingsInfo( name=cast(str, mutable_configs.get("name", "")), model=cast(str, mutable_configs.get("model", "")), diff --git a/robot-server/robot_server/service/notifications/notification_client.py b/robot-server/robot_server/service/notifications/notification_client.py index f53de3bbe39..360763cb800 100644 --- a/robot-server/robot_server/service/notifications/notification_client.py +++ b/robot-server/robot_server/service/notifications/notification_client.py @@ -105,7 +105,7 @@ def publish_advise_refetch( Args: topic: The topic to publish the message on. """ - message = NotifyRefetchBody.construct() + message = NotifyRefetchBody.model_construct() payload = message.json() self._client.publish( topic=topic, @@ -123,7 +123,7 @@ def publish_advise_unsubscribe( Args: topic: The topic to publish the message on. """ - message = NotifyUnsubscribeBody.construct() + message = NotifyUnsubscribeBody.model_construct() payload = message.json() self._client.publish( topic=topic, diff --git a/robot-server/robot_server/service/session/models/command.py b/robot-server/robot_server/service/session/models/command.py index 3e23c8a080d..b83fd1c3450 100644 --- a/robot-server/robot_server/service/session/models/command.py +++ b/robot-server/robot_server/service/session/models/command.py @@ -100,7 +100,8 @@ def make_response( class SessionCommandResponse( DeprecatedResponseDataModel, - BaseModel, typing.Generic[CommandT, RequestDataT, ResponseDataT], + BaseModel, + typing.Generic[CommandT, RequestDataT, ResponseDataT], ): """A session command response.""" diff --git a/robot-server/robot_server/service/tip_length/models.py b/robot-server/robot_server/service/tip_length/models.py index 2ff8f81b5ef..eca3757be5c 100644 --- a/robot-server/robot_server/service/tip_length/models.py +++ b/robot-server/robot_server/service/tip_length/models.py @@ -24,7 +24,7 @@ class TipLengthCalibration(DeprecatedResponseDataModel): " This is deprecated because it was prone to bugs where semantically identical" " definitions had different hashes." " Use `uri` instead.", - deprecated=True, + json_schema_extra={"deprecated": True}, ) pipette: str = Field(..., description="The pipette ID") lastModified: datetime = Field( diff --git a/robot-server/robot_server/subsystems/router.py b/robot-server/robot_server/subsystems/router.py index bf0ca0edac8..e5359715a63 100644 --- a/robot-server/robot_server/subsystems/router.py +++ b/robot-server/robot_server/subsystems/router.py @@ -122,7 +122,7 @@ async def get_attached_subsystems( """Return all subsystems currently present on the machine.""" hardware = get_ot3_hardware(thread_manager) data = [ - PresentSubsystem.construct( + PresentSubsystem.model_construct( name=SubSystem.from_hw(subsystem_id), ok=subsystem_details.ok, current_fw_version=str(subsystem_details.current_fw_version), @@ -134,7 +134,7 @@ async def get_attached_subsystems( ] meta = MultiBodyMeta(cursor=0, totalLength=len(data)) return await PydanticResponse.create( - content=SimpleMultiBody.construct(data=data, meta=meta) + content=SimpleMultiBody.model_construct(data=data, meta=meta) ) @@ -164,8 +164,8 @@ async def get_attached_subsystem( status.HTTP_404_NOT_FOUND ) return await PydanticResponse.create( - content=SimpleBody.construct( - data=PresentSubsystem.construct( + content=SimpleBody.model_construct( + data=PresentSubsystem.model_construct( name=subsystem, ok=subsystem_status.ok, current_fw_version=str(subsystem_status.current_fw_version), @@ -195,7 +195,7 @@ async def get_subsystem_updates( """Return all currently-running firmware update process summaries.""" handles = await update_manager.all_ongoing_processes() data = [ - UpdateProgressSummary.construct( + UpdateProgressSummary.model_construct( id=handle.process_details.update_id, subsystem=handle.process_details.subsystem, updateStatus=handle.cached_state, @@ -205,7 +205,7 @@ async def get_subsystem_updates( ] meta = MultiBodyMeta(cursor=0, totalLength=len(data)) return await PydanticResponse.create( - content=SimpleMultiBody.construct(data=data, meta=meta) + content=SimpleMultiBody.model_construct(data=data, meta=meta) ) @@ -234,8 +234,8 @@ async def get_subsystem_update( ) from e progress = await handle.get_progress() return await PydanticResponse.create( - content=SimpleBody.construct( - data=UpdateProgressData.construct( + content=SimpleBody.model_construct( + data=UpdateProgressData.model_construct( id=handle.process_details.update_id, createdAt=handle.process_details.created_at, subsystem=handle.process_details.subsystem, @@ -276,7 +276,7 @@ async def get_update_processes( ] meta = MultiBodyMeta(cursor=0, totalLength=len(data)) return await PydanticResponse.create( - content=SimpleMultiBody.construct(data=data, meta=meta) + content=SimpleMultiBody.model_construct(data=data, meta=meta) ) @@ -298,8 +298,8 @@ async def get_update_process( raise IDNotFound(detail=id).as_error(status.HTTP_404_NOT_FOUND) from e progress = await handle.get_progress() return await PydanticResponse.create( - content=SimpleBody.construct( - data=UpdateProgressData.construct( + content=SimpleBody.model_construct( + data=UpdateProgressData.model_construct( id=handle.process_details.update_id, subsystem=handle.process_details.subsystem, createdAt=handle.process_details.created_at, @@ -365,8 +365,8 @@ async def begin_subsystem_update( ) progress = await summary.get_progress() return await PydanticResponse.create( - content=SimpleBody.construct( - data=UpdateProgressData.construct( + content=SimpleBody.model_construct( + data=UpdateProgressData.model_construct( id=summary.process_details.update_id, createdAt=summary.process_details.created_at, subsystem=subsystem, diff --git a/robot-server/tests/instruments/test_router.py b/robot-server/tests/instruments/test_router.py index 8d45c10c5d8..1ea353a7880 100644 --- a/robot-server/tests/instruments/test_router.py +++ b/robot-server/tests/instruments/test_router.py @@ -214,7 +214,7 @@ async def rehearse_instrument_retrievals(skip_if_would_block: bool = False) -> N result = await get_attached_instruments(hardware=ot3_hardware_api) assert result.content.data == [ - Pipette.construct( + Pipette.model_construct( ok=True, mount="left", instrumentType="pipette", @@ -236,7 +236,7 @@ async def rehearse_instrument_retrievals(skip_if_would_block: bool = False) -> N ), state=PipetteState(tip_detected=True), ), - Pipette.construct( + Pipette.model_construct( ok=True, mount="right", firmwareVersion="11", @@ -258,7 +258,7 @@ async def rehearse_instrument_retrievals(skip_if_would_block: bool = False) -> N ), state=PipetteState(tip_detected=False), ), - Gripper.construct( + Gripper.model_construct( ok=True, mount="extension", firmwareVersion="11", @@ -307,7 +307,7 @@ async def test_get_ot2_instruments( decoy.verify(await ot2_hardware_api.cache_instruments(), times=0) assert result2.status_code == 200 assert result2.content.data == [ - Pipette.construct( + Pipette.model_construct( # type: ignore[call-arg] ok=True, mount="right", instrumentType="pipette", @@ -353,7 +353,7 @@ async def test_get_96_channel_instruments( decoy.when(ot3_hardware_api.get_instrument_offset(OT3Mount.RIGHT)).then_return(None) assert result2.status_code == 200 assert result2.content.data == [ - Pipette.construct( + Pipette.model_construct( # type: ignore[call-arg] ok=True, mount="left", instrumentType="pipette", diff --git a/robot-server/tests/maintenance_runs/test_engine_store.py b/robot-server/tests/maintenance_runs/test_engine_store.py index 15855ab48d1..6dad28761e2 100644 --- a/robot-server/tests/maintenance_runs/test_engine_store.py +++ b/robot-server/tests/maintenance_runs/test_engine_store.py @@ -103,7 +103,7 @@ async def test_create_engine_with_labware_offsets( ) assert result.labwareOffsets == [ - pe_types.LabwareOffset.construct( + pe_types.LabwareOffset.model_construct( id=matchers.IsA(str), createdAt=matchers.IsA(datetime), definitionUri="namespace/load_name/version", diff --git a/robot-server/tests/maintenance_runs/test_run_data_manager.py b/robot-server/tests/maintenance_runs/test_run_data_manager.py index 0046b3098db..64e858ccb4e 100644 --- a/robot-server/tests/maintenance_runs/test_run_data_manager.py +++ b/robot-server/tests/maintenance_runs/test_run_data_manager.py @@ -60,11 +60,11 @@ def engine_state_summary() -> StateSummary: """Get a StateSummary value object.""" return StateSummary( status=EngineStatus.IDLE, - errors=[ErrorOccurrence.construct(id="some-error-id")], # type: ignore[call-arg] - labware=[LoadedLabware.construct(id="some-labware-id")], # type: ignore[call-arg] - labwareOffsets=[LabwareOffset.construct(id="some-labware-offset-id")], # type: ignore[call-arg] - pipettes=[LoadedPipette.construct(id="some-pipette-id")], # type: ignore[call-arg] - modules=[LoadedModule.construct(id="some-module-id")], # type: ignore[call-arg] + errors=[ErrorOccurrence.model_construct(id="some-error-id")], # type: ignore[call-arg] + labware=[LoadedLabware.model_construct(id="some-labware-id")], # type: ignore[call-arg] + labwareOffsets=[LabwareOffset.model_construct(id="some-labware-offset-id")], # type: ignore[call-arg] + pipettes=[LoadedPipette.model_construct(id="some-pipette-id")], # type: ignore[call-arg] + modules=[LoadedModule.model_construct(id="some-module-id")], # type: ignore[call-arg] liquids=[Liquid(id="some-liquid-id", displayName="liquid", description="desc")], ) diff --git a/robot-server/tests/modules/test_module_data_mapper.py b/robot-server/tests/modules/test_module_data_mapper.py index 7eb50854428..33cf7f08b98 100644 --- a/robot-server/tests/modules/test_module_data_mapper.py +++ b/robot-server/tests/modules/test_module_data_mapper.py @@ -100,7 +100,7 @@ def test_maps_magnetic_module_data( has_available_update=True, live_data=input_data, usb_port=hardware_usb_port, - module_offset=ModuleCalibrationData.construct( + module_offset=ModuleCalibrationData.model_construct( offset=Vec3f(x=0, y=0, z=0), ), ) @@ -164,7 +164,7 @@ def test_maps_temperature_module_data(input_model: str, input_data: LiveData) -> has_available_update=True, live_data=input_data, usb_port=hardware_usb_port, - module_offset=ModuleCalibrationData.construct( + module_offset=ModuleCalibrationData.model_construct( offset=Vec3f(x=0, y=0, z=0), ), ) @@ -261,7 +261,7 @@ def test_maps_thermocycler_module_data(input_model: str, input_data: LiveData) - has_available_update=True, live_data=input_data, usb_port=hardware_usb_port, - module_offset=ModuleCalibrationData.construct( + module_offset=ModuleCalibrationData.model_construct( offset=Vec3f(x=0, y=0, z=0), ), ) @@ -360,7 +360,7 @@ def test_maps_heater_shaker_module_data(input_model: str, input_data: LiveData) has_available_update=True, live_data=input_data, usb_port=hardware_usb_port, - module_offset=ModuleCalibrationData.construct( + module_offset=ModuleCalibrationData.model_construct( offset=Vec3f(x=0, y=0, z=0), ), ) diff --git a/robot-server/tests/modules/test_router.py b/robot-server/tests/modules/test_router.py index 6aa92b3fee7..f341fda6397 100644 --- a/robot-server/tests/modules/test_router.py +++ b/robot-server/tests/modules/test_router.py @@ -94,7 +94,7 @@ async def test_get_modules_maps_data_and_id( hubPort=None, path="/dev/null", ), - moduleOffset=ModuleCalibrationData.construct( + moduleOffset=ModuleCalibrationData.model_construct( offset=Vec3f(x=0, y=0, z=0), ), data=MagneticModuleData( @@ -166,7 +166,7 @@ async def test_get_modules_maps_data_and_id( port_group=PortGroup.UNKNOWN, hub_port=None, ), - module_offset=ModuleCalibrationData.construct( + module_offset=ModuleCalibrationData.model_construct( offset=Vec3f( x=calibration_offset.offset.x, y=calibration_offset.offset.y, diff --git a/robot-server/tests/protocols/test_protocol_analyzer.py b/robot-server/tests/protocols/test_protocol_analyzer.py index c0c63e9e34f..c18d21bfc18 100644 --- a/robot-server/tests/protocols/test_protocol_analyzer.py +++ b/robot-server/tests/protocols/test_protocol_analyzer.py @@ -206,7 +206,7 @@ async def test_analyze_updates_pending_on_error( raised_exception = Exception("You got me!!") - error_occurrence = pe_errors.ErrorOccurrence.construct( + error_occurrence = pe_errors.ErrorOccurrence.model_construct( id="internal-error", createdAt=datetime(year=2023, month=3, day=3), errorType="EnumeratedError", diff --git a/robot-server/tests/protocols/test_protocols_router.py b/robot-server/tests/protocols/test_protocols_router.py index 88605f81a3b..9c3479915d4 100644 --- a/robot-server/tests/protocols/test_protocols_router.py +++ b/robot-server/tests/protocols/test_protocols_router.py @@ -294,7 +294,7 @@ async def test_get_protocol_by_id( key="dummy-key-111", ) - assert result.content.links == ProtocolLinks.construct(referencingRuns=[]) + assert result.content.links == ProtocolLinks.model_construct(referencingRuns=[]) assert result.status_code == 200 diff --git a/robot-server/tests/runs/router/test_labware_router.py b/robot-server/tests/runs/router/test_labware_router.py index 14326a8889a..e0a8b69e2a9 100644 --- a/robot-server/tests/runs/router/test_labware_router.py +++ b/robot-server/tests/runs/router/test_labware_router.py @@ -159,8 +159,8 @@ async def test_get_run_labware_definition( mock_run_data_manager.get_run_loaded_labware_definitions(run_id="run-id") ).then_return( [ - SD_LabwareDefinition.construct(namespace="test_1"), # type: ignore[call-arg] - SD_LabwareDefinition.construct(namespace="test_2"), # type: ignore[call-arg] + SD_LabwareDefinition.model_construct(namespace="test_1"), # type: ignore[call-arg] + SD_LabwareDefinition.model_construct(namespace="test_2"), # type: ignore[call-arg] ] ) @@ -168,8 +168,8 @@ async def test_get_run_labware_definition( runId="run-id", run_data_manager=mock_run_data_manager ) - assert result.content.data.__root__ == [ - SD_LabwareDefinition.construct(namespace="test_1"), # type: ignore[call-arg] - SD_LabwareDefinition.construct(namespace="test_2"), # type: ignore[call-arg] + assert result.content.data.root == [ + SD_LabwareDefinition.model_construct(namespace="test_1"), # type: ignore[call-arg] + SD_LabwareDefinition.model_construct(namespace="test_2"), # type: ignore[call-arg] ] assert result.status_code == 200 diff --git a/robot-server/tests/runs/test_engine_store.py b/robot-server/tests/runs/test_engine_store.py index 7a1f79b903a..52e22221937 100644 --- a/robot-server/tests/runs/test_engine_store.py +++ b/robot-server/tests/runs/test_engine_store.py @@ -140,7 +140,7 @@ async def test_create_engine_with_labware_offsets(subject: EngineStore) -> None: ) assert result.labwareOffsets == [ - pe_types.LabwareOffset.construct( + pe_types.LabwareOffset.model_construct( id=matchers.IsA(str), createdAt=matchers.IsA(datetime), definitionUri="namespace/load_name/version", diff --git a/robot-server/tests/runs/test_run_controller.py b/robot-server/tests/runs/test_run_controller.py index 5bf5778c486..944c80581fa 100644 --- a/robot-server/tests/runs/test_run_controller.py +++ b/robot-server/tests/runs/test_run_controller.py @@ -64,7 +64,7 @@ def engine_state_summary() -> StateSummary: def protocol_commands() -> List[pe_commands.Command]: """Get a StateSummary value object.""" return [ - pe_commands.WaitForResume.construct( # type: ignore[call-arg] + pe_commands.WaitForResume.model_construct( # type: ignore[call-arg] params=pe_commands.WaitForResumeParams(message="hello world") ) ] diff --git a/robot-server/tests/runs/test_run_data_manager.py b/robot-server/tests/runs/test_run_data_manager.py index 6f26cabf1a7..7957296da50 100644 --- a/robot-server/tests/runs/test_run_data_manager.py +++ b/robot-server/tests/runs/test_run_data_manager.py @@ -76,11 +76,11 @@ def engine_state_summary() -> StateSummary: """Get a StateSummary value object.""" return StateSummary( status=EngineStatus.IDLE, - errors=[ErrorOccurrence.construct(id="some-error-id")], # type: ignore[call-arg] - labware=[LoadedLabware.construct(id="some-labware-id")], # type: ignore[call-arg] - labwareOffsets=[LabwareOffset.construct(id="some-labware-offset-id")], # type: ignore[call-arg] - pipettes=[LoadedPipette.construct(id="some-pipette-id")], # type: ignore[call-arg] - modules=[LoadedModule.construct(id="some-module-id")], # type: ignore[call-arg] + errors=[ErrorOccurrence.model_construct(id="some-error-id")], # type: ignore[call-arg] + labware=[LoadedLabware.model_construct(id="some-labware-id")], # type: ignore[call-arg] + labwareOffsets=[LabwareOffset.model_construct(id="some-labware-offset-id")], # type: ignore[call-arg] + pipettes=[LoadedPipette.model_construct(id="some-pipette-id")], # type: ignore[call-arg] + modules=[LoadedModule.model_construct(id="some-module-id")], # type: ignore[call-arg] liquids=[Liquid(id="some-liquid-id", displayName="liquid", description="desc")], ) @@ -410,21 +410,21 @@ async def test_get_all_runs( """It should get all runs, including current and historical.""" current_run_data = StateSummary( status=EngineStatus.IDLE, - errors=[ErrorOccurrence.construct(id="current-error-id")], # type: ignore[call-arg] - labware=[LoadedLabware.construct(id="current-labware-id")], # type: ignore[call-arg] - labwareOffsets=[LabwareOffset.construct(id="current-labware-offset-id")], # type: ignore[call-arg] - pipettes=[LoadedPipette.construct(id="current-pipette-id")], # type: ignore[call-arg] - modules=[LoadedModule.construct(id="current-module-id")], # type: ignore[call-arg] + errors=[ErrorOccurrence.model_construct(id="current-error-id")], # type: ignore[call-arg] + labware=[LoadedLabware.model_construct(id="current-labware-id")], # type: ignore[call-arg] + labwareOffsets=[LabwareOffset.model_construct(id="current-labware-offset-id")], # type: ignore[call-arg] + pipettes=[LoadedPipette.model_construct(id="current-pipette-id")], # type: ignore[call-arg] + modules=[LoadedModule.model_construct(id="current-module-id")], # type: ignore[call-arg] liquids=[Liquid(id="some-liquid-id", displayName="liquid", description="desc")], ) historical_run_data = StateSummary( status=EngineStatus.STOPPED, - errors=[ErrorOccurrence.construct(id="old-error-id")], # type: ignore[call-arg] - labware=[LoadedLabware.construct(id="old-labware-id")], # type: ignore[call-arg] - labwareOffsets=[LabwareOffset.construct(id="old-labware-offset-id")], # type: ignore[call-arg] - pipettes=[LoadedPipette.construct(id="old-pipette-id")], # type: ignore[call-arg] - modules=[LoadedModule.construct(id="old-module-id")], # type: ignore[call-arg] + errors=[ErrorOccurrence.model_construct(id="old-error-id")], # type: ignore[call-arg] + labware=[LoadedLabware.model_construct(id="old-labware-id")], # type: ignore[call-arg] + labwareOffsets=[LabwareOffset.model_construct(id="old-labware-offset-id")], # type: ignore[call-arg] + pipettes=[LoadedPipette.model_construct(id="old-pipette-id")], # type: ignore[call-arg] + modules=[LoadedModule.model_construct(id="old-module-id")], # type: ignore[call-arg] liquids=[], ) @@ -878,14 +878,14 @@ async def test_get_current_run_labware_definition( mock_engine_store.engine.state_view.labware.get_loaded_labware_definitions() ).then_return( [ - LabwareDefinition.construct(namespace="test_1"), # type: ignore[call-arg] - LabwareDefinition.construct(namespace="test_2"), # type: ignore[call-arg] + LabwareDefinition.model_construct(namespace="test_1"), # type: ignore[call-arg] + LabwareDefinition.model_construct(namespace="test_2"), # type: ignore[call-arg] ] ) result = subject.get_run_loaded_labware_definitions(run_id="run-id") assert result == [ - LabwareDefinition.construct(namespace="test_1"), # type: ignore[call-arg] - LabwareDefinition.construct(namespace="test_2"), # type: ignore[call-arg] + LabwareDefinition.model_construct(namespace="test_1"), # type: ignore[call-arg] + LabwareDefinition.model_construct(namespace="test_2"), # type: ignore[call-arg] ] diff --git a/robot-server/tests/runs/test_run_store.py b/robot-server/tests/runs/test_run_store.py index 31cabbe56bd..5ecfb1499e8 100644 --- a/robot-server/tests/runs/test_run_store.py +++ b/robot-server/tests/runs/test_run_store.py @@ -123,10 +123,10 @@ def state_summary() -> StateSummary: @pytest.fixture def invalid_state_summary() -> StateSummary: """Should fail pydantic validation.""" - analysis_error = pe_errors.ErrorOccurrence.construct( + analysis_error = pe_errors.ErrorOccurrence.model_construct( id="error-id", # Invalid value here should fail analysis - createdAt=MountType.LEFT, # type: ignore + createdAt=MountType.LEFT, # type: ignore[arg-type] errorType="BadError", detail="oh no", ) diff --git a/robot-server/tests/subsystems/test_router.py b/robot-server/tests/subsystems/test_router.py index 387b5001a40..193f9f33cd0 100644 --- a/robot-server/tests/subsystems/test_router.py +++ b/robot-server/tests/subsystems/test_router.py @@ -104,7 +104,7 @@ def _build_attached_subsystems( def _build_subsystem_data( subsystem: SubSystem, state: SubSystemState ) -> PresentSubsystem: - return PresentSubsystem.construct( + return PresentSubsystem.model_construct( name=subsystem, ok=state.ok, current_fw_version=str(state.current_fw_version), @@ -230,25 +230,25 @@ async def test_get_subsystem_updates_with_some( response = await get_subsystem_updates(update_manager) assert response.content.data == [ - UpdateProgressSummary.construct( + UpdateProgressSummary.model_construct( id=x_process_details.update_id, createdAt=x_process_details.created_at, subsystem=x_process_details.subsystem, updateStatus=x_state, ), - UpdateProgressSummary.construct( + UpdateProgressSummary.model_construct( id=y_process_details.update_id, createdAt=y_process_details.created_at, subsystem=y_process_details.subsystem, updateStatus=y_state, ), - UpdateProgressSummary.construct( + UpdateProgressSummary.model_construct( id=head_process_details.update_id, createdAt=head_process_details.created_at, subsystem=head_process_details.subsystem, updateStatus=head_state, ), - UpdateProgressSummary.construct( + UpdateProgressSummary.model_construct( id=pipette_process_details.update_id, createdAt=pipette_process_details.created_at, subsystem=pipette_process_details.subsystem, @@ -284,7 +284,7 @@ async def test_get_subsystem_update_succeeds( await update_manager.get_ongoing_update_process_handle_by_subsystem(subsystem) ).then_return(handle) response = await get_subsystem_update(subsystem, update_manager) - assert response.content.data == UpdateProgressData.construct( + assert response.content.data == UpdateProgressData.model_construct( id=details.update_id, createdAt=details.created_at, subsystem=details.subsystem, @@ -329,7 +329,7 @@ async def test_get_subsystem_update_error( await update_manager.get_ongoing_update_process_handle_by_subsystem(subsystem) ).then_return(handle) response = await get_subsystem_update(subsystem, update_manager) - assert response.content.data == UpdateProgressData.construct( + assert response.content.data == UpdateProgressData.model_construct( id=details.update_id, createdAt=details.created_at, subsystem=details.subsystem, @@ -386,25 +386,25 @@ async def test_get_all_updates_some( ) response = await get_update_processes(update_manager) assert response.content.data == [ - UpdateProgressSummary.construct( + UpdateProgressSummary.model_construct( id=x_process_details.update_id, createdAt=x_process_details.created_at, subsystem=x_process_details.subsystem, updateStatus=x_state, ), - UpdateProgressSummary.construct( + UpdateProgressSummary.model_construct( id=y_process_details.update_id, createdAt=y_process_details.created_at, subsystem=y_process_details.subsystem, updateStatus=y_state, ), - UpdateProgressSummary.construct( + UpdateProgressSummary.model_construct( id=head_process_details.update_id, createdAt=head_process_details.created_at, subsystem=head_process_details.subsystem, updateStatus=head_state, ), - UpdateProgressSummary.construct( + UpdateProgressSummary.model_construct( id=pipette_process_details.update_id, createdAt=pipette_process_details.created_at, subsystem=pipette_process_details.subsystem, @@ -526,7 +526,7 @@ async def test_begin_update( headers["Location"] == f"http://127.0.0.1:31950/subsystems/updates/current/{subsystem.value}" ) - assert response_data.content.data == UpdateProgressData.construct( + assert response_data.content.data == UpdateProgressData.model_construct( id=update_id, createdAt=created_at, subsystem=subsystem,