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.
- opentrons/__init__.py +150 -0
- opentrons/_version.py +34 -0
- opentrons/calibration_storage/__init__.py +54 -0
- opentrons/calibration_storage/deck_configuration.py +62 -0
- opentrons/calibration_storage/encoder_decoder.py +31 -0
- opentrons/calibration_storage/file_operators.py +142 -0
- opentrons/calibration_storage/helpers.py +103 -0
- opentrons/calibration_storage/ot2/__init__.py +34 -0
- opentrons/calibration_storage/ot2/deck_attitude.py +85 -0
- opentrons/calibration_storage/ot2/mark_bad_calibration.py +27 -0
- opentrons/calibration_storage/ot2/models/__init__.py +0 -0
- opentrons/calibration_storage/ot2/models/v1.py +149 -0
- opentrons/calibration_storage/ot2/pipette_offset.py +129 -0
- opentrons/calibration_storage/ot2/tip_length.py +281 -0
- opentrons/calibration_storage/ot3/__init__.py +31 -0
- opentrons/calibration_storage/ot3/deck_attitude.py +83 -0
- opentrons/calibration_storage/ot3/gripper_offset.py +156 -0
- opentrons/calibration_storage/ot3/models/__init__.py +0 -0
- opentrons/calibration_storage/ot3/models/v1.py +122 -0
- opentrons/calibration_storage/ot3/module_offset.py +138 -0
- opentrons/calibration_storage/ot3/pipette_offset.py +95 -0
- opentrons/calibration_storage/types.py +45 -0
- opentrons/cli/__init__.py +21 -0
- opentrons/cli/__main__.py +5 -0
- opentrons/cli/analyze.py +501 -0
- opentrons/config/__init__.py +631 -0
- opentrons/config/advanced_settings.py +871 -0
- opentrons/config/defaults_ot2.py +214 -0
- opentrons/config/defaults_ot3.py +499 -0
- opentrons/config/feature_flags.py +86 -0
- opentrons/config/gripper_config.py +55 -0
- opentrons/config/reset.py +203 -0
- opentrons/config/robot_configs.py +187 -0
- opentrons/config/types.py +183 -0
- opentrons/drivers/__init__.py +0 -0
- opentrons/drivers/absorbance_reader/__init__.py +11 -0
- opentrons/drivers/absorbance_reader/abstract.py +72 -0
- opentrons/drivers/absorbance_reader/async_byonoy.py +352 -0
- opentrons/drivers/absorbance_reader/driver.py +81 -0
- opentrons/drivers/absorbance_reader/hid_protocol.py +161 -0
- opentrons/drivers/absorbance_reader/simulator.py +84 -0
- opentrons/drivers/asyncio/__init__.py +0 -0
- opentrons/drivers/asyncio/communication/__init__.py +22 -0
- opentrons/drivers/asyncio/communication/async_serial.py +183 -0
- opentrons/drivers/asyncio/communication/errors.py +88 -0
- opentrons/drivers/asyncio/communication/serial_connection.py +552 -0
- opentrons/drivers/command_builder.py +102 -0
- opentrons/drivers/flex_stacker/__init__.py +13 -0
- opentrons/drivers/flex_stacker/abstract.py +214 -0
- opentrons/drivers/flex_stacker/driver.py +768 -0
- opentrons/drivers/flex_stacker/errors.py +68 -0
- opentrons/drivers/flex_stacker/simulator.py +309 -0
- opentrons/drivers/flex_stacker/types.py +367 -0
- opentrons/drivers/flex_stacker/utils.py +19 -0
- opentrons/drivers/heater_shaker/__init__.py +5 -0
- opentrons/drivers/heater_shaker/abstract.py +76 -0
- opentrons/drivers/heater_shaker/driver.py +204 -0
- opentrons/drivers/heater_shaker/simulator.py +94 -0
- opentrons/drivers/mag_deck/__init__.py +6 -0
- opentrons/drivers/mag_deck/abstract.py +44 -0
- opentrons/drivers/mag_deck/driver.py +208 -0
- opentrons/drivers/mag_deck/simulator.py +63 -0
- opentrons/drivers/rpi_drivers/__init__.py +33 -0
- opentrons/drivers/rpi_drivers/dev_types.py +94 -0
- opentrons/drivers/rpi_drivers/gpio.py +282 -0
- opentrons/drivers/rpi_drivers/gpio_simulator.py +127 -0
- opentrons/drivers/rpi_drivers/interfaces.py +15 -0
- opentrons/drivers/rpi_drivers/types.py +364 -0
- opentrons/drivers/rpi_drivers/usb.py +102 -0
- opentrons/drivers/rpi_drivers/usb_simulator.py +22 -0
- opentrons/drivers/serial_communication.py +151 -0
- opentrons/drivers/smoothie_drivers/__init__.py +4 -0
- opentrons/drivers/smoothie_drivers/connection.py +51 -0
- opentrons/drivers/smoothie_drivers/constants.py +121 -0
- opentrons/drivers/smoothie_drivers/driver_3_0.py +1933 -0
- opentrons/drivers/smoothie_drivers/errors.py +49 -0
- opentrons/drivers/smoothie_drivers/parse_utils.py +143 -0
- opentrons/drivers/smoothie_drivers/simulator.py +99 -0
- opentrons/drivers/smoothie_drivers/types.py +16 -0
- opentrons/drivers/temp_deck/__init__.py +10 -0
- opentrons/drivers/temp_deck/abstract.py +54 -0
- opentrons/drivers/temp_deck/driver.py +197 -0
- opentrons/drivers/temp_deck/simulator.py +57 -0
- opentrons/drivers/thermocycler/__init__.py +12 -0
- opentrons/drivers/thermocycler/abstract.py +99 -0
- opentrons/drivers/thermocycler/driver.py +395 -0
- opentrons/drivers/thermocycler/simulator.py +126 -0
- opentrons/drivers/types.py +107 -0
- opentrons/drivers/utils.py +222 -0
- opentrons/execute.py +742 -0
- opentrons/hardware_control/__init__.py +65 -0
- opentrons/hardware_control/__main__.py +77 -0
- opentrons/hardware_control/adapters.py +98 -0
- opentrons/hardware_control/api.py +1347 -0
- opentrons/hardware_control/backends/__init__.py +7 -0
- opentrons/hardware_control/backends/controller.py +400 -0
- opentrons/hardware_control/backends/errors.py +9 -0
- opentrons/hardware_control/backends/estop_state.py +164 -0
- opentrons/hardware_control/backends/flex_protocol.py +497 -0
- opentrons/hardware_control/backends/ot3controller.py +1930 -0
- opentrons/hardware_control/backends/ot3simulator.py +900 -0
- opentrons/hardware_control/backends/ot3utils.py +664 -0
- opentrons/hardware_control/backends/simulator.py +442 -0
- opentrons/hardware_control/backends/status_bar_state.py +240 -0
- opentrons/hardware_control/backends/subsystem_manager.py +431 -0
- opentrons/hardware_control/backends/tip_presence_manager.py +173 -0
- opentrons/hardware_control/backends/types.py +14 -0
- opentrons/hardware_control/constants.py +6 -0
- opentrons/hardware_control/dev_types.py +125 -0
- opentrons/hardware_control/emulation/__init__.py +0 -0
- opentrons/hardware_control/emulation/abstract_emulator.py +21 -0
- opentrons/hardware_control/emulation/app.py +56 -0
- opentrons/hardware_control/emulation/connection_handler.py +38 -0
- opentrons/hardware_control/emulation/heater_shaker.py +150 -0
- opentrons/hardware_control/emulation/magdeck.py +60 -0
- opentrons/hardware_control/emulation/module_server/__init__.py +8 -0
- opentrons/hardware_control/emulation/module_server/client.py +78 -0
- opentrons/hardware_control/emulation/module_server/helpers.py +130 -0
- opentrons/hardware_control/emulation/module_server/models.py +31 -0
- opentrons/hardware_control/emulation/module_server/server.py +110 -0
- opentrons/hardware_control/emulation/parser.py +74 -0
- opentrons/hardware_control/emulation/proxy.py +241 -0
- opentrons/hardware_control/emulation/run_emulator.py +68 -0
- opentrons/hardware_control/emulation/scripts/__init__.py +0 -0
- opentrons/hardware_control/emulation/scripts/run_app.py +54 -0
- opentrons/hardware_control/emulation/scripts/run_module_emulator.py +72 -0
- opentrons/hardware_control/emulation/scripts/run_smoothie.py +37 -0
- opentrons/hardware_control/emulation/settings.py +119 -0
- opentrons/hardware_control/emulation/simulations.py +133 -0
- opentrons/hardware_control/emulation/smoothie.py +192 -0
- opentrons/hardware_control/emulation/tempdeck.py +69 -0
- opentrons/hardware_control/emulation/thermocycler.py +128 -0
- opentrons/hardware_control/emulation/types.py +10 -0
- opentrons/hardware_control/emulation/util.py +38 -0
- opentrons/hardware_control/errors.py +43 -0
- opentrons/hardware_control/execution_manager.py +164 -0
- opentrons/hardware_control/instruments/__init__.py +5 -0
- opentrons/hardware_control/instruments/instrument_abc.py +39 -0
- opentrons/hardware_control/instruments/ot2/__init__.py +0 -0
- opentrons/hardware_control/instruments/ot2/instrument_calibration.py +152 -0
- opentrons/hardware_control/instruments/ot2/pipette.py +777 -0
- opentrons/hardware_control/instruments/ot2/pipette_handler.py +995 -0
- opentrons/hardware_control/instruments/ot3/__init__.py +0 -0
- opentrons/hardware_control/instruments/ot3/gripper.py +420 -0
- opentrons/hardware_control/instruments/ot3/gripper_handler.py +173 -0
- opentrons/hardware_control/instruments/ot3/instrument_calibration.py +214 -0
- opentrons/hardware_control/instruments/ot3/pipette.py +858 -0
- opentrons/hardware_control/instruments/ot3/pipette_handler.py +1030 -0
- opentrons/hardware_control/module_control.py +332 -0
- opentrons/hardware_control/modules/__init__.py +69 -0
- opentrons/hardware_control/modules/absorbance_reader.py +373 -0
- opentrons/hardware_control/modules/errors.py +7 -0
- opentrons/hardware_control/modules/flex_stacker.py +948 -0
- opentrons/hardware_control/modules/heater_shaker.py +426 -0
- opentrons/hardware_control/modules/lid_temp_status.py +35 -0
- opentrons/hardware_control/modules/magdeck.py +233 -0
- opentrons/hardware_control/modules/mod_abc.py +245 -0
- opentrons/hardware_control/modules/module_calibration.py +93 -0
- opentrons/hardware_control/modules/plate_temp_status.py +61 -0
- opentrons/hardware_control/modules/tempdeck.py +299 -0
- opentrons/hardware_control/modules/thermocycler.py +731 -0
- opentrons/hardware_control/modules/types.py +417 -0
- opentrons/hardware_control/modules/update.py +255 -0
- opentrons/hardware_control/modules/utils.py +73 -0
- opentrons/hardware_control/motion_utilities.py +318 -0
- opentrons/hardware_control/nozzle_manager.py +422 -0
- opentrons/hardware_control/ot3_calibration.py +1171 -0
- opentrons/hardware_control/ot3api.py +3227 -0
- opentrons/hardware_control/pause_manager.py +31 -0
- opentrons/hardware_control/poller.py +112 -0
- opentrons/hardware_control/protocols/__init__.py +106 -0
- opentrons/hardware_control/protocols/asyncio_configurable.py +11 -0
- opentrons/hardware_control/protocols/calibratable.py +45 -0
- opentrons/hardware_control/protocols/chassis_accessory_manager.py +90 -0
- opentrons/hardware_control/protocols/configurable.py +48 -0
- opentrons/hardware_control/protocols/event_sourcer.py +18 -0
- opentrons/hardware_control/protocols/execution_controllable.py +33 -0
- opentrons/hardware_control/protocols/flex_calibratable.py +96 -0
- opentrons/hardware_control/protocols/flex_instrument_configurer.py +52 -0
- opentrons/hardware_control/protocols/gripper_controller.py +55 -0
- opentrons/hardware_control/protocols/hardware_manager.py +51 -0
- opentrons/hardware_control/protocols/identifiable.py +16 -0
- opentrons/hardware_control/protocols/instrument_configurer.py +206 -0
- opentrons/hardware_control/protocols/liquid_handler.py +266 -0
- opentrons/hardware_control/protocols/module_provider.py +16 -0
- opentrons/hardware_control/protocols/motion_controller.py +243 -0
- opentrons/hardware_control/protocols/position_estimator.py +45 -0
- opentrons/hardware_control/protocols/simulatable.py +10 -0
- opentrons/hardware_control/protocols/stoppable.py +9 -0
- opentrons/hardware_control/protocols/types.py +27 -0
- opentrons/hardware_control/robot_calibration.py +224 -0
- opentrons/hardware_control/scripts/README.md +28 -0
- opentrons/hardware_control/scripts/__init__.py +1 -0
- opentrons/hardware_control/scripts/gripper_control.py +208 -0
- opentrons/hardware_control/scripts/ot3gripper +7 -0
- opentrons/hardware_control/scripts/ot3repl +7 -0
- opentrons/hardware_control/scripts/repl.py +187 -0
- opentrons/hardware_control/scripts/tc_control.py +97 -0
- opentrons/hardware_control/simulator_setup.py +260 -0
- opentrons/hardware_control/thread_manager.py +431 -0
- opentrons/hardware_control/threaded_async_lock.py +97 -0
- opentrons/hardware_control/types.py +792 -0
- opentrons/hardware_control/util.py +234 -0
- opentrons/legacy_broker.py +53 -0
- opentrons/legacy_commands/__init__.py +1 -0
- opentrons/legacy_commands/commands.py +483 -0
- opentrons/legacy_commands/helpers.py +153 -0
- opentrons/legacy_commands/module_commands.py +215 -0
- opentrons/legacy_commands/protocol_commands.py +54 -0
- opentrons/legacy_commands/publisher.py +155 -0
- opentrons/legacy_commands/robot_commands.py +51 -0
- opentrons/legacy_commands/types.py +1115 -0
- opentrons/motion_planning/__init__.py +32 -0
- opentrons/motion_planning/adjacent_slots_getters.py +168 -0
- opentrons/motion_planning/deck_conflict.py +396 -0
- opentrons/motion_planning/errors.py +35 -0
- opentrons/motion_planning/types.py +42 -0
- opentrons/motion_planning/waypoints.py +218 -0
- opentrons/ordered_set.py +138 -0
- opentrons/protocol_api/__init__.py +105 -0
- opentrons/protocol_api/_liquid.py +157 -0
- opentrons/protocol_api/_liquid_properties.py +814 -0
- opentrons/protocol_api/_nozzle_layout.py +31 -0
- opentrons/protocol_api/_parameter_context.py +300 -0
- opentrons/protocol_api/_parameters.py +31 -0
- opentrons/protocol_api/_transfer_liquid_validation.py +108 -0
- opentrons/protocol_api/_types.py +43 -0
- opentrons/protocol_api/config.py +23 -0
- opentrons/protocol_api/core/__init__.py +23 -0
- opentrons/protocol_api/core/common.py +33 -0
- opentrons/protocol_api/core/core_map.py +74 -0
- opentrons/protocol_api/core/engine/__init__.py +22 -0
- opentrons/protocol_api/core/engine/_default_labware_versions.py +179 -0
- opentrons/protocol_api/core/engine/deck_conflict.py +348 -0
- opentrons/protocol_api/core/engine/exceptions.py +19 -0
- opentrons/protocol_api/core/engine/instrument.py +2391 -0
- opentrons/protocol_api/core/engine/labware.py +238 -0
- opentrons/protocol_api/core/engine/load_labware_params.py +73 -0
- opentrons/protocol_api/core/engine/module_core.py +1025 -0
- opentrons/protocol_api/core/engine/overlap_versions.py +20 -0
- opentrons/protocol_api/core/engine/pipette_movement_conflict.py +358 -0
- opentrons/protocol_api/core/engine/point_calculations.py +64 -0
- opentrons/protocol_api/core/engine/protocol.py +1153 -0
- opentrons/protocol_api/core/engine/robot.py +139 -0
- opentrons/protocol_api/core/engine/stringify.py +74 -0
- opentrons/protocol_api/core/engine/transfer_components_executor.py +990 -0
- opentrons/protocol_api/core/engine/well.py +241 -0
- opentrons/protocol_api/core/instrument.py +459 -0
- opentrons/protocol_api/core/labware.py +151 -0
- opentrons/protocol_api/core/legacy/__init__.py +11 -0
- opentrons/protocol_api/core/legacy/_labware_geometry.py +37 -0
- opentrons/protocol_api/core/legacy/deck.py +369 -0
- opentrons/protocol_api/core/legacy/labware_offset_provider.py +108 -0
- opentrons/protocol_api/core/legacy/legacy_instrument_core.py +709 -0
- opentrons/protocol_api/core/legacy/legacy_labware_core.py +235 -0
- opentrons/protocol_api/core/legacy/legacy_module_core.py +592 -0
- opentrons/protocol_api/core/legacy/legacy_protocol_core.py +612 -0
- opentrons/protocol_api/core/legacy/legacy_well_core.py +162 -0
- opentrons/protocol_api/core/legacy/load_info.py +67 -0
- opentrons/protocol_api/core/legacy/module_geometry.py +547 -0
- opentrons/protocol_api/core/legacy/well_geometry.py +148 -0
- opentrons/protocol_api/core/legacy_simulator/__init__.py +16 -0
- opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +624 -0
- opentrons/protocol_api/core/legacy_simulator/legacy_protocol_core.py +85 -0
- opentrons/protocol_api/core/module.py +484 -0
- opentrons/protocol_api/core/protocol.py +311 -0
- opentrons/protocol_api/core/robot.py +51 -0
- opentrons/protocol_api/core/well.py +116 -0
- opentrons/protocol_api/core/well_grid.py +45 -0
- opentrons/protocol_api/create_protocol_context.py +177 -0
- opentrons/protocol_api/deck.py +223 -0
- opentrons/protocol_api/disposal_locations.py +244 -0
- opentrons/protocol_api/instrument_context.py +3212 -0
- opentrons/protocol_api/labware.py +1579 -0
- opentrons/protocol_api/module_contexts.py +1425 -0
- opentrons/protocol_api/module_validation_and_errors.py +61 -0
- opentrons/protocol_api/protocol_context.py +1688 -0
- opentrons/protocol_api/robot_context.py +303 -0
- opentrons/protocol_api/validation.py +761 -0
- opentrons/protocol_engine/__init__.py +155 -0
- opentrons/protocol_engine/actions/__init__.py +65 -0
- opentrons/protocol_engine/actions/action_dispatcher.py +30 -0
- opentrons/protocol_engine/actions/action_handler.py +13 -0
- opentrons/protocol_engine/actions/actions.py +302 -0
- opentrons/protocol_engine/actions/get_state_update.py +38 -0
- opentrons/protocol_engine/clients/__init__.py +5 -0
- opentrons/protocol_engine/clients/sync_client.py +174 -0
- opentrons/protocol_engine/clients/transports.py +197 -0
- opentrons/protocol_engine/commands/__init__.py +757 -0
- opentrons/protocol_engine/commands/absorbance_reader/__init__.py +61 -0
- opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +154 -0
- opentrons/protocol_engine/commands/absorbance_reader/common.py +6 -0
- opentrons/protocol_engine/commands/absorbance_reader/initialize.py +151 -0
- opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +154 -0
- opentrons/protocol_engine/commands/absorbance_reader/read.py +226 -0
- opentrons/protocol_engine/commands/air_gap_in_place.py +162 -0
- opentrons/protocol_engine/commands/aspirate.py +244 -0
- opentrons/protocol_engine/commands/aspirate_in_place.py +184 -0
- opentrons/protocol_engine/commands/aspirate_while_tracking.py +211 -0
- opentrons/protocol_engine/commands/blow_out.py +146 -0
- opentrons/protocol_engine/commands/blow_out_in_place.py +119 -0
- opentrons/protocol_engine/commands/calibration/__init__.py +60 -0
- opentrons/protocol_engine/commands/calibration/calibrate_gripper.py +166 -0
- opentrons/protocol_engine/commands/calibration/calibrate_module.py +117 -0
- opentrons/protocol_engine/commands/calibration/calibrate_pipette.py +96 -0
- opentrons/protocol_engine/commands/calibration/move_to_maintenance_position.py +156 -0
- opentrons/protocol_engine/commands/command.py +308 -0
- opentrons/protocol_engine/commands/command_unions.py +974 -0
- opentrons/protocol_engine/commands/comment.py +57 -0
- opentrons/protocol_engine/commands/configure_for_volume.py +108 -0
- opentrons/protocol_engine/commands/configure_nozzle_layout.py +115 -0
- opentrons/protocol_engine/commands/custom.py +67 -0
- opentrons/protocol_engine/commands/dispense.py +194 -0
- opentrons/protocol_engine/commands/dispense_in_place.py +179 -0
- opentrons/protocol_engine/commands/dispense_while_tracking.py +204 -0
- opentrons/protocol_engine/commands/drop_tip.py +232 -0
- opentrons/protocol_engine/commands/drop_tip_in_place.py +205 -0
- opentrons/protocol_engine/commands/flex_stacker/__init__.py +64 -0
- opentrons/protocol_engine/commands/flex_stacker/common.py +900 -0
- opentrons/protocol_engine/commands/flex_stacker/empty.py +293 -0
- opentrons/protocol_engine/commands/flex_stacker/fill.py +281 -0
- opentrons/protocol_engine/commands/flex_stacker/retrieve.py +339 -0
- opentrons/protocol_engine/commands/flex_stacker/set_stored_labware.py +328 -0
- opentrons/protocol_engine/commands/flex_stacker/store.py +326 -0
- opentrons/protocol_engine/commands/generate_command_schema.py +61 -0
- opentrons/protocol_engine/commands/get_next_tip.py +134 -0
- opentrons/protocol_engine/commands/get_tip_presence.py +87 -0
- opentrons/protocol_engine/commands/hash_command_params.py +38 -0
- opentrons/protocol_engine/commands/heater_shaker/__init__.py +102 -0
- opentrons/protocol_engine/commands/heater_shaker/close_labware_latch.py +83 -0
- opentrons/protocol_engine/commands/heater_shaker/deactivate_heater.py +82 -0
- opentrons/protocol_engine/commands/heater_shaker/deactivate_shaker.py +84 -0
- opentrons/protocol_engine/commands/heater_shaker/open_labware_latch.py +110 -0
- opentrons/protocol_engine/commands/heater_shaker/set_and_wait_for_shake_speed.py +125 -0
- opentrons/protocol_engine/commands/heater_shaker/set_target_temperature.py +90 -0
- opentrons/protocol_engine/commands/heater_shaker/wait_for_temperature.py +102 -0
- opentrons/protocol_engine/commands/home.py +100 -0
- opentrons/protocol_engine/commands/identify_module.py +86 -0
- opentrons/protocol_engine/commands/labware_handling_common.py +29 -0
- opentrons/protocol_engine/commands/liquid_probe.py +464 -0
- opentrons/protocol_engine/commands/load_labware.py +210 -0
- opentrons/protocol_engine/commands/load_lid.py +154 -0
- opentrons/protocol_engine/commands/load_lid_stack.py +272 -0
- opentrons/protocol_engine/commands/load_liquid.py +95 -0
- opentrons/protocol_engine/commands/load_liquid_class.py +144 -0
- opentrons/protocol_engine/commands/load_module.py +223 -0
- opentrons/protocol_engine/commands/load_pipette.py +167 -0
- opentrons/protocol_engine/commands/magnetic_module/__init__.py +32 -0
- opentrons/protocol_engine/commands/magnetic_module/disengage.py +97 -0
- opentrons/protocol_engine/commands/magnetic_module/engage.py +119 -0
- opentrons/protocol_engine/commands/move_labware.py +546 -0
- opentrons/protocol_engine/commands/move_relative.py +102 -0
- opentrons/protocol_engine/commands/move_to_addressable_area.py +176 -0
- opentrons/protocol_engine/commands/move_to_addressable_area_for_drop_tip.py +198 -0
- opentrons/protocol_engine/commands/move_to_coordinates.py +107 -0
- opentrons/protocol_engine/commands/move_to_well.py +119 -0
- opentrons/protocol_engine/commands/movement_common.py +338 -0
- opentrons/protocol_engine/commands/pick_up_tip.py +241 -0
- opentrons/protocol_engine/commands/pipetting_common.py +443 -0
- opentrons/protocol_engine/commands/prepare_to_aspirate.py +121 -0
- opentrons/protocol_engine/commands/pressure_dispense.py +155 -0
- opentrons/protocol_engine/commands/reload_labware.py +90 -0
- opentrons/protocol_engine/commands/retract_axis.py +75 -0
- opentrons/protocol_engine/commands/robot/__init__.py +70 -0
- opentrons/protocol_engine/commands/robot/close_gripper_jaw.py +96 -0
- opentrons/protocol_engine/commands/robot/common.py +18 -0
- opentrons/protocol_engine/commands/robot/move_axes_relative.py +101 -0
- opentrons/protocol_engine/commands/robot/move_axes_to.py +100 -0
- opentrons/protocol_engine/commands/robot/move_to.py +94 -0
- opentrons/protocol_engine/commands/robot/open_gripper_jaw.py +86 -0
- opentrons/protocol_engine/commands/save_position.py +109 -0
- opentrons/protocol_engine/commands/seal_pipette_to_tip.py +353 -0
- opentrons/protocol_engine/commands/set_rail_lights.py +67 -0
- opentrons/protocol_engine/commands/set_status_bar.py +89 -0
- opentrons/protocol_engine/commands/temperature_module/__init__.py +46 -0
- opentrons/protocol_engine/commands/temperature_module/deactivate.py +86 -0
- opentrons/protocol_engine/commands/temperature_module/set_target_temperature.py +97 -0
- opentrons/protocol_engine/commands/temperature_module/wait_for_temperature.py +104 -0
- opentrons/protocol_engine/commands/thermocycler/__init__.py +152 -0
- opentrons/protocol_engine/commands/thermocycler/close_lid.py +87 -0
- opentrons/protocol_engine/commands/thermocycler/deactivate_block.py +80 -0
- opentrons/protocol_engine/commands/thermocycler/deactivate_lid.py +80 -0
- opentrons/protocol_engine/commands/thermocycler/open_lid.py +87 -0
- opentrons/protocol_engine/commands/thermocycler/run_extended_profile.py +171 -0
- opentrons/protocol_engine/commands/thermocycler/run_profile.py +124 -0
- opentrons/protocol_engine/commands/thermocycler/set_target_block_temperature.py +140 -0
- opentrons/protocol_engine/commands/thermocycler/set_target_lid_temperature.py +100 -0
- opentrons/protocol_engine/commands/thermocycler/wait_for_block_temperature.py +93 -0
- opentrons/protocol_engine/commands/thermocycler/wait_for_lid_temperature.py +89 -0
- opentrons/protocol_engine/commands/touch_tip.py +189 -0
- opentrons/protocol_engine/commands/unsafe/__init__.py +161 -0
- opentrons/protocol_engine/commands/unsafe/unsafe_blow_out_in_place.py +100 -0
- opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +121 -0
- opentrons/protocol_engine/commands/unsafe/unsafe_engage_axes.py +82 -0
- opentrons/protocol_engine/commands/unsafe/unsafe_place_labware.py +208 -0
- opentrons/protocol_engine/commands/unsafe/unsafe_stacker_close_latch.py +94 -0
- opentrons/protocol_engine/commands/unsafe/unsafe_stacker_manual_retrieve.py +295 -0
- opentrons/protocol_engine/commands/unsafe/unsafe_stacker_open_latch.py +91 -0
- opentrons/protocol_engine/commands/unsafe/unsafe_stacker_prepare_shuttle.py +136 -0
- opentrons/protocol_engine/commands/unsafe/unsafe_ungrip_labware.py +77 -0
- opentrons/protocol_engine/commands/unsafe/update_position_estimators.py +90 -0
- opentrons/protocol_engine/commands/unseal_pipette_from_tip.py +153 -0
- opentrons/protocol_engine/commands/verify_tip_presence.py +100 -0
- opentrons/protocol_engine/commands/wait_for_duration.py +76 -0
- opentrons/protocol_engine/commands/wait_for_resume.py +75 -0
- opentrons/protocol_engine/create_protocol_engine.py +193 -0
- opentrons/protocol_engine/engine_support.py +28 -0
- opentrons/protocol_engine/error_recovery_policy.py +81 -0
- opentrons/protocol_engine/errors/__init__.py +191 -0
- opentrons/protocol_engine/errors/error_occurrence.py +182 -0
- opentrons/protocol_engine/errors/exceptions.py +1308 -0
- opentrons/protocol_engine/execution/__init__.py +50 -0
- opentrons/protocol_engine/execution/command_executor.py +216 -0
- opentrons/protocol_engine/execution/create_queue_worker.py +102 -0
- opentrons/protocol_engine/execution/door_watcher.py +119 -0
- opentrons/protocol_engine/execution/equipment.py +819 -0
- opentrons/protocol_engine/execution/error_recovery_hardware_state_synchronizer.py +101 -0
- opentrons/protocol_engine/execution/gantry_mover.py +686 -0
- opentrons/protocol_engine/execution/hardware_stopper.py +147 -0
- opentrons/protocol_engine/execution/heater_shaker_movement_flagger.py +207 -0
- opentrons/protocol_engine/execution/labware_movement.py +297 -0
- opentrons/protocol_engine/execution/movement.py +349 -0
- opentrons/protocol_engine/execution/pipetting.py +607 -0
- opentrons/protocol_engine/execution/queue_worker.py +86 -0
- opentrons/protocol_engine/execution/rail_lights.py +25 -0
- opentrons/protocol_engine/execution/run_control.py +33 -0
- opentrons/protocol_engine/execution/status_bar.py +34 -0
- opentrons/protocol_engine/execution/thermocycler_movement_flagger.py +188 -0
- opentrons/protocol_engine/execution/thermocycler_plate_lifter.py +81 -0
- opentrons/protocol_engine/execution/tip_handler.py +550 -0
- opentrons/protocol_engine/labware_offset_standardization.py +194 -0
- opentrons/protocol_engine/notes/__init__.py +17 -0
- opentrons/protocol_engine/notes/notes.py +59 -0
- opentrons/protocol_engine/plugins.py +104 -0
- opentrons/protocol_engine/protocol_engine.py +683 -0
- opentrons/protocol_engine/resources/__init__.py +26 -0
- opentrons/protocol_engine/resources/deck_configuration_provider.py +232 -0
- opentrons/protocol_engine/resources/deck_data_provider.py +94 -0
- opentrons/protocol_engine/resources/file_provider.py +161 -0
- opentrons/protocol_engine/resources/fixture_validation.py +58 -0
- opentrons/protocol_engine/resources/labware_data_provider.py +106 -0
- opentrons/protocol_engine/resources/labware_validation.py +73 -0
- opentrons/protocol_engine/resources/model_utils.py +32 -0
- opentrons/protocol_engine/resources/module_data_provider.py +44 -0
- opentrons/protocol_engine/resources/ot3_validation.py +21 -0
- opentrons/protocol_engine/resources/pipette_data_provider.py +379 -0
- opentrons/protocol_engine/slot_standardization.py +128 -0
- opentrons/protocol_engine/state/__init__.py +1 -0
- opentrons/protocol_engine/state/_abstract_store.py +27 -0
- opentrons/protocol_engine/state/_axis_aligned_bounding_box.py +50 -0
- opentrons/protocol_engine/state/_labware_origin_math.py +636 -0
- opentrons/protocol_engine/state/_move_types.py +83 -0
- opentrons/protocol_engine/state/_well_math.py +193 -0
- opentrons/protocol_engine/state/addressable_areas.py +699 -0
- opentrons/protocol_engine/state/command_history.py +309 -0
- opentrons/protocol_engine/state/commands.py +1158 -0
- opentrons/protocol_engine/state/config.py +39 -0
- opentrons/protocol_engine/state/files.py +57 -0
- opentrons/protocol_engine/state/fluid_stack.py +138 -0
- opentrons/protocol_engine/state/geometry.py +2359 -0
- opentrons/protocol_engine/state/inner_well_math_utils.py +548 -0
- opentrons/protocol_engine/state/labware.py +1459 -0
- opentrons/protocol_engine/state/liquid_classes.py +82 -0
- opentrons/protocol_engine/state/liquids.py +73 -0
- opentrons/protocol_engine/state/module_substates/__init__.py +45 -0
- opentrons/protocol_engine/state/module_substates/absorbance_reader_substate.py +35 -0
- opentrons/protocol_engine/state/module_substates/flex_stacker_substate.py +112 -0
- opentrons/protocol_engine/state/module_substates/heater_shaker_module_substate.py +115 -0
- opentrons/protocol_engine/state/module_substates/magnetic_block_substate.py +17 -0
- opentrons/protocol_engine/state/module_substates/magnetic_module_substate.py +65 -0
- opentrons/protocol_engine/state/module_substates/temperature_module_substate.py +67 -0
- opentrons/protocol_engine/state/module_substates/thermocycler_module_substate.py +163 -0
- opentrons/protocol_engine/state/modules.py +1500 -0
- opentrons/protocol_engine/state/motion.py +373 -0
- opentrons/protocol_engine/state/pipettes.py +905 -0
- opentrons/protocol_engine/state/state.py +421 -0
- opentrons/protocol_engine/state/state_summary.py +36 -0
- opentrons/protocol_engine/state/tips.py +420 -0
- opentrons/protocol_engine/state/update_types.py +904 -0
- opentrons/protocol_engine/state/wells.py +290 -0
- opentrons/protocol_engine/types/__init__.py +308 -0
- opentrons/protocol_engine/types/automatic_tip_selection.py +39 -0
- opentrons/protocol_engine/types/command_annotations.py +53 -0
- opentrons/protocol_engine/types/deck_configuration.py +81 -0
- opentrons/protocol_engine/types/execution.py +96 -0
- opentrons/protocol_engine/types/hardware_passthrough.py +25 -0
- opentrons/protocol_engine/types/instrument.py +47 -0
- opentrons/protocol_engine/types/instrument_sensors.py +47 -0
- opentrons/protocol_engine/types/labware.py +131 -0
- opentrons/protocol_engine/types/labware_movement.py +22 -0
- opentrons/protocol_engine/types/labware_offset_location.py +111 -0
- opentrons/protocol_engine/types/labware_offset_vector.py +16 -0
- opentrons/protocol_engine/types/liquid.py +40 -0
- opentrons/protocol_engine/types/liquid_class.py +59 -0
- opentrons/protocol_engine/types/liquid_handling.py +13 -0
- opentrons/protocol_engine/types/liquid_level_detection.py +191 -0
- opentrons/protocol_engine/types/location.py +194 -0
- opentrons/protocol_engine/types/module.py +303 -0
- opentrons/protocol_engine/types/partial_tip_configuration.py +76 -0
- opentrons/protocol_engine/types/run_time_parameters.py +133 -0
- opentrons/protocol_engine/types/tip.py +18 -0
- opentrons/protocol_engine/types/util.py +21 -0
- opentrons/protocol_engine/types/well_position.py +124 -0
- opentrons/protocol_reader/__init__.py +37 -0
- opentrons/protocol_reader/extract_labware_definitions.py +66 -0
- opentrons/protocol_reader/file_format_validator.py +152 -0
- opentrons/protocol_reader/file_hasher.py +27 -0
- opentrons/protocol_reader/file_identifier.py +284 -0
- opentrons/protocol_reader/file_reader_writer.py +90 -0
- opentrons/protocol_reader/input_file.py +16 -0
- opentrons/protocol_reader/protocol_files_invalid_error.py +6 -0
- opentrons/protocol_reader/protocol_reader.py +188 -0
- opentrons/protocol_reader/protocol_source.py +124 -0
- opentrons/protocol_reader/role_analyzer.py +86 -0
- opentrons/protocol_runner/__init__.py +26 -0
- opentrons/protocol_runner/create_simulating_orchestrator.py +118 -0
- opentrons/protocol_runner/json_file_reader.py +55 -0
- opentrons/protocol_runner/json_translator.py +314 -0
- opentrons/protocol_runner/legacy_command_mapper.py +848 -0
- opentrons/protocol_runner/legacy_context_plugin.py +116 -0
- opentrons/protocol_runner/protocol_runner.py +530 -0
- opentrons/protocol_runner/python_protocol_wrappers.py +179 -0
- opentrons/protocol_runner/run_orchestrator.py +496 -0
- opentrons/protocol_runner/task_queue.py +95 -0
- opentrons/protocols/__init__.py +6 -0
- opentrons/protocols/advanced_control/__init__.py +0 -0
- opentrons/protocols/advanced_control/common.py +38 -0
- opentrons/protocols/advanced_control/mix.py +60 -0
- opentrons/protocols/advanced_control/transfers/__init__.py +0 -0
- opentrons/protocols/advanced_control/transfers/common.py +180 -0
- opentrons/protocols/advanced_control/transfers/transfer.py +972 -0
- opentrons/protocols/advanced_control/transfers/transfer_liquid_utils.py +231 -0
- opentrons/protocols/api_support/__init__.py +0 -0
- opentrons/protocols/api_support/constants.py +8 -0
- opentrons/protocols/api_support/deck_type.py +110 -0
- opentrons/protocols/api_support/definitions.py +18 -0
- opentrons/protocols/api_support/instrument.py +151 -0
- opentrons/protocols/api_support/labware_like.py +233 -0
- opentrons/protocols/api_support/tip_tracker.py +175 -0
- opentrons/protocols/api_support/types.py +32 -0
- opentrons/protocols/api_support/util.py +403 -0
- opentrons/protocols/bundle.py +89 -0
- opentrons/protocols/duration/__init__.py +4 -0
- opentrons/protocols/duration/errors.py +5 -0
- opentrons/protocols/duration/estimator.py +628 -0
- opentrons/protocols/execution/__init__.py +0 -0
- opentrons/protocols/execution/dev_types.py +181 -0
- opentrons/protocols/execution/errors.py +40 -0
- opentrons/protocols/execution/execute.py +84 -0
- opentrons/protocols/execution/execute_json_v3.py +275 -0
- opentrons/protocols/execution/execute_json_v4.py +359 -0
- opentrons/protocols/execution/execute_json_v5.py +28 -0
- opentrons/protocols/execution/execute_python.py +169 -0
- opentrons/protocols/execution/json_dispatchers.py +87 -0
- opentrons/protocols/execution/types.py +7 -0
- opentrons/protocols/geometry/__init__.py +0 -0
- opentrons/protocols/geometry/planning.py +297 -0
- opentrons/protocols/labware.py +312 -0
- opentrons/protocols/models/__init__.py +0 -0
- opentrons/protocols/models/json_protocol.py +679 -0
- opentrons/protocols/parameters/__init__.py +0 -0
- opentrons/protocols/parameters/csv_parameter_definition.py +77 -0
- opentrons/protocols/parameters/csv_parameter_interface.py +96 -0
- opentrons/protocols/parameters/exceptions.py +34 -0
- opentrons/protocols/parameters/parameter_definition.py +272 -0
- opentrons/protocols/parameters/types.py +17 -0
- opentrons/protocols/parameters/validation.py +267 -0
- opentrons/protocols/parse.py +671 -0
- opentrons/protocols/types.py +159 -0
- opentrons/py.typed +0 -0
- opentrons/resources/scripts/lpc21isp +0 -0
- opentrons/resources/smoothie-edge-8414642.hex +23010 -0
- opentrons/simulate.py +1065 -0
- opentrons/system/__init__.py +6 -0
- opentrons/system/camera.py +51 -0
- opentrons/system/log_control.py +59 -0
- opentrons/system/nmcli.py +856 -0
- opentrons/system/resin.py +24 -0
- opentrons/system/smoothie_update.py +15 -0
- opentrons/system/wifi.py +204 -0
- opentrons/tools/__init__.py +0 -0
- opentrons/tools/args_handler.py +22 -0
- opentrons/tools/write_pipette_memory.py +157 -0
- opentrons/types.py +618 -0
- opentrons/util/__init__.py +1 -0
- opentrons/util/async_helpers.py +166 -0
- opentrons/util/broker.py +84 -0
- opentrons/util/change_notifier.py +47 -0
- opentrons/util/entrypoint_util.py +278 -0
- opentrons/util/get_union_elements.py +26 -0
- opentrons/util/helpers.py +6 -0
- opentrons/util/linal.py +178 -0
- opentrons/util/logging_config.py +265 -0
- opentrons/util/logging_queue_handler.py +61 -0
- opentrons/util/performance_helpers.py +157 -0
- opentrons-8.6.0a1.dist-info/METADATA +37 -0
- opentrons-8.6.0a1.dist-info/RECORD +600 -0
- opentrons-8.6.0a1.dist-info/WHEEL +4 -0
- opentrons-8.6.0a1.dist-info/entry_points.txt +3 -0
- opentrons-8.6.0a1.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,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)
|