opentrons 8.6.0__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 (601) 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 +557 -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 +187 -0
  45. opentrons/drivers/asyncio/communication/errors.py +88 -0
  46. opentrons/drivers/asyncio/communication/serial_connection.py +557 -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/scripts/update_module_fw.py +274 -0
  200. opentrons/hardware_control/simulator_setup.py +260 -0
  201. opentrons/hardware_control/thread_manager.py +431 -0
  202. opentrons/hardware_control/threaded_async_lock.py +97 -0
  203. opentrons/hardware_control/types.py +792 -0
  204. opentrons/hardware_control/util.py +234 -0
  205. opentrons/legacy_broker.py +53 -0
  206. opentrons/legacy_commands/__init__.py +1 -0
  207. opentrons/legacy_commands/commands.py +483 -0
  208. opentrons/legacy_commands/helpers.py +153 -0
  209. opentrons/legacy_commands/module_commands.py +276 -0
  210. opentrons/legacy_commands/protocol_commands.py +54 -0
  211. opentrons/legacy_commands/publisher.py +155 -0
  212. opentrons/legacy_commands/robot_commands.py +51 -0
  213. opentrons/legacy_commands/types.py +1186 -0
  214. opentrons/motion_planning/__init__.py +32 -0
  215. opentrons/motion_planning/adjacent_slots_getters.py +168 -0
  216. opentrons/motion_planning/deck_conflict.py +501 -0
  217. opentrons/motion_planning/errors.py +35 -0
  218. opentrons/motion_planning/types.py +42 -0
  219. opentrons/motion_planning/waypoints.py +218 -0
  220. opentrons/ordered_set.py +138 -0
  221. opentrons/protocol_api/__init__.py +105 -0
  222. opentrons/protocol_api/_liquid.py +157 -0
  223. opentrons/protocol_api/_liquid_properties.py +814 -0
  224. opentrons/protocol_api/_nozzle_layout.py +31 -0
  225. opentrons/protocol_api/_parameter_context.py +300 -0
  226. opentrons/protocol_api/_parameters.py +31 -0
  227. opentrons/protocol_api/_transfer_liquid_validation.py +108 -0
  228. opentrons/protocol_api/_types.py +43 -0
  229. opentrons/protocol_api/config.py +23 -0
  230. opentrons/protocol_api/core/__init__.py +23 -0
  231. opentrons/protocol_api/core/common.py +33 -0
  232. opentrons/protocol_api/core/core_map.py +74 -0
  233. opentrons/protocol_api/core/engine/__init__.py +22 -0
  234. opentrons/protocol_api/core/engine/_default_labware_versions.py +179 -0
  235. opentrons/protocol_api/core/engine/deck_conflict.py +400 -0
  236. opentrons/protocol_api/core/engine/exceptions.py +19 -0
  237. opentrons/protocol_api/core/engine/instrument.py +2391 -0
  238. opentrons/protocol_api/core/engine/labware.py +238 -0
  239. opentrons/protocol_api/core/engine/load_labware_params.py +73 -0
  240. opentrons/protocol_api/core/engine/module_core.py +1027 -0
  241. opentrons/protocol_api/core/engine/overlap_versions.py +20 -0
  242. opentrons/protocol_api/core/engine/pipette_movement_conflict.py +358 -0
  243. opentrons/protocol_api/core/engine/point_calculations.py +64 -0
  244. opentrons/protocol_api/core/engine/protocol.py +1153 -0
  245. opentrons/protocol_api/core/engine/robot.py +139 -0
  246. opentrons/protocol_api/core/engine/stringify.py +74 -0
  247. opentrons/protocol_api/core/engine/transfer_components_executor.py +1006 -0
  248. opentrons/protocol_api/core/engine/well.py +241 -0
  249. opentrons/protocol_api/core/instrument.py +459 -0
  250. opentrons/protocol_api/core/labware.py +151 -0
  251. opentrons/protocol_api/core/legacy/__init__.py +11 -0
  252. opentrons/protocol_api/core/legacy/_labware_geometry.py +37 -0
  253. opentrons/protocol_api/core/legacy/deck.py +369 -0
  254. opentrons/protocol_api/core/legacy/labware_offset_provider.py +108 -0
  255. opentrons/protocol_api/core/legacy/legacy_instrument_core.py +709 -0
  256. opentrons/protocol_api/core/legacy/legacy_labware_core.py +235 -0
  257. opentrons/protocol_api/core/legacy/legacy_module_core.py +592 -0
  258. opentrons/protocol_api/core/legacy/legacy_protocol_core.py +612 -0
  259. opentrons/protocol_api/core/legacy/legacy_well_core.py +162 -0
  260. opentrons/protocol_api/core/legacy/load_info.py +67 -0
  261. opentrons/protocol_api/core/legacy/module_geometry.py +547 -0
  262. opentrons/protocol_api/core/legacy/well_geometry.py +148 -0
  263. opentrons/protocol_api/core/legacy_simulator/__init__.py +16 -0
  264. opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +624 -0
  265. opentrons/protocol_api/core/legacy_simulator/legacy_protocol_core.py +85 -0
  266. opentrons/protocol_api/core/module.py +484 -0
  267. opentrons/protocol_api/core/protocol.py +311 -0
  268. opentrons/protocol_api/core/robot.py +51 -0
  269. opentrons/protocol_api/core/well.py +116 -0
  270. opentrons/protocol_api/core/well_grid.py +45 -0
  271. opentrons/protocol_api/create_protocol_context.py +177 -0
  272. opentrons/protocol_api/deck.py +223 -0
  273. opentrons/protocol_api/disposal_locations.py +244 -0
  274. opentrons/protocol_api/instrument_context.py +3272 -0
  275. opentrons/protocol_api/labware.py +1579 -0
  276. opentrons/protocol_api/module_contexts.py +1447 -0
  277. opentrons/protocol_api/module_validation_and_errors.py +61 -0
  278. opentrons/protocol_api/protocol_context.py +1688 -0
  279. opentrons/protocol_api/robot_context.py +303 -0
  280. opentrons/protocol_api/validation.py +761 -0
  281. opentrons/protocol_engine/__init__.py +155 -0
  282. opentrons/protocol_engine/actions/__init__.py +65 -0
  283. opentrons/protocol_engine/actions/action_dispatcher.py +30 -0
  284. opentrons/protocol_engine/actions/action_handler.py +13 -0
  285. opentrons/protocol_engine/actions/actions.py +302 -0
  286. opentrons/protocol_engine/actions/get_state_update.py +38 -0
  287. opentrons/protocol_engine/clients/__init__.py +5 -0
  288. opentrons/protocol_engine/clients/sync_client.py +174 -0
  289. opentrons/protocol_engine/clients/transports.py +197 -0
  290. opentrons/protocol_engine/commands/__init__.py +757 -0
  291. opentrons/protocol_engine/commands/absorbance_reader/__init__.py +61 -0
  292. opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +154 -0
  293. opentrons/protocol_engine/commands/absorbance_reader/common.py +6 -0
  294. opentrons/protocol_engine/commands/absorbance_reader/initialize.py +151 -0
  295. opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +154 -0
  296. opentrons/protocol_engine/commands/absorbance_reader/read.py +226 -0
  297. opentrons/protocol_engine/commands/air_gap_in_place.py +162 -0
  298. opentrons/protocol_engine/commands/aspirate.py +244 -0
  299. opentrons/protocol_engine/commands/aspirate_in_place.py +184 -0
  300. opentrons/protocol_engine/commands/aspirate_while_tracking.py +211 -0
  301. opentrons/protocol_engine/commands/blow_out.py +146 -0
  302. opentrons/protocol_engine/commands/blow_out_in_place.py +119 -0
  303. opentrons/protocol_engine/commands/calibration/__init__.py +60 -0
  304. opentrons/protocol_engine/commands/calibration/calibrate_gripper.py +166 -0
  305. opentrons/protocol_engine/commands/calibration/calibrate_module.py +117 -0
  306. opentrons/protocol_engine/commands/calibration/calibrate_pipette.py +96 -0
  307. opentrons/protocol_engine/commands/calibration/move_to_maintenance_position.py +156 -0
  308. opentrons/protocol_engine/commands/command.py +308 -0
  309. opentrons/protocol_engine/commands/command_unions.py +974 -0
  310. opentrons/protocol_engine/commands/comment.py +57 -0
  311. opentrons/protocol_engine/commands/configure_for_volume.py +108 -0
  312. opentrons/protocol_engine/commands/configure_nozzle_layout.py +115 -0
  313. opentrons/protocol_engine/commands/custom.py +67 -0
  314. opentrons/protocol_engine/commands/dispense.py +194 -0
  315. opentrons/protocol_engine/commands/dispense_in_place.py +179 -0
  316. opentrons/protocol_engine/commands/dispense_while_tracking.py +204 -0
  317. opentrons/protocol_engine/commands/drop_tip.py +232 -0
  318. opentrons/protocol_engine/commands/drop_tip_in_place.py +205 -0
  319. opentrons/protocol_engine/commands/flex_stacker/__init__.py +64 -0
  320. opentrons/protocol_engine/commands/flex_stacker/common.py +900 -0
  321. opentrons/protocol_engine/commands/flex_stacker/empty.py +293 -0
  322. opentrons/protocol_engine/commands/flex_stacker/fill.py +281 -0
  323. opentrons/protocol_engine/commands/flex_stacker/retrieve.py +339 -0
  324. opentrons/protocol_engine/commands/flex_stacker/set_stored_labware.py +328 -0
  325. opentrons/protocol_engine/commands/flex_stacker/store.py +339 -0
  326. opentrons/protocol_engine/commands/generate_command_schema.py +61 -0
  327. opentrons/protocol_engine/commands/get_next_tip.py +134 -0
  328. opentrons/protocol_engine/commands/get_tip_presence.py +87 -0
  329. opentrons/protocol_engine/commands/hash_command_params.py +38 -0
  330. opentrons/protocol_engine/commands/heater_shaker/__init__.py +102 -0
  331. opentrons/protocol_engine/commands/heater_shaker/close_labware_latch.py +83 -0
  332. opentrons/protocol_engine/commands/heater_shaker/deactivate_heater.py +82 -0
  333. opentrons/protocol_engine/commands/heater_shaker/deactivate_shaker.py +84 -0
  334. opentrons/protocol_engine/commands/heater_shaker/open_labware_latch.py +110 -0
  335. opentrons/protocol_engine/commands/heater_shaker/set_and_wait_for_shake_speed.py +125 -0
  336. opentrons/protocol_engine/commands/heater_shaker/set_target_temperature.py +90 -0
  337. opentrons/protocol_engine/commands/heater_shaker/wait_for_temperature.py +102 -0
  338. opentrons/protocol_engine/commands/home.py +100 -0
  339. opentrons/protocol_engine/commands/identify_module.py +86 -0
  340. opentrons/protocol_engine/commands/labware_handling_common.py +29 -0
  341. opentrons/protocol_engine/commands/liquid_probe.py +464 -0
  342. opentrons/protocol_engine/commands/load_labware.py +210 -0
  343. opentrons/protocol_engine/commands/load_lid.py +154 -0
  344. opentrons/protocol_engine/commands/load_lid_stack.py +272 -0
  345. opentrons/protocol_engine/commands/load_liquid.py +95 -0
  346. opentrons/protocol_engine/commands/load_liquid_class.py +144 -0
  347. opentrons/protocol_engine/commands/load_module.py +223 -0
  348. opentrons/protocol_engine/commands/load_pipette.py +167 -0
  349. opentrons/protocol_engine/commands/magnetic_module/__init__.py +32 -0
  350. opentrons/protocol_engine/commands/magnetic_module/disengage.py +97 -0
  351. opentrons/protocol_engine/commands/magnetic_module/engage.py +119 -0
  352. opentrons/protocol_engine/commands/move_labware.py +546 -0
  353. opentrons/protocol_engine/commands/move_relative.py +102 -0
  354. opentrons/protocol_engine/commands/move_to_addressable_area.py +176 -0
  355. opentrons/protocol_engine/commands/move_to_addressable_area_for_drop_tip.py +198 -0
  356. opentrons/protocol_engine/commands/move_to_coordinates.py +107 -0
  357. opentrons/protocol_engine/commands/move_to_well.py +119 -0
  358. opentrons/protocol_engine/commands/movement_common.py +338 -0
  359. opentrons/protocol_engine/commands/pick_up_tip.py +241 -0
  360. opentrons/protocol_engine/commands/pipetting_common.py +443 -0
  361. opentrons/protocol_engine/commands/prepare_to_aspirate.py +121 -0
  362. opentrons/protocol_engine/commands/pressure_dispense.py +155 -0
  363. opentrons/protocol_engine/commands/reload_labware.py +90 -0
  364. opentrons/protocol_engine/commands/retract_axis.py +75 -0
  365. opentrons/protocol_engine/commands/robot/__init__.py +70 -0
  366. opentrons/protocol_engine/commands/robot/close_gripper_jaw.py +96 -0
  367. opentrons/protocol_engine/commands/robot/common.py +18 -0
  368. opentrons/protocol_engine/commands/robot/move_axes_relative.py +101 -0
  369. opentrons/protocol_engine/commands/robot/move_axes_to.py +100 -0
  370. opentrons/protocol_engine/commands/robot/move_to.py +94 -0
  371. opentrons/protocol_engine/commands/robot/open_gripper_jaw.py +86 -0
  372. opentrons/protocol_engine/commands/save_position.py +109 -0
  373. opentrons/protocol_engine/commands/seal_pipette_to_tip.py +353 -0
  374. opentrons/protocol_engine/commands/set_rail_lights.py +67 -0
  375. opentrons/protocol_engine/commands/set_status_bar.py +89 -0
  376. opentrons/protocol_engine/commands/temperature_module/__init__.py +46 -0
  377. opentrons/protocol_engine/commands/temperature_module/deactivate.py +86 -0
  378. opentrons/protocol_engine/commands/temperature_module/set_target_temperature.py +97 -0
  379. opentrons/protocol_engine/commands/temperature_module/wait_for_temperature.py +104 -0
  380. opentrons/protocol_engine/commands/thermocycler/__init__.py +152 -0
  381. opentrons/protocol_engine/commands/thermocycler/close_lid.py +87 -0
  382. opentrons/protocol_engine/commands/thermocycler/deactivate_block.py +80 -0
  383. opentrons/protocol_engine/commands/thermocycler/deactivate_lid.py +80 -0
  384. opentrons/protocol_engine/commands/thermocycler/open_lid.py +87 -0
  385. opentrons/protocol_engine/commands/thermocycler/run_extended_profile.py +171 -0
  386. opentrons/protocol_engine/commands/thermocycler/run_profile.py +124 -0
  387. opentrons/protocol_engine/commands/thermocycler/set_target_block_temperature.py +140 -0
  388. opentrons/protocol_engine/commands/thermocycler/set_target_lid_temperature.py +100 -0
  389. opentrons/protocol_engine/commands/thermocycler/wait_for_block_temperature.py +93 -0
  390. opentrons/protocol_engine/commands/thermocycler/wait_for_lid_temperature.py +89 -0
  391. opentrons/protocol_engine/commands/touch_tip.py +189 -0
  392. opentrons/protocol_engine/commands/unsafe/__init__.py +161 -0
  393. opentrons/protocol_engine/commands/unsafe/unsafe_blow_out_in_place.py +100 -0
  394. opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +121 -0
  395. opentrons/protocol_engine/commands/unsafe/unsafe_engage_axes.py +82 -0
  396. opentrons/protocol_engine/commands/unsafe/unsafe_place_labware.py +208 -0
  397. opentrons/protocol_engine/commands/unsafe/unsafe_stacker_close_latch.py +94 -0
  398. opentrons/protocol_engine/commands/unsafe/unsafe_stacker_manual_retrieve.py +295 -0
  399. opentrons/protocol_engine/commands/unsafe/unsafe_stacker_open_latch.py +91 -0
  400. opentrons/protocol_engine/commands/unsafe/unsafe_stacker_prepare_shuttle.py +136 -0
  401. opentrons/protocol_engine/commands/unsafe/unsafe_ungrip_labware.py +77 -0
  402. opentrons/protocol_engine/commands/unsafe/update_position_estimators.py +90 -0
  403. opentrons/protocol_engine/commands/unseal_pipette_from_tip.py +153 -0
  404. opentrons/protocol_engine/commands/verify_tip_presence.py +100 -0
  405. opentrons/protocol_engine/commands/wait_for_duration.py +76 -0
  406. opentrons/protocol_engine/commands/wait_for_resume.py +75 -0
  407. opentrons/protocol_engine/create_protocol_engine.py +193 -0
  408. opentrons/protocol_engine/engine_support.py +28 -0
  409. opentrons/protocol_engine/error_recovery_policy.py +81 -0
  410. opentrons/protocol_engine/errors/__init__.py +191 -0
  411. opentrons/protocol_engine/errors/error_occurrence.py +182 -0
  412. opentrons/protocol_engine/errors/exceptions.py +1308 -0
  413. opentrons/protocol_engine/execution/__init__.py +50 -0
  414. opentrons/protocol_engine/execution/command_executor.py +216 -0
  415. opentrons/protocol_engine/execution/create_queue_worker.py +102 -0
  416. opentrons/protocol_engine/execution/door_watcher.py +119 -0
  417. opentrons/protocol_engine/execution/equipment.py +819 -0
  418. opentrons/protocol_engine/execution/error_recovery_hardware_state_synchronizer.py +101 -0
  419. opentrons/protocol_engine/execution/gantry_mover.py +686 -0
  420. opentrons/protocol_engine/execution/hardware_stopper.py +147 -0
  421. opentrons/protocol_engine/execution/heater_shaker_movement_flagger.py +207 -0
  422. opentrons/protocol_engine/execution/labware_movement.py +297 -0
  423. opentrons/protocol_engine/execution/movement.py +350 -0
  424. opentrons/protocol_engine/execution/pipetting.py +607 -0
  425. opentrons/protocol_engine/execution/queue_worker.py +86 -0
  426. opentrons/protocol_engine/execution/rail_lights.py +25 -0
  427. opentrons/protocol_engine/execution/run_control.py +33 -0
  428. opentrons/protocol_engine/execution/status_bar.py +34 -0
  429. opentrons/protocol_engine/execution/thermocycler_movement_flagger.py +188 -0
  430. opentrons/protocol_engine/execution/thermocycler_plate_lifter.py +81 -0
  431. opentrons/protocol_engine/execution/tip_handler.py +550 -0
  432. opentrons/protocol_engine/labware_offset_standardization.py +194 -0
  433. opentrons/protocol_engine/notes/__init__.py +17 -0
  434. opentrons/protocol_engine/notes/notes.py +59 -0
  435. opentrons/protocol_engine/plugins.py +104 -0
  436. opentrons/protocol_engine/protocol_engine.py +683 -0
  437. opentrons/protocol_engine/resources/__init__.py +26 -0
  438. opentrons/protocol_engine/resources/deck_configuration_provider.py +232 -0
  439. opentrons/protocol_engine/resources/deck_data_provider.py +94 -0
  440. opentrons/protocol_engine/resources/file_provider.py +161 -0
  441. opentrons/protocol_engine/resources/fixture_validation.py +68 -0
  442. opentrons/protocol_engine/resources/labware_data_provider.py +106 -0
  443. opentrons/protocol_engine/resources/labware_validation.py +73 -0
  444. opentrons/protocol_engine/resources/model_utils.py +32 -0
  445. opentrons/protocol_engine/resources/module_data_provider.py +44 -0
  446. opentrons/protocol_engine/resources/ot3_validation.py +21 -0
  447. opentrons/protocol_engine/resources/pipette_data_provider.py +379 -0
  448. opentrons/protocol_engine/slot_standardization.py +128 -0
  449. opentrons/protocol_engine/state/__init__.py +1 -0
  450. opentrons/protocol_engine/state/_abstract_store.py +27 -0
  451. opentrons/protocol_engine/state/_axis_aligned_bounding_box.py +50 -0
  452. opentrons/protocol_engine/state/_labware_origin_math.py +636 -0
  453. opentrons/protocol_engine/state/_move_types.py +83 -0
  454. opentrons/protocol_engine/state/_well_math.py +193 -0
  455. opentrons/protocol_engine/state/addressable_areas.py +699 -0
  456. opentrons/protocol_engine/state/command_history.py +309 -0
  457. opentrons/protocol_engine/state/commands.py +1164 -0
  458. opentrons/protocol_engine/state/config.py +39 -0
  459. opentrons/protocol_engine/state/files.py +57 -0
  460. opentrons/protocol_engine/state/fluid_stack.py +138 -0
  461. opentrons/protocol_engine/state/geometry.py +2408 -0
  462. opentrons/protocol_engine/state/inner_well_math_utils.py +548 -0
  463. opentrons/protocol_engine/state/labware.py +1432 -0
  464. opentrons/protocol_engine/state/liquid_classes.py +82 -0
  465. opentrons/protocol_engine/state/liquids.py +73 -0
  466. opentrons/protocol_engine/state/module_substates/__init__.py +45 -0
  467. opentrons/protocol_engine/state/module_substates/absorbance_reader_substate.py +35 -0
  468. opentrons/protocol_engine/state/module_substates/flex_stacker_substate.py +112 -0
  469. opentrons/protocol_engine/state/module_substates/heater_shaker_module_substate.py +115 -0
  470. opentrons/protocol_engine/state/module_substates/magnetic_block_substate.py +17 -0
  471. opentrons/protocol_engine/state/module_substates/magnetic_module_substate.py +65 -0
  472. opentrons/protocol_engine/state/module_substates/temperature_module_substate.py +67 -0
  473. opentrons/protocol_engine/state/module_substates/thermocycler_module_substate.py +163 -0
  474. opentrons/protocol_engine/state/modules.py +1515 -0
  475. opentrons/protocol_engine/state/motion.py +373 -0
  476. opentrons/protocol_engine/state/pipettes.py +905 -0
  477. opentrons/protocol_engine/state/state.py +421 -0
  478. opentrons/protocol_engine/state/state_summary.py +36 -0
  479. opentrons/protocol_engine/state/tips.py +420 -0
  480. opentrons/protocol_engine/state/update_types.py +904 -0
  481. opentrons/protocol_engine/state/wells.py +290 -0
  482. opentrons/protocol_engine/types/__init__.py +310 -0
  483. opentrons/protocol_engine/types/automatic_tip_selection.py +39 -0
  484. opentrons/protocol_engine/types/command_annotations.py +53 -0
  485. opentrons/protocol_engine/types/deck_configuration.py +81 -0
  486. opentrons/protocol_engine/types/execution.py +96 -0
  487. opentrons/protocol_engine/types/hardware_passthrough.py +25 -0
  488. opentrons/protocol_engine/types/instrument.py +47 -0
  489. opentrons/protocol_engine/types/instrument_sensors.py +47 -0
  490. opentrons/protocol_engine/types/labware.py +131 -0
  491. opentrons/protocol_engine/types/labware_movement.py +22 -0
  492. opentrons/protocol_engine/types/labware_offset_location.py +111 -0
  493. opentrons/protocol_engine/types/labware_offset_vector.py +16 -0
  494. opentrons/protocol_engine/types/liquid.py +40 -0
  495. opentrons/protocol_engine/types/liquid_class.py +59 -0
  496. opentrons/protocol_engine/types/liquid_handling.py +13 -0
  497. opentrons/protocol_engine/types/liquid_level_detection.py +191 -0
  498. opentrons/protocol_engine/types/location.py +194 -0
  499. opentrons/protocol_engine/types/module.py +310 -0
  500. opentrons/protocol_engine/types/partial_tip_configuration.py +76 -0
  501. opentrons/protocol_engine/types/run_time_parameters.py +133 -0
  502. opentrons/protocol_engine/types/tip.py +18 -0
  503. opentrons/protocol_engine/types/util.py +21 -0
  504. opentrons/protocol_engine/types/well_position.py +124 -0
  505. opentrons/protocol_reader/__init__.py +37 -0
  506. opentrons/protocol_reader/extract_labware_definitions.py +66 -0
  507. opentrons/protocol_reader/file_format_validator.py +152 -0
  508. opentrons/protocol_reader/file_hasher.py +27 -0
  509. opentrons/protocol_reader/file_identifier.py +284 -0
  510. opentrons/protocol_reader/file_reader_writer.py +90 -0
  511. opentrons/protocol_reader/input_file.py +16 -0
  512. opentrons/protocol_reader/protocol_files_invalid_error.py +6 -0
  513. opentrons/protocol_reader/protocol_reader.py +188 -0
  514. opentrons/protocol_reader/protocol_source.py +124 -0
  515. opentrons/protocol_reader/role_analyzer.py +86 -0
  516. opentrons/protocol_runner/__init__.py +26 -0
  517. opentrons/protocol_runner/create_simulating_orchestrator.py +118 -0
  518. opentrons/protocol_runner/json_file_reader.py +55 -0
  519. opentrons/protocol_runner/json_translator.py +314 -0
  520. opentrons/protocol_runner/legacy_command_mapper.py +852 -0
  521. opentrons/protocol_runner/legacy_context_plugin.py +116 -0
  522. opentrons/protocol_runner/protocol_runner.py +530 -0
  523. opentrons/protocol_runner/python_protocol_wrappers.py +179 -0
  524. opentrons/protocol_runner/run_orchestrator.py +496 -0
  525. opentrons/protocol_runner/task_queue.py +95 -0
  526. opentrons/protocols/__init__.py +6 -0
  527. opentrons/protocols/advanced_control/__init__.py +0 -0
  528. opentrons/protocols/advanced_control/common.py +38 -0
  529. opentrons/protocols/advanced_control/mix.py +60 -0
  530. opentrons/protocols/advanced_control/transfers/__init__.py +0 -0
  531. opentrons/protocols/advanced_control/transfers/common.py +180 -0
  532. opentrons/protocols/advanced_control/transfers/transfer.py +972 -0
  533. opentrons/protocols/advanced_control/transfers/transfer_liquid_utils.py +231 -0
  534. opentrons/protocols/api_support/__init__.py +0 -0
  535. opentrons/protocols/api_support/constants.py +8 -0
  536. opentrons/protocols/api_support/deck_type.py +110 -0
  537. opentrons/protocols/api_support/definitions.py +18 -0
  538. opentrons/protocols/api_support/instrument.py +151 -0
  539. opentrons/protocols/api_support/labware_like.py +233 -0
  540. opentrons/protocols/api_support/tip_tracker.py +175 -0
  541. opentrons/protocols/api_support/types.py +32 -0
  542. opentrons/protocols/api_support/util.py +403 -0
  543. opentrons/protocols/bundle.py +89 -0
  544. opentrons/protocols/duration/__init__.py +4 -0
  545. opentrons/protocols/duration/errors.py +5 -0
  546. opentrons/protocols/duration/estimator.py +628 -0
  547. opentrons/protocols/execution/__init__.py +0 -0
  548. opentrons/protocols/execution/dev_types.py +181 -0
  549. opentrons/protocols/execution/errors.py +40 -0
  550. opentrons/protocols/execution/execute.py +84 -0
  551. opentrons/protocols/execution/execute_json_v3.py +275 -0
  552. opentrons/protocols/execution/execute_json_v4.py +359 -0
  553. opentrons/protocols/execution/execute_json_v5.py +28 -0
  554. opentrons/protocols/execution/execute_python.py +169 -0
  555. opentrons/protocols/execution/json_dispatchers.py +87 -0
  556. opentrons/protocols/execution/types.py +7 -0
  557. opentrons/protocols/geometry/__init__.py +0 -0
  558. opentrons/protocols/geometry/planning.py +297 -0
  559. opentrons/protocols/labware.py +312 -0
  560. opentrons/protocols/models/__init__.py +0 -0
  561. opentrons/protocols/models/json_protocol.py +679 -0
  562. opentrons/protocols/parameters/__init__.py +0 -0
  563. opentrons/protocols/parameters/csv_parameter_definition.py +77 -0
  564. opentrons/protocols/parameters/csv_parameter_interface.py +96 -0
  565. opentrons/protocols/parameters/exceptions.py +34 -0
  566. opentrons/protocols/parameters/parameter_definition.py +272 -0
  567. opentrons/protocols/parameters/types.py +17 -0
  568. opentrons/protocols/parameters/validation.py +267 -0
  569. opentrons/protocols/parse.py +671 -0
  570. opentrons/protocols/types.py +159 -0
  571. opentrons/py.typed +0 -0
  572. opentrons/resources/scripts/lpc21isp +0 -0
  573. opentrons/resources/smoothie-edge-8414642.hex +23010 -0
  574. opentrons/simulate.py +1065 -0
  575. opentrons/system/__init__.py +6 -0
  576. opentrons/system/camera.py +51 -0
  577. opentrons/system/log_control.py +59 -0
  578. opentrons/system/nmcli.py +856 -0
  579. opentrons/system/resin.py +24 -0
  580. opentrons/system/smoothie_update.py +15 -0
  581. opentrons/system/wifi.py +204 -0
  582. opentrons/tools/__init__.py +0 -0
  583. opentrons/tools/args_handler.py +22 -0
  584. opentrons/tools/write_pipette_memory.py +157 -0
  585. opentrons/types.py +618 -0
  586. opentrons/util/__init__.py +1 -0
  587. opentrons/util/async_helpers.py +166 -0
  588. opentrons/util/broker.py +84 -0
  589. opentrons/util/change_notifier.py +47 -0
  590. opentrons/util/entrypoint_util.py +278 -0
  591. opentrons/util/get_union_elements.py +26 -0
  592. opentrons/util/helpers.py +6 -0
  593. opentrons/util/linal.py +178 -0
  594. opentrons/util/logging_config.py +265 -0
  595. opentrons/util/logging_queue_handler.py +61 -0
  596. opentrons/util/performance_helpers.py +157 -0
  597. opentrons-8.6.0.dist-info/METADATA +37 -0
  598. opentrons-8.6.0.dist-info/RECORD +601 -0
  599. opentrons-8.6.0.dist-info/WHEEL +4 -0
  600. opentrons-8.6.0.dist-info/entry_points.txt +3 -0
  601. opentrons-8.6.0.dist-info/licenses/LICENSE +202 -0
@@ -0,0 +1,284 @@
1
+ """File identifier interface."""
2
+
3
+ import json
4
+ from dataclasses import dataclass
5
+ from typing import Any, Dict, Sequence, Union, Optional
6
+
7
+ import anyio
8
+
9
+ from opentrons_shared_data.robot.types import RobotType
10
+ from opentrons_shared_data.errors.exceptions import EnumeratedError, PythonException
11
+
12
+ from opentrons.protocols.api_support.definitions import MAX_SUPPORTED_VERSION
13
+ from opentrons.protocols.api_support.types import APIVersion
14
+ from opentrons.protocols import parse
15
+ from opentrons.protocols.types import MalformedPythonProtocolError, PythonProtocol
16
+
17
+ from .file_reader_writer import BufferedFile
18
+ from .protocol_files_invalid_error import ProtocolFilesInvalidError
19
+ from .protocol_source import Metadata
20
+
21
+
22
+ JsonDict = Dict[str, Any]
23
+
24
+
25
+ @dataclass(frozen=True)
26
+ class IdentifiedJsonMain:
27
+ """A file identified as a JSON protocol's main .json file."""
28
+
29
+ original_file: BufferedFile
30
+ """The original file that this was identified from."""
31
+
32
+ unvalidated_json: JsonDict
33
+ """The parsed JSON contents.
34
+
35
+ Believed, but not confirmed at this point, to conform to one of our JSON protocol
36
+ schemas.
37
+ """
38
+
39
+ schema_version: int
40
+ """The JSON protocol schema that this file is believed to conform to."""
41
+
42
+ robot_type: RobotType
43
+ """The type of robot on which this protocol is meant to run."""
44
+
45
+ metadata: Metadata
46
+ """The protocol metadata extracted from this file."""
47
+
48
+
49
+ @dataclass(frozen=True)
50
+ class IdentifiedPythonMain:
51
+ """A file identified as a Python protocol's main .py file."""
52
+
53
+ original_file: BufferedFile
54
+ """The original file that this was identified from."""
55
+
56
+ api_level: APIVersion
57
+ """The Python Protocol API apiLevel declared by the Python source."""
58
+
59
+ robot_type: RobotType
60
+ """The type of robot on which this protocol is meant to run."""
61
+
62
+ metadata: Metadata
63
+ """The protocol metadata extracted from this file."""
64
+
65
+
66
+ @dataclass(frozen=True)
67
+ class IdentifiedLabwareDefinition:
68
+ """A file identified as a labware definition."""
69
+
70
+ original_file: BufferedFile
71
+ """The original file that this was identified from."""
72
+
73
+ unvalidated_json: JsonDict
74
+ """The parsed JSON contents.
75
+
76
+ Believed, but not confirmed at this point, to conform to our labware definition
77
+ schema v2.
78
+ """
79
+
80
+
81
+ @dataclass(frozen=True)
82
+ class IdentifiedData:
83
+ """A file identified as a user-defined data file."""
84
+
85
+ original_file: BufferedFile
86
+ """The original file that this was identified from."""
87
+
88
+
89
+ IdentifiedFile = Union[
90
+ IdentifiedJsonMain,
91
+ IdentifiedPythonMain,
92
+ IdentifiedLabwareDefinition,
93
+ IdentifiedData,
94
+ ]
95
+
96
+
97
+ class FileIdentificationError(ProtocolFilesInvalidError):
98
+ """Raised when FileIdentifier detects an invalid file."""
99
+
100
+ def __init__(
101
+ self,
102
+ message: str,
103
+ detail: Optional[Dict[str, Any]] = None,
104
+ wrapping: Optional[Sequence[EnumeratedError]] = None,
105
+ only_message: bool = False,
106
+ ) -> None:
107
+ super().__init__(message=message)
108
+ self._only_message = only_message
109
+
110
+ def __str__(self) -> str:
111
+ """Special stringifier to conform to expecations about python protocol errors."""
112
+ if self._only_message:
113
+ return self.message
114
+ else:
115
+ return super().__str__()
116
+
117
+
118
+ class FileIdentifier:
119
+ """File identifier interface."""
120
+
121
+ @staticmethod
122
+ async def identify(
123
+ files: Sequence[BufferedFile], python_parse_mode: parse.PythonParseMode
124
+ ) -> Sequence[IdentifiedFile]:
125
+ """Identify the type and extract basic information from each file.
126
+
127
+ This is intended to take ≲1 second per protocol on an OT-2, so it can extract
128
+ basic information about all stored protocols relatively quickly. Fully parsing
129
+ and validating protocols can take 10-100x longer, so that's left to other units,
130
+ for only when it's really needed.
131
+ """
132
+ return [await _identify(file, python_parse_mode) for file in files]
133
+
134
+
135
+ async def _identify(
136
+ file: BufferedFile, python_parse_mode: parse.PythonParseMode
137
+ ) -> IdentifiedFile:
138
+ lower_file_name = file.name.lower()
139
+ if lower_file_name.endswith(".json"):
140
+ return await _analyze_json(json_file=file)
141
+ elif lower_file_name.endswith(".py"):
142
+ return _analyze_python_protocol(
143
+ py_file=file, python_parse_mode=python_parse_mode
144
+ )
145
+ elif lower_file_name.endswith(".csv") or lower_file_name.endswith(".txt"):
146
+ return IdentifiedData(original_file=file)
147
+ else:
148
+ raise FileIdentificationError(
149
+ message=f"{file.name} has an unrecognized file extension.",
150
+ detail={"type": "bad-file-extension", "file": file.name},
151
+ )
152
+
153
+
154
+ async def _analyze_json(
155
+ json_file: BufferedFile,
156
+ ) -> Union[IdentifiedJsonMain, IdentifiedLabwareDefinition]:
157
+ try:
158
+ json_contents = await anyio.to_thread.run_sync(json.loads, json_file.contents)
159
+ except json.JSONDecodeError as e:
160
+ raise FileIdentificationError(
161
+ message=f"{json_file.name} is not valid JSON. {str(e)}",
162
+ detail={"type": "invalid-json", "file": json_file.name},
163
+ wrapping=[PythonException(e)],
164
+ ) from e
165
+
166
+ if _json_seems_like_labware(json_contents):
167
+ return IdentifiedLabwareDefinition(
168
+ original_file=json_file,
169
+ unvalidated_json=json_contents,
170
+ )
171
+ elif _json_seems_like_protocol(json_contents):
172
+ return _analyze_json_protocol(
173
+ original_file=json_file,
174
+ json_contents=json_contents,
175
+ )
176
+ else:
177
+ raise FileIdentificationError(
178
+ message=f"{json_file.name} is not a known Opentrons format.",
179
+ detail={"type": "no-schema-match", "file": json_file.name},
180
+ )
181
+
182
+
183
+ def _json_seems_like_labware(json: JsonDict) -> bool:
184
+ # "ordering" and "wells" are required properties in our labware schema v2.
185
+ return "ordering" in json and "wells" in json
186
+
187
+
188
+ def _json_seems_like_protocol(json: JsonDict) -> bool:
189
+ # "schemaVersion" and "commands" are required properties in all of our JSON
190
+ # protocol schemas since schema v3. (v7 is the latest at the time of writing.)
191
+ #
192
+ # When we stop supporting v3 files, we can look at "$otSharedSchema" instead,
193
+ # which is more precise.
194
+ return "schemaVersion" in json and "commands" in json
195
+
196
+
197
+ def _analyze_json_protocol(
198
+ original_file: BufferedFile, json_contents: JsonDict
199
+ ) -> IdentifiedJsonMain:
200
+ error_message = f"{original_file.name} is not a valid JSON protocol."
201
+
202
+ try:
203
+ metadata = json_contents["metadata"]
204
+ schema_version = json_contents["schemaVersion"]
205
+ robot_type = json_contents["robot"]["model"]
206
+ except KeyError as e:
207
+ raise FileIdentificationError(
208
+ message=error_message,
209
+ detail={"kind": "missing-json-metadata", "missing-key": str(e)},
210
+ wrapping=[PythonException(e)],
211
+ ) from e
212
+
213
+ # todo(mm, 2022-12-22): A JSON protocol file's metadata is not quite just an
214
+ # arbitrary dict: its fields are supposed to follow a schema. Should we validate
215
+ # this metadata against that schema instead of doing this simple isinstance() check?
216
+ if not isinstance(metadata, dict):
217
+ raise FileIdentificationError(
218
+ message=error_message, detail={"kind": "json-metadata-not-object"}
219
+ )
220
+
221
+ if not isinstance(schema_version, int):
222
+ raise FileIdentificationError(
223
+ message=error_message,
224
+ detail={
225
+ "kind": "json-schema-version-not-int",
226
+ "schema-version": schema_version,
227
+ },
228
+ )
229
+
230
+ if robot_type not in ("OT-2 Standard", "OT-3 Standard"):
231
+ raise FileIdentificationError(
232
+ message=error_message,
233
+ detail={"kind": "bad-json-protocol-robot-type", "robot-type": robot_type},
234
+ )
235
+
236
+ return IdentifiedJsonMain(
237
+ original_file=original_file,
238
+ unvalidated_json=json_contents,
239
+ schema_version=schema_version,
240
+ robot_type=robot_type,
241
+ metadata=metadata,
242
+ )
243
+
244
+
245
+ def _analyze_python_protocol(
246
+ py_file: BufferedFile,
247
+ python_parse_mode: parse.PythonParseMode,
248
+ ) -> IdentifiedPythonMain:
249
+ try:
250
+ parsed = parse.parse(
251
+ protocol_file=py_file.contents,
252
+ filename=py_file.name,
253
+ python_parse_mode=python_parse_mode,
254
+ )
255
+ except MalformedPythonProtocolError as e:
256
+ raise FileIdentificationError(
257
+ message=e.short_message,
258
+ detail={"kind": "malformed-python-protocol"},
259
+ wrapping=[PythonException(e)],
260
+ only_message=True,
261
+ ) from e
262
+
263
+ # We know this should never be a JsonProtocol. Help out the type-checker.
264
+ assert isinstance(
265
+ parsed, PythonProtocol
266
+ ), "Parsing a Python file returned something other than a Python protocol."
267
+
268
+ if parsed.api_level > MAX_SUPPORTED_VERSION:
269
+ raise FileIdentificationError(
270
+ message=(
271
+ f"API version {parsed.api_level} is not supported by this "
272
+ f"robot software. Please either reduce your requested API "
273
+ f"version or update your robot."
274
+ ),
275
+ detail={"kind": "future-api-version", "api-version": str(parsed.api_level)},
276
+ only_message=True,
277
+ )
278
+
279
+ return IdentifiedPythonMain(
280
+ original_file=py_file,
281
+ metadata=parsed.metadata or {},
282
+ robot_type=parsed.robot_type,
283
+ api_level=parsed.api_level,
284
+ )
@@ -0,0 +1,90 @@
1
+ """Input file reading."""
2
+
3
+ import os
4
+ import pathlib
5
+ import platform
6
+ from dataclasses import dataclass
7
+ from typing import List, Optional, Sequence, Union
8
+
9
+ import anyio
10
+
11
+ from .input_file import AbstractInputFile
12
+ from .protocol_files_invalid_error import ProtocolFilesInvalidError
13
+
14
+
15
+ @dataclass(frozen=True)
16
+ class BufferedFile:
17
+ """A file that has been read into memory."""
18
+
19
+ name: str
20
+ contents: bytes
21
+ path: Optional[pathlib.Path]
22
+
23
+
24
+ class FileReadError(ProtocolFilesInvalidError):
25
+ """An error raised if input files cannot be read."""
26
+
27
+
28
+ class FileReaderWriter:
29
+ """Input file reader/writer interface."""
30
+
31
+ @staticmethod
32
+ async def read(
33
+ files: Sequence[Union[AbstractInputFile, pathlib.Path]]
34
+ ) -> List[BufferedFile]:
35
+ """Read a set of input files into memory."""
36
+ return [await _read_file(input_file=file) for file in files]
37
+
38
+ @staticmethod
39
+ async def write(directory: pathlib.Path, files: Sequence[BufferedFile]) -> None:
40
+ """Write a set of previously buffered files to disk."""
41
+ await anyio.Path(directory).mkdir(parents=True, exist_ok=True)
42
+
43
+ for file in files:
44
+ path = directory / file.name
45
+ await _write_and_fsync_file(path=path, contents=file.contents)
46
+
47
+ await _fsync_directory(path=directory)
48
+
49
+
50
+ async def _read_file(
51
+ input_file: Union[AbstractInputFile, pathlib.Path]
52
+ ) -> BufferedFile:
53
+ if isinstance(input_file, pathlib.Path):
54
+ path: Optional[pathlib.Path] = input_file
55
+ filename = input_file.name
56
+ contents = await anyio.Path(input_file).read_bytes()
57
+ elif not input_file.filename:
58
+ raise FileReadError("File was missing a name")
59
+ else:
60
+ path = None
61
+ filename = input_file.filename
62
+ async with anyio.wrap_file(input_file.file) as f:
63
+ contents = await f.read()
64
+
65
+ return BufferedFile(
66
+ name=filename,
67
+ contents=contents,
68
+ path=path,
69
+ )
70
+
71
+
72
+ async def _write_and_fsync_file(path: pathlib.Path, contents: bytes) -> None:
73
+ async with await anyio.open_file(path, "wb") as file:
74
+ await file.write(contents)
75
+ await file.flush()
76
+ await anyio.to_thread.run_sync(os.fsync, file.wrapped.fileno())
77
+
78
+
79
+ async def _fsync_directory(path: pathlib.Path) -> None:
80
+ def _fsync_directory_sync() -> None:
81
+ # We can't fsync directories on Windows because doing os.open() on them raises
82
+ # PermissionError. https://stackoverflow.com/questions/21785127
83
+ if platform.system() != "Windows":
84
+ fd = os.open(path, os.O_RDONLY)
85
+ try:
86
+ os.fsync(fd)
87
+ finally:
88
+ os.close(fd)
89
+
90
+ await anyio.to_thread.run_sync(_fsync_directory_sync)
@@ -0,0 +1,16 @@
1
+ """Input file value objects."""
2
+ from __future__ import annotations
3
+ from typing import BinaryIO, Protocol
4
+
5
+
6
+ class AbstractInputFile(Protocol):
7
+ """An individual file to be read as part of a protocol.
8
+
9
+ Properties:
10
+ filename: The basename, including extension, of the file.
11
+ file: A [file](https://docs.python.org/3/glossary.html#term-file-object)
12
+ providing the contents of the protocol to be read.
13
+ """
14
+
15
+ filename: str
16
+ file: BinaryIO
@@ -0,0 +1,6 @@
1
+ # noqa: D100
2
+ from opentrons_shared_data.errors.exceptions import InvalidProtocolData
3
+
4
+
5
+ class ProtocolFilesInvalidError(InvalidProtocolData):
6
+ """Raised when the input to a ProtocolReader is not a well-formed protocol."""
@@ -0,0 +1,188 @@
1
+ """Read relevant protocol information from a set of files."""
2
+ from pathlib import Path
3
+ from typing import Optional, Sequence
4
+
5
+ from opentrons.protocols.parse import PythonParseMode
6
+
7
+ from .file_identifier import (
8
+ FileIdentifier,
9
+ IdentifiedFile,
10
+ IdentifiedJsonMain,
11
+ IdentifiedPythonMain,
12
+ IdentifiedLabwareDefinition,
13
+ IdentifiedData,
14
+ )
15
+ from .role_analyzer import RoleAnalyzer, RoleAnalysis
16
+ from .file_format_validator import FileFormatValidator
17
+ from .file_reader_writer import FileReaderWriter, BufferedFile
18
+ from .file_hasher import FileHasher
19
+ from .protocol_source import (
20
+ ProtocolSource,
21
+ ProtocolSourceFile,
22
+ ProtocolConfig,
23
+ ProtocolFileRole,
24
+ JsonProtocolConfig,
25
+ PythonProtocolConfig,
26
+ )
27
+
28
+
29
+ class ProtocolReader:
30
+ """Collaborator to turn a set of files into a protocol object."""
31
+
32
+ def __init__(
33
+ self,
34
+ file_reader_writer: Optional[FileReaderWriter] = None,
35
+ file_identifier: Optional[FileIdentifier] = None,
36
+ role_analyzer: Optional[RoleAnalyzer] = None,
37
+ file_format_validator: Optional[FileFormatValidator] = None,
38
+ file_hasher: Optional[FileHasher] = None,
39
+ ) -> None:
40
+ """Initialize the reader with its dependencies.
41
+
42
+ Arguments:
43
+ file_reader_writer: Input file reader/writer. Default impl. used if None.
44
+ file_identifier: File identifier. Default impl. used if None.
45
+ role_analyzer: File role analyzer. Default impl. used if None.
46
+ file_format_validator: File format validator. Default impl. used if None.
47
+ file_hasher: File hasher. Default impl. used if None.
48
+ """
49
+ self._file_reader_writer = file_reader_writer or FileReaderWriter()
50
+ self._file_identifier = file_identifier or FileIdentifier()
51
+ self._role_analyzer = role_analyzer or RoleAnalyzer()
52
+ self._file_format_validator = file_format_validator or FileFormatValidator()
53
+ self._file_hasher = file_hasher or FileHasher()
54
+
55
+ async def save(
56
+ self,
57
+ files: Sequence[BufferedFile],
58
+ directory: Path,
59
+ content_hash: str,
60
+ ) -> ProtocolSource:
61
+ """Compute a `ProtocolSource` from buffered files and save them as files.
62
+
63
+ The input is parsed and statically analyzed to ensure it's basically
64
+ well-formed. For example, labware definition files must conform to the labware
65
+ definition schema.
66
+
67
+ Arguments:
68
+ files: List buffered files. Do not attempt to reuse any objects
69
+ in this list once they've been passed to the ProtocolReader.
70
+ directory: Name of the directory to create and place files in.
71
+
72
+ Returns:
73
+ A ProtocolSource describing the validated protocol.
74
+
75
+ Raises:
76
+ ProtocolFilesInvalidError: Input file list given to the reader
77
+ could not be validated as a protocol.
78
+ """
79
+ identified_files = await self._file_identifier.identify(
80
+ files, python_parse_mode=PythonParseMode.NORMAL
81
+ )
82
+ role_analysis = self._role_analyzer.analyze(identified_files)
83
+ await self._file_format_validator.validate(role_analysis.all_files)
84
+
85
+ files_to_write = [f.original_file for f in role_analysis.all_files]
86
+ await self._file_reader_writer.write(directory=directory, files=files_to_write)
87
+
88
+ main_file = directory / role_analysis.main_file.original_file.name
89
+ output_files = [
90
+ ProtocolSourceFile(
91
+ path=directory / f.original_file.name, role=self._map_file_role(f)
92
+ )
93
+ for f in role_analysis.all_files
94
+ ]
95
+
96
+ return ProtocolSource(
97
+ directory=directory,
98
+ main_file=main_file,
99
+ content_hash=content_hash,
100
+ files=output_files,
101
+ config=self._map_config(role_analysis),
102
+ robot_type=role_analysis.main_file.robot_type,
103
+ metadata=role_analysis.main_file.metadata,
104
+ )
105
+
106
+ async def read_saved(
107
+ self,
108
+ files: Sequence[Path],
109
+ directory: Optional[Path],
110
+ files_are_prevalidated: bool = False,
111
+ python_parse_mode: PythonParseMode = PythonParseMode.NORMAL,
112
+ ) -> ProtocolSource:
113
+ """Compute a `ProtocolSource` from protocol source files on the filesystem.
114
+
115
+ The input is parsed and statically analyzed to ensure it's basically
116
+ well-formed. For example, labware definition files must conform to the labware
117
+ definition schema.
118
+
119
+ Arguments:
120
+ files: The files comprising the protocol.
121
+ directory: Passed through to `ProtocolSource.directory`. Otherwise unused.
122
+ files_are_prevalidated: Assume that the input files are valid. Skip full
123
+ parsing and validation, doing only the minimum required to extract the
124
+ stuff for the `ProtocolSource`. This can be 10-100x faster, but you
125
+ should only do it with protocols that have already been validated
126
+ by this module.
127
+ python_parse_mode: See the documentation in `PythonParseMode`.
128
+
129
+ Returns:
130
+ A `ProtocolSource` describing the validated protocol.
131
+
132
+ Raises:
133
+ ProtocolFilesInvalidError: Input file list given to the reader
134
+ could not be validated as a protocol.
135
+ """
136
+ buffered_files = await self._file_reader_writer.read(files)
137
+ identified_files = await self._file_identifier.identify(
138
+ files=buffered_files,
139
+ python_parse_mode=python_parse_mode,
140
+ )
141
+ role_analysis = self._role_analyzer.analyze(identified_files)
142
+ if not files_are_prevalidated:
143
+ await self._file_format_validator.validate(role_analysis.all_files)
144
+
145
+ # We know these paths will not be None because we supplied real Paths,
146
+ # not AbstractInputFiles, to FileReaderWriter.
147
+ # TODO(mc, 2022-04-01): these asserts are a bit awkward,
148
+ # consider restructuring so they're not needed.
149
+ assert isinstance(role_analysis.main_file.original_file.path, Path)
150
+ assert all(
151
+ isinstance(f.original_file.path, Path) for f in role_analysis.all_files
152
+ )
153
+
154
+ main_file = role_analysis.main_file.original_file.path
155
+ content_hash = await self._file_hasher.hash(buffered_files)
156
+
157
+ output_files = [
158
+ ProtocolSourceFile(path=f.original_file.path, role=self._map_file_role(f)) # type: ignore[arg-type]
159
+ for f in role_analysis.all_files
160
+ ]
161
+
162
+ return ProtocolSource(
163
+ directory=directory,
164
+ main_file=main_file,
165
+ content_hash=content_hash,
166
+ files=output_files,
167
+ config=self._map_config(role_analysis),
168
+ robot_type=role_analysis.main_file.robot_type,
169
+ metadata=role_analysis.main_file.metadata,
170
+ )
171
+
172
+ @staticmethod
173
+ def _map_file_role(file: IdentifiedFile) -> ProtocolFileRole:
174
+ if isinstance(file, (IdentifiedJsonMain, IdentifiedPythonMain)):
175
+ return ProtocolFileRole.MAIN
176
+ elif isinstance(file, IdentifiedLabwareDefinition):
177
+ return ProtocolFileRole.LABWARE
178
+ elif isinstance(file, IdentifiedData):
179
+ return ProtocolFileRole.DATA
180
+
181
+ @staticmethod
182
+ def _map_config(protocol_info: RoleAnalysis) -> ProtocolConfig:
183
+ if isinstance(protocol_info.main_file, IdentifiedJsonMain):
184
+ return JsonProtocolConfig(
185
+ schema_version=protocol_info.main_file.schema_version
186
+ )
187
+ elif isinstance(protocol_info.main_file, IdentifiedPythonMain):
188
+ return PythonProtocolConfig(api_version=protocol_info.main_file.api_level)