opentrons 8.6.0a1__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 (600) hide show
  1. opentrons/__init__.py +150 -0
  2. opentrons/_version.py +34 -0
  3. opentrons/calibration_storage/__init__.py +54 -0
  4. opentrons/calibration_storage/deck_configuration.py +62 -0
  5. opentrons/calibration_storage/encoder_decoder.py +31 -0
  6. opentrons/calibration_storage/file_operators.py +142 -0
  7. opentrons/calibration_storage/helpers.py +103 -0
  8. opentrons/calibration_storage/ot2/__init__.py +34 -0
  9. opentrons/calibration_storage/ot2/deck_attitude.py +85 -0
  10. opentrons/calibration_storage/ot2/mark_bad_calibration.py +27 -0
  11. opentrons/calibration_storage/ot2/models/__init__.py +0 -0
  12. opentrons/calibration_storage/ot2/models/v1.py +149 -0
  13. opentrons/calibration_storage/ot2/pipette_offset.py +129 -0
  14. opentrons/calibration_storage/ot2/tip_length.py +281 -0
  15. opentrons/calibration_storage/ot3/__init__.py +31 -0
  16. opentrons/calibration_storage/ot3/deck_attitude.py +83 -0
  17. opentrons/calibration_storage/ot3/gripper_offset.py +156 -0
  18. opentrons/calibration_storage/ot3/models/__init__.py +0 -0
  19. opentrons/calibration_storage/ot3/models/v1.py +122 -0
  20. opentrons/calibration_storage/ot3/module_offset.py +138 -0
  21. opentrons/calibration_storage/ot3/pipette_offset.py +95 -0
  22. opentrons/calibration_storage/types.py +45 -0
  23. opentrons/cli/__init__.py +21 -0
  24. opentrons/cli/__main__.py +5 -0
  25. opentrons/cli/analyze.py +501 -0
  26. opentrons/config/__init__.py +631 -0
  27. opentrons/config/advanced_settings.py +871 -0
  28. opentrons/config/defaults_ot2.py +214 -0
  29. opentrons/config/defaults_ot3.py +499 -0
  30. opentrons/config/feature_flags.py +86 -0
  31. opentrons/config/gripper_config.py +55 -0
  32. opentrons/config/reset.py +203 -0
  33. opentrons/config/robot_configs.py +187 -0
  34. opentrons/config/types.py +183 -0
  35. opentrons/drivers/__init__.py +0 -0
  36. opentrons/drivers/absorbance_reader/__init__.py +11 -0
  37. opentrons/drivers/absorbance_reader/abstract.py +72 -0
  38. opentrons/drivers/absorbance_reader/async_byonoy.py +352 -0
  39. opentrons/drivers/absorbance_reader/driver.py +81 -0
  40. opentrons/drivers/absorbance_reader/hid_protocol.py +161 -0
  41. opentrons/drivers/absorbance_reader/simulator.py +84 -0
  42. opentrons/drivers/asyncio/__init__.py +0 -0
  43. opentrons/drivers/asyncio/communication/__init__.py +22 -0
  44. opentrons/drivers/asyncio/communication/async_serial.py +183 -0
  45. opentrons/drivers/asyncio/communication/errors.py +88 -0
  46. opentrons/drivers/asyncio/communication/serial_connection.py +552 -0
  47. opentrons/drivers/command_builder.py +102 -0
  48. opentrons/drivers/flex_stacker/__init__.py +13 -0
  49. opentrons/drivers/flex_stacker/abstract.py +214 -0
  50. opentrons/drivers/flex_stacker/driver.py +768 -0
  51. opentrons/drivers/flex_stacker/errors.py +68 -0
  52. opentrons/drivers/flex_stacker/simulator.py +309 -0
  53. opentrons/drivers/flex_stacker/types.py +367 -0
  54. opentrons/drivers/flex_stacker/utils.py +19 -0
  55. opentrons/drivers/heater_shaker/__init__.py +5 -0
  56. opentrons/drivers/heater_shaker/abstract.py +76 -0
  57. opentrons/drivers/heater_shaker/driver.py +204 -0
  58. opentrons/drivers/heater_shaker/simulator.py +94 -0
  59. opentrons/drivers/mag_deck/__init__.py +6 -0
  60. opentrons/drivers/mag_deck/abstract.py +44 -0
  61. opentrons/drivers/mag_deck/driver.py +208 -0
  62. opentrons/drivers/mag_deck/simulator.py +63 -0
  63. opentrons/drivers/rpi_drivers/__init__.py +33 -0
  64. opentrons/drivers/rpi_drivers/dev_types.py +94 -0
  65. opentrons/drivers/rpi_drivers/gpio.py +282 -0
  66. opentrons/drivers/rpi_drivers/gpio_simulator.py +127 -0
  67. opentrons/drivers/rpi_drivers/interfaces.py +15 -0
  68. opentrons/drivers/rpi_drivers/types.py +364 -0
  69. opentrons/drivers/rpi_drivers/usb.py +102 -0
  70. opentrons/drivers/rpi_drivers/usb_simulator.py +22 -0
  71. opentrons/drivers/serial_communication.py +151 -0
  72. opentrons/drivers/smoothie_drivers/__init__.py +4 -0
  73. opentrons/drivers/smoothie_drivers/connection.py +51 -0
  74. opentrons/drivers/smoothie_drivers/constants.py +121 -0
  75. opentrons/drivers/smoothie_drivers/driver_3_0.py +1933 -0
  76. opentrons/drivers/smoothie_drivers/errors.py +49 -0
  77. opentrons/drivers/smoothie_drivers/parse_utils.py +143 -0
  78. opentrons/drivers/smoothie_drivers/simulator.py +99 -0
  79. opentrons/drivers/smoothie_drivers/types.py +16 -0
  80. opentrons/drivers/temp_deck/__init__.py +10 -0
  81. opentrons/drivers/temp_deck/abstract.py +54 -0
  82. opentrons/drivers/temp_deck/driver.py +197 -0
  83. opentrons/drivers/temp_deck/simulator.py +57 -0
  84. opentrons/drivers/thermocycler/__init__.py +12 -0
  85. opentrons/drivers/thermocycler/abstract.py +99 -0
  86. opentrons/drivers/thermocycler/driver.py +395 -0
  87. opentrons/drivers/thermocycler/simulator.py +126 -0
  88. opentrons/drivers/types.py +107 -0
  89. opentrons/drivers/utils.py +222 -0
  90. opentrons/execute.py +742 -0
  91. opentrons/hardware_control/__init__.py +65 -0
  92. opentrons/hardware_control/__main__.py +77 -0
  93. opentrons/hardware_control/adapters.py +98 -0
  94. opentrons/hardware_control/api.py +1347 -0
  95. opentrons/hardware_control/backends/__init__.py +7 -0
  96. opentrons/hardware_control/backends/controller.py +400 -0
  97. opentrons/hardware_control/backends/errors.py +9 -0
  98. opentrons/hardware_control/backends/estop_state.py +164 -0
  99. opentrons/hardware_control/backends/flex_protocol.py +497 -0
  100. opentrons/hardware_control/backends/ot3controller.py +1930 -0
  101. opentrons/hardware_control/backends/ot3simulator.py +900 -0
  102. opentrons/hardware_control/backends/ot3utils.py +664 -0
  103. opentrons/hardware_control/backends/simulator.py +442 -0
  104. opentrons/hardware_control/backends/status_bar_state.py +240 -0
  105. opentrons/hardware_control/backends/subsystem_manager.py +431 -0
  106. opentrons/hardware_control/backends/tip_presence_manager.py +173 -0
  107. opentrons/hardware_control/backends/types.py +14 -0
  108. opentrons/hardware_control/constants.py +6 -0
  109. opentrons/hardware_control/dev_types.py +125 -0
  110. opentrons/hardware_control/emulation/__init__.py +0 -0
  111. opentrons/hardware_control/emulation/abstract_emulator.py +21 -0
  112. opentrons/hardware_control/emulation/app.py +56 -0
  113. opentrons/hardware_control/emulation/connection_handler.py +38 -0
  114. opentrons/hardware_control/emulation/heater_shaker.py +150 -0
  115. opentrons/hardware_control/emulation/magdeck.py +60 -0
  116. opentrons/hardware_control/emulation/module_server/__init__.py +8 -0
  117. opentrons/hardware_control/emulation/module_server/client.py +78 -0
  118. opentrons/hardware_control/emulation/module_server/helpers.py +130 -0
  119. opentrons/hardware_control/emulation/module_server/models.py +31 -0
  120. opentrons/hardware_control/emulation/module_server/server.py +110 -0
  121. opentrons/hardware_control/emulation/parser.py +74 -0
  122. opentrons/hardware_control/emulation/proxy.py +241 -0
  123. opentrons/hardware_control/emulation/run_emulator.py +68 -0
  124. opentrons/hardware_control/emulation/scripts/__init__.py +0 -0
  125. opentrons/hardware_control/emulation/scripts/run_app.py +54 -0
  126. opentrons/hardware_control/emulation/scripts/run_module_emulator.py +72 -0
  127. opentrons/hardware_control/emulation/scripts/run_smoothie.py +37 -0
  128. opentrons/hardware_control/emulation/settings.py +119 -0
  129. opentrons/hardware_control/emulation/simulations.py +133 -0
  130. opentrons/hardware_control/emulation/smoothie.py +192 -0
  131. opentrons/hardware_control/emulation/tempdeck.py +69 -0
  132. opentrons/hardware_control/emulation/thermocycler.py +128 -0
  133. opentrons/hardware_control/emulation/types.py +10 -0
  134. opentrons/hardware_control/emulation/util.py +38 -0
  135. opentrons/hardware_control/errors.py +43 -0
  136. opentrons/hardware_control/execution_manager.py +164 -0
  137. opentrons/hardware_control/instruments/__init__.py +5 -0
  138. opentrons/hardware_control/instruments/instrument_abc.py +39 -0
  139. opentrons/hardware_control/instruments/ot2/__init__.py +0 -0
  140. opentrons/hardware_control/instruments/ot2/instrument_calibration.py +152 -0
  141. opentrons/hardware_control/instruments/ot2/pipette.py +777 -0
  142. opentrons/hardware_control/instruments/ot2/pipette_handler.py +995 -0
  143. opentrons/hardware_control/instruments/ot3/__init__.py +0 -0
  144. opentrons/hardware_control/instruments/ot3/gripper.py +420 -0
  145. opentrons/hardware_control/instruments/ot3/gripper_handler.py +173 -0
  146. opentrons/hardware_control/instruments/ot3/instrument_calibration.py +214 -0
  147. opentrons/hardware_control/instruments/ot3/pipette.py +858 -0
  148. opentrons/hardware_control/instruments/ot3/pipette_handler.py +1030 -0
  149. opentrons/hardware_control/module_control.py +332 -0
  150. opentrons/hardware_control/modules/__init__.py +69 -0
  151. opentrons/hardware_control/modules/absorbance_reader.py +373 -0
  152. opentrons/hardware_control/modules/errors.py +7 -0
  153. opentrons/hardware_control/modules/flex_stacker.py +948 -0
  154. opentrons/hardware_control/modules/heater_shaker.py +426 -0
  155. opentrons/hardware_control/modules/lid_temp_status.py +35 -0
  156. opentrons/hardware_control/modules/magdeck.py +233 -0
  157. opentrons/hardware_control/modules/mod_abc.py +245 -0
  158. opentrons/hardware_control/modules/module_calibration.py +93 -0
  159. opentrons/hardware_control/modules/plate_temp_status.py +61 -0
  160. opentrons/hardware_control/modules/tempdeck.py +299 -0
  161. opentrons/hardware_control/modules/thermocycler.py +731 -0
  162. opentrons/hardware_control/modules/types.py +417 -0
  163. opentrons/hardware_control/modules/update.py +255 -0
  164. opentrons/hardware_control/modules/utils.py +73 -0
  165. opentrons/hardware_control/motion_utilities.py +318 -0
  166. opentrons/hardware_control/nozzle_manager.py +422 -0
  167. opentrons/hardware_control/ot3_calibration.py +1171 -0
  168. opentrons/hardware_control/ot3api.py +3227 -0
  169. opentrons/hardware_control/pause_manager.py +31 -0
  170. opentrons/hardware_control/poller.py +112 -0
  171. opentrons/hardware_control/protocols/__init__.py +106 -0
  172. opentrons/hardware_control/protocols/asyncio_configurable.py +11 -0
  173. opentrons/hardware_control/protocols/calibratable.py +45 -0
  174. opentrons/hardware_control/protocols/chassis_accessory_manager.py +90 -0
  175. opentrons/hardware_control/protocols/configurable.py +48 -0
  176. opentrons/hardware_control/protocols/event_sourcer.py +18 -0
  177. opentrons/hardware_control/protocols/execution_controllable.py +33 -0
  178. opentrons/hardware_control/protocols/flex_calibratable.py +96 -0
  179. opentrons/hardware_control/protocols/flex_instrument_configurer.py +52 -0
  180. opentrons/hardware_control/protocols/gripper_controller.py +55 -0
  181. opentrons/hardware_control/protocols/hardware_manager.py +51 -0
  182. opentrons/hardware_control/protocols/identifiable.py +16 -0
  183. opentrons/hardware_control/protocols/instrument_configurer.py +206 -0
  184. opentrons/hardware_control/protocols/liquid_handler.py +266 -0
  185. opentrons/hardware_control/protocols/module_provider.py +16 -0
  186. opentrons/hardware_control/protocols/motion_controller.py +243 -0
  187. opentrons/hardware_control/protocols/position_estimator.py +45 -0
  188. opentrons/hardware_control/protocols/simulatable.py +10 -0
  189. opentrons/hardware_control/protocols/stoppable.py +9 -0
  190. opentrons/hardware_control/protocols/types.py +27 -0
  191. opentrons/hardware_control/robot_calibration.py +224 -0
  192. opentrons/hardware_control/scripts/README.md +28 -0
  193. opentrons/hardware_control/scripts/__init__.py +1 -0
  194. opentrons/hardware_control/scripts/gripper_control.py +208 -0
  195. opentrons/hardware_control/scripts/ot3gripper +7 -0
  196. opentrons/hardware_control/scripts/ot3repl +7 -0
  197. opentrons/hardware_control/scripts/repl.py +187 -0
  198. opentrons/hardware_control/scripts/tc_control.py +97 -0
  199. opentrons/hardware_control/simulator_setup.py +260 -0
  200. opentrons/hardware_control/thread_manager.py +431 -0
  201. opentrons/hardware_control/threaded_async_lock.py +97 -0
  202. opentrons/hardware_control/types.py +792 -0
  203. opentrons/hardware_control/util.py +234 -0
  204. opentrons/legacy_broker.py +53 -0
  205. opentrons/legacy_commands/__init__.py +1 -0
  206. opentrons/legacy_commands/commands.py +483 -0
  207. opentrons/legacy_commands/helpers.py +153 -0
  208. opentrons/legacy_commands/module_commands.py +215 -0
  209. opentrons/legacy_commands/protocol_commands.py +54 -0
  210. opentrons/legacy_commands/publisher.py +155 -0
  211. opentrons/legacy_commands/robot_commands.py +51 -0
  212. opentrons/legacy_commands/types.py +1115 -0
  213. opentrons/motion_planning/__init__.py +32 -0
  214. opentrons/motion_planning/adjacent_slots_getters.py +168 -0
  215. opentrons/motion_planning/deck_conflict.py +396 -0
  216. opentrons/motion_planning/errors.py +35 -0
  217. opentrons/motion_planning/types.py +42 -0
  218. opentrons/motion_planning/waypoints.py +218 -0
  219. opentrons/ordered_set.py +138 -0
  220. opentrons/protocol_api/__init__.py +105 -0
  221. opentrons/protocol_api/_liquid.py +157 -0
  222. opentrons/protocol_api/_liquid_properties.py +814 -0
  223. opentrons/protocol_api/_nozzle_layout.py +31 -0
  224. opentrons/protocol_api/_parameter_context.py +300 -0
  225. opentrons/protocol_api/_parameters.py +31 -0
  226. opentrons/protocol_api/_transfer_liquid_validation.py +108 -0
  227. opentrons/protocol_api/_types.py +43 -0
  228. opentrons/protocol_api/config.py +23 -0
  229. opentrons/protocol_api/core/__init__.py +23 -0
  230. opentrons/protocol_api/core/common.py +33 -0
  231. opentrons/protocol_api/core/core_map.py +74 -0
  232. opentrons/protocol_api/core/engine/__init__.py +22 -0
  233. opentrons/protocol_api/core/engine/_default_labware_versions.py +179 -0
  234. opentrons/protocol_api/core/engine/deck_conflict.py +348 -0
  235. opentrons/protocol_api/core/engine/exceptions.py +19 -0
  236. opentrons/protocol_api/core/engine/instrument.py +2391 -0
  237. opentrons/protocol_api/core/engine/labware.py +238 -0
  238. opentrons/protocol_api/core/engine/load_labware_params.py +73 -0
  239. opentrons/protocol_api/core/engine/module_core.py +1025 -0
  240. opentrons/protocol_api/core/engine/overlap_versions.py +20 -0
  241. opentrons/protocol_api/core/engine/pipette_movement_conflict.py +358 -0
  242. opentrons/protocol_api/core/engine/point_calculations.py +64 -0
  243. opentrons/protocol_api/core/engine/protocol.py +1153 -0
  244. opentrons/protocol_api/core/engine/robot.py +139 -0
  245. opentrons/protocol_api/core/engine/stringify.py +74 -0
  246. opentrons/protocol_api/core/engine/transfer_components_executor.py +990 -0
  247. opentrons/protocol_api/core/engine/well.py +241 -0
  248. opentrons/protocol_api/core/instrument.py +459 -0
  249. opentrons/protocol_api/core/labware.py +151 -0
  250. opentrons/protocol_api/core/legacy/__init__.py +11 -0
  251. opentrons/protocol_api/core/legacy/_labware_geometry.py +37 -0
  252. opentrons/protocol_api/core/legacy/deck.py +369 -0
  253. opentrons/protocol_api/core/legacy/labware_offset_provider.py +108 -0
  254. opentrons/protocol_api/core/legacy/legacy_instrument_core.py +709 -0
  255. opentrons/protocol_api/core/legacy/legacy_labware_core.py +235 -0
  256. opentrons/protocol_api/core/legacy/legacy_module_core.py +592 -0
  257. opentrons/protocol_api/core/legacy/legacy_protocol_core.py +612 -0
  258. opentrons/protocol_api/core/legacy/legacy_well_core.py +162 -0
  259. opentrons/protocol_api/core/legacy/load_info.py +67 -0
  260. opentrons/protocol_api/core/legacy/module_geometry.py +547 -0
  261. opentrons/protocol_api/core/legacy/well_geometry.py +148 -0
  262. opentrons/protocol_api/core/legacy_simulator/__init__.py +16 -0
  263. opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +624 -0
  264. opentrons/protocol_api/core/legacy_simulator/legacy_protocol_core.py +85 -0
  265. opentrons/protocol_api/core/module.py +484 -0
  266. opentrons/protocol_api/core/protocol.py +311 -0
  267. opentrons/protocol_api/core/robot.py +51 -0
  268. opentrons/protocol_api/core/well.py +116 -0
  269. opentrons/protocol_api/core/well_grid.py +45 -0
  270. opentrons/protocol_api/create_protocol_context.py +177 -0
  271. opentrons/protocol_api/deck.py +223 -0
  272. opentrons/protocol_api/disposal_locations.py +244 -0
  273. opentrons/protocol_api/instrument_context.py +3212 -0
  274. opentrons/protocol_api/labware.py +1579 -0
  275. opentrons/protocol_api/module_contexts.py +1425 -0
  276. opentrons/protocol_api/module_validation_and_errors.py +61 -0
  277. opentrons/protocol_api/protocol_context.py +1688 -0
  278. opentrons/protocol_api/robot_context.py +303 -0
  279. opentrons/protocol_api/validation.py +761 -0
  280. opentrons/protocol_engine/__init__.py +155 -0
  281. opentrons/protocol_engine/actions/__init__.py +65 -0
  282. opentrons/protocol_engine/actions/action_dispatcher.py +30 -0
  283. opentrons/protocol_engine/actions/action_handler.py +13 -0
  284. opentrons/protocol_engine/actions/actions.py +302 -0
  285. opentrons/protocol_engine/actions/get_state_update.py +38 -0
  286. opentrons/protocol_engine/clients/__init__.py +5 -0
  287. opentrons/protocol_engine/clients/sync_client.py +174 -0
  288. opentrons/protocol_engine/clients/transports.py +197 -0
  289. opentrons/protocol_engine/commands/__init__.py +757 -0
  290. opentrons/protocol_engine/commands/absorbance_reader/__init__.py +61 -0
  291. opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +154 -0
  292. opentrons/protocol_engine/commands/absorbance_reader/common.py +6 -0
  293. opentrons/protocol_engine/commands/absorbance_reader/initialize.py +151 -0
  294. opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +154 -0
  295. opentrons/protocol_engine/commands/absorbance_reader/read.py +226 -0
  296. opentrons/protocol_engine/commands/air_gap_in_place.py +162 -0
  297. opentrons/protocol_engine/commands/aspirate.py +244 -0
  298. opentrons/protocol_engine/commands/aspirate_in_place.py +184 -0
  299. opentrons/protocol_engine/commands/aspirate_while_tracking.py +211 -0
  300. opentrons/protocol_engine/commands/blow_out.py +146 -0
  301. opentrons/protocol_engine/commands/blow_out_in_place.py +119 -0
  302. opentrons/protocol_engine/commands/calibration/__init__.py +60 -0
  303. opentrons/protocol_engine/commands/calibration/calibrate_gripper.py +166 -0
  304. opentrons/protocol_engine/commands/calibration/calibrate_module.py +117 -0
  305. opentrons/protocol_engine/commands/calibration/calibrate_pipette.py +96 -0
  306. opentrons/protocol_engine/commands/calibration/move_to_maintenance_position.py +156 -0
  307. opentrons/protocol_engine/commands/command.py +308 -0
  308. opentrons/protocol_engine/commands/command_unions.py +974 -0
  309. opentrons/protocol_engine/commands/comment.py +57 -0
  310. opentrons/protocol_engine/commands/configure_for_volume.py +108 -0
  311. opentrons/protocol_engine/commands/configure_nozzle_layout.py +115 -0
  312. opentrons/protocol_engine/commands/custom.py +67 -0
  313. opentrons/protocol_engine/commands/dispense.py +194 -0
  314. opentrons/protocol_engine/commands/dispense_in_place.py +179 -0
  315. opentrons/protocol_engine/commands/dispense_while_tracking.py +204 -0
  316. opentrons/protocol_engine/commands/drop_tip.py +232 -0
  317. opentrons/protocol_engine/commands/drop_tip_in_place.py +205 -0
  318. opentrons/protocol_engine/commands/flex_stacker/__init__.py +64 -0
  319. opentrons/protocol_engine/commands/flex_stacker/common.py +900 -0
  320. opentrons/protocol_engine/commands/flex_stacker/empty.py +293 -0
  321. opentrons/protocol_engine/commands/flex_stacker/fill.py +281 -0
  322. opentrons/protocol_engine/commands/flex_stacker/retrieve.py +339 -0
  323. opentrons/protocol_engine/commands/flex_stacker/set_stored_labware.py +328 -0
  324. opentrons/protocol_engine/commands/flex_stacker/store.py +326 -0
  325. opentrons/protocol_engine/commands/generate_command_schema.py +61 -0
  326. opentrons/protocol_engine/commands/get_next_tip.py +134 -0
  327. opentrons/protocol_engine/commands/get_tip_presence.py +87 -0
  328. opentrons/protocol_engine/commands/hash_command_params.py +38 -0
  329. opentrons/protocol_engine/commands/heater_shaker/__init__.py +102 -0
  330. opentrons/protocol_engine/commands/heater_shaker/close_labware_latch.py +83 -0
  331. opentrons/protocol_engine/commands/heater_shaker/deactivate_heater.py +82 -0
  332. opentrons/protocol_engine/commands/heater_shaker/deactivate_shaker.py +84 -0
  333. opentrons/protocol_engine/commands/heater_shaker/open_labware_latch.py +110 -0
  334. opentrons/protocol_engine/commands/heater_shaker/set_and_wait_for_shake_speed.py +125 -0
  335. opentrons/protocol_engine/commands/heater_shaker/set_target_temperature.py +90 -0
  336. opentrons/protocol_engine/commands/heater_shaker/wait_for_temperature.py +102 -0
  337. opentrons/protocol_engine/commands/home.py +100 -0
  338. opentrons/protocol_engine/commands/identify_module.py +86 -0
  339. opentrons/protocol_engine/commands/labware_handling_common.py +29 -0
  340. opentrons/protocol_engine/commands/liquid_probe.py +464 -0
  341. opentrons/protocol_engine/commands/load_labware.py +210 -0
  342. opentrons/protocol_engine/commands/load_lid.py +154 -0
  343. opentrons/protocol_engine/commands/load_lid_stack.py +272 -0
  344. opentrons/protocol_engine/commands/load_liquid.py +95 -0
  345. opentrons/protocol_engine/commands/load_liquid_class.py +144 -0
  346. opentrons/protocol_engine/commands/load_module.py +223 -0
  347. opentrons/protocol_engine/commands/load_pipette.py +167 -0
  348. opentrons/protocol_engine/commands/magnetic_module/__init__.py +32 -0
  349. opentrons/protocol_engine/commands/magnetic_module/disengage.py +97 -0
  350. opentrons/protocol_engine/commands/magnetic_module/engage.py +119 -0
  351. opentrons/protocol_engine/commands/move_labware.py +546 -0
  352. opentrons/protocol_engine/commands/move_relative.py +102 -0
  353. opentrons/protocol_engine/commands/move_to_addressable_area.py +176 -0
  354. opentrons/protocol_engine/commands/move_to_addressable_area_for_drop_tip.py +198 -0
  355. opentrons/protocol_engine/commands/move_to_coordinates.py +107 -0
  356. opentrons/protocol_engine/commands/move_to_well.py +119 -0
  357. opentrons/protocol_engine/commands/movement_common.py +338 -0
  358. opentrons/protocol_engine/commands/pick_up_tip.py +241 -0
  359. opentrons/protocol_engine/commands/pipetting_common.py +443 -0
  360. opentrons/protocol_engine/commands/prepare_to_aspirate.py +121 -0
  361. opentrons/protocol_engine/commands/pressure_dispense.py +155 -0
  362. opentrons/protocol_engine/commands/reload_labware.py +90 -0
  363. opentrons/protocol_engine/commands/retract_axis.py +75 -0
  364. opentrons/protocol_engine/commands/robot/__init__.py +70 -0
  365. opentrons/protocol_engine/commands/robot/close_gripper_jaw.py +96 -0
  366. opentrons/protocol_engine/commands/robot/common.py +18 -0
  367. opentrons/protocol_engine/commands/robot/move_axes_relative.py +101 -0
  368. opentrons/protocol_engine/commands/robot/move_axes_to.py +100 -0
  369. opentrons/protocol_engine/commands/robot/move_to.py +94 -0
  370. opentrons/protocol_engine/commands/robot/open_gripper_jaw.py +86 -0
  371. opentrons/protocol_engine/commands/save_position.py +109 -0
  372. opentrons/protocol_engine/commands/seal_pipette_to_tip.py +353 -0
  373. opentrons/protocol_engine/commands/set_rail_lights.py +67 -0
  374. opentrons/protocol_engine/commands/set_status_bar.py +89 -0
  375. opentrons/protocol_engine/commands/temperature_module/__init__.py +46 -0
  376. opentrons/protocol_engine/commands/temperature_module/deactivate.py +86 -0
  377. opentrons/protocol_engine/commands/temperature_module/set_target_temperature.py +97 -0
  378. opentrons/protocol_engine/commands/temperature_module/wait_for_temperature.py +104 -0
  379. opentrons/protocol_engine/commands/thermocycler/__init__.py +152 -0
  380. opentrons/protocol_engine/commands/thermocycler/close_lid.py +87 -0
  381. opentrons/protocol_engine/commands/thermocycler/deactivate_block.py +80 -0
  382. opentrons/protocol_engine/commands/thermocycler/deactivate_lid.py +80 -0
  383. opentrons/protocol_engine/commands/thermocycler/open_lid.py +87 -0
  384. opentrons/protocol_engine/commands/thermocycler/run_extended_profile.py +171 -0
  385. opentrons/protocol_engine/commands/thermocycler/run_profile.py +124 -0
  386. opentrons/protocol_engine/commands/thermocycler/set_target_block_temperature.py +140 -0
  387. opentrons/protocol_engine/commands/thermocycler/set_target_lid_temperature.py +100 -0
  388. opentrons/protocol_engine/commands/thermocycler/wait_for_block_temperature.py +93 -0
  389. opentrons/protocol_engine/commands/thermocycler/wait_for_lid_temperature.py +89 -0
  390. opentrons/protocol_engine/commands/touch_tip.py +189 -0
  391. opentrons/protocol_engine/commands/unsafe/__init__.py +161 -0
  392. opentrons/protocol_engine/commands/unsafe/unsafe_blow_out_in_place.py +100 -0
  393. opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +121 -0
  394. opentrons/protocol_engine/commands/unsafe/unsafe_engage_axes.py +82 -0
  395. opentrons/protocol_engine/commands/unsafe/unsafe_place_labware.py +208 -0
  396. opentrons/protocol_engine/commands/unsafe/unsafe_stacker_close_latch.py +94 -0
  397. opentrons/protocol_engine/commands/unsafe/unsafe_stacker_manual_retrieve.py +295 -0
  398. opentrons/protocol_engine/commands/unsafe/unsafe_stacker_open_latch.py +91 -0
  399. opentrons/protocol_engine/commands/unsafe/unsafe_stacker_prepare_shuttle.py +136 -0
  400. opentrons/protocol_engine/commands/unsafe/unsafe_ungrip_labware.py +77 -0
  401. opentrons/protocol_engine/commands/unsafe/update_position_estimators.py +90 -0
  402. opentrons/protocol_engine/commands/unseal_pipette_from_tip.py +153 -0
  403. opentrons/protocol_engine/commands/verify_tip_presence.py +100 -0
  404. opentrons/protocol_engine/commands/wait_for_duration.py +76 -0
  405. opentrons/protocol_engine/commands/wait_for_resume.py +75 -0
  406. opentrons/protocol_engine/create_protocol_engine.py +193 -0
  407. opentrons/protocol_engine/engine_support.py +28 -0
  408. opentrons/protocol_engine/error_recovery_policy.py +81 -0
  409. opentrons/protocol_engine/errors/__init__.py +191 -0
  410. opentrons/protocol_engine/errors/error_occurrence.py +182 -0
  411. opentrons/protocol_engine/errors/exceptions.py +1308 -0
  412. opentrons/protocol_engine/execution/__init__.py +50 -0
  413. opentrons/protocol_engine/execution/command_executor.py +216 -0
  414. opentrons/protocol_engine/execution/create_queue_worker.py +102 -0
  415. opentrons/protocol_engine/execution/door_watcher.py +119 -0
  416. opentrons/protocol_engine/execution/equipment.py +819 -0
  417. opentrons/protocol_engine/execution/error_recovery_hardware_state_synchronizer.py +101 -0
  418. opentrons/protocol_engine/execution/gantry_mover.py +686 -0
  419. opentrons/protocol_engine/execution/hardware_stopper.py +147 -0
  420. opentrons/protocol_engine/execution/heater_shaker_movement_flagger.py +207 -0
  421. opentrons/protocol_engine/execution/labware_movement.py +297 -0
  422. opentrons/protocol_engine/execution/movement.py +349 -0
  423. opentrons/protocol_engine/execution/pipetting.py +607 -0
  424. opentrons/protocol_engine/execution/queue_worker.py +86 -0
  425. opentrons/protocol_engine/execution/rail_lights.py +25 -0
  426. opentrons/protocol_engine/execution/run_control.py +33 -0
  427. opentrons/protocol_engine/execution/status_bar.py +34 -0
  428. opentrons/protocol_engine/execution/thermocycler_movement_flagger.py +188 -0
  429. opentrons/protocol_engine/execution/thermocycler_plate_lifter.py +81 -0
  430. opentrons/protocol_engine/execution/tip_handler.py +550 -0
  431. opentrons/protocol_engine/labware_offset_standardization.py +194 -0
  432. opentrons/protocol_engine/notes/__init__.py +17 -0
  433. opentrons/protocol_engine/notes/notes.py +59 -0
  434. opentrons/protocol_engine/plugins.py +104 -0
  435. opentrons/protocol_engine/protocol_engine.py +683 -0
  436. opentrons/protocol_engine/resources/__init__.py +26 -0
  437. opentrons/protocol_engine/resources/deck_configuration_provider.py +232 -0
  438. opentrons/protocol_engine/resources/deck_data_provider.py +94 -0
  439. opentrons/protocol_engine/resources/file_provider.py +161 -0
  440. opentrons/protocol_engine/resources/fixture_validation.py +58 -0
  441. opentrons/protocol_engine/resources/labware_data_provider.py +106 -0
  442. opentrons/protocol_engine/resources/labware_validation.py +73 -0
  443. opentrons/protocol_engine/resources/model_utils.py +32 -0
  444. opentrons/protocol_engine/resources/module_data_provider.py +44 -0
  445. opentrons/protocol_engine/resources/ot3_validation.py +21 -0
  446. opentrons/protocol_engine/resources/pipette_data_provider.py +379 -0
  447. opentrons/protocol_engine/slot_standardization.py +128 -0
  448. opentrons/protocol_engine/state/__init__.py +1 -0
  449. opentrons/protocol_engine/state/_abstract_store.py +27 -0
  450. opentrons/protocol_engine/state/_axis_aligned_bounding_box.py +50 -0
  451. opentrons/protocol_engine/state/_labware_origin_math.py +636 -0
  452. opentrons/protocol_engine/state/_move_types.py +83 -0
  453. opentrons/protocol_engine/state/_well_math.py +193 -0
  454. opentrons/protocol_engine/state/addressable_areas.py +699 -0
  455. opentrons/protocol_engine/state/command_history.py +309 -0
  456. opentrons/protocol_engine/state/commands.py +1158 -0
  457. opentrons/protocol_engine/state/config.py +39 -0
  458. opentrons/protocol_engine/state/files.py +57 -0
  459. opentrons/protocol_engine/state/fluid_stack.py +138 -0
  460. opentrons/protocol_engine/state/geometry.py +2359 -0
  461. opentrons/protocol_engine/state/inner_well_math_utils.py +548 -0
  462. opentrons/protocol_engine/state/labware.py +1459 -0
  463. opentrons/protocol_engine/state/liquid_classes.py +82 -0
  464. opentrons/protocol_engine/state/liquids.py +73 -0
  465. opentrons/protocol_engine/state/module_substates/__init__.py +45 -0
  466. opentrons/protocol_engine/state/module_substates/absorbance_reader_substate.py +35 -0
  467. opentrons/protocol_engine/state/module_substates/flex_stacker_substate.py +112 -0
  468. opentrons/protocol_engine/state/module_substates/heater_shaker_module_substate.py +115 -0
  469. opentrons/protocol_engine/state/module_substates/magnetic_block_substate.py +17 -0
  470. opentrons/protocol_engine/state/module_substates/magnetic_module_substate.py +65 -0
  471. opentrons/protocol_engine/state/module_substates/temperature_module_substate.py +67 -0
  472. opentrons/protocol_engine/state/module_substates/thermocycler_module_substate.py +163 -0
  473. opentrons/protocol_engine/state/modules.py +1500 -0
  474. opentrons/protocol_engine/state/motion.py +373 -0
  475. opentrons/protocol_engine/state/pipettes.py +905 -0
  476. opentrons/protocol_engine/state/state.py +421 -0
  477. opentrons/protocol_engine/state/state_summary.py +36 -0
  478. opentrons/protocol_engine/state/tips.py +420 -0
  479. opentrons/protocol_engine/state/update_types.py +904 -0
  480. opentrons/protocol_engine/state/wells.py +290 -0
  481. opentrons/protocol_engine/types/__init__.py +308 -0
  482. opentrons/protocol_engine/types/automatic_tip_selection.py +39 -0
  483. opentrons/protocol_engine/types/command_annotations.py +53 -0
  484. opentrons/protocol_engine/types/deck_configuration.py +81 -0
  485. opentrons/protocol_engine/types/execution.py +96 -0
  486. opentrons/protocol_engine/types/hardware_passthrough.py +25 -0
  487. opentrons/protocol_engine/types/instrument.py +47 -0
  488. opentrons/protocol_engine/types/instrument_sensors.py +47 -0
  489. opentrons/protocol_engine/types/labware.py +131 -0
  490. opentrons/protocol_engine/types/labware_movement.py +22 -0
  491. opentrons/protocol_engine/types/labware_offset_location.py +111 -0
  492. opentrons/protocol_engine/types/labware_offset_vector.py +16 -0
  493. opentrons/protocol_engine/types/liquid.py +40 -0
  494. opentrons/protocol_engine/types/liquid_class.py +59 -0
  495. opentrons/protocol_engine/types/liquid_handling.py +13 -0
  496. opentrons/protocol_engine/types/liquid_level_detection.py +191 -0
  497. opentrons/protocol_engine/types/location.py +194 -0
  498. opentrons/protocol_engine/types/module.py +303 -0
  499. opentrons/protocol_engine/types/partial_tip_configuration.py +76 -0
  500. opentrons/protocol_engine/types/run_time_parameters.py +133 -0
  501. opentrons/protocol_engine/types/tip.py +18 -0
  502. opentrons/protocol_engine/types/util.py +21 -0
  503. opentrons/protocol_engine/types/well_position.py +124 -0
  504. opentrons/protocol_reader/__init__.py +37 -0
  505. opentrons/protocol_reader/extract_labware_definitions.py +66 -0
  506. opentrons/protocol_reader/file_format_validator.py +152 -0
  507. opentrons/protocol_reader/file_hasher.py +27 -0
  508. opentrons/protocol_reader/file_identifier.py +284 -0
  509. opentrons/protocol_reader/file_reader_writer.py +90 -0
  510. opentrons/protocol_reader/input_file.py +16 -0
  511. opentrons/protocol_reader/protocol_files_invalid_error.py +6 -0
  512. opentrons/protocol_reader/protocol_reader.py +188 -0
  513. opentrons/protocol_reader/protocol_source.py +124 -0
  514. opentrons/protocol_reader/role_analyzer.py +86 -0
  515. opentrons/protocol_runner/__init__.py +26 -0
  516. opentrons/protocol_runner/create_simulating_orchestrator.py +118 -0
  517. opentrons/protocol_runner/json_file_reader.py +55 -0
  518. opentrons/protocol_runner/json_translator.py +314 -0
  519. opentrons/protocol_runner/legacy_command_mapper.py +848 -0
  520. opentrons/protocol_runner/legacy_context_plugin.py +116 -0
  521. opentrons/protocol_runner/protocol_runner.py +530 -0
  522. opentrons/protocol_runner/python_protocol_wrappers.py +179 -0
  523. opentrons/protocol_runner/run_orchestrator.py +496 -0
  524. opentrons/protocol_runner/task_queue.py +95 -0
  525. opentrons/protocols/__init__.py +6 -0
  526. opentrons/protocols/advanced_control/__init__.py +0 -0
  527. opentrons/protocols/advanced_control/common.py +38 -0
  528. opentrons/protocols/advanced_control/mix.py +60 -0
  529. opentrons/protocols/advanced_control/transfers/__init__.py +0 -0
  530. opentrons/protocols/advanced_control/transfers/common.py +180 -0
  531. opentrons/protocols/advanced_control/transfers/transfer.py +972 -0
  532. opentrons/protocols/advanced_control/transfers/transfer_liquid_utils.py +231 -0
  533. opentrons/protocols/api_support/__init__.py +0 -0
  534. opentrons/protocols/api_support/constants.py +8 -0
  535. opentrons/protocols/api_support/deck_type.py +110 -0
  536. opentrons/protocols/api_support/definitions.py +18 -0
  537. opentrons/protocols/api_support/instrument.py +151 -0
  538. opentrons/protocols/api_support/labware_like.py +233 -0
  539. opentrons/protocols/api_support/tip_tracker.py +175 -0
  540. opentrons/protocols/api_support/types.py +32 -0
  541. opentrons/protocols/api_support/util.py +403 -0
  542. opentrons/protocols/bundle.py +89 -0
  543. opentrons/protocols/duration/__init__.py +4 -0
  544. opentrons/protocols/duration/errors.py +5 -0
  545. opentrons/protocols/duration/estimator.py +628 -0
  546. opentrons/protocols/execution/__init__.py +0 -0
  547. opentrons/protocols/execution/dev_types.py +181 -0
  548. opentrons/protocols/execution/errors.py +40 -0
  549. opentrons/protocols/execution/execute.py +84 -0
  550. opentrons/protocols/execution/execute_json_v3.py +275 -0
  551. opentrons/protocols/execution/execute_json_v4.py +359 -0
  552. opentrons/protocols/execution/execute_json_v5.py +28 -0
  553. opentrons/protocols/execution/execute_python.py +169 -0
  554. opentrons/protocols/execution/json_dispatchers.py +87 -0
  555. opentrons/protocols/execution/types.py +7 -0
  556. opentrons/protocols/geometry/__init__.py +0 -0
  557. opentrons/protocols/geometry/planning.py +297 -0
  558. opentrons/protocols/labware.py +312 -0
  559. opentrons/protocols/models/__init__.py +0 -0
  560. opentrons/protocols/models/json_protocol.py +679 -0
  561. opentrons/protocols/parameters/__init__.py +0 -0
  562. opentrons/protocols/parameters/csv_parameter_definition.py +77 -0
  563. opentrons/protocols/parameters/csv_parameter_interface.py +96 -0
  564. opentrons/protocols/parameters/exceptions.py +34 -0
  565. opentrons/protocols/parameters/parameter_definition.py +272 -0
  566. opentrons/protocols/parameters/types.py +17 -0
  567. opentrons/protocols/parameters/validation.py +267 -0
  568. opentrons/protocols/parse.py +671 -0
  569. opentrons/protocols/types.py +159 -0
  570. opentrons/py.typed +0 -0
  571. opentrons/resources/scripts/lpc21isp +0 -0
  572. opentrons/resources/smoothie-edge-8414642.hex +23010 -0
  573. opentrons/simulate.py +1065 -0
  574. opentrons/system/__init__.py +6 -0
  575. opentrons/system/camera.py +51 -0
  576. opentrons/system/log_control.py +59 -0
  577. opentrons/system/nmcli.py +856 -0
  578. opentrons/system/resin.py +24 -0
  579. opentrons/system/smoothie_update.py +15 -0
  580. opentrons/system/wifi.py +204 -0
  581. opentrons/tools/__init__.py +0 -0
  582. opentrons/tools/args_handler.py +22 -0
  583. opentrons/tools/write_pipette_memory.py +157 -0
  584. opentrons/types.py +618 -0
  585. opentrons/util/__init__.py +1 -0
  586. opentrons/util/async_helpers.py +166 -0
  587. opentrons/util/broker.py +84 -0
  588. opentrons/util/change_notifier.py +47 -0
  589. opentrons/util/entrypoint_util.py +278 -0
  590. opentrons/util/get_union_elements.py +26 -0
  591. opentrons/util/helpers.py +6 -0
  592. opentrons/util/linal.py +178 -0
  593. opentrons/util/logging_config.py +265 -0
  594. opentrons/util/logging_queue_handler.py +61 -0
  595. opentrons/util/performance_helpers.py +157 -0
  596. opentrons-8.6.0a1.dist-info/METADATA +37 -0
  597. opentrons-8.6.0a1.dist-info/RECORD +600 -0
  598. opentrons-8.6.0a1.dist-info/WHEEL +4 -0
  599. opentrons-8.6.0a1.dist-info/entry_points.txt +3 -0
  600. opentrons-8.6.0a1.dist-info/licenses/LICENSE +202 -0
@@ -0,0 +1,761 @@
1
+ from __future__ import annotations
2
+ from typing import (
3
+ Any,
4
+ Dict,
5
+ List,
6
+ Optional,
7
+ Sequence,
8
+ Union,
9
+ Tuple,
10
+ Mapping,
11
+ NamedTuple,
12
+ TYPE_CHECKING,
13
+ )
14
+ from math import isinf, isnan
15
+ from typing_extensions import TypeGuard
16
+
17
+ from opentrons_shared_data.labware.labware_definition import (
18
+ LabwareDefinition,
19
+ LabwareRole,
20
+ )
21
+ from opentrons_shared_data.pipette.types import PipetteNameType, PIPETTE_API_NAMES_MAP
22
+ from opentrons_shared_data.robot.types import RobotType
23
+
24
+ from opentrons.protocols.api_support.types import APIVersion, ThermocyclerStep
25
+ from opentrons.protocols.api_support.util import APIVersionError
26
+ from opentrons.protocols.advanced_control.transfers.common import TransferTipPolicyV2
27
+ from opentrons.types import (
28
+ Mount,
29
+ DeckSlotName,
30
+ StagingSlotName,
31
+ Location,
32
+ AxisType,
33
+ AxisMapType,
34
+ StringAxisMap,
35
+ )
36
+ from opentrons.hardware_control.modules.types import (
37
+ ModuleModel,
38
+ MagneticModuleModel,
39
+ TemperatureModuleModel,
40
+ ThermocyclerModuleModel,
41
+ HeaterShakerModuleModel,
42
+ MagneticBlockModel,
43
+ AbsorbanceReaderModel,
44
+ FlexStackerModuleModel,
45
+ )
46
+
47
+ from .disposal_locations import TrashBin, WasteChute
48
+
49
+ if TYPE_CHECKING:
50
+ from .labware import Well
51
+
52
+
53
+ # The first APIVersion where Python protocols can specify deck labels like "D1" instead of "1".
54
+ _COORDINATE_DECK_LABEL_VERSION_GATE = APIVersion(2, 15)
55
+
56
+ # The first APIVersion where Python protocols can specify staging deck slots (e.g. "D4")
57
+ _STAGING_DECK_SLOT_VERSION_GATE = APIVersion(2, 16)
58
+
59
+ # The first APIVersion where Python protocols can load lids as stacks and treat them as attributes of a parent labware.
60
+ LID_STACK_VERSION_GATE = APIVersion(2, 23)
61
+
62
+ # The first APIVersion where Python protocols can use the Flex Stacker module.
63
+ FLEX_STACKER_VERSION_GATE = APIVersion(2, 23)
64
+
65
+
66
+ class InvalidPipetteMountError(ValueError):
67
+ """An error raised when attempting to load pipettes on an invalid mount."""
68
+
69
+
70
+ class PipetteMountTypeError(TypeError):
71
+ """An error raised when an invalid mount type is used for loading pipettes."""
72
+
73
+
74
+ class InstrumentMountTypeError(TypeError):
75
+ """An error raised when an invalid mount type is used for any available instruments."""
76
+
77
+
78
+ class IncorrectAxisError(TypeError):
79
+ """An error raised when an invalid axis key is provided in an axis map."""
80
+
81
+
82
+ class LabwareDefinitionIsNotAdapterError(ValueError):
83
+ """An error raised when an adapter is attempted to be loaded as a labware."""
84
+
85
+
86
+ class LabwareDefinitionIsNotLabwareError(ValueError):
87
+ """An error raised when a labware is not loaded using `load_labware`."""
88
+
89
+
90
+ class InvalidTrashBinLocationError(ValueError):
91
+ """An error raised when attempting to load trash bins in invalid slots."""
92
+
93
+
94
+ class InvalidFixtureLocationError(ValueError):
95
+ """An error raised when attempting to load a fixture in an invalid cutout."""
96
+
97
+
98
+ def ensure_mount_for_pipette(
99
+ mount: Union[str, Mount, None], pipette: PipetteNameType
100
+ ) -> Mount:
101
+ """Ensure that an input value represents a valid mount, and is valid for the given pipette."""
102
+ if pipette in [PipetteNameType.P1000_96, PipetteNameType.P200_96]:
103
+ # Always validate the raw mount input, even if the pipette is a 96-channel and we're not going
104
+ # to use the mount value.
105
+ if mount is not None:
106
+ _ensure_mount(mount)
107
+ # Internal layers treat the 96-channel as being on the left mount.
108
+ return Mount.LEFT
109
+ else:
110
+ if mount is None:
111
+ raise InvalidPipetteMountError(
112
+ f"You must specify a left or right mount to load {pipette.value}."
113
+ )
114
+ else:
115
+ return _ensure_mount(mount)
116
+
117
+
118
+ def _ensure_mount(mount: Union[str, Mount]) -> Mount:
119
+ """Ensure that an input value represents a valid Mount."""
120
+ if mount in [Mount.EXTENSION, "extension"]:
121
+ # This would cause existing protocols that might be iterating over mount types
122
+ # for loading pipettes to raise an error because Mount now includes Extension mount.
123
+ # For example, this would raise error-
124
+ # ```
125
+ # for i, mount in enumerate(Mount):
126
+ # pipette[i] = ctx.load_instrument("p300_single", mount)
127
+ # ```
128
+ # But this is a very rare use case and none of the protocols in protocol library
129
+ # or protocols seen/ built by support/ science/ apps engg do this so it might be
130
+ # safe to raise this error now?
131
+ raise InvalidPipetteMountError(
132
+ f"Loading pipettes on {mount} is not allowed."
133
+ f"Use the left or right mounts instead."
134
+ )
135
+ if isinstance(mount, Mount):
136
+ return mount
137
+
138
+ if isinstance(mount, str):
139
+ try:
140
+ return Mount[mount.upper()]
141
+ except KeyError as e:
142
+ raise InvalidPipetteMountError(
143
+ "If mount is specified as a string, it must be 'left' or 'right';"
144
+ f" instead, {mount} was given."
145
+ ) from e
146
+
147
+ raise PipetteMountTypeError(
148
+ "Instrument mount should be 'left', 'right', or an opentrons.types.Mount;"
149
+ f" instead, {mount} was given."
150
+ )
151
+
152
+
153
+ def ensure_instrument_mount(mount: Union[str, Mount]) -> Mount:
154
+ """Ensure that an input value represents a valid Mount for all instruments."""
155
+ if isinstance(mount, Mount):
156
+ return mount
157
+
158
+ if isinstance(mount, str):
159
+ if mount == "gripper":
160
+ # TODO (lc 08-02-2024) We should decide on the user facing name for
161
+ # the gripper mount axis.
162
+ mount = "extension"
163
+ try:
164
+ return Mount[mount.upper()]
165
+ except KeyError as e:
166
+ raise InstrumentMountTypeError(
167
+ "If mount is specified as a string, it must be 'left', 'right', 'gripper', or 'extension';"
168
+ f" instead, {mount} was given."
169
+ ) from e
170
+
171
+
172
+ def ensure_pipette_name(pipette_name: str) -> PipetteNameType:
173
+ """Ensure that an input value represents a valid pipette name."""
174
+ pipette_name = ensure_lowercase_name(pipette_name)
175
+
176
+ try:
177
+ return PIPETTE_API_NAMES_MAP[pipette_name]
178
+ except KeyError:
179
+ raise ValueError(
180
+ f"Cannot resolve {pipette_name} to pipette, must be given valid pipette name."
181
+ ) from None
182
+
183
+
184
+ def _check_ot2_axis_type(
185
+ robot_type: RobotType, axis_map_keys: Union[List[str], List[AxisType]]
186
+ ) -> None:
187
+ if robot_type == "OT-2 Standard" and isinstance(axis_map_keys[0], AxisType):
188
+ if any(k not in AxisType.ot2_axes() for k in axis_map_keys):
189
+ raise IncorrectAxisError(
190
+ f"An OT-2 Robot only accepts the following axes {AxisType.ot2_axes()}"
191
+ )
192
+ if robot_type == "OT-2 Standard" and isinstance(axis_map_keys[0], str):
193
+ if any(k.upper() not in [axis.value for axis in AxisType.ot2_axes()] for k in axis_map_keys): # type: ignore [union-attr]
194
+ raise IncorrectAxisError(
195
+ f"An OT-2 Robot only accepts the following axes {AxisType.ot2_axes()}"
196
+ )
197
+
198
+
199
+ def _check_96_channel_axis_type(
200
+ is_96_channel: bool, axis_map_keys: Union[List[str], List[AxisType]]
201
+ ) -> None:
202
+ if is_96_channel and any(
203
+ key_variation in axis_map_keys for key_variation in ["Z_R", "z_r", AxisType.Z_R]
204
+ ):
205
+ raise IncorrectAxisError(
206
+ "A 96 channel is attached. You cannot move the `Z_R` mount."
207
+ )
208
+ if not is_96_channel and any(
209
+ key_variation in axis_map_keys for key_variation in ["Q", "q", AxisType.Q]
210
+ ):
211
+ raise IncorrectAxisError(
212
+ "A 96 channel is not attached. The clamp `Q` motor does not exist."
213
+ )
214
+
215
+
216
+ def ensure_axis_map_type(
217
+ axis_map: Union[AxisMapType, StringAxisMap],
218
+ robot_type: RobotType,
219
+ is_96_channel: bool = False,
220
+ ) -> AxisMapType:
221
+ """Ensure that the axis map provided is in the correct shape and contains the correct keys."""
222
+ axis_map_keys: Union[List[str], List[AxisType]] = list(axis_map.keys()) # type: ignore
223
+ key_type = set(type(k) for k in axis_map_keys)
224
+
225
+ if len(key_type) > 1:
226
+ raise IncorrectAxisError(
227
+ "Please provide an `axis_map` with only string or only AxisType keys."
228
+ )
229
+ _check_ot2_axis_type(robot_type, axis_map_keys)
230
+ _check_96_channel_axis_type(is_96_channel, axis_map_keys)
231
+
232
+ if all(isinstance(k, AxisType) for k in axis_map_keys):
233
+ return_map: AxisMapType = axis_map # type: ignore
234
+ return return_map
235
+ try:
236
+ return {AxisType[k.upper()]: v for k, v in axis_map.items()} # type: ignore [union-attr]
237
+ except KeyError as e:
238
+ raise IncorrectAxisError(f"{e} is not a supported `AxisMapType`")
239
+
240
+
241
+ def ensure_only_gantry_axis_map_type(
242
+ axis_map: AxisMapType, robot_type: RobotType
243
+ ) -> None:
244
+ """Ensure that the axis map provided is in the correct shape and matches the gantry axes for the robot."""
245
+ if robot_type == "OT-2 Standard":
246
+ if any(k not in AxisType.ot2_gantry_axes() for k in axis_map.keys()):
247
+ raise IncorrectAxisError(
248
+ f"A critical point only accepts OT-2 gantry axes which are {AxisType.ot2_gantry_axes()}"
249
+ )
250
+ else:
251
+ if any(k not in AxisType.flex_gantry_axes() for k in axis_map.keys()):
252
+ raise IncorrectAxisError(
253
+ f"A critical point only accepts Flex gantry axes which are {AxisType.flex_gantry_axes()}"
254
+ )
255
+
256
+
257
+ # TODO(jbl 11-17-2023) this function's original purpose was ensure a valid deck slot for a given robot type
258
+ # With deck configuration, the shape of this should change to better represent it checking if a deck slot
259
+ # (and maybe any addressable area) being valid for that deck configuration
260
+ def ensure_and_convert_deck_slot(
261
+ deck_slot: Union[int, str], api_version: APIVersion, robot_type: RobotType
262
+ ) -> Union[DeckSlotName, StagingSlotName]:
263
+ """Ensure that a primitive value matches a named deck slot.
264
+
265
+ Also, convert the deck slot to match the given `robot_type`.
266
+
267
+ Params:
268
+ deck_slot: The primitive value to validate. Valid values are like `5`, `"5"`, or `"C2"`.
269
+ api_version: The Python Protocol API version whose rules to use to validate the value.
270
+ Values like `"C2"` are only supported in newer versions.
271
+
272
+ Raises:
273
+ TypeError: If you provide something that's not an `int` or `str`.
274
+ ValueError: If the value does not match a known deck slot.
275
+ APIVersionError: If you provide a value like `"C2"`, but `api_version` is too old.
276
+
277
+ Returns:
278
+ A `DeckSlotName` appropriate for the given `robot_type`. For example, given `"5"`,
279
+ this will return `DeckSlotName.SLOT_C2` on a Flex.
280
+ """
281
+ if not isinstance(deck_slot, (int, str)):
282
+ raise TypeError(f"Deck slot must be a string or integer, but got {deck_slot}")
283
+
284
+ if str(deck_slot).upper() in {"A4", "B4", "C4", "D4"}:
285
+ if api_version < APIVersion(2, 16):
286
+ raise APIVersionError(
287
+ api_element="Using a staging deck slot",
288
+ until_version=f"{_STAGING_DECK_SLOT_VERSION_GATE}",
289
+ current_version=f"{api_version}",
290
+ )
291
+ # Don't need a try/except since we're already pre-validating this
292
+ parsed_staging_slot = StagingSlotName.from_primitive(str(deck_slot))
293
+ return parsed_staging_slot
294
+ else:
295
+ try:
296
+ parsed_slot = DeckSlotName.from_primitive(deck_slot)
297
+ except ValueError as e:
298
+ raise ValueError(f"'{deck_slot}' is not a valid deck slot") from e
299
+ is_ot2_style = parsed_slot.to_ot2_equivalent() == parsed_slot
300
+ if not is_ot2_style and api_version < _COORDINATE_DECK_LABEL_VERSION_GATE:
301
+ alternative = parsed_slot.to_ot2_equivalent().id
302
+ raise APIVersionError(
303
+ api_element=f"Specifying a deck slot like '{deck_slot}'",
304
+ until_version=f"{_COORDINATE_DECK_LABEL_VERSION_GATE}",
305
+ current_version=f"{api_version}",
306
+ extra_message=f"Increase your protocol's apiLevel, or use slot '{alternative}' instead.",
307
+ )
308
+
309
+ return parsed_slot.to_equivalent_for_robot_type(robot_type)
310
+
311
+
312
+ def internal_slot_to_public_string(
313
+ slot_name: Union[DeckSlotName, StagingSlotName], robot_type: RobotType
314
+ ) -> str:
315
+ """Convert an internal `DeckSlotName` to a user-facing Python Protocol API string.
316
+
317
+ This normalizes the string to the robot type's native format, like "5" for OT-2s or "C2" for
318
+ Flexes. This probably won't change anything because the internal `DeckSlotName` should already
319
+ match the robot's native format, but it's nice to have an explicit interface barrier.
320
+ """
321
+ if isinstance(slot_name, DeckSlotName):
322
+ return slot_name.to_equivalent_for_robot_type(robot_type).id
323
+ else:
324
+ # No need to convert staging slot names per robot type, since they only exist on Flex.
325
+ return slot_name.id
326
+
327
+
328
+ def ensure_lowercase_name(name: str) -> str:
329
+ """Ensure that a given name string is all lowercase."""
330
+ if not isinstance(name, str):
331
+ raise TypeError(f"Value must be a string, but got {name}")
332
+
333
+ return name.lower()
334
+
335
+
336
+ def ensure_definition_is_adapter(definition: LabwareDefinition) -> None:
337
+ """Ensure that one of the definition's allowed roles is `adapter`."""
338
+ if LabwareRole.adapter not in definition.allowedRoles:
339
+ raise LabwareDefinitionIsNotAdapterError(
340
+ f"Labware {definition.parameters.loadName} is not an adapter."
341
+ )
342
+
343
+
344
+ def ensure_definition_is_labware(definition: LabwareDefinition) -> None:
345
+ """Ensure that one of the definition's allowed roles is `labware` or that that field is empty."""
346
+ if definition.allowedRoles and LabwareRole.labware not in definition.allowedRoles:
347
+ raise LabwareDefinitionIsNotLabwareError(
348
+ f"Labware {definition.parameters.loadName} is not defined as a normal labware."
349
+ )
350
+
351
+
352
+ def ensure_definition_is_lid(definition: LabwareDefinition) -> None:
353
+ """Ensure that one of the definition's allowed roles is `lid` or that that field is empty."""
354
+ if LabwareRole.lid not in definition.allowedRoles:
355
+ raise LabwareDefinitionIsNotLabwareError(
356
+ f"Labware {definition.parameters.loadName} is not a lid."
357
+ )
358
+
359
+
360
+ def ensure_definition_is_not_lid_after_api_version(
361
+ api_version: APIVersion, definition: LabwareDefinition
362
+ ) -> None:
363
+ """Ensure that one of the definition's allowed roles is not `lid` or that the API Version is below the release where lid loading was seperated."""
364
+ if (
365
+ LabwareRole.lid in definition.allowedRoles
366
+ and api_version >= LID_STACK_VERSION_GATE
367
+ ):
368
+ raise APIVersionError(
369
+ f"Labware Lids cannot be loaded like standard labware in Protocols written with an API version greater than {LID_STACK_VERSION_GATE}."
370
+ )
371
+
372
+
373
+ _MODULE_ALIASES: Dict[str, ModuleModel] = {
374
+ "magdeck": MagneticModuleModel.MAGNETIC_V1,
375
+ "magnetic module": MagneticModuleModel.MAGNETIC_V1,
376
+ "magnetic module gen2": MagneticModuleModel.MAGNETIC_V2,
377
+ "tempdeck": TemperatureModuleModel.TEMPERATURE_V1,
378
+ "temperature module": TemperatureModuleModel.TEMPERATURE_V1,
379
+ "temperature module gen2": TemperatureModuleModel.TEMPERATURE_V2,
380
+ "thermocycler": ThermocyclerModuleModel.THERMOCYCLER_V1,
381
+ "thermocycler module": ThermocyclerModuleModel.THERMOCYCLER_V1,
382
+ "thermocycler module gen2": ThermocyclerModuleModel.THERMOCYCLER_V2,
383
+ # No alias for heater-shaker. Use heater-shaker model name for loading.
384
+ }
385
+
386
+ _MODULE_MODELS: Dict[str, ModuleModel] = {
387
+ "magneticModuleV1": MagneticModuleModel.MAGNETIC_V1,
388
+ "magneticModuleV2": MagneticModuleModel.MAGNETIC_V2,
389
+ "temperatureModuleV1": TemperatureModuleModel.TEMPERATURE_V1,
390
+ "temperatureModuleV2": TemperatureModuleModel.TEMPERATURE_V2,
391
+ "thermocyclerModuleV1": ThermocyclerModuleModel.THERMOCYCLER_V1,
392
+ "thermocyclerModuleV2": ThermocyclerModuleModel.THERMOCYCLER_V2,
393
+ "heaterShakerModuleV1": HeaterShakerModuleModel.HEATER_SHAKER_V1,
394
+ "magneticBlockV1": MagneticBlockModel.MAGNETIC_BLOCK_V1,
395
+ "absorbanceReaderV1": AbsorbanceReaderModel.ABSORBANCE_READER_V1,
396
+ "flexStackerModuleV1": FlexStackerModuleModel.FLEX_STACKER_V1,
397
+ }
398
+
399
+
400
+ def ensure_module_model(load_name: str) -> ModuleModel:
401
+ """Ensure that a requested module load name matches a known module model."""
402
+ if not isinstance(load_name, str):
403
+ raise TypeError(f"Module load name must be a string, but got {load_name}")
404
+
405
+ model = _MODULE_ALIASES.get(load_name.lower()) or _MODULE_MODELS.get(load_name)
406
+
407
+ if model is None:
408
+ valid_names = '", "'.join(_MODULE_ALIASES.keys())
409
+ valid_models = '", "'.join(_MODULE_MODELS.keys())
410
+ raise ValueError(
411
+ f"{load_name} is not a valid module load name.\n"
412
+ f'Valid names (ignoring case): "{valid_names}"\n'
413
+ f'You may also use their exact models: "{valid_models}"'
414
+ )
415
+
416
+ return model
417
+
418
+
419
+ def ensure_and_convert_trash_bin_location(
420
+ deck_slot: Union[int, str], api_version: APIVersion, robot_type: RobotType
421
+ ) -> str:
422
+ """Ensure trash bin load location is valid.
423
+
424
+ Also, convert the deck slot to a valid trash bin addressable area.
425
+ """
426
+
427
+ if robot_type == "OT-2 Standard":
428
+ raise InvalidTrashBinLocationError("Cannot load trash on OT-2.")
429
+
430
+ # map trash bin location to addressable area
431
+ trash_bin_slots = [
432
+ DeckSlotName(slot) for slot in ["A1", "B1", "C1", "D1", "A3", "B3", "C3", "D3"]
433
+ ]
434
+ trash_bin_addressable_areas = [
435
+ "movableTrashA1",
436
+ "movableTrashB1",
437
+ "movableTrashC1",
438
+ "movableTrashD1",
439
+ "movableTrashA3",
440
+ "movableTrashB3",
441
+ "movableTrashC3",
442
+ "movableTrashD3",
443
+ ]
444
+ map_trash_bin_addressable_area = {
445
+ slot: addressable_area
446
+ for slot, addressable_area in zip(trash_bin_slots, trash_bin_addressable_areas)
447
+ }
448
+
449
+ slot_name_ot3 = ensure_and_convert_deck_slot(deck_slot, api_version, robot_type)
450
+ if not isinstance(slot_name_ot3, DeckSlotName):
451
+ raise ValueError("Staging areas not permitted for trash bin.")
452
+ if slot_name_ot3 not in trash_bin_slots:
453
+ raise InvalidTrashBinLocationError(
454
+ f"Invalid location for trash bin: {slot_name_ot3}.\n"
455
+ f"Valid slots: Any slot in column 1 or 3."
456
+ )
457
+
458
+ return map_trash_bin_addressable_area[slot_name_ot3]
459
+
460
+
461
+ def ensure_hold_time_seconds(
462
+ seconds: Optional[float], minutes: Optional[float]
463
+ ) -> float:
464
+ """Ensure that hold time is expressed in seconds."""
465
+ if seconds is None:
466
+ seconds = 0
467
+ if minutes is not None:
468
+ seconds += minutes * 60
469
+ return seconds
470
+
471
+
472
+ def ensure_thermocycler_repetition_count(repetitions: int) -> int:
473
+ """Ensure thermocycler repetitions is a positive integer."""
474
+ if repetitions <= 0:
475
+ raise ValueError("repetitions must be a positive integer")
476
+ return repetitions
477
+
478
+
479
+ def ensure_thermocycler_profile_steps(
480
+ steps: List[ThermocyclerStep],
481
+ ) -> List[ThermocyclerStep]:
482
+ """Ensure thermocycler steps are valid and hold time is expressed in seconds only."""
483
+ validated_steps = []
484
+ for step in steps:
485
+ temperature = step.get("temperature")
486
+ hold_mins = step.get("hold_time_minutes")
487
+ hold_secs = step.get("hold_time_seconds")
488
+ if temperature is None:
489
+ raise ValueError("temperature must be defined for each step in cycle")
490
+ if hold_mins is None and hold_secs is None:
491
+ raise ValueError(
492
+ "either hold_time_minutes or hold_time_seconds must be"
493
+ "defined for each step in cycle"
494
+ )
495
+ validated_seconds = ensure_hold_time_seconds(hold_secs, hold_mins)
496
+ validated_steps.append(
497
+ ThermocyclerStep(
498
+ temperature=temperature, hold_time_seconds=validated_seconds
499
+ )
500
+ )
501
+ return validated_steps
502
+
503
+
504
+ def is_all_integers(items: Sequence[Any]) -> TypeGuard[Sequence[int]]:
505
+ """Check that every item in a list is an integer."""
506
+ return all(isinstance(i, int) for i in items)
507
+
508
+
509
+ def is_all_strings(items: Sequence[Any]) -> TypeGuard[Sequence[str]]:
510
+ """Check that every item in a list is a string."""
511
+ return all(isinstance(i, str) for i in items)
512
+
513
+
514
+ def ensure_valid_labware_offset_vector(
515
+ offset: Mapping[str, float]
516
+ ) -> Tuple[float, float, float]:
517
+ if not isinstance(offset, dict):
518
+ raise TypeError("Labware offset must be a dictionary.")
519
+
520
+ try:
521
+ offsets = (offset["x"], offset["y"], offset["z"])
522
+ except KeyError:
523
+ raise TypeError(
524
+ "Labware offset vector is expected to be a dictionary with"
525
+ " with floating point offset values for all 3 axes."
526
+ " For example: {'x': 1.1, 'y': 2.2, 'z': 3.3}"
527
+ )
528
+ if not all(isinstance(v, (float, int)) for v in offsets):
529
+ raise TypeError("Offset values should be a number (int or float).")
530
+ return offsets
531
+
532
+
533
+ class WellTarget(NamedTuple):
534
+ """A movement target that is a well."""
535
+
536
+ well: Well
537
+ location: Optional[Location]
538
+ in_place: bool
539
+
540
+
541
+ class PointTarget(NamedTuple):
542
+ """A movement to coordinates"""
543
+
544
+ location: Location
545
+ in_place: bool
546
+
547
+
548
+ class DisposalTarget(NamedTuple):
549
+ location: Union[TrashBin, WasteChute]
550
+ in_place: bool
551
+
552
+
553
+ class NoLocationError(ValueError):
554
+ """Error representing that no location was supplied."""
555
+
556
+
557
+ class LocationTypeError(TypeError):
558
+ """Error representing that the location supplied is of different expected type."""
559
+
560
+
561
+ ValidTarget = Union[WellTarget, PointTarget, DisposalTarget]
562
+
563
+
564
+ def validate_location(
565
+ location: Optional[Union[Location, Well, TrashBin, WasteChute]],
566
+ last_location: Optional[Union[Location, TrashBin, WasteChute]],
567
+ ) -> ValidTarget:
568
+ """Validate a given location for a liquid handling command.
569
+
570
+ Args:
571
+ location: The input location.
572
+ last_location: The last location accessed by the pipette.
573
+
574
+ Returns:
575
+ A `WellTarget` if the input location represents a well.
576
+ A `PointTarget` if the input location is an x, y, z coordinate.
577
+ A `TrashBin` if the input location is a trash bin
578
+ A `WasteChute` if the input location is a waste chute
579
+
580
+ Raises:
581
+ NoLocationError: There is no input location and no cached location.
582
+ LocationTypeError: The location supplied is of unexpected type.
583
+ """
584
+ from .labware import Well
585
+
586
+ target_location = location or last_location
587
+
588
+ if target_location is None:
589
+ raise NoLocationError()
590
+
591
+ if not isinstance(target_location, (Location, Well, TrashBin, WasteChute)):
592
+ raise LocationTypeError(
593
+ f"location should be a Well, Location, TrashBin or WasteChute, but it is {location}"
594
+ )
595
+
596
+ in_place = target_location == last_location
597
+
598
+ if isinstance(target_location, (TrashBin, WasteChute)):
599
+ return DisposalTarget(location=target_location, in_place=in_place)
600
+
601
+ if isinstance(target_location, Well):
602
+ return WellTarget(well=target_location, location=None, in_place=in_place)
603
+
604
+ _, well = target_location.labware.get_parent_labware_and_well()
605
+
606
+ return (
607
+ WellTarget(well=well, location=target_location, in_place=in_place)
608
+ if well is not None
609
+ else PointTarget(location=target_location, in_place=in_place)
610
+ )
611
+
612
+
613
+ def ensure_boolean(value: bool) -> bool:
614
+ """Ensure value is a boolean."""
615
+ if not isinstance(value, bool):
616
+ raise ValueError("Value must be a boolean.")
617
+ return value
618
+
619
+
620
+ def ensure_float(value: Union[int, float]) -> float:
621
+ """Ensure value is a float (or an integer) and return it as a float."""
622
+ if not isinstance(value, (int, float)):
623
+ raise ValueError("Value must be a floating point number.")
624
+ return float(value)
625
+
626
+
627
+ def ensure_positive_float(value: Union[int, float]) -> float:
628
+ """Ensure value is a positive and real float value."""
629
+ float_value = ensure_float(value)
630
+ if isnan(float_value) or isinf(float_value):
631
+ raise ValueError("Value must be a defined, non-infinite number.")
632
+ if float_value < 0:
633
+ raise ValueError("Value must be a positive float.")
634
+ return float_value
635
+
636
+
637
+ def ensure_greater_than_zero_float(value: Union[int, float]) -> float:
638
+ """Ensure value is a positive and real float value."""
639
+ float_value = ensure_float(value)
640
+ if isnan(float_value) or isinf(float_value):
641
+ raise ValueError("Value must be a defined, non-infinite number.")
642
+ if float_value <= 0:
643
+ raise ValueError("Value must be a positive float greater than 0.")
644
+ return float_value
645
+
646
+
647
+ def ensure_positive_int(value: int) -> int:
648
+ """Ensure value is a positive integer."""
649
+ if not isinstance(value, int):
650
+ raise ValueError("Value must be an integer.")
651
+ if value < 0:
652
+ raise ValueError("Value must be a positive integer.")
653
+ return value
654
+
655
+
656
+ def validate_coordinates(value: Sequence[float]) -> Tuple[float, float, float]:
657
+ """Ensure value is a valid sequence of 3 floats and return a tuple of 3 floats."""
658
+ if len(value) != 3:
659
+ raise ValueError("Coordinates must be a sequence of exactly three numbers")
660
+ if not all(isinstance(v, (float, int)) for v in value):
661
+ raise ValueError("All values in coordinates must be floats.")
662
+ return float(value[0]), float(value[1]), float(value[2])
663
+
664
+
665
+ def ensure_new_tip_policy(value: str) -> TransferTipPolicyV2:
666
+ """Ensure that new_tip value is a valid TransferTipPolicy value."""
667
+ try:
668
+ return TransferTipPolicyV2(value.lower())
669
+ except ValueError:
670
+ raise ValueError(
671
+ f"'{value}' is invalid value for 'new_tip'."
672
+ f" Acceptable value is either 'never', 'once', 'always', 'per source' or 'per destination'."
673
+ )
674
+
675
+
676
+ def _verify_each_list_element_is_valid_location(locations: Sequence[Well]) -> None:
677
+ from .labware import Well
678
+
679
+ for loc in locations:
680
+ if not isinstance(loc, Well):
681
+ raise ValueError(
682
+ f"'{loc}' is not a valid location for transfer."
683
+ f" Location should be a well instance."
684
+ )
685
+
686
+
687
+ def ensure_valid_flat_wells_list_for_transfer_v2(
688
+ target: Union[Well, Sequence[Well], Sequence[Sequence[Well]]],
689
+ ) -> List[Well]:
690
+ """Ensure that the given target(s) for a liquid transfer are valid and in a flat list."""
691
+ from .labware import Well
692
+
693
+ if isinstance(target, Well):
694
+ return [target]
695
+
696
+ if isinstance(target, (list, tuple)):
697
+ if len(target) == 0:
698
+ raise ValueError("No target well(s) specified for transfer.")
699
+ if isinstance(target[0], (list, tuple)):
700
+ for sub_sequence in target:
701
+ _verify_each_list_element_is_valid_location(sub_sequence)
702
+ return [loc for sub_sequence in target for loc in sub_sequence]
703
+ else:
704
+ _verify_each_list_element_is_valid_location(target)
705
+ return list(target)
706
+ else:
707
+ raise ValueError(
708
+ f"'{target}' is not a valid location for transfer."
709
+ f" Location should be a well instance, or a 1-dimensional or"
710
+ f" 2-dimensional sequence of well instances."
711
+ )
712
+
713
+
714
+ def ensure_valid_trash_location_for_transfer_v2(
715
+ trash_location: Union[Location, Well, TrashBin, WasteChute]
716
+ ) -> Union[Location, TrashBin, WasteChute]:
717
+ """Ensure that the trash location is valid for v2 transfer."""
718
+ from .labware import Well
719
+
720
+ if isinstance(trash_location, TrashBin) or isinstance(trash_location, WasteChute):
721
+ return trash_location
722
+ elif isinstance(trash_location, Well):
723
+ return trash_location.top()
724
+ elif isinstance(trash_location, Location):
725
+ _, maybe_well = trash_location.labware.get_parent_labware_and_well()
726
+
727
+ if maybe_well is None:
728
+ raise TypeError(
729
+ "If a location is specified as a `types.Location`"
730
+ " (for instance, as the result of a call to `Well.top()`),"
731
+ " it must be a location relative to a well,"
732
+ " since that is where a tip is dropped."
733
+ " However, the given location doesn't refer to any well."
734
+ )
735
+ return trash_location
736
+ else:
737
+ raise TypeError(
738
+ f"If specified, location should be an instance of"
739
+ f" `types.Location` (e.g. the return value from `Well.top()`)"
740
+ f" or `Well` (e.g. `reservoir.wells()[0]`) or an instance of `TrashBin` or `WasteChute`."
741
+ f" However, it is '{trash_location}'."
742
+ )
743
+
744
+
745
+ def convert_flex_stacker_load_slot(slot_name: StagingSlotName) -> DeckSlotName:
746
+ """
747
+ Ensure a Flex Stacker load location to a deck slot location.
748
+
749
+ Args:
750
+ slot_name: The input staging slot location.
751
+
752
+ Returns:
753
+ A `DeckSlotName` on the deck.
754
+ """
755
+ _map = {
756
+ StagingSlotName.SLOT_A4: DeckSlotName.SLOT_A3,
757
+ StagingSlotName.SLOT_B4: DeckSlotName.SLOT_B3,
758
+ StagingSlotName.SLOT_C4: DeckSlotName.SLOT_C3,
759
+ StagingSlotName.SLOT_D4: DeckSlotName.SLOT_D3,
760
+ }
761
+ return _map[slot_name]