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,1579 @@
1
+ """opentrons.protocol_api.labware: classes and functions for labware handling
2
+
3
+ This module provides things like :py:class:`Labware`, and :py:class:`Well`
4
+ to encapsulate labware instances used in protocols
5
+ and their wells. It also provides helper functions to load and save labware
6
+ and labware calibration offsets. It contains all the code necessary to
7
+ transform from labware symbolic points (such as "well a1 of an opentrons
8
+ tiprack") to points in deck coordinates.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import logging
14
+
15
+ from itertools import dropwhile
16
+ from typing import (
17
+ TYPE_CHECKING,
18
+ Any,
19
+ List,
20
+ Dict,
21
+ Optional,
22
+ Tuple,
23
+ cast,
24
+ Sequence,
25
+ Mapping,
26
+ Union,
27
+ Literal,
28
+ Callable,
29
+ )
30
+
31
+ from opentrons_shared_data.labware.types import (
32
+ LabwareDefinition,
33
+ LabwareDefinition2,
34
+ LabwareParameters2,
35
+ LabwareParameters3,
36
+ )
37
+
38
+ from opentrons.types import (
39
+ Location,
40
+ Point,
41
+ NozzleMapInterface,
42
+ MeniscusTrackingTarget,
43
+ Mount,
44
+ )
45
+ from opentrons.protocols.api_support.types import APIVersion
46
+ from opentrons.protocols.api_support.util import (
47
+ requires_version,
48
+ APIVersionError,
49
+ UnsupportedAPIError,
50
+ )
51
+ from opentrons.protocol_engine.types import LiquidTrackingType
52
+
53
+ # TODO(mc, 2022-09-02): re-exports provided for backwards compatibility
54
+ # remove when their usage is no longer needed
55
+ from opentrons.protocols.labware import ( # noqa: F401
56
+ get_labware_definition as get_labware_definition,
57
+ verify_definition as verify_definition,
58
+ save_definition as save_definition,
59
+ )
60
+
61
+ from . import validation
62
+ from ._liquid import Liquid
63
+ from ._types import OffDeckType
64
+ from .core import well_grid
65
+ from .core.engine import (
66
+ ENGINE_CORE_API_VERSION,
67
+ SET_OFFSET_RESTORED_API_VERSION,
68
+ )
69
+ from .core.labware import AbstractLabware
70
+ from .core.module import AbstractModuleCore
71
+ from .core.core_map import LoadedCoreMap
72
+ from .core.legacy.legacy_labware_core import LegacyLabwareCore
73
+ from .core.legacy.legacy_well_core import LegacyWellCore
74
+ from .core.legacy.well_geometry import WellGeometry
75
+
76
+
77
+ if TYPE_CHECKING:
78
+ from .core.common import LabwareCore, WellCore, ProtocolCore
79
+ from .protocol_context import ModuleTypes
80
+
81
+
82
+ _log = logging.getLogger(__name__)
83
+
84
+
85
+ _IGNORE_API_VERSION_BREAKPOINT = APIVersion(2, 13)
86
+ """API version after which to respect... the API version setting.
87
+
88
+ At this API version and below, `Labware` objects were always
89
+ erroneously constructed set to MAX_SUPPORTED_VERSION.
90
+ """
91
+
92
+
93
+ class TipSelectionError(Exception):
94
+ pass
95
+
96
+
97
+ class OutOfTipsError(Exception):
98
+ pass
99
+
100
+
101
+ class Well:
102
+ """
103
+ The Well class represents a single well in a :py:class:`Labware`. It provides parameters and functions for three major uses:
104
+
105
+ - Calculating positions relative to the well. See :ref:`position-relative-labware` for details.
106
+
107
+ - Returning well measurements. See :ref:`new-labware-well-properties` for details.
108
+
109
+ - Specifying what liquid should be in the well at the beginning of a protocol. See :ref:`labeling-liquids` for details.
110
+ """
111
+
112
+ def __init__(self, parent: Labware, core: WellCore, api_version: APIVersion):
113
+ if api_version <= _IGNORE_API_VERSION_BREAKPOINT:
114
+ api_version = _IGNORE_API_VERSION_BREAKPOINT
115
+
116
+ self._parent = parent
117
+ self._core = core
118
+ self._api_version = api_version
119
+
120
+ @property
121
+ @requires_version(2, 0)
122
+ def api_version(self) -> APIVersion:
123
+ return self._api_version
124
+
125
+ @property
126
+ @requires_version(2, 0)
127
+ def parent(self) -> Labware:
128
+ """The :py:class:`.Labware` object that the well is a part of."""
129
+ return self._parent
130
+
131
+ @property
132
+ @requires_version(2, 0)
133
+ def has_tip(self) -> bool:
134
+ """Whether this well contains an unused tip.
135
+
136
+ From API v2.2 on:
137
+
138
+ - Returns ``False`` if:
139
+
140
+ - the well has no tip present, or
141
+ - the well has a tip that's been used by the protocol previously
142
+
143
+ - Returns ``True`` if the well has an unused tip.
144
+
145
+ Before API v2.2:
146
+
147
+ - Returns ``True`` as long as the well has a tip, even if it is used.
148
+
149
+ Always ``False`` if the parent labware isn't a tip rack.
150
+ """
151
+ return self._core.has_tip()
152
+
153
+ @has_tip.setter
154
+ def has_tip(self, value: bool) -> None:
155
+ _log.warning(
156
+ "Setting the `Well.has_tip` property manually has been deprecated"
157
+ " and will raise an error in a future version of the Python Protocol API."
158
+ )
159
+
160
+ self._core.set_has_tip(value)
161
+
162
+ @property
163
+ def max_volume(self) -> float:
164
+ """The maximum volume, in µL, that the well can hold.
165
+
166
+ This amount is set by the JSON labware definition, specifically the ``totalLiquidVolume`` property of the particular well.
167
+ """
168
+ return self._core.get_max_volume()
169
+
170
+ @property
171
+ def geometry(self) -> WellGeometry:
172
+ if isinstance(self._core, LegacyWellCore):
173
+ return self._core.geometry
174
+ raise UnsupportedAPIError(api_element="Well.geometry")
175
+
176
+ @property
177
+ @requires_version(2, 0)
178
+ def diameter(self) -> Optional[float]:
179
+ """
180
+ The diameter, in mm, of a circular well. Returns ``None``
181
+ if the well is not circular.
182
+ """
183
+ return self._core.diameter
184
+
185
+ @property
186
+ @requires_version(2, 9)
187
+ def length(self) -> Optional[float]:
188
+ """
189
+ The length, in mm, of a rectangular well along the x-axis (left to right).
190
+ Returns ``None`` if the well is not rectangular.
191
+ """
192
+ return self._core.length
193
+
194
+ @property
195
+ @requires_version(2, 9)
196
+ def width(self) -> Optional[float]:
197
+ """
198
+ The width, in mm, of a rectangular well along the y-axis (front to back).
199
+ Returns ``None`` if the well is not rectangular.
200
+ """
201
+ return self._core.width
202
+
203
+ @property
204
+ @requires_version(2, 9)
205
+ def depth(self) -> float:
206
+ """
207
+ The depth, in mm, of a well along the z-axis, from the very top of the well to
208
+ the very bottom.
209
+ """
210
+ return self._core.depth
211
+
212
+ @property
213
+ def display_name(self) -> str:
214
+ """A human-readable name for the well, including labware and deck location.
215
+
216
+ For example, "A1 of Corning 96 Well Plate 360 µL Flat on slot D1". Run log
217
+ entries use this format for identifying wells. See
218
+ :py:meth:`.ProtocolContext.commands`.
219
+ """
220
+ return self._core.get_display_name()
221
+
222
+ @property
223
+ @requires_version(2, 7)
224
+ def well_name(self) -> str:
225
+ """A string representing the well's coordinates.
226
+
227
+ For example, "A1" or "H12".
228
+
229
+ The format of strings that this property returns is the same format as the key
230
+ for :ref:`accessing wells in a dictionary <well-dictionary-access>`.
231
+ """
232
+ return self._core.get_name()
233
+
234
+ @requires_version(2, 0)
235
+ def top(self, z: float = 0.0) -> Location:
236
+ """
237
+ :param z: An offset on the z-axis, in mm. Positive offsets are higher and
238
+ negative offsets are lower.
239
+
240
+ :return: A :py:class:`~opentrons.types.Location` corresponding to the
241
+ absolute position of the top-center of the well, plus the ``z`` offset
242
+ (if specified).
243
+ """
244
+ return Location(self._core.get_top(z_offset=z), self)
245
+
246
+ @requires_version(2, 0)
247
+ def bottom(self, z: float = 0.0) -> Location:
248
+ """
249
+ :param z: An offset on the z-axis, in mm. Positive offsets are higher and
250
+ negative offsets are lower.
251
+
252
+ :return: A :py:class:`~opentrons.types.Location` corresponding to the
253
+ absolute position of the bottom-center of the well, plus the ``z`` offset
254
+ (if specified).
255
+ """
256
+ return Location(self._core.get_bottom(z_offset=z), self)
257
+
258
+ @requires_version(2, 0)
259
+ def center(self) -> Location:
260
+ """
261
+ :return: A :py:class:`~opentrons.types.Location` corresponding to the
262
+ absolute position of the center of the well (in all three dimensions).
263
+ """
264
+ return Location(self._core.get_center(), self)
265
+
266
+ @requires_version(2, 21)
267
+ def meniscus(
268
+ self, z: float = 0.0, target: Literal["start", "end", "dynamic"] = "end"
269
+ ) -> Location:
270
+ """
271
+ :param z: An offset on the z-axis, in mm. Positive offsets are higher and
272
+ negative offsets are lower.
273
+ :param target: The relative position of the liquid meniscus inside the well to target when performing a liquid handling operation.
274
+
275
+ :return: A :py:class:`~opentrons.types.Location` corresponding to the liquid meniscus, plus a target position and ``z`` offset as specified.
276
+
277
+ """
278
+ return Location(
279
+ point=Point(x=0, y=0, z=z),
280
+ labware=self,
281
+ _meniscus_tracking=MeniscusTrackingTarget(target),
282
+ )
283
+
284
+ @requires_version(2, 8)
285
+ def from_center_cartesian(self, x: float, y: float, z: float) -> Point:
286
+ """
287
+ Specifies a :py:class:`~opentrons.types.Point` based on fractions of the
288
+ distance from the center of the well to the edge along each axis.
289
+
290
+ For example, ``from_center_cartesian(0, 0, 0.5)`` specifies a point at the
291
+ well's center on the x- and y-axis, and half of the distance from the center of
292
+ the well to its top along the z-axis. To move the pipette to that location,
293
+ construct a :py:class:`~opentrons.types.Location` relative to the same well::
294
+
295
+ location = types.Location(
296
+ plate["A1"].from_center_cartesian(0, 0, 0.5), plate["A1"]
297
+ )
298
+ pipette.move_to(location)
299
+
300
+ See :ref:`points-locations` for more information.
301
+
302
+ :param x: The fraction of the distance from the well's center to its edge
303
+ along the x-axis. Negative values are to the left, and positive values
304
+ are to the right.
305
+ :param y: The fraction of the distance from the well's center to its edge
306
+ along the y-axis. Negative values are to the front, and positive values
307
+ are to the back.
308
+ :param z: The fraction of the distance from the well's center to its edge
309
+ along the x-axis. Negative values are down, and positive values are up.
310
+
311
+ :return: A :py:class:`~opentrons.types.Point` representing the specified
312
+ position in absolute deck coordinates.
313
+
314
+ .. note:: Even if the absolute values of ``x``, ``y``, and ``z`` are all less
315
+ than 1, a location constructed from the well and the result of
316
+ ``from_center_cartesian`` may be outside of the physical well. For example,
317
+ ``from_center_cartesian(0.9, 0.9, 0)`` would be outside of a cylindrical
318
+ well, but inside a square well.
319
+
320
+ """
321
+ return self._core.from_center_cartesian(x, y, z)
322
+
323
+ @requires_version(2, 14)
324
+ def load_liquid(self, liquid: Liquid, volume: float) -> None:
325
+ """
326
+ Load a liquid into a well.
327
+
328
+ :param Liquid liquid: The liquid to load into the well.
329
+ :param float volume: The volume of liquid to load, in µL.
330
+
331
+ .. deprecated:: 2.22
332
+ Use :py:meth:`.Labware.load_liquid`, :py:meth:`.Labware.load_liquid_by_well`, or :py:meth:`.Labware.load_empty` instead.
333
+
334
+ """
335
+ self._core.load_liquid(
336
+ liquid=liquid,
337
+ volume=volume,
338
+ )
339
+
340
+ @requires_version(2, 21)
341
+ def current_liquid_height(self) -> LiquidTrackingType:
342
+ """Get the current liquid height in a well."""
343
+ return self._core.current_liquid_height()
344
+
345
+ @requires_version(2, 21)
346
+ def current_liquid_volume(self) -> LiquidTrackingType:
347
+ """Get the current liquid volume in a well."""
348
+ return self._core.get_liquid_volume()
349
+
350
+ @requires_version(2, 24)
351
+ def volume_from_height(self, height: LiquidTrackingType) -> LiquidTrackingType:
352
+ """Return the volume contained in a well at any height."""
353
+ return self._core.volume_from_height(height)
354
+
355
+ @requires_version(2, 24)
356
+ def height_from_volume(self, volume: LiquidTrackingType) -> LiquidTrackingType:
357
+ """Return the height in a well corresponding to a given volume."""
358
+ return self._core.height_from_volume(volume)
359
+
360
+ @requires_version(2, 21)
361
+ def estimate_liquid_height_after_pipetting(
362
+ self,
363
+ mount: Mount | str,
364
+ operation_volume: float,
365
+ ) -> LiquidTrackingType:
366
+ """Check the height of the liquid within a well.
367
+
368
+ :returns: The height, in mm, of the liquid from the deck.
369
+
370
+ :meta private:
371
+
372
+ This is intended for Opentrons internal use only and is not a guaranteed API.
373
+ """
374
+
375
+ projected_final_height = self._core.estimate_liquid_height_after_pipetting(
376
+ operation_volume=operation_volume, mount=mount
377
+ )
378
+ return projected_final_height
379
+
380
+ def _from_center_cartesian(self, x: float, y: float, z: float) -> Point:
381
+ """
382
+ Private version of from_center_cartesian. Present only for backward
383
+ compatibility.
384
+ """
385
+ _log.warning(
386
+ "This method is deprecated. Please use 'from_center_cartesian' instead."
387
+ )
388
+ return self.from_center_cartesian(x, y, z)
389
+
390
+ def __repr__(self) -> str:
391
+ return self._core.get_display_name()
392
+
393
+ def __eq__(self, other: object) -> bool:
394
+ """
395
+ Assuming that equality of wells in this system is having the same
396
+ absolute coordinates for the top.
397
+ """
398
+ if not isinstance(other, Well):
399
+ return NotImplemented
400
+ return self.top().point == other.top().point
401
+
402
+ def __hash__(self) -> int:
403
+ return hash(self.top().point)
404
+
405
+
406
+ class Labware:
407
+ """
408
+ This class represents a piece of labware.
409
+
410
+ Labware available in the API generally fall under two categories.
411
+
412
+ - Consumable labware: well plates, tubes in racks, reservoirs, tip racks, etc.
413
+ - Adapters: durable items that hold other labware, either on modules or directly
414
+ on the deck.
415
+
416
+ The ``Labware`` class defines the physical geometry of the labware
417
+ and provides methods for :ref:`accessing wells <new-well-access>` within the labware.
418
+
419
+ Create ``Labware`` objects by calling the appropriate ``load_labware()`` method,
420
+ depending on where you are loading the labware. For example, to load labware on a
421
+ Thermocycler Module, use :py:meth:`.ThermocyclerContext.load_labware`. To load
422
+ labware directly on the deck, use :py:meth:`.ProtocolContext.load_labware`. See
423
+ :ref:`loading-labware`.
424
+
425
+ """
426
+
427
+ def __init__(
428
+ self,
429
+ core: AbstractLabware[Any],
430
+ api_version: APIVersion,
431
+ protocol_core: ProtocolCore,
432
+ core_map: LoadedCoreMap,
433
+ ) -> None:
434
+ """
435
+ :param core: The class that implements the public interface
436
+ of the class.
437
+ :param APIVersion api_level: the API version to set for the instance.
438
+ The :py:class:`.Labware` will
439
+ conform to this level. If not specified,
440
+ defaults to
441
+ :py:attr:`.MAX_SUPPORTED_VERSION`.
442
+ """
443
+ if api_version <= _IGNORE_API_VERSION_BREAKPOINT:
444
+ api_version = _IGNORE_API_VERSION_BREAKPOINT
445
+
446
+ self._api_version = api_version
447
+ self._core: LabwareCore = core
448
+ self._protocol_core = protocol_core
449
+ self._core_map = core_map
450
+
451
+ well_columns = core.get_well_columns()
452
+ self._well_grid = well_grid.create(columns=well_columns)
453
+ self._wells_by_name = {
454
+ well_name: Well(
455
+ parent=self, core=core.get_well_core(well_name), api_version=api_version
456
+ )
457
+ for column in well_columns
458
+ for well_name in column
459
+ }
460
+
461
+ @property
462
+ def separate_calibration(self) -> bool:
463
+ if self._api_version >= ENGINE_CORE_API_VERSION:
464
+ raise UnsupportedAPIError(
465
+ api_element="Labware.separate_calibration",
466
+ since_version=f"{ENGINE_CORE_API_VERSION}",
467
+ current_version=f"{self._api_version}",
468
+ )
469
+
470
+ _log.warning(
471
+ "Labware.separate_calibrations is a deprecated internal property."
472
+ " It no longer has meaning, but will always return `False`"
473
+ )
474
+ return False
475
+
476
+ @classmethod
477
+ def _builder_for_core_map(
478
+ cls,
479
+ api_version: APIVersion,
480
+ protocol_core: ProtocolCore,
481
+ core_map: LoadedCoreMap,
482
+ ) -> Callable[[AbstractLabware[Any]], Labware]:
483
+ def _do_build(core: AbstractLabware[Any]) -> Labware:
484
+ return Labware(
485
+ core=core,
486
+ api_version=api_version,
487
+ protocol_core=protocol_core,
488
+ core_map=core_map,
489
+ )
490
+
491
+ return _do_build
492
+
493
+ @property
494
+ @requires_version(2, 0)
495
+ def api_version(self) -> APIVersion:
496
+ """See :py:obj:`.ProtocolContext.api_version`."""
497
+ return self._api_version
498
+
499
+ def __getitem__(self, key: str) -> Well:
500
+ return self.wells_by_name()[key]
501
+
502
+ @property
503
+ @requires_version(2, 0)
504
+ def uri(self) -> str:
505
+ """A string fully identifying the labware.
506
+
507
+ The URI has three parts and follows the pattern ``"namespace/load_name/version"``.
508
+ For example, ``opentrons/corning_96_wellplate_360ul_flat/2``.
509
+ """
510
+ return self._core.get_uri()
511
+
512
+ @property
513
+ @requires_version(2, 0)
514
+ def parent(self) -> Union[str, Labware, ModuleTypes, OffDeckType]:
515
+ """Where the labware is loaded.
516
+
517
+ This corresponds to the physical object that the labware *directly* rests upon.
518
+
519
+ Returns:
520
+ If the labware is directly on the robot's deck, the ``str`` name of the deck slot,
521
+ like ``"D1"`` (Flex) or ``"1"`` (OT-2). See :ref:`deck-slots`.
522
+
523
+ If the labware is on a module, a module context.
524
+
525
+ If the labware is on a labware or adapter, a :py:class:`Labware`.
526
+
527
+ If the labware is off-deck, :py:obj:`OFF_DECK`.
528
+
529
+ .. versionchanged:: 2.14
530
+ Return type for module parent changed.
531
+ Formerly, the API returned an internal geometry interface.
532
+ .. versionchanged:: 2.15
533
+ Returns a :py:class:`Labware` if the labware is loaded onto a labware/adapter.
534
+ Returns :py:obj:`OFF_DECK` if the labware is off-deck.
535
+ Formerly, if the labware was removed by using ``del`` on :py:obj:`.deck`,
536
+ this would return where it was before its removal.
537
+ """
538
+ if isinstance(self._core, LegacyLabwareCore):
539
+ # Type ignoring to preserve backwards compatibility
540
+ return self._core.get_geometry().parent.labware.object # type: ignore
541
+
542
+ assert self._protocol_core and self._core_map, "Labware initialized incorrectly"
543
+
544
+ labware_location = self._protocol_core.get_labware_location(self._core)
545
+
546
+ if isinstance(labware_location, (AbstractLabware, AbstractModuleCore)):
547
+ return self._core_map.get(labware_location)
548
+
549
+ return labware_location
550
+
551
+ @property
552
+ @requires_version(2, 0)
553
+ def name(self) -> str:
554
+ """The display name of the labware.
555
+
556
+ If you specified a value for ``label`` when loading the labware, ``name`` is
557
+ that value.
558
+
559
+ Otherwise, it is the :py:obj:`~.Labware.load_name` of the labware.
560
+ """
561
+ return self._core.get_name()
562
+
563
+ @name.setter
564
+ def name(self, new_name: str) -> None:
565
+ """Set the labware name.
566
+
567
+ .. deprecated: 2.14
568
+ Set the name of labware in `load_labware` instead.
569
+ """
570
+ if self._api_version >= ENGINE_CORE_API_VERSION:
571
+ raise UnsupportedAPIError(
572
+ api_element="Labware.name setter",
573
+ since_version=f"{ENGINE_CORE_API_VERSION}",
574
+ current_version=f"{self._api_version}",
575
+ )
576
+
577
+ # TODO(mc, 2023-02-06): this assert should be enough for mypy
578
+ # investigate if upgrading mypy allows the `cast` to be removed
579
+ assert isinstance(self._core, LegacyLabwareCore)
580
+ cast(LegacyLabwareCore, self._core).set_name(new_name)
581
+
582
+ @property
583
+ @requires_version(2, 0)
584
+ def load_name(self) -> str:
585
+ """The API load name of the labware definition."""
586
+ return self._core.load_name
587
+
588
+ @property
589
+ @requires_version(2, 0)
590
+ def parameters(self) -> "LabwareParameters2 | LabwareParameters3":
591
+ """Internal properties of a labware including type and quirks."""
592
+ return self._core.get_parameters()
593
+
594
+ @property
595
+ @requires_version(2, 0)
596
+ def quirks(self) -> List[str]:
597
+ """Quirks specific to this labware."""
598
+ return self._core.get_quirks()
599
+
600
+ # TODO(mm, 2023-02-08):
601
+ # Specify units and origin after we resolve RSS-110.
602
+ # Remove warning once we resolve RSS-109 more broadly.
603
+ @property
604
+ @requires_version(2, 0)
605
+ def magdeck_engage_height(self) -> Optional[float]:
606
+ """Return the default magnet engage height that
607
+ :py:meth:`.MagneticModuleContext.engage` will use for this labware.
608
+
609
+ .. warning::
610
+ This currently returns confusing and unpredictable results that do not
611
+ necessarily match what :py:meth:`.MagneticModuleContext.engage` will
612
+ actually choose for its default height.
613
+
614
+ The confusion is related to how this height's units and origin point are
615
+ defined, and differences between Magnetic Module generations.
616
+
617
+ For now, we recommend you avoid accessing this property directly.
618
+ """
619
+ # Return the raw value straight from the labware definition. For several
620
+ # reasons (see RSS-109), this may not match the actual default height chosen
621
+ # by MagneticModuleContext.engage().
622
+ p = self._core.get_parameters()
623
+ if not p["isMagneticModuleCompatible"]:
624
+ return None
625
+ else:
626
+ return p["magneticModuleEngageHeight"]
627
+
628
+ @property
629
+ @requires_version(2, 15)
630
+ def child(self) -> Optional[Labware]:
631
+ """The labware (if any) present on this labware."""
632
+ labware_core = self._protocol_core.get_labware_on_labware(self._core)
633
+ return self._core_map.get(labware_core)
634
+
635
+ @requires_version(2, 15)
636
+ def load_labware(
637
+ self,
638
+ name: str,
639
+ label: Optional[str] = None,
640
+ lid: Optional[str] = None,
641
+ namespace: Optional[str] = None,
642
+ version: Optional[int] = None,
643
+ ) -> Labware:
644
+ """Load a compatible labware onto the labware using its load parameters.
645
+
646
+ The parameters of this function behave like those of
647
+ :py:obj:`ProtocolContext.load_labware` (which loads labware directly
648
+ onto the deck). Note that the parameter ``name`` here corresponds to
649
+ ``load_name`` on the ``ProtocolContext`` function.
650
+
651
+ :returns: The initialized and loaded labware object.
652
+ """
653
+ labware_core = self._protocol_core.load_labware(
654
+ load_name=name,
655
+ label=label,
656
+ namespace=namespace,
657
+ version=version,
658
+ location=self._core,
659
+ )
660
+
661
+ labware = Labware(
662
+ core=labware_core,
663
+ api_version=self._api_version,
664
+ protocol_core=self._protocol_core,
665
+ core_map=self._core_map,
666
+ )
667
+
668
+ self._core_map.add(labware_core, labware)
669
+
670
+ if lid is not None:
671
+ if self._api_version < validation.LID_STACK_VERSION_GATE:
672
+ raise APIVersionError(
673
+ api_element="Loading a Lid on a Labware",
674
+ until_version="2.23",
675
+ current_version=f"{self._api_version}",
676
+ )
677
+ self._protocol_core.load_lid(
678
+ load_name=lid,
679
+ location=labware_core,
680
+ namespace=namespace,
681
+ version=version,
682
+ )
683
+
684
+ return labware
685
+
686
+ @requires_version(2, 15)
687
+ def load_labware_from_definition(
688
+ self, definition: LabwareDefinition, label: Optional[str] = None
689
+ ) -> Labware:
690
+ """Load a compatible labware onto the labware using an inline definition.
691
+
692
+ :param definition: The labware definition.
693
+ :param str label: An optional special name to give the labware. If specified,
694
+ this is how the labware will appear in the run log, Labware Position
695
+ Check, and elsewhere in the Opentrons App and on the touchscreen.
696
+
697
+ :returns: The initialized and loaded labware object.
698
+ """
699
+ load_params = self._protocol_core.add_labware_definition(definition)
700
+
701
+ return self.load_labware(
702
+ name=load_params.load_name,
703
+ namespace=load_params.namespace,
704
+ version=load_params.version,
705
+ label=label,
706
+ )
707
+
708
+ @requires_version(2, 23)
709
+ def load_lid_stack(
710
+ self,
711
+ load_name: str,
712
+ quantity: int,
713
+ namespace: Optional[str] = None,
714
+ version: Optional[int] = None,
715
+ ) -> Labware:
716
+ """
717
+ Load a stack of Opentrons Tough Auto-Sealing Lids onto a valid deck location or adapter.
718
+
719
+ :param str load_name: A string to use for looking up a lid definition.
720
+ You can find the ``load_name`` for any standard lid on the Opentrons
721
+ `Labware Library <https://labware.opentrons.com>`_.
722
+ :param int quantity: The quantity of lids to be loaded in the stack.
723
+ :param str namespace: The namespace that the lid labware definition belongs to.
724
+ If unspecified, the API will automatically search two namespaces:
725
+
726
+ - ``"opentrons"``, to load standard Opentrons labware definitions.
727
+ - ``"custom_beta"``, to load custom labware definitions created with the
728
+ `Custom Labware Creator <https://labware.opentrons.com/create>`__.
729
+
730
+ You might need to specify an explicit ``namespace`` if you have a custom
731
+ definition whose ``load_name`` is the same as an Opentrons-verified
732
+ definition, and you want to explicitly choose one or the other.
733
+
734
+ :param version: The version of the labware definition. You should normally
735
+ leave this unspecified to let ``load_lid_stack()`` choose a version
736
+ automatically.
737
+
738
+ :return: The initialized and loaded labware object representing the lid stack.
739
+ """
740
+ if self._api_version < validation.LID_STACK_VERSION_GATE:
741
+ raise APIVersionError(
742
+ api_element="Loading a Lid Stack",
743
+ until_version="2.23",
744
+ current_version=f"{self._api_version}",
745
+ )
746
+
747
+ load_location = self._core
748
+
749
+ load_name = validation.ensure_lowercase_name(load_name)
750
+
751
+ result = self._protocol_core.load_lid_stack(
752
+ load_name=load_name,
753
+ location=load_location,
754
+ quantity=quantity,
755
+ namespace=namespace,
756
+ version=version,
757
+ )
758
+
759
+ labware = Labware(
760
+ core=result,
761
+ api_version=self._api_version,
762
+ protocol_core=self._protocol_core,
763
+ core_map=self._core_map,
764
+ )
765
+ return labware
766
+
767
+ def set_calibration(self, delta: Point) -> None:
768
+ """
769
+ An internal, deprecated method used for updating the labware offset.
770
+
771
+ .. deprecated:: 2.14
772
+ """
773
+ if self._api_version >= ENGINE_CORE_API_VERSION:
774
+ raise UnsupportedAPIError(
775
+ api_element="Labware.set_calibration()",
776
+ since_version=f"{ENGINE_CORE_API_VERSION}",
777
+ current_version=f"{self._api_version}",
778
+ extra_message="Try using the Opentrons App's Labware Position Check.",
779
+ )
780
+ self._core.set_calibration(delta)
781
+
782
+ @requires_version(2, 12)
783
+ def set_offset(self, x: float, y: float, z: float) -> None:
784
+ """Set the labware's position offset.
785
+
786
+ The offset is an x, y, z vector in deck coordinates
787
+ (see :ref:`protocol-api-deck-coords`).
788
+
789
+ How the motion system applies the offset depends on the API level of the protocol.
790
+
791
+ .. list-table::
792
+ :header-rows: 1
793
+ :widths: 1 5
794
+
795
+ * - API level
796
+ - Offset behavior
797
+ * - 2.12–2.13
798
+ - Offsets only apply to the exact :py:class:`.Labware` instance.
799
+
800
+ If your protocol has multiple instances of the same type of labware,
801
+ you must either use ``set_offset()`` on all of them or none of them.
802
+ * - 2.14–2.17
803
+ - ``set_offset()`` is not available, and the API raises an error.
804
+ * - 2.18--2.22
805
+ -
806
+ - Offsets apply to any labware of the same type, in the same on-deck location.
807
+ - Offsets can't be set on labware that is currently off-deck.
808
+ - Offsets do not follow a labware instance when using :py:meth:`.move_labware`.
809
+ * - 2.23 and newer
810
+ -
811
+ On Flex, offsets can apply to all labware of the same type, regardless of their on-deck location.
812
+
813
+ .. note::
814
+
815
+ Setting offsets with this method will override any labware offsets set
816
+ by running Labware Position Check in the Opentrons App.
817
+
818
+ This method is designed for use with mechanisms like
819
+ :obj:`opentrons.execute.get_protocol_api`, which lack an interactive way
820
+ to adjust labware offsets. (See :ref:`advanced-control`.)
821
+
822
+ .. versionchanged:: 2.14
823
+ Temporarily removed.
824
+
825
+ .. versionchanged:: 2.18
826
+ Restored, and now applies to labware type–location pairs.
827
+ """
828
+ if (
829
+ self._api_version >= ENGINE_CORE_API_VERSION
830
+ and self._api_version < SET_OFFSET_RESTORED_API_VERSION
831
+ ):
832
+ raise APIVersionError(
833
+ api_element="Labware.set_offset()",
834
+ until_version=f"{SET_OFFSET_RESTORED_API_VERSION}",
835
+ current_version=f"{self._api_version}",
836
+ extra_message="This feature not available in versions 2.14 thorugh 2.17. You can also use the Opentrons App's Labware Position Check.",
837
+ )
838
+ else:
839
+ self._core.set_calibration(Point(x=x, y=y, z=z))
840
+
841
+ @property
842
+ @requires_version(2, 0)
843
+ def calibrated_offset(self) -> Point:
844
+ """The front-left-bottom corner of the labware, including its labware offset.
845
+
846
+ When running a protocol in the Opentrons App or on the touchscreen, Labware
847
+ Position Check sets the labware offset.
848
+ """
849
+ return self._core.get_calibrated_offset()
850
+
851
+ @requires_version(2, 0)
852
+ def well(self, idx: Union[int, str]) -> Well:
853
+ """Deprecated. Use result of :py:meth:`wells` or :py:meth:`wells_by_name`."""
854
+ if isinstance(idx, int):
855
+ return self.wells()[idx]
856
+ elif isinstance(idx, str):
857
+ return self.wells_by_name()[idx]
858
+ else:
859
+ raise TypeError(
860
+ f"`Labware.well` must be called with an `int` or `str`, but got {idx}"
861
+ )
862
+
863
+ @requires_version(2, 0)
864
+ def wells(self, *args: Union[str, int]) -> List[Well]:
865
+ """
866
+ Accessor function to navigate a labware top to bottom, left to right.
867
+
868
+ i.e., this method returns a list ordered A1, B1, C1…A2, B2, C2….
869
+
870
+ Use indexing to access individual wells contained in the list.
871
+ For example, access well A1 with ``labware.wells()[0]``.
872
+
873
+ .. note::
874
+ Using args with this method is deprecated. Use indexing instead.
875
+
876
+ If your code uses args, they can be either strings or integers, but not a
877
+ mix of the two. For example, ``.wells(1, 4)`` or ``.wells("1", "4")`` is
878
+ valid, but ``.wells("1", 4)`` is not.
879
+
880
+ :return: Ordered list of all wells in a labware.
881
+ """
882
+ if not args:
883
+ return list(self._wells_by_name.values())
884
+
885
+ elif validation.is_all_integers(args):
886
+ wells = self.wells()
887
+ return [wells[idx] for idx in args]
888
+
889
+ elif validation.is_all_strings(args):
890
+ wells_by_name = self.wells_by_name()
891
+ return [wells_by_name[idx] for idx in args]
892
+
893
+ else:
894
+ raise TypeError(
895
+ "`Labware.wells` must be called with all `int`'s or all `str`'s,"
896
+ f" but was called with {args}"
897
+ )
898
+
899
+ @requires_version(2, 0)
900
+ def wells_by_name(self) -> Dict[str, Well]:
901
+ """
902
+ Accessor function used to navigate through a labware by well name.
903
+
904
+ Use indexing to access individual wells contained in the dictionary.
905
+ For example, access well A1 with ``labware.wells_by_name()["A1"]``.
906
+
907
+ :return: Dictionary of :py:class:`.Well` objects keyed by well name.
908
+ """
909
+ return dict(self._wells_by_name)
910
+
911
+ @requires_version(2, 0)
912
+ def wells_by_index(self) -> Dict[str, Well]:
913
+ """
914
+ .. deprecated:: 2.0
915
+ Use :py:meth:`wells_by_name` or dict access instead.
916
+ """
917
+ _log.warning(
918
+ "wells_by_index is deprecated. Use wells_by_name or dict access instead."
919
+ )
920
+ return self.wells_by_name()
921
+
922
+ @requires_version(2, 0)
923
+ def rows(self, *args: Union[int, str]) -> List[List[Well]]:
924
+ """
925
+ Accessor function to navigate through a labware by row.
926
+
927
+ Use indexing to access individual rows or wells contained in the nested list.
928
+ On a standard 96-well plate, this will output a list of :py:class:`.Well`
929
+ objects containing A1 through A12.
930
+
931
+ .. note::
932
+ Using args with this method is deprecated. Use indexing instead.
933
+
934
+ If your code uses args, they can be either strings or integers, but not a
935
+ mix of the two. For example, ``.rows(1, 4)`` or ``.rows("1", "4")`` is
936
+ valid, but ``.rows("1", 4)`` is not.
937
+
938
+ :return: A list of row lists.
939
+ """
940
+ if not args:
941
+ return [
942
+ [self._wells_by_name[well_name] for well_name in row]
943
+ for row in self._well_grid.rows_by_name.values()
944
+ ]
945
+
946
+ elif validation.is_all_integers(args):
947
+ rows = self.rows()
948
+ return [rows[idx] for idx in args]
949
+
950
+ elif validation.is_all_strings(args):
951
+ rows_by_name = self.rows_by_name()
952
+ return [rows_by_name[idx] for idx in args]
953
+
954
+ else:
955
+ raise TypeError(
956
+ "`Labware.rows` must be called with all `int`'s or all `str`'s,"
957
+ f" but was called with {args}"
958
+ )
959
+
960
+ @requires_version(2, 0)
961
+ def rows_by_name(self) -> Dict[str, List[Well]]:
962
+ """
963
+ Accessor function to navigate through a labware by row name.
964
+
965
+ Use indexing to access individual rows or wells contained in the dictionary.
966
+ For example, access row A with ``labware.rows_by_name()["A"]``.
967
+ On a standard 96-well plate, this will output a list of :py:class:`.Well`
968
+ objects containing A1 through A12.
969
+
970
+ :return: Dictionary of :py:class:`.Well` lists keyed by row name.
971
+ """
972
+ return {
973
+ row_name: [self._wells_by_name[well_name] for well_name in row]
974
+ for row_name, row in self._well_grid.rows_by_name.items()
975
+ }
976
+
977
+ @requires_version(2, 0)
978
+ def rows_by_index(self) -> Dict[str, List[Well]]:
979
+ """
980
+ .. deprecated:: 2.0
981
+ Use :py:meth:`rows_by_name` instead.
982
+ """
983
+ _log.warning("rows_by_index is deprecated. Use rows_by_name instead.")
984
+ return self.rows_by_name()
985
+
986
+ @requires_version(2, 0)
987
+ def columns(self, *args: Union[int, str]) -> List[List[Well]]:
988
+ """
989
+ Accessor function to navigate through a labware by column.
990
+
991
+ Use indexing to access individual columns or wells contained in the nested list.
992
+ For example, access column 1 with ``labware.columns()[0]``.
993
+ On a standard 96-well plate, this will output a list of :py:class:`.Well`
994
+ objects containing A1 through H1.
995
+
996
+ .. note::
997
+ Using args with this method is deprecated. Use indexing instead.
998
+
999
+ If your code uses args, they can be either strings or integers, but not a
1000
+ mix of the two. For example, ``.columns(1, 4)`` or ``.columns("1", "4")`` is
1001
+ valid, but ``.columns("1", 4)`` is not.
1002
+
1003
+ :return: A list of column lists.
1004
+ """
1005
+ if not args:
1006
+ return [
1007
+ [self._wells_by_name[well_name] for well_name in column]
1008
+ for column in self._well_grid.columns_by_name.values()
1009
+ ]
1010
+
1011
+ elif validation.is_all_integers(args):
1012
+ columns = self.columns()
1013
+ return [columns[idx] for idx in args]
1014
+
1015
+ elif validation.is_all_strings(args):
1016
+ columns_by_name = self.columns_by_name()
1017
+ return [columns_by_name[idx] for idx in args]
1018
+
1019
+ else:
1020
+ raise TypeError(
1021
+ "`Labware.columns` must be called with all `int`'s or all `str`'s,"
1022
+ f" but was called with {args}"
1023
+ )
1024
+
1025
+ @requires_version(2, 0)
1026
+ def columns_by_name(self) -> Dict[str, List[Well]]:
1027
+ """
1028
+ Accessor function to navigate through a labware by column name.
1029
+
1030
+ Use indexing to access individual columns or wells contained in the dictionary.
1031
+ For example, access column 1 with ``labware.columns_by_name()["1"]``.
1032
+ On a standard 96-well plate, this will output a list of :py:class:`.Well`
1033
+ objects containing A1 through H1.
1034
+
1035
+ :return: Dictionary of :py:class:`.Well` lists keyed by column name.
1036
+ """
1037
+ return {
1038
+ column_name: [self._wells_by_name[well_name] for well_name in column]
1039
+ for column_name, column in self._well_grid.columns_by_name.items()
1040
+ }
1041
+
1042
+ @requires_version(2, 0)
1043
+ def columns_by_index(self) -> Dict[str, List[Well]]:
1044
+ """
1045
+ .. deprecated:: 2.0
1046
+ Use :py:meth:`columns_by_name` instead.
1047
+ """
1048
+ _log.warning("columns_by_index is deprecated. Use columns_by_name instead.")
1049
+ return self.columns_by_name()
1050
+
1051
+ @property
1052
+ @requires_version(2, 0)
1053
+ def highest_z(self) -> float:
1054
+ """
1055
+ The z-coordinate of the highest single point anywhere on the labware.
1056
+
1057
+ This is taken from the ``zDimension`` property of the ``dimensions`` object in the
1058
+ labware definition and takes into account the labware offset.
1059
+ """
1060
+ return self._core.highest_z
1061
+
1062
+ @property
1063
+ def _is_tiprack(self) -> bool:
1064
+ """as is_tiprack but not subject to version checking for speed"""
1065
+ return self._core.is_tip_rack()
1066
+
1067
+ @property
1068
+ @requires_version(2, 0)
1069
+ def is_tiprack(self) -> bool:
1070
+ """Whether the labware behaves as a tip rack.
1071
+
1072
+ Returns ``True`` if the labware definition specifies ``isTiprack`` as ``True``.
1073
+ """
1074
+ return self._is_tiprack
1075
+
1076
+ @property
1077
+ @requires_version(2, 15)
1078
+ def is_adapter(self) -> bool:
1079
+ """Whether the labware behaves as an adapter.
1080
+
1081
+ Returns ``True`` if the labware definition specifies ``adapter`` as one of the
1082
+ labware's ``allowedRoles``.
1083
+ """
1084
+ return self._core.is_adapter()
1085
+
1086
+ @property
1087
+ @requires_version(2, 0)
1088
+ def tip_length(self) -> float:
1089
+ """For a tip rack labware, the length of the tips it holds, in mm.
1090
+
1091
+ This is taken from the ``tipLength`` property of the ``parameters`` object in the labware definition.
1092
+
1093
+ This method will raise an exception if you call it on a labware that isn’t a tip rack.
1094
+ """
1095
+ return self._core.get_tip_length()
1096
+
1097
+ @tip_length.setter
1098
+ def tip_length(self, length: float) -> None:
1099
+ """
1100
+ Set the tip rack's tip length.
1101
+
1102
+ .. deprecated: 2.14
1103
+ Ensure tip length is set properly in your tip rack's definition
1104
+ and/or use the Opentrons App's tip length calibration feature.
1105
+ """
1106
+ if self._api_version >= ENGINE_CORE_API_VERSION:
1107
+ raise UnsupportedAPIError(
1108
+ api_element="Labware.tip_length setter",
1109
+ since_version=f"{ENGINE_CORE_API_VERSION}",
1110
+ current_version=f"{self._api_version}",
1111
+ )
1112
+
1113
+ # TODO(mc, 2023-02-06): this assert should be enough for mypy
1114
+ # investigate if upgrading mypy allows the `cast` to be removed
1115
+ assert isinstance(self._core, LegacyLabwareCore)
1116
+ cast(LegacyLabwareCore, self._core).set_tip_length(length)
1117
+
1118
+ # TODO(mc, 2022-11-09): implementation detail; deprecate public method
1119
+ def next_tip(
1120
+ self,
1121
+ num_tips: int = 1,
1122
+ starting_tip: Optional[Well] = None,
1123
+ *,
1124
+ nozzle_map: Optional[NozzleMapInterface] = None,
1125
+ ) -> Optional[Well]:
1126
+ """
1127
+ Find the next valid well for pick-up.
1128
+
1129
+ Determines the next valid start tip from which to retrieve the
1130
+ specified number of tips. There must be at least `num_tips` sequential
1131
+ wells for which all wells have tips, in the same column.
1132
+
1133
+ :param num_tips: target number of sequential tips in the same column
1134
+ :type num_tips: int
1135
+ :param starting_tip: The :py:class:`.Well` from which to start search.
1136
+ for an available tip.
1137
+ :type starting_tip: :py:class:`.Well`
1138
+ :return: the :py:class:`.Well` meeting the target criteria, or None
1139
+ """
1140
+ assert num_tips > 0, f"num_tips must be positive integer, but got {num_tips}"
1141
+
1142
+ well_name = self._core.get_next_tip(
1143
+ num_tips=num_tips,
1144
+ starting_tip=starting_tip._core if starting_tip else None,
1145
+ nozzle_map=nozzle_map,
1146
+ )
1147
+
1148
+ return self._wells_by_name[well_name] if well_name is not None else None
1149
+
1150
+ def use_tips(self, start_well: Well, num_channels: int = 1) -> None:
1151
+ """
1152
+ Removes tips from the tip tracker.
1153
+
1154
+ This method should be called when a tip is picked up. Generally, it
1155
+ will be called with `num_channels=1` or `num_channels=8` for single-
1156
+ and multi-channel respectively. If picking up with more than one
1157
+ channel, this method will automatically determine which tips are used
1158
+ based on the start well, the number of channels, and the geometry of
1159
+ the tiprack.
1160
+
1161
+ :param start_well: The :py:class:`.Well` from which to pick up a tip.
1162
+ For a single-channel pipette, this is the well to
1163
+ send the pipette to. For a multi-channel pipette,
1164
+ this is the well to send the back-most nozzle of the
1165
+ pipette to.
1166
+ :type start_well: :py:class:`.Well`
1167
+ :param num_channels: The number of channels for the current pipette
1168
+ :type num_channels: int
1169
+
1170
+ .. deprecated:: 2.14
1171
+ Modification of tip tracking state outside :py:meth:`.reset` has been deprecated.
1172
+ """
1173
+ if self._api_version >= ENGINE_CORE_API_VERSION:
1174
+ raise UnsupportedAPIError(
1175
+ api_element="Labware.use_tips",
1176
+ since_version=f"{ENGINE_CORE_API_VERSION}",
1177
+ current_version=f"{self._api_version}",
1178
+ extra_message="To modify tip state, use Labware.reset.",
1179
+ )
1180
+
1181
+ assert num_channels > 0, "Bad call to use_tips: num_channels<=0"
1182
+ fail_if_full = self._api_version < APIVersion(2, 2)
1183
+
1184
+ # TODO(mc, 2023-02-13): this assert should be enough for mypy
1185
+ # investigate if upgrading mypy allows the `cast` to be removed
1186
+ assert isinstance(self._core, LegacyLabwareCore)
1187
+ cast(LegacyLabwareCore, self._core).get_tip_tracker().use_tips(
1188
+ start_well=start_well._core,
1189
+ num_channels=num_channels,
1190
+ fail_if_full=fail_if_full,
1191
+ )
1192
+
1193
+ def __repr__(self) -> str:
1194
+ return self._core.get_display_name()
1195
+
1196
+ def __eq__(self, other: object) -> bool:
1197
+ if not isinstance(other, Labware):
1198
+ return NotImplemented
1199
+ return self._core == other._core
1200
+
1201
+ def __hash__(self) -> int:
1202
+ return hash((self._core, self._api_version))
1203
+
1204
+ def previous_tip(self, num_tips: int = 1) -> Optional[Well]:
1205
+ """
1206
+ Find the best well to drop a tip in.
1207
+
1208
+ This is the well from which the last tip was picked up, if there's
1209
+ room. It can be used to return tips to the tip tracker.
1210
+
1211
+ :param num_tips: target number of tips to return, sequential in a
1212
+ column
1213
+ :type num_tips: int
1214
+ :return: The :py:class:`.Well` meeting the target criteria, or ``None``
1215
+
1216
+ .. versionchanged:: 2.14
1217
+ This method has been removed.
1218
+ """
1219
+ if self._api_version >= ENGINE_CORE_API_VERSION:
1220
+ raise UnsupportedAPIError(
1221
+ api_element="Labware.previous_tip",
1222
+ since_version=f"{ENGINE_CORE_API_VERSION}",
1223
+ current_version=f"{self._api_version}",
1224
+ )
1225
+
1226
+ # This logic is the inverse of :py:meth:`next_tip`
1227
+ assert num_tips > 0, "Bad call to previous_tip: num_tips <= 0"
1228
+ # TODO(mc, 2023-02-13): this assert should be enough for mypy
1229
+ # investigate if upgrading mypy allows the `cast` to be removed
1230
+ assert isinstance(self._core, LegacyLabwareCore)
1231
+ well_core = (
1232
+ cast(LegacyLabwareCore, self._core)
1233
+ .get_tip_tracker()
1234
+ .previous_tip(num_tips=num_tips)
1235
+ )
1236
+ return self._wells_by_name[well_core.get_name()] if well_core else None
1237
+
1238
+ # TODO(mc, 2022-11-09): implementation detail; deprecate public method
1239
+ def return_tips(self, start_well: Well, num_channels: int = 1) -> None:
1240
+ """
1241
+ Re-adds tips to the tip tracker
1242
+
1243
+ This method should be called when a tip is dropped in a tiprack. It
1244
+ should be called with ``num_channels=1`` or ``num_channels=8`` for
1245
+ single- and multi-channel respectively. If returning more than one
1246
+ channel, this method will automatically determine which tips are
1247
+ returned based on the start well, the number of channels,
1248
+ and the tiprack geometry.
1249
+
1250
+ Note that unlike :py:meth:`use_tips`, calling this method in a way
1251
+ that would drop tips into wells with tips in them will raise an
1252
+ exception; this should only be called on a valid return of
1253
+ :py:meth:`previous_tip`.
1254
+
1255
+ :param start_well: The :py:class:`.Well` into which to return a tip.
1256
+ :type start_well: :py:class:`.Well`
1257
+ :param num_channels: The number of channels for the current pipette
1258
+ :type num_channels: int
1259
+
1260
+ .. versionchanged:: 2.14
1261
+ This method has been removed. Use :py:meth:`.reset` instead.
1262
+ """
1263
+ if self._api_version >= ENGINE_CORE_API_VERSION:
1264
+ raise UnsupportedAPIError(
1265
+ api_element="Labware.return_tips()",
1266
+ since_version=f"{ENGINE_CORE_API_VERSION}",
1267
+ current_version=f"{self._api_version}",
1268
+ extra_message="Use Labware.reset() instead.",
1269
+ )
1270
+
1271
+ # This logic is the inverse of :py:meth:`use_tips`
1272
+ assert num_channels > 0, "Bad call to return_tips: num_channels <= 0"
1273
+
1274
+ # TODO(mc, 2023-02-13): this assert should be enough for mypy
1275
+ # investigate if upgrading mypy allows the `cast` to be removed
1276
+ assert isinstance(self._core, LegacyLabwareCore)
1277
+ cast(LegacyLabwareCore, self._core).get_tip_tracker().return_tips(
1278
+ start_well=start_well._core, num_channels=num_channels
1279
+ )
1280
+
1281
+ @requires_version(2, 0)
1282
+ def reset(self) -> None:
1283
+ """Reset tip tracking for a tip rack.
1284
+
1285
+ After resetting, the API treats all wells on the rack as if they contain unused tips.
1286
+ This is useful if you want to reuse tips after calling :py:meth:`.return_tip()`.
1287
+
1288
+ If you need to physically replace an empty tip rack in the middle of your protocol,
1289
+ use :py:meth:`.move_labware()` instead. See :ref:`off-deck-location` for an example.
1290
+
1291
+ .. versionchanged:: 2.14
1292
+ This method will raise an exception if you call it on a labware that isn't
1293
+ a tip rack. Formerly, it would do nothing.
1294
+ """
1295
+ self._core.reset_tips()
1296
+
1297
+ @requires_version(2, 22)
1298
+ def load_liquid(
1299
+ self, wells: Sequence[Union[str, Well]], volume: float, liquid: Liquid
1300
+ ) -> None:
1301
+ """Mark several wells as containing the same amount of liquid.
1302
+
1303
+ This method should be called at the beginning of a protocol, soon after loading labware and before
1304
+ liquid handling operations begin. Loading liquids is required for liquid tracking functionality. If a well
1305
+ hasn't been assigned a starting volume with :py:meth:`~Labware.load_empty`, :py:meth:`~Labware.load_liquid`, or
1306
+ :py:meth:`~Labware.load_liquid_by_well`, the volume it contains is unknown and the well's liquid will not be tracked throughout the protocol.
1307
+
1308
+ :param wells: The wells to load the liquid into.
1309
+ :type wells: List of string well names or list of :py:class:`.Well` objects (e.g., from :py:meth:`~Labware.wells`).
1310
+
1311
+ :param volume: The volume of liquid to load into each well.
1312
+ :type volume: float
1313
+
1314
+ :param liquid: The liquid to load into each well, previously defined by :py:meth:`~ProtocolContext.define_liquid`
1315
+ :type liquid: Liquid
1316
+ """
1317
+ well_names: List[str] = []
1318
+ for well in wells:
1319
+ if isinstance(well, str):
1320
+ if well not in self.wells_by_name():
1321
+ raise KeyError(
1322
+ f"{well} is not a well in labware {self.name}. The elements of wells should name wells in this labware."
1323
+ )
1324
+ well_names.append(well)
1325
+ elif isinstance(well, Well):
1326
+ if well.parent is not self:
1327
+ raise KeyError(
1328
+ f"{well.well_name} is not a well in labware {self.name}. The elements of wells should be wells of this labware."
1329
+ )
1330
+ well_names.append(well.well_name)
1331
+ else:
1332
+ raise TypeError(
1333
+ f"Unexpected type for element {repr(well)}. The elements of wells should be Well instances or well names."
1334
+ )
1335
+ if not isinstance(volume, (float, int)):
1336
+ raise TypeError(
1337
+ f"Unexpected type for volume {repr(volume)}. Volume should be a number in microliters."
1338
+ )
1339
+ self._core.load_liquid({well_name: volume for well_name in well_names}, liquid)
1340
+
1341
+ @requires_version(2, 22)
1342
+ def load_liquid_by_well(
1343
+ self, volumes: Mapping[Union[str, Well], float], liquid: Liquid
1344
+ ) -> None:
1345
+ """Mark several wells as containing unique volumes of liquid.
1346
+
1347
+ This method should be called at the beginning of a protocol, soon after loading labware and before
1348
+ liquid handling begins. Loading liquids is required for liquid tracking functionality. If a well hasn't been assigned a starting volume with :py:meth:`~Labware.load_empty`, :py:meth:`~Labware.load_liquid`, or
1349
+ :py:meth:`~Labware.load_liquid_by_well`, the volume it contains is unknown and the well's liquid will not be tracked throughout the protocol.
1350
+
1351
+ :param volumes: A dictionary of well names (or :py:class:`Well` objects, for instance from ``labware['A1']``)
1352
+ :type wells: Dict[Union[str, Well], float]
1353
+
1354
+ :param liquid: The liquid to load into each well, previously defined by :py:meth:`~ProtocolContext.define_liquid`
1355
+ :type liquid: Liquid
1356
+ """
1357
+ verified_volumes: Dict[str, float] = {}
1358
+ for well, volume in volumes.items():
1359
+ if isinstance(well, str):
1360
+ if well not in self.wells_by_name():
1361
+ raise KeyError(
1362
+ f"{well} is not a well in {self.name}. The keys of volumes should name wells in this labware"
1363
+ )
1364
+ verified_volumes[well] = volume
1365
+ elif isinstance(well, Well):
1366
+ if well.parent is not self:
1367
+ raise KeyError(
1368
+ f"{well.well_name} is not a well in {self.name}. The keys of volumes should be wells of this labware"
1369
+ )
1370
+ verified_volumes[well.well_name] = volume
1371
+ else:
1372
+ raise TypeError(
1373
+ f"Unexpected type for well name {repr(well)}. The keys of volumes should be Well instances or well names."
1374
+ )
1375
+ if not isinstance(volume, (float, int)):
1376
+ raise TypeError(
1377
+ f"Unexpected type for volume {repr(volume)}. The values of volumes should be numbers in microliters."
1378
+ )
1379
+ self._core.load_liquid(verified_volumes, liquid)
1380
+
1381
+ @requires_version(2, 22)
1382
+ def load_empty(self, wells: Sequence[Union[Well, str]]) -> None:
1383
+ """Mark several wells as empty.
1384
+
1385
+ This method should be called at the beginning of a protocol, after loading the labware and before liquid handling
1386
+ begins. Loading liquids is required for liquid tracking functionality. If a well in a labware hasn't been assigned a starting volume with :py:meth:`Labware.load_empty`, :py:meth:`Labware.load_liquid`, or :py:meth:`Labware.load_liquid_by_well`, the
1387
+ volume it contains is unknown and the well's liquid will not be tracked throughout the protocol.
1388
+
1389
+ :param wells: The list of wells to mark empty. To mark all wells as empty, pass ``labware.wells()``. You can also specify
1390
+ wells by their names (for instance, ``labware.load_empty(['A1', 'A2'])``).
1391
+ :type wells: Union[List[Well], List[str]]
1392
+ """
1393
+ well_names: List[str] = []
1394
+ for well in wells:
1395
+ if isinstance(well, str):
1396
+ if well not in self.wells_by_name():
1397
+ raise KeyError(
1398
+ f"{well} is not a well in {self.name}. The elements of wells should name wells in this labware."
1399
+ )
1400
+ well_names.append(well)
1401
+ elif isinstance(well, Well):
1402
+ if well.parent is not self:
1403
+ raise KeyError(
1404
+ f"{well.well_name} is not a well in {self.name}. The elements of wells should be wells of this labware."
1405
+ )
1406
+ well_names.append(well.well_name)
1407
+ else:
1408
+ raise TypeError(
1409
+ f"Unexpected type for well name {repr(well)}. The elements of wells should be Well instances or well names."
1410
+ )
1411
+ self._core.load_empty(well_names)
1412
+
1413
+
1414
+ # TODO(mc, 2022-11-09): implementation detail, move to core
1415
+ def split_tipracks(tip_racks: List[Labware]) -> Tuple[Labware, List[Labware]]:
1416
+ try:
1417
+ rest = tip_racks[1:]
1418
+ except IndexError:
1419
+ rest = []
1420
+ return tip_racks[0], rest
1421
+
1422
+
1423
+ # TODO(mc, 2022-11-09): implementation detail, move to core
1424
+ def select_tiprack_from_list(
1425
+ tip_racks: List[Labware],
1426
+ num_channels: int,
1427
+ starting_point: Optional[Well] = None,
1428
+ *,
1429
+ nozzle_map: Optional[NozzleMapInterface] = None,
1430
+ ) -> Tuple[Labware, Well]:
1431
+ try:
1432
+ first, rest = split_tipracks(tip_racks)
1433
+ except IndexError:
1434
+ raise OutOfTipsError
1435
+
1436
+ if starting_point and starting_point.parent != first:
1437
+ raise TipSelectionError(
1438
+ f"The starting tip you selected does not exist in {first}"
1439
+ )
1440
+ elif starting_point:
1441
+ first_well = starting_point
1442
+ elif nozzle_map:
1443
+ first_well = None
1444
+ else:
1445
+ first_well = first.wells()[0]
1446
+
1447
+ next_tip = first.next_tip(num_channels, first_well, nozzle_map=nozzle_map)
1448
+ if next_tip:
1449
+ return first, next_tip
1450
+ else:
1451
+ return select_tiprack_from_list(rest, num_channels, None, nozzle_map=nozzle_map)
1452
+
1453
+
1454
+ # TODO(mc, 2022-11-09): implementation detail, move to core
1455
+ def filter_tipracks_to_start(
1456
+ starting_point: Well, tipracks: List[Labware]
1457
+ ) -> List[Labware]:
1458
+ return list(dropwhile(lambda tr: starting_point.parent != tr, tipracks))
1459
+
1460
+
1461
+ # TODO(mc, 2022-11-09): implementation detail, move to core
1462
+ def next_available_tip(
1463
+ starting_tip: Optional[Well],
1464
+ tip_racks: List[Labware],
1465
+ channels: int,
1466
+ *,
1467
+ nozzle_map: Optional[NozzleMapInterface] = None,
1468
+ ) -> Tuple[Labware, Well]:
1469
+ start = starting_tip
1470
+ if start is None:
1471
+ return select_tiprack_from_list(
1472
+ tip_racks, channels, None, nozzle_map=nozzle_map
1473
+ )
1474
+ else:
1475
+ return select_tiprack_from_list(
1476
+ filter_tipracks_to_start(start, tip_racks),
1477
+ channels,
1478
+ start,
1479
+ nozzle_map=nozzle_map,
1480
+ )
1481
+
1482
+
1483
+ # TODO(mc, 2022-11-09): implementation detail, move somewhere else
1484
+ # only used in old calibration flows by robot-server
1485
+ def load_from_definition(
1486
+ definition: "LabwareDefinition2",
1487
+ parent: Location,
1488
+ label: Optional[str] = None,
1489
+ api_level: Optional[APIVersion] = None,
1490
+ ) -> Labware:
1491
+ """
1492
+ Return a labware object constructed from a provided labware definition dict
1493
+
1494
+ :param definition: A dict representing all required data for a labware,
1495
+ including metadata such as the display name of the labware, a
1496
+ definition of the order to iterate over wells, the shape of wells
1497
+ (shape, physical dimensions, etc), and so on. The correct shape of
1498
+ this definition is governed by the "labware-designer" project in
1499
+ the Opentrons/opentrons repo.
1500
+ :param parent: A :py:class:`.Location` representing the location where
1501
+ the front and left most point of the outside of labware is
1502
+ (often the front-left corner of a slot on the deck).
1503
+ :param str label: An optional label that will override the labware's
1504
+ display name from its definition
1505
+ :param api_level: the API version to set for the loaded labware
1506
+ instance. The :py:class:`.Labware` will
1507
+ conform to this level. If not specified,
1508
+ defaults to ``APIVersion(2, 13)``.
1509
+ """
1510
+ return Labware(
1511
+ core=LegacyLabwareCore(
1512
+ definition=definition,
1513
+ parent=parent,
1514
+ label=label,
1515
+ ),
1516
+ api_version=api_level or APIVersion(2, 13),
1517
+ protocol_core=None, # type: ignore[arg-type]
1518
+ core_map=None, # type: ignore[arg-type]
1519
+ )
1520
+
1521
+
1522
+ # TODO(mc, 2022-11-09): implementation detail, move somewhere else
1523
+ # only used in old calibration flows by robot-server
1524
+ def load(
1525
+ load_name: str,
1526
+ parent: Location,
1527
+ label: Optional[str] = None,
1528
+ namespace: Optional[str] = None,
1529
+ version: int = 1,
1530
+ bundled_defs: Optional[Mapping[str, LabwareDefinition2]] = None,
1531
+ extra_defs: Optional[Mapping[str, LabwareDefinition2]] = None,
1532
+ api_level: Optional[APIVersion] = None,
1533
+ ) -> Labware:
1534
+ """
1535
+ Return a labware object constructed from a labware definition dict looked
1536
+ up by name (definition must have been previously stored locally on the
1537
+ robot)
1538
+
1539
+ :param load_name: A string to use for looking up a labware definition
1540
+ previously saved to disc. The definition file must have been saved in a
1541
+ known location
1542
+ :param parent: A :py:class:`.Location` representing the location where
1543
+ the front and left most point of the outside of labware is
1544
+ (often the front-left corner of a slot on the deck).
1545
+ :param str label: An optional label that will override the labware's
1546
+ display name from its definition
1547
+ :param str namespace: The namespace the labware definition belongs to.
1548
+ If unspecified, will search 'opentrons' then 'custom_beta'
1549
+ :param int version: The version of the labware definition. If unspecified,
1550
+ will use version 1.
1551
+ :param bundled_defs: If specified, a mapping of labware names to labware
1552
+ definitions. Only the bundle will be searched for definitions.
1553
+ :param extra_defs: If specified, a mapping of labware names to labware
1554
+ definitions. If no bundle is passed, these definitions will also be
1555
+ searched.
1556
+ :param api_level: the API version to set for the loaded labware
1557
+ instance. The :py:class:`.Labware` will
1558
+ conform to this level. If not specified,
1559
+ defaults to ``APIVersion(2, 13)``.
1560
+ """
1561
+ definition = get_labware_definition(
1562
+ load_name,
1563
+ namespace,
1564
+ version,
1565
+ bundled_defs=bundled_defs,
1566
+ extra_defs=extra_defs,
1567
+ )
1568
+
1569
+ # The legacy `load_from_definition()` function that we're calling only supports
1570
+ # schemaVersion==2 labware. Fortunately, when robot-server calls this function,
1571
+ # we only expect it to try to load schemaVersion==2 labware, so we never expect
1572
+ # this ValueError to be raised in practice.
1573
+ if definition["schemaVersion"] != 2:
1574
+ raise ValueError(
1575
+ f"{namespace}/{load_name}/{version} has schema {definition['schemaVersion']}."
1576
+ " Only schema 2 is supported."
1577
+ )
1578
+
1579
+ return load_from_definition(definition, parent, label, api_level)