opentrons 8.2.0a3__py2.py3-none-any.whl → 8.3.0__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of opentrons might be problematic. Click here for more details.

Files changed (238) hide show
  1. opentrons/calibration_storage/deck_configuration.py +3 -3
  2. opentrons/calibration_storage/file_operators.py +3 -3
  3. opentrons/calibration_storage/helpers.py +3 -1
  4. opentrons/calibration_storage/ot2/models/v1.py +16 -29
  5. opentrons/calibration_storage/ot2/tip_length.py +7 -4
  6. opentrons/calibration_storage/ot3/models/v1.py +14 -23
  7. opentrons/cli/analyze.py +18 -6
  8. opentrons/config/defaults_ot3.py +1 -0
  9. opentrons/drivers/asyncio/communication/__init__.py +2 -0
  10. opentrons/drivers/asyncio/communication/errors.py +16 -3
  11. opentrons/drivers/asyncio/communication/serial_connection.py +24 -9
  12. opentrons/drivers/command_builder.py +2 -2
  13. opentrons/drivers/flex_stacker/__init__.py +9 -0
  14. opentrons/drivers/flex_stacker/abstract.py +89 -0
  15. opentrons/drivers/flex_stacker/driver.py +260 -0
  16. opentrons/drivers/flex_stacker/simulator.py +109 -0
  17. opentrons/drivers/flex_stacker/types.py +138 -0
  18. opentrons/drivers/heater_shaker/driver.py +18 -3
  19. opentrons/drivers/temp_deck/driver.py +13 -3
  20. opentrons/drivers/thermocycler/driver.py +17 -3
  21. opentrons/execute.py +3 -1
  22. opentrons/hardware_control/__init__.py +1 -2
  23. opentrons/hardware_control/api.py +33 -21
  24. opentrons/hardware_control/backends/flex_protocol.py +17 -7
  25. opentrons/hardware_control/backends/ot3controller.py +213 -63
  26. opentrons/hardware_control/backends/ot3simulator.py +18 -9
  27. opentrons/hardware_control/backends/ot3utils.py +43 -15
  28. opentrons/hardware_control/dev_types.py +4 -0
  29. opentrons/hardware_control/emulation/heater_shaker.py +4 -0
  30. opentrons/hardware_control/emulation/module_server/client.py +1 -1
  31. opentrons/hardware_control/emulation/module_server/server.py +5 -3
  32. opentrons/hardware_control/emulation/settings.py +3 -4
  33. opentrons/hardware_control/instruments/ot2/instrument_calibration.py +2 -1
  34. opentrons/hardware_control/instruments/ot2/pipette.py +15 -22
  35. opentrons/hardware_control/instruments/ot2/pipette_handler.py +8 -1
  36. opentrons/hardware_control/instruments/ot3/gripper.py +2 -2
  37. opentrons/hardware_control/instruments/ot3/pipette.py +23 -22
  38. opentrons/hardware_control/instruments/ot3/pipette_handler.py +10 -1
  39. opentrons/hardware_control/modules/mod_abc.py +2 -2
  40. opentrons/hardware_control/motion_utilities.py +68 -0
  41. opentrons/hardware_control/nozzle_manager.py +39 -41
  42. opentrons/hardware_control/ot3_calibration.py +1 -1
  43. opentrons/hardware_control/ot3api.py +78 -31
  44. opentrons/hardware_control/protocols/gripper_controller.py +3 -0
  45. opentrons/hardware_control/protocols/hardware_manager.py +5 -1
  46. opentrons/hardware_control/protocols/liquid_handler.py +22 -1
  47. opentrons/hardware_control/protocols/motion_controller.py +7 -0
  48. opentrons/hardware_control/robot_calibration.py +1 -1
  49. opentrons/hardware_control/types.py +61 -0
  50. opentrons/legacy_commands/commands.py +37 -0
  51. opentrons/legacy_commands/types.py +39 -0
  52. opentrons/protocol_api/__init__.py +20 -1
  53. opentrons/protocol_api/_liquid.py +24 -49
  54. opentrons/protocol_api/_liquid_properties.py +754 -0
  55. opentrons/protocol_api/_types.py +24 -0
  56. opentrons/protocol_api/core/common.py +2 -0
  57. opentrons/protocol_api/core/engine/instrument.py +191 -10
  58. opentrons/protocol_api/core/engine/labware.py +29 -7
  59. opentrons/protocol_api/core/engine/protocol.py +130 -5
  60. opentrons/protocol_api/core/engine/robot.py +139 -0
  61. opentrons/protocol_api/core/engine/well.py +4 -1
  62. opentrons/protocol_api/core/instrument.py +73 -4
  63. opentrons/protocol_api/core/labware.py +13 -4
  64. opentrons/protocol_api/core/legacy/legacy_instrument_core.py +87 -3
  65. opentrons/protocol_api/core/legacy/legacy_labware_core.py +13 -4
  66. opentrons/protocol_api/core/legacy/legacy_protocol_core.py +32 -1
  67. opentrons/protocol_api/core/legacy/legacy_robot_core.py +0 -0
  68. opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +61 -3
  69. opentrons/protocol_api/core/protocol.py +34 -1
  70. opentrons/protocol_api/core/robot.py +51 -0
  71. opentrons/protocol_api/instrument_context.py +299 -44
  72. opentrons/protocol_api/labware.py +248 -9
  73. opentrons/protocol_api/module_contexts.py +21 -17
  74. opentrons/protocol_api/protocol_context.py +125 -4
  75. opentrons/protocol_api/robot_context.py +204 -32
  76. opentrons/protocol_api/validation.py +262 -3
  77. opentrons/protocol_engine/__init__.py +4 -0
  78. opentrons/protocol_engine/actions/actions.py +2 -3
  79. opentrons/protocol_engine/clients/sync_client.py +18 -0
  80. opentrons/protocol_engine/commands/__init__.py +121 -0
  81. opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +1 -3
  82. opentrons/protocol_engine/commands/absorbance_reader/initialize.py +20 -6
  83. opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +1 -2
  84. opentrons/protocol_engine/commands/absorbance_reader/read.py +40 -10
  85. opentrons/protocol_engine/commands/air_gap_in_place.py +160 -0
  86. opentrons/protocol_engine/commands/aspirate.py +103 -53
  87. opentrons/protocol_engine/commands/aspirate_in_place.py +55 -51
  88. opentrons/protocol_engine/commands/blow_out.py +44 -39
  89. opentrons/protocol_engine/commands/blow_out_in_place.py +21 -32
  90. opentrons/protocol_engine/commands/calibration/calibrate_gripper.py +13 -6
  91. opentrons/protocol_engine/commands/calibration/calibrate_module.py +1 -1
  92. opentrons/protocol_engine/commands/calibration/calibrate_pipette.py +3 -3
  93. opentrons/protocol_engine/commands/calibration/move_to_maintenance_position.py +1 -1
  94. opentrons/protocol_engine/commands/command.py +73 -66
  95. opentrons/protocol_engine/commands/command_unions.py +140 -1
  96. opentrons/protocol_engine/commands/comment.py +1 -1
  97. opentrons/protocol_engine/commands/configure_for_volume.py +10 -3
  98. opentrons/protocol_engine/commands/configure_nozzle_layout.py +6 -4
  99. opentrons/protocol_engine/commands/custom.py +6 -12
  100. opentrons/protocol_engine/commands/dispense.py +82 -48
  101. opentrons/protocol_engine/commands/dispense_in_place.py +71 -51
  102. opentrons/protocol_engine/commands/drop_tip.py +52 -31
  103. opentrons/protocol_engine/commands/drop_tip_in_place.py +79 -8
  104. opentrons/protocol_engine/commands/evotip_dispense.py +156 -0
  105. opentrons/protocol_engine/commands/evotip_seal_pipette.py +331 -0
  106. opentrons/protocol_engine/commands/evotip_unseal_pipette.py +160 -0
  107. opentrons/protocol_engine/commands/generate_command_schema.py +4 -11
  108. opentrons/protocol_engine/commands/get_next_tip.py +134 -0
  109. opentrons/protocol_engine/commands/get_tip_presence.py +1 -1
  110. opentrons/protocol_engine/commands/heater_shaker/close_labware_latch.py +1 -1
  111. opentrons/protocol_engine/commands/heater_shaker/deactivate_heater.py +1 -1
  112. opentrons/protocol_engine/commands/heater_shaker/deactivate_shaker.py +1 -1
  113. opentrons/protocol_engine/commands/heater_shaker/open_labware_latch.py +1 -1
  114. opentrons/protocol_engine/commands/heater_shaker/set_and_wait_for_shake_speed.py +1 -1
  115. opentrons/protocol_engine/commands/heater_shaker/set_target_temperature.py +1 -1
  116. opentrons/protocol_engine/commands/heater_shaker/wait_for_temperature.py +10 -4
  117. opentrons/protocol_engine/commands/home.py +13 -4
  118. opentrons/protocol_engine/commands/liquid_probe.py +125 -31
  119. opentrons/protocol_engine/commands/load_labware.py +33 -6
  120. opentrons/protocol_engine/commands/load_lid.py +146 -0
  121. opentrons/protocol_engine/commands/load_lid_stack.py +189 -0
  122. opentrons/protocol_engine/commands/load_liquid.py +12 -4
  123. opentrons/protocol_engine/commands/load_liquid_class.py +144 -0
  124. opentrons/protocol_engine/commands/load_module.py +31 -10
  125. opentrons/protocol_engine/commands/load_pipette.py +19 -8
  126. opentrons/protocol_engine/commands/magnetic_module/disengage.py +1 -1
  127. opentrons/protocol_engine/commands/magnetic_module/engage.py +1 -1
  128. opentrons/protocol_engine/commands/move_labware.py +28 -6
  129. opentrons/protocol_engine/commands/move_relative.py +35 -25
  130. opentrons/protocol_engine/commands/move_to_addressable_area.py +40 -27
  131. opentrons/protocol_engine/commands/move_to_addressable_area_for_drop_tip.py +53 -32
  132. opentrons/protocol_engine/commands/move_to_coordinates.py +36 -22
  133. opentrons/protocol_engine/commands/move_to_well.py +40 -24
  134. opentrons/protocol_engine/commands/movement_common.py +338 -0
  135. opentrons/protocol_engine/commands/pick_up_tip.py +49 -27
  136. opentrons/protocol_engine/commands/pipetting_common.py +169 -87
  137. opentrons/protocol_engine/commands/prepare_to_aspirate.py +24 -33
  138. opentrons/protocol_engine/commands/reload_labware.py +1 -1
  139. opentrons/protocol_engine/commands/retract_axis.py +1 -1
  140. opentrons/protocol_engine/commands/robot/__init__.py +69 -0
  141. opentrons/protocol_engine/commands/robot/close_gripper_jaw.py +86 -0
  142. opentrons/protocol_engine/commands/robot/common.py +18 -0
  143. opentrons/protocol_engine/commands/robot/move_axes_relative.py +101 -0
  144. opentrons/protocol_engine/commands/robot/move_axes_to.py +100 -0
  145. opentrons/protocol_engine/commands/robot/move_to.py +94 -0
  146. opentrons/protocol_engine/commands/robot/open_gripper_jaw.py +77 -0
  147. opentrons/protocol_engine/commands/save_position.py +14 -5
  148. opentrons/protocol_engine/commands/set_rail_lights.py +1 -1
  149. opentrons/protocol_engine/commands/set_status_bar.py +1 -1
  150. opentrons/protocol_engine/commands/temperature_module/deactivate.py +1 -1
  151. opentrons/protocol_engine/commands/temperature_module/set_target_temperature.py +1 -1
  152. opentrons/protocol_engine/commands/temperature_module/wait_for_temperature.py +10 -4
  153. opentrons/protocol_engine/commands/thermocycler/close_lid.py +1 -1
  154. opentrons/protocol_engine/commands/thermocycler/deactivate_block.py +1 -1
  155. opentrons/protocol_engine/commands/thermocycler/deactivate_lid.py +1 -1
  156. opentrons/protocol_engine/commands/thermocycler/open_lid.py +1 -1
  157. opentrons/protocol_engine/commands/thermocycler/run_extended_profile.py +9 -3
  158. opentrons/protocol_engine/commands/thermocycler/run_profile.py +9 -3
  159. opentrons/protocol_engine/commands/thermocycler/set_target_block_temperature.py +11 -4
  160. opentrons/protocol_engine/commands/thermocycler/set_target_lid_temperature.py +1 -1
  161. opentrons/protocol_engine/commands/thermocycler/wait_for_block_temperature.py +1 -1
  162. opentrons/protocol_engine/commands/thermocycler/wait_for_lid_temperature.py +1 -1
  163. opentrons/protocol_engine/commands/touch_tip.py +65 -16
  164. opentrons/protocol_engine/commands/unsafe/unsafe_blow_out_in_place.py +5 -2
  165. opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +13 -4
  166. opentrons/protocol_engine/commands/unsafe/unsafe_engage_axes.py +2 -5
  167. opentrons/protocol_engine/commands/unsafe/unsafe_place_labware.py +1 -1
  168. opentrons/protocol_engine/commands/unsafe/unsafe_ungrip_labware.py +4 -2
  169. opentrons/protocol_engine/commands/unsafe/update_position_estimators.py +2 -5
  170. opentrons/protocol_engine/commands/verify_tip_presence.py +11 -4
  171. opentrons/protocol_engine/commands/wait_for_duration.py +10 -3
  172. opentrons/protocol_engine/commands/wait_for_resume.py +10 -3
  173. opentrons/protocol_engine/errors/__init__.py +12 -0
  174. opentrons/protocol_engine/errors/error_occurrence.py +19 -20
  175. opentrons/protocol_engine/errors/exceptions.py +76 -0
  176. opentrons/protocol_engine/execution/command_executor.py +1 -1
  177. opentrons/protocol_engine/execution/equipment.py +73 -5
  178. opentrons/protocol_engine/execution/gantry_mover.py +369 -8
  179. opentrons/protocol_engine/execution/hardware_stopper.py +7 -7
  180. opentrons/protocol_engine/execution/movement.py +27 -0
  181. opentrons/protocol_engine/execution/pipetting.py +5 -1
  182. opentrons/protocol_engine/execution/tip_handler.py +34 -15
  183. opentrons/protocol_engine/notes/notes.py +1 -1
  184. opentrons/protocol_engine/protocol_engine.py +7 -6
  185. opentrons/protocol_engine/resources/labware_data_provider.py +1 -1
  186. opentrons/protocol_engine/resources/labware_validation.py +18 -0
  187. opentrons/protocol_engine/resources/module_data_provider.py +1 -1
  188. opentrons/protocol_engine/resources/pipette_data_provider.py +26 -0
  189. opentrons/protocol_engine/slot_standardization.py +9 -9
  190. opentrons/protocol_engine/state/_move_types.py +9 -5
  191. opentrons/protocol_engine/state/_well_math.py +193 -0
  192. opentrons/protocol_engine/state/addressable_areas.py +25 -61
  193. opentrons/protocol_engine/state/command_history.py +12 -0
  194. opentrons/protocol_engine/state/commands.py +22 -14
  195. opentrons/protocol_engine/state/files.py +10 -12
  196. opentrons/protocol_engine/state/fluid_stack.py +138 -0
  197. opentrons/protocol_engine/state/frustum_helpers.py +63 -69
  198. opentrons/protocol_engine/state/geometry.py +47 -1
  199. opentrons/protocol_engine/state/labware.py +92 -26
  200. opentrons/protocol_engine/state/liquid_classes.py +82 -0
  201. opentrons/protocol_engine/state/liquids.py +16 -4
  202. opentrons/protocol_engine/state/modules.py +56 -71
  203. opentrons/protocol_engine/state/motion.py +6 -1
  204. opentrons/protocol_engine/state/pipettes.py +149 -58
  205. opentrons/protocol_engine/state/state.py +21 -2
  206. opentrons/protocol_engine/state/state_summary.py +4 -2
  207. opentrons/protocol_engine/state/tips.py +11 -44
  208. opentrons/protocol_engine/state/update_types.py +343 -48
  209. opentrons/protocol_engine/state/wells.py +19 -11
  210. opentrons/protocol_engine/types.py +176 -28
  211. opentrons/protocol_reader/extract_labware_definitions.py +5 -2
  212. opentrons/protocol_reader/file_format_validator.py +5 -5
  213. opentrons/protocol_runner/json_file_reader.py +9 -3
  214. opentrons/protocol_runner/json_translator.py +51 -25
  215. opentrons/protocol_runner/legacy_command_mapper.py +66 -64
  216. opentrons/protocol_runner/protocol_runner.py +35 -4
  217. opentrons/protocol_runner/python_protocol_wrappers.py +1 -1
  218. opentrons/protocol_runner/run_orchestrator.py +13 -3
  219. opentrons/protocols/advanced_control/common.py +38 -0
  220. opentrons/protocols/advanced_control/mix.py +1 -1
  221. opentrons/protocols/advanced_control/transfers/__init__.py +0 -0
  222. opentrons/protocols/advanced_control/transfers/common.py +56 -0
  223. opentrons/protocols/advanced_control/{transfers.py → transfers/transfer.py} +10 -85
  224. opentrons/protocols/api_support/definitions.py +1 -1
  225. opentrons/protocols/api_support/instrument.py +1 -1
  226. opentrons/protocols/api_support/util.py +10 -0
  227. opentrons/protocols/labware.py +70 -8
  228. opentrons/protocols/models/json_protocol.py +5 -9
  229. opentrons/simulate.py +3 -1
  230. opentrons/types.py +162 -2
  231. opentrons/util/entrypoint_util.py +2 -5
  232. opentrons/util/logging_config.py +1 -1
  233. {opentrons-8.2.0a3.dist-info → opentrons-8.3.0.dist-info}/METADATA +16 -15
  234. {opentrons-8.2.0a3.dist-info → opentrons-8.3.0.dist-info}/RECORD +238 -208
  235. {opentrons-8.2.0a3.dist-info → opentrons-8.3.0.dist-info}/WHEEL +1 -1
  236. {opentrons-8.2.0a3.dist-info → opentrons-8.3.0.dist-info}/LICENSE +0 -0
  237. {opentrons-8.2.0a3.dist-info → opentrons-8.3.0.dist-info}/entry_points.txt +0 -0
  238. {opentrons-8.2.0a3.dist-info → opentrons-8.3.0.dist-info}/top_level.txt +0 -0
@@ -4,7 +4,7 @@ from logging import getLogger
4
4
  from datetime import datetime
5
5
  from textwrap import dedent
6
6
  from typing import Any, Dict, Mapping, List, Type, Union, Optional, Sequence
7
- from pydantic import BaseModel, Field
7
+ from pydantic import BaseModel, Field, ConfigDict
8
8
  from opentrons_shared_data.errors.codes import ErrorCodes
9
9
  from .exceptions import ProtocolEngineError
10
10
  from opentrons_shared_data.errors.exceptions import EnumeratedError
@@ -29,7 +29,7 @@ class ErrorOccurrence(BaseModel):
29
29
  wrappedErrors = [
30
30
  cls.from_failed(id, createdAt, err) for err in error.wrapping
31
31
  ]
32
- return cls.construct(
32
+ return cls.model_construct(
33
33
  id=id,
34
34
  createdAt=createdAt,
35
35
  errorType=type(error).__name__,
@@ -39,6 +39,21 @@ class ErrorOccurrence(BaseModel):
39
39
  wrappedErrors=wrappedErrors,
40
40
  )
41
41
 
42
+ @staticmethod
43
+ def schema_extra(schema: Dict[str, Any], model: object) -> None:
44
+ """Append the schema to make the errorCode appear required.
45
+
46
+ `errorCode`, `wrappedErrors`, and `errorInfo` have defaults because they are not included in earlier
47
+ versions of this model, _and_ this model is loaded directly from
48
+ the on-robot store. That means that, without a default, it will
49
+ fail to parse. Once a default is defined, the automated schema will
50
+ mark this as a non-required field, which is misleading as this is
51
+ a response from the server to the client and it will always have an
52
+ errorCode defined. This hack is required because it informs the client
53
+ that it does not, in fact, have to account for a missing errorCode, wrappedError, or errorInfo.
54
+ """
55
+ schema["required"].extend(["errorCode", "wrappedErrors", "errorInfo"])
56
+
42
57
  id: str = Field(..., description="Unique identifier of this error occurrence.")
43
58
  createdAt: datetime = Field(..., description="When the error occurred.")
44
59
 
@@ -145,23 +160,7 @@ class ErrorOccurrence(BaseModel):
145
160
  default=[], description="Errors that may have caused this one."
146
161
  )
147
162
 
148
- class Config:
149
- """Customize configuration for this model."""
150
-
151
- @staticmethod
152
- def schema_extra(schema: Dict[str, Any], model: object) -> None:
153
- """Append the schema to make the errorCode appear required.
154
-
155
- `errorCode`, `wrappedErrors`, and `errorInfo` have defaults because they are not included in earlier
156
- versions of this model, _and_ this model is loaded directly from
157
- the on-robot store. That means that, without a default, it will
158
- fail to parse. Once a default is defined, the automated schema will
159
- mark this as a non-required field, which is misleading as this is
160
- a response from the server to the client and it will always have an
161
- errorCode defined. This hack is required because it informs the client
162
- that it does not, in fact, have to account for a missing errorCode, wrappedError, or errorInfo.
163
- """
164
- schema["required"].extend(["errorCode", "wrappedErrors", "errorInfo"])
163
+ model_config = ConfigDict(json_schema_extra=schema_extra)
165
164
 
166
165
 
167
166
  # TODO (tz, 7-12-23): move this to exceptions.py when we stop relaying on ErrorOccurrence.
@@ -180,4 +179,4 @@ class ProtocolCommandFailedError(ProtocolEngineError):
180
179
  self.original_error = original_error
181
180
 
182
181
 
183
- ErrorOccurrence.update_forward_refs()
182
+ ErrorOccurrence.model_rebuild()
@@ -244,6 +244,19 @@ class LiquidDoesNotExistError(ProtocolEngineError):
244
244
  super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping)
245
245
 
246
246
 
247
+ class InvalidLiquidError(ProtocolEngineError):
248
+ """Raised when attempting to add a liquid with an invalid property."""
249
+
250
+ def __init__(
251
+ self,
252
+ message: Optional[str] = None,
253
+ details: Optional[Dict[str, Any]] = None,
254
+ wrapping: Optional[Sequence[EnumeratedError]] = None,
255
+ ) -> None:
256
+ """Build an InvalidLiquidError."""
257
+ super().__init__(ErrorCodes.INVALID_PROTOCOL_DATA, message, details, wrapping)
258
+
259
+
247
260
  class LabwareDefinitionDoesNotExistError(ProtocolEngineError):
248
261
  """Raised when referencing a labware definition that does not exist."""
249
262
 
@@ -270,6 +283,19 @@ class LabwareCannotBeStackedError(ProtocolEngineError):
270
283
  super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping)
271
284
 
272
285
 
286
+ class LabwareCannotSitOnDeckError(ProtocolEngineError):
287
+ """Raised when a labware is incompatible with a deck slot."""
288
+
289
+ def __init__(
290
+ self,
291
+ message: Optional[str] = None,
292
+ details: Optional[Dict[str, Any]] = None,
293
+ wrapping: Optional[Sequence[EnumeratedError]] = None,
294
+ ) -> None:
295
+ """Build a LabwareCannotSitOnDeckError."""
296
+ super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping)
297
+
298
+
273
299
  class LabwareIsInStackError(ProtocolEngineError):
274
300
  """Raised when trying to move to or physically interact with a labware that has another labware on top."""
275
301
 
@@ -348,6 +374,32 @@ class TouchTipDisabledError(ProtocolEngineError):
348
374
  super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping)
349
375
 
350
376
 
377
+ class TouchTipIncompatibleArgumentsError(ProtocolEngineError):
378
+ """Raised when touch tip is used with both a custom radius and a mmFromEdge argument."""
379
+
380
+ def __init__(
381
+ self,
382
+ message: Optional[str] = None,
383
+ details: Optional[Dict[str, Any]] = None,
384
+ wrapping: Optional[Sequence[EnumeratedError]] = None,
385
+ ) -> None:
386
+ """Build a TouchTipIncompatibleArgumentsError."""
387
+ super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping)
388
+
389
+
390
+ class UnsupportedLabwareForActionError(ProtocolEngineError):
391
+ """Raised when trying to use an unsupported labware for a command."""
392
+
393
+ def __init__(
394
+ self,
395
+ message: Optional[str] = None,
396
+ details: Optional[Dict[str, Any]] = None,
397
+ wrapping: Optional[Sequence[EnumeratedError]] = None,
398
+ ) -> None:
399
+ """Build a UnsupportedLabwareForActionError."""
400
+ super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping)
401
+
402
+
351
403
  class WellDoesNotExistError(ProtocolEngineError):
352
404
  """Raised when referencing a well that does not exist."""
353
405
 
@@ -1155,3 +1207,27 @@ class StorageLimitReachedError(ProtocolEngineError):
1155
1207
  ) -> None:
1156
1208
  """Build an StorageLimitReached."""
1157
1209
  super().__init__(ErrorCodes.GENERAL_ERROR, message, detail, wrapping)
1210
+
1211
+
1212
+ class LiquidClassDoesNotExistError(ProtocolEngineError):
1213
+ """Raised when referencing a liquid class that has not been loaded."""
1214
+
1215
+ def __init__(
1216
+ self,
1217
+ message: Optional[str] = None,
1218
+ details: Optional[Dict[str, Any]] = None,
1219
+ wrapping: Optional[Sequence[EnumeratedError]] = None,
1220
+ ) -> None:
1221
+ super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping)
1222
+
1223
+
1224
+ class LiquidClassRedefinitionError(ProtocolEngineError):
1225
+ """Raised when attempting to load a liquid class that conflicts with a liquid class already loaded."""
1226
+
1227
+ def __init__(
1228
+ self,
1229
+ message: Optional[str] = None,
1230
+ details: Optional[Dict[str, Any]] = None,
1231
+ wrapping: Optional[Sequence[EnumeratedError]] = None,
1232
+ ) -> None:
1233
+ super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping)
@@ -188,7 +188,7 @@ class CommandExecutor:
188
188
  "completedAt": self._model_utils.get_timestamp(),
189
189
  "notes": note_tracker.get_notes(),
190
190
  }
191
- succeeded_command = running_command.copy(update=update)
191
+ succeeded_command = running_command.model_copy(update=update)
192
192
  self._action_dispatcher.dispatch(
193
193
  SucceedCommandAction(
194
194
  command=succeeded_command,
@@ -1,6 +1,6 @@
1
1
  """Equipment command side-effect logic."""
2
2
  from dataclasses import dataclass
3
- from typing import Optional, overload, Union
3
+ from typing import Optional, overload, Union, List
4
4
 
5
5
  from opentrons_shared_data.pipette.types import PipetteNameType
6
6
 
@@ -152,10 +152,6 @@ class EquipmentHandler:
152
152
  Returns:
153
153
  A LoadedLabwareData object.
154
154
  """
155
- labware_id = (
156
- labware_id if labware_id is not None else self._model_utils.generate_id()
157
- )
158
-
159
155
  definition_uri = uri_from_details(
160
156
  load_name=load_name,
161
157
  namespace=namespace,
@@ -172,6 +168,10 @@ class EquipmentHandler:
172
168
  version=version,
173
169
  )
174
170
 
171
+ labware_id = (
172
+ labware_id if labware_id is not None else self._model_utils.generate_id()
173
+ )
174
+
175
175
  # Allow propagation of ModuleNotLoadedError.
176
176
  offset_id = self.find_applicable_labware_offset_id(
177
177
  labware_definition_uri=definition_uri,
@@ -379,6 +379,74 @@ class EquipmentHandler:
379
379
  definition=attached_module.definition,
380
380
  )
381
381
 
382
+ async def load_lids(
383
+ self,
384
+ load_name: str,
385
+ namespace: str,
386
+ version: int,
387
+ location: LabwareLocation,
388
+ quantity: int,
389
+ ) -> List[LoadedLabwareData]:
390
+ """Load one or many lid labware by assigning an identifier and pulling required data.
391
+
392
+ Args:
393
+ load_name: The lid labware's load name.
394
+ namespace: The lid labware's namespace.
395
+ version: The lid labware's version.
396
+ location: The deck location at which lid(s) will be placed.
397
+ labware_ids: An optional list of identifiers to assign the labware. If None,
398
+ an identifier will be generated.
399
+
400
+ Raises:
401
+ ModuleNotLoadedError: If `location` references a module ID
402
+ that doesn't point to a valid loaded module.
403
+
404
+ Returns:
405
+ A list of LoadedLabwareData objects.
406
+ """
407
+ definition_uri = uri_from_details(
408
+ load_name=load_name,
409
+ namespace=namespace,
410
+ version=version,
411
+ )
412
+ try:
413
+ # Try to use existing definition in state.
414
+ definition = self._state_store.labware.get_definition_by_uri(definition_uri)
415
+ except LabwareDefinitionDoesNotExistError:
416
+ definition = await self._labware_data_provider.get_labware_definition(
417
+ load_name=load_name,
418
+ namespace=namespace,
419
+ version=version,
420
+ )
421
+
422
+ stack_limit = definition.stackLimit if definition.stackLimit is not None else 1
423
+ if quantity > stack_limit:
424
+ raise ValueError(
425
+ f"Requested quantity {quantity} is greater than the stack limit of {stack_limit} provided by definition for {load_name}."
426
+ )
427
+
428
+ # Allow propagation of ModuleNotLoadedError.
429
+ if (
430
+ isinstance(location, DeckSlotLocation)
431
+ and definition.parameters.isDeckSlotCompatible is not None
432
+ and not definition.parameters.isDeckSlotCompatible
433
+ ):
434
+ raise ValueError(
435
+ f"Lid Labware {load_name} cannot be loaded onto a Deck Slot."
436
+ )
437
+
438
+ load_labware_data_list = []
439
+ for i in range(quantity):
440
+ load_labware_data_list.append(
441
+ LoadedLabwareData(
442
+ labware_id=self._model_utils.generate_id(),
443
+ definition=definition,
444
+ offsetId=None,
445
+ )
446
+ )
447
+
448
+ return load_labware_data_list
449
+
382
450
  async def configure_for_volume(
383
451
  self, pipette_id: str, volume: float, tip_overlap_version: Optional[str]
384
452
  ) -> LoadedConfigureForVolumeData: