opentrons 8.4.0a2__py2.py3-none-any.whl → 8.4.0a4__py2.py3-none-any.whl

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

Potentially problematic release.


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

Files changed (45) hide show
  1. opentrons/legacy_commands/commands.py +83 -2
  2. opentrons/legacy_commands/helpers.py +59 -1
  3. opentrons/legacy_commands/types.py +30 -0
  4. opentrons/protocol_api/core/engine/instrument.py +158 -87
  5. opentrons/protocol_api/core/engine/pipette_movement_conflict.py +6 -14
  6. opentrons/protocol_api/core/engine/transfer_components_executor.py +12 -23
  7. opentrons/protocol_api/core/instrument.py +7 -4
  8. opentrons/protocol_api/core/legacy/legacy_instrument_core.py +7 -30
  9. opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +7 -4
  10. opentrons/protocol_api/core/well.py +1 -1
  11. opentrons/protocol_api/instrument_context.py +189 -75
  12. opentrons/protocol_api/labware.py +7 -6
  13. opentrons/protocol_api/protocol_context.py +18 -16
  14. opentrons/protocol_engine/commands/__init__.py +38 -38
  15. opentrons/protocol_engine/commands/aspirate_while_tracking.py +0 -6
  16. opentrons/protocol_engine/commands/command_unions.py +33 -33
  17. opentrons/protocol_engine/commands/dispense_while_tracking.py +1 -6
  18. opentrons/protocol_engine/commands/flex_stacker/empty.py +6 -6
  19. opentrons/protocol_engine/commands/flex_stacker/fill.py +6 -6
  20. opentrons/protocol_engine/commands/flex_stacker/retrieve.py +6 -6
  21. opentrons/protocol_engine/commands/flex_stacker/set_stored_labware.py +9 -9
  22. opentrons/protocol_engine/commands/flex_stacker/store.py +16 -13
  23. opentrons/protocol_engine/commands/labware_handling_common.py +6 -1
  24. opentrons/protocol_engine/commands/liquid_probe.py +1 -2
  25. opentrons/protocol_engine/commands/move_to_well.py +5 -11
  26. opentrons/protocol_engine/commands/{evotip_dispense.py → pressure_dispense.py} +27 -27
  27. opentrons/protocol_engine/commands/{evotip_seal_pipette.py → seal_pipette_to_tip.py} +32 -27
  28. opentrons/protocol_engine/commands/{evotip_unseal_pipette.py → unseal_pipette_from_tip.py} +22 -22
  29. opentrons/protocol_engine/labware_offset_standardization.py +22 -1
  30. opentrons/protocol_engine/resources/deck_configuration_provider.py +8 -4
  31. opentrons/protocol_engine/state/frustum_helpers.py +12 -4
  32. opentrons/protocol_engine/state/geometry.py +122 -73
  33. opentrons/protocol_engine/state/update_types.py +1 -1
  34. opentrons/protocol_engine/state/wells.py +1 -1
  35. opentrons/protocol_engine/types/__init__.py +6 -0
  36. opentrons/protocol_engine/types/location.py +2 -1
  37. opentrons/protocol_engine/types/well_position.py +18 -1
  38. opentrons/protocols/advanced_control/transfers/transfer_liquid_utils.py +1 -1
  39. opentrons/protocols/labware.py +23 -18
  40. {opentrons-8.4.0a2.dist-info → opentrons-8.4.0a4.dist-info}/METADATA +4 -4
  41. {opentrons-8.4.0a2.dist-info → opentrons-8.4.0a4.dist-info}/RECORD +45 -45
  42. {opentrons-8.4.0a2.dist-info → opentrons-8.4.0a4.dist-info}/LICENSE +0 -0
  43. {opentrons-8.4.0a2.dist-info → opentrons-8.4.0a4.dist-info}/WHEEL +0 -0
  44. {opentrons-8.4.0a2.dist-info → opentrons-8.4.0a4.dist-info}/entry_points.txt +0 -0
  45. {opentrons-8.4.0a2.dist-info → opentrons-8.4.0a4.dist-info}/top_level.txt +0 -0
@@ -31,6 +31,7 @@ from ..errors import (
31
31
  LabwareMovementNotAllowedError,
32
32
  OperationLocationNotInWellError,
33
33
  )
34
+ from ..errors.exceptions import InvalidLiquidHeightFound
34
35
  from ..resources import (
35
36
  fixture_validation,
36
37
  labware_validation,
@@ -79,6 +80,8 @@ from ..types import (
79
80
  AreaType,
80
81
  labware_location_is_off_deck,
81
82
  labware_location_is_system,
83
+ WellLocationType,
84
+ WellLocationFunction,
82
85
  )
83
86
  from ..types.liquid_level_detection import SimulatedProbeResult, LiquidTrackingType
84
87
  from .config import Config
@@ -310,7 +313,6 @@ class GeometryView:
310
313
  child_definition=self._labware.get_definition(labware_id),
311
314
  parent=self._labware.get(labware_id).location,
312
315
  )
313
-
314
316
  return Point(
315
317
  parent_pos.x + offset_from_parent.x,
316
318
  parent_pos.y + offset_from_parent.y,
@@ -455,20 +457,15 @@ class GeometryView:
455
457
  """Get the calibrated origin of the labware."""
456
458
  origin_pos = self.get_labware_origin_position(labware_id)
457
459
  cal_offset = self._labware.get_labware_offset_vector(labware_id)
458
-
459
460
  return Point(
460
461
  x=origin_pos.x + cal_offset.x,
461
462
  y=origin_pos.y + cal_offset.y,
462
463
  z=origin_pos.z + cal_offset.z,
463
464
  )
464
465
 
465
- WellLocations = Union[
466
- WellLocation, LiquidHandlingWellLocation, PickUpTipWellLocation
467
- ]
468
-
469
466
  def validate_well_position(
470
467
  self,
471
- well_location: WellLocations,
468
+ well_location: WellLocationType,
472
469
  z_offset: float,
473
470
  pipette_id: Optional[str] = None,
474
471
  ) -> None:
@@ -477,34 +474,34 @@ class GeometryView:
477
474
  Primarily this checks if there is not enough liquid in a well to do meniscus-relative static aspiration.
478
475
  """
479
476
  if well_location.origin == WellOrigin.MENISCUS:
480
- assert pipette_id is not None
477
+ assert pipette_id is not None, "pipette id is None"
481
478
  lld_min_height = self._pipettes.get_current_tip_lld_settings(
482
479
  pipette_id=pipette_id
483
480
  )
484
481
  if z_offset < lld_min_height:
485
- if isinstance(well_location, PickUpTipWellLocation):
482
+ if isinstance(well_location, LiquidHandlingWellLocation):
486
483
  raise OperationLocationNotInWellError(
487
- f"Specifying {well_location.origin} with an offset of {well_location.offset} results in an operation location that could be below the bottom of the well"
484
+ f"Specifying {well_location.origin} with a height offset of {well_location.offset.z} results in a height of {z_offset} mm; the minimum allowed height for liquid tracking is {lld_min_height} mm"
488
485
  )
489
486
  else:
490
487
  raise OperationLocationNotInWellError(
491
- f"Specifying {well_location.origin} with an offset of {well_location.offset} and a volume offset of {well_location.volumeOffset} results in an operation location that could be below the bottom of the well"
488
+ f"Specifying {well_location.origin} with an offset of {well_location.offset} results in an operation location that could be below the bottom of the well"
492
489
  )
493
490
  elif z_offset < 0:
494
- if isinstance(well_location, PickUpTipWellLocation):
491
+ if isinstance(well_location, LiquidHandlingWellLocation):
495
492
  raise OperationLocationNotInWellError(
496
- f"Specifying {well_location.origin} with an offset of {well_location.offset} results in an operation location below the bottom of the well"
493
+ f"Specifying {well_location.origin} with an offset of {well_location.offset} and a volume offset of {well_location.volumeOffset} results in an operation location below the bottom of the well"
497
494
  )
498
495
  else:
499
496
  raise OperationLocationNotInWellError(
500
- f"Specifying {well_location.origin} with an offset of {well_location.offset} and a volume offset of {well_location.volumeOffset} results in an operation location below the bottom of the well"
497
+ f"Specifying {well_location.origin} with an offset of {well_location.offset} results in an operation location below the bottom of the well"
501
498
  )
502
499
 
503
500
  def get_well_position(
504
501
  self,
505
502
  labware_id: str,
506
503
  well_name: str,
507
- well_location: Optional[WellLocations] = None,
504
+ well_location: Optional[WellLocationType] = None,
508
505
  operation_volume: Optional[float] = None,
509
506
  pipette_id: Optional[str] = None,
510
507
  ) -> Point:
@@ -530,7 +527,6 @@ class GeometryView:
530
527
  z_offset=offset.z,
531
528
  pipette_id=pipette_id,
532
529
  )
533
-
534
530
  return Point(
535
531
  x=labware_pos.x + offset.x + well_def.x,
536
532
  y=labware_pos.y + offset.y + well_def.y,
@@ -552,25 +548,14 @@ class GeometryView:
552
548
  z=parent_pos.z + origin_offset.z + well_def.z + well_def.depth,
553
549
  )
554
550
 
555
- def get_relative_well_location(
556
- self,
557
- labware_id: str,
558
- well_name: str,
559
- absolute_point: Point,
560
- ) -> WellLocation:
561
- """Given absolute position, get relative location of a well in a labware."""
562
- well_absolute_point = self.get_well_position(labware_id, well_name)
563
- delta = absolute_point - well_absolute_point
564
-
565
- return WellLocation(offset=WellOffset(x=delta.x, y=delta.y, z=delta.z))
566
-
567
- def get_relative_liquid_handling_well_location(
551
+ def _get_relative_liquid_handling_well_location(
568
552
  self,
569
553
  labware_id: str,
570
554
  well_name: str,
571
555
  absolute_point: Point,
556
+ delta: Point,
572
557
  meniscus_tracking: Optional[MeniscusTrackingTarget] = None,
573
- ) -> Tuple[LiquidHandlingWellLocation, bool]:
558
+ ) -> Tuple[WellLocationType, bool]:
574
559
  """Given absolute position, get relative location of a well in a labware."""
575
560
  dynamic_liquid_tracking = False
576
561
  if meniscus_tracking:
@@ -578,29 +563,50 @@ class GeometryView:
578
563
  origin=WellOrigin.MENISCUS,
579
564
  offset=WellOffset(x=0, y=0, z=absolute_point.z),
580
565
  )
566
+ # TODO(cm): handle operationVolume being a float other than 0
581
567
  if meniscus_tracking == MeniscusTrackingTarget.END:
582
568
  location.volumeOffset = "operationVolume"
583
569
  elif meniscus_tracking == MeniscusTrackingTarget.DYNAMIC:
584
570
  dynamic_liquid_tracking = True
585
571
  else:
586
- well_absolute_point = self.get_well_position(labware_id, well_name)
587
- delta = absolute_point - well_absolute_point
588
572
  location = LiquidHandlingWellLocation(
589
573
  offset=WellOffset(x=delta.x, y=delta.y, z=delta.z)
590
574
  )
591
575
  return location, dynamic_liquid_tracking
592
576
 
593
- def get_relative_pick_up_tip_well_location(
577
+ def get_relative_well_location(
594
578
  self,
595
579
  labware_id: str,
596
580
  well_name: str,
597
581
  absolute_point: Point,
598
- ) -> PickUpTipWellLocation:
582
+ location_type: WellLocationFunction,
583
+ meniscus_tracking: Optional[MeniscusTrackingTarget] = None,
584
+ ) -> Tuple[WellLocationType, bool]:
599
585
  """Given absolute position, get relative location of a well in a labware."""
600
586
  well_absolute_point = self.get_well_position(labware_id, well_name)
601
587
  delta = absolute_point - well_absolute_point
602
-
603
- return PickUpTipWellLocation(offset=WellOffset(x=delta.x, y=delta.y, z=delta.z))
588
+ match location_type:
589
+ case WellLocationFunction.BASE | WellLocationFunction.DROP_TIP:
590
+ return (
591
+ WellLocation(offset=WellOffset(x=delta.x, y=delta.y, z=delta.z)),
592
+ False,
593
+ )
594
+ case WellLocationFunction.PICK_UP_TIP:
595
+ return (
596
+ PickUpTipWellLocation(
597
+ offset=WellOffset(x=delta.x, y=delta.y, z=delta.z)
598
+ ),
599
+ False,
600
+ )
601
+ case WellLocationFunction.LIQUID_HANDLING:
602
+ return self._get_relative_liquid_handling_well_location(
603
+ labware_id=labware_id,
604
+ well_name=well_name,
605
+ absolute_point=absolute_point,
606
+ delta=delta,
607
+ meniscus_tracking=meniscus_tracking,
608
+ )
609
+ return NotImplemented
604
610
 
605
611
  def get_well_height(
606
612
  self,
@@ -761,7 +767,6 @@ class GeometryView:
761
767
  """Get the slot name of the labware or the module that the labware is on."""
762
768
  labware = self._labware.get(labware_id)
763
769
  slot_name: Union[DeckSlotName, StagingSlotName]
764
-
765
770
  if isinstance(labware.location, DeckSlotLocation):
766
771
  slot_name = labware.location.slotName
767
772
  elif isinstance(labware.location, ModuleLocation):
@@ -1748,7 +1753,13 @@ class GeometryView:
1748
1753
  labware_location: LabwareLocation,
1749
1754
  labware_pending_load: dict[str, LoadedLabware] | None = None,
1750
1755
  ) -> Optional[LabwareOffsetLocationSequence]:
1751
- """Get the offset location that a labware loaded into this location would match."""
1756
+ """Get the offset location that a labware loaded into this location would match.
1757
+
1758
+ `None` indicates that the very concept of a labware offset would not make sense
1759
+ for the given location, such as if it's some kind of off-deck location. This
1760
+ is a difference from `get_predicted_location_sequence()`, where off-deck
1761
+ locations are still represented as lists, but with special final elements.
1762
+ """
1752
1763
  return self._recurse_labware_offset_location(
1753
1764
  labware_location, [], labware_pending_load or {}
1754
1765
  )
@@ -1847,15 +1858,19 @@ class GeometryView:
1847
1858
  # this function is only called by
1848
1859
  # HardwarePipetteHandler::aspirate/dispense while_tracking, and shouldn't
1849
1860
  # be reached in the case of a simulated liquid_probe
1850
- assert not isinstance(initial_handling_height, SimulatedProbeResult)
1851
- assert not isinstance(final_height, SimulatedProbeResult)
1861
+ assert not isinstance(
1862
+ initial_handling_height, SimulatedProbeResult
1863
+ ), "Initial handling height got SimulatedProbeResult"
1864
+ assert not isinstance(
1865
+ final_height, SimulatedProbeResult
1866
+ ), "final height is SimulatedProbeResult"
1852
1867
  return final_height - initial_handling_height
1853
1868
 
1854
1869
  def get_well_offset_adjustment(
1855
1870
  self,
1856
1871
  labware_id: str,
1857
1872
  well_name: str,
1858
- well_location: WellLocations,
1873
+ well_location: WellLocationType,
1859
1874
  well_depth: float,
1860
1875
  operation_volume: Optional[float] = None,
1861
1876
  ) -> LiquidTrackingType:
@@ -1878,12 +1893,16 @@ class GeometryView:
1878
1893
  and not well_location.volumeOffset
1879
1894
  ):
1880
1895
  return initial_handling_height
1896
+ volume: Optional[float] = None
1881
1897
  if isinstance(well_location, PickUpTipWellLocation):
1882
1898
  volume = 0.0
1883
- elif isinstance(well_location.volumeOffset, float):
1884
- volume = well_location.volumeOffset
1885
- elif well_location.volumeOffset == "operationVolume":
1886
- volume = operation_volume or 0.0
1899
+ elif isinstance(well_location, LiquidHandlingWellLocation):
1900
+ if well_location.volumeOffset == "operationVolume":
1901
+ volume = operation_volume or 0.0
1902
+ else:
1903
+ if not isinstance(well_location.volumeOffset, float):
1904
+ raise ValueError("Invalid volume offset.")
1905
+ volume = well_location.volumeOffset
1887
1906
 
1888
1907
  if volume:
1889
1908
  liquid_height_after = self.get_well_height_after_liquid_handling(
@@ -1991,7 +2010,7 @@ class GeometryView:
1991
2010
  self,
1992
2011
  labware_id: str,
1993
2012
  well_name: str,
1994
- well_location: WellLocations,
2013
+ well_location: WellLocationType,
1995
2014
  well_depth: float,
1996
2015
  ) -> LiquidTrackingType:
1997
2016
  """Return the handling height for a labware well (with reference to the well bottom)."""
@@ -2020,13 +2039,19 @@ class GeometryView:
2020
2039
  well_geometry = self._labware.get_well_geometry(
2021
2040
  labware_id=labware_id, well_name=well_name
2022
2041
  )
2023
- initial_volume = find_volume_at_well_height(
2024
- target_height=initial_height, well_geometry=well_geometry
2025
- )
2026
- final_volume = initial_volume + volume
2027
- return find_height_at_well_volume(
2028
- target_volume=final_volume, well_geometry=well_geometry
2029
- )
2042
+ try:
2043
+ initial_volume = find_volume_at_well_height(
2044
+ target_height=initial_height, well_geometry=well_geometry
2045
+ )
2046
+ final_volume = initial_volume + volume
2047
+ return find_height_at_well_volume(
2048
+ target_volume=final_volume, well_geometry=well_geometry
2049
+ )
2050
+ except InvalidLiquidHeightFound as _exception:
2051
+ raise InvalidLiquidHeightFound(
2052
+ message=_exception.message
2053
+ + f"for well {well_name} of {self._labware.get_display_name(labware_id)} on slot {self.get_ancestor_slot_name(labware_id)}"
2054
+ )
2030
2055
 
2031
2056
  def get_well_height_after_liquid_handling_no_error(
2032
2057
  self,
@@ -2043,25 +2068,37 @@ class GeometryView:
2043
2068
  well_geometry = self._labware.get_well_geometry(
2044
2069
  labware_id=labware_id, well_name=well_name
2045
2070
  )
2046
- initial_volume = find_volume_at_well_height(
2047
- target_height=initial_height, well_geometry=well_geometry
2048
- )
2049
- final_volume = initial_volume + volume
2050
- well_volume = find_height_at_well_volume(
2051
- target_volume=final_volume,
2052
- well_geometry=well_geometry,
2053
- raise_error_if_result_invalid=False,
2054
- )
2055
- return well_volume
2071
+ try:
2072
+ initial_volume = find_volume_at_well_height(
2073
+ target_height=initial_height, well_geometry=well_geometry
2074
+ )
2075
+ final_volume = initial_volume + volume
2076
+ well_volume = find_height_at_well_volume(
2077
+ target_volume=final_volume,
2078
+ well_geometry=well_geometry,
2079
+ raise_error_if_result_invalid=False,
2080
+ )
2081
+ return well_volume
2082
+ except InvalidLiquidHeightFound as _exception:
2083
+ raise InvalidLiquidHeightFound(
2084
+ message=_exception.message
2085
+ + f"for well {well_name} of {self._labware.get_display_name(labware_id)} on slot {self.get_ancestor_slot_name(labware_id)}"
2086
+ )
2056
2087
 
2057
2088
  def get_well_height_at_volume(
2058
2089
  self, labware_id: str, well_name: str, volume: LiquidTrackingType
2059
2090
  ) -> LiquidTrackingType:
2060
2091
  """Convert well volume to height."""
2061
2092
  well_geometry = self._labware.get_well_geometry(labware_id, well_name)
2062
- return find_height_at_well_volume(
2063
- target_volume=volume, well_geometry=well_geometry
2064
- )
2093
+ try:
2094
+ return find_height_at_well_volume(
2095
+ target_volume=volume, well_geometry=well_geometry
2096
+ )
2097
+ except InvalidLiquidHeightFound as _exception:
2098
+ raise InvalidLiquidHeightFound(
2099
+ message=_exception.message
2100
+ + f"for well {well_name} of {self._labware.get_display_name(labware_id)} on slot {self.get_ancestor_slot_name(labware_id)}"
2101
+ )
2065
2102
 
2066
2103
  def get_well_volume_at_height(
2067
2104
  self,
@@ -2071,15 +2108,21 @@ class GeometryView:
2071
2108
  ) -> LiquidTrackingType:
2072
2109
  """Convert well height to volume."""
2073
2110
  well_geometry = self._labware.get_well_geometry(labware_id, well_name)
2074
- return find_volume_at_well_height(
2075
- target_height=height, well_geometry=well_geometry
2076
- )
2111
+ try:
2112
+ return find_volume_at_well_height(
2113
+ target_height=height, well_geometry=well_geometry
2114
+ )
2115
+ except InvalidLiquidHeightFound as _exception:
2116
+ raise InvalidLiquidHeightFound(
2117
+ message=_exception.message
2118
+ + f"for well {well_name} of {self._labware.get_display_name(labware_id)} on slot {self.get_ancestor_slot_name(labware_id)}"
2119
+ )
2077
2120
 
2078
2121
  def validate_dispense_volume_into_well(
2079
2122
  self,
2080
2123
  labware_id: str,
2081
2124
  well_name: str,
2082
- well_location: WellLocations,
2125
+ well_location: WellLocationType,
2083
2126
  volume: float,
2084
2127
  ) -> None:
2085
2128
  """Raise InvalidDispenseVolumeError if planned dispense volume will overflow well."""
@@ -2091,9 +2134,15 @@ class GeometryView:
2091
2134
  meniscus_height = self.get_meniscus_height(
2092
2135
  labware_id=labware_id, well_name=well_name
2093
2136
  )
2094
- meniscus_volume = find_volume_at_well_height(
2095
- target_height=meniscus_height, well_geometry=well_geometry
2096
- )
2137
+ try:
2138
+ meniscus_volume = find_volume_at_well_height(
2139
+ target_height=meniscus_height, well_geometry=well_geometry
2140
+ )
2141
+ except InvalidLiquidHeightFound as _exception:
2142
+ raise InvalidLiquidHeightFound(
2143
+ message=_exception.message
2144
+ + f"for well {well_name} of {self._labware.get_display_name(labware_id)} on slot {self.get_ancestor_slot_name(labware_id)}"
2145
+ )
2097
2146
  # if meniscus volume is a simulated value, comparisons aren't meaningful
2098
2147
  if isinstance(meniscus_volume, SimulatedProbeResult):
2099
2148
  return
@@ -18,8 +18,8 @@ from opentrons.protocol_engine.types import (
18
18
  AspiratedFluid,
19
19
  LiquidClassRecord,
20
20
  ABSMeasureMode,
21
+ LiquidTrackingType,
21
22
  )
22
- from opentrons.protocol_engine.types.liquid_level_detection import LiquidTrackingType
23
23
  from opentrons.types import MountType
24
24
  from opentrons_shared_data.labware.labware_definition import LabwareDefinition
25
25
  from opentrons_shared_data.pipette.types import PipetteNameType
@@ -116,7 +116,7 @@ class WellStore(HasState[WellState], HandlesActions):
116
116
  del self._state.loaded_volumes[labware_id][well_name]
117
117
  else:
118
118
  prev_loaded_vol_info = self._state.loaded_volumes[labware_id][well_name]
119
- assert prev_loaded_vol_info.volume is not None
119
+ assert prev_loaded_vol_info.volume is not None, "volume info not loaded"
120
120
  self._state.loaded_volumes[labware_id][well_name] = LoadedVolumeInfo(
121
121
  volume=prev_loaded_vol_info.volume + volume_added,
122
122
  last_loaded=prev_loaded_vol_info.last_loaded,
@@ -117,6 +117,8 @@ from .well_position import (
117
117
  LiquidHandlingWellLocation,
118
118
  PickUpTipWellLocation,
119
119
  DropTipWellLocation,
120
+ WellLocationType,
121
+ WellLocationFunction,
120
122
  )
121
123
  from .instrument import (
122
124
  LoadedPipette,
@@ -132,6 +134,7 @@ from .liquid_level_detection import (
132
134
  ProbedVolumeInfo,
133
135
  WellInfoSummary,
134
136
  WellLiquidInfo,
137
+ LiquidTrackingType,
135
138
  )
136
139
  from .liquid_handling import FlowRates
137
140
  from .labware_movement import LabwareMovementStrategy, LabwareMovementOffsetData
@@ -259,6 +262,8 @@ __all__ = [
259
262
  "LiquidHandlingWellLocation",
260
263
  "PickUpTipWellLocation",
261
264
  "DropTipWellLocation",
265
+ "WellLocationType",
266
+ "WellLocationFunction",
262
267
  # Execution
263
268
  "EngineStatus",
264
269
  "PostRunHardwareState",
@@ -274,6 +279,7 @@ __all__ = [
274
279
  "ProbedVolumeInfo",
275
280
  "WellInfoSummary",
276
281
  "WellLiquidInfo",
282
+ "LiquidTrackingType",
277
283
  # Liquid handling
278
284
  "FlowRates",
279
285
  # Labware movement
@@ -4,6 +4,7 @@ from __future__ import annotations
4
4
  from typing import Literal, Union, TypeGuard
5
5
 
6
6
  from pydantic import BaseModel, Field
7
+ from pydantic.json_schema import SkipJsonSchema
7
8
 
8
9
  from opentrons.types import DeckSlotName, StagingSlotName
9
10
 
@@ -107,7 +108,7 @@ class OnLabwareLocationSequenceComponent(BaseModel):
107
108
 
108
109
  kind: Literal["onLabware"] = "onLabware"
109
110
  labwareId: str
110
- lidId: str | None
111
+ lidId: str | SkipJsonSchema[None] = Field(None)
111
112
 
112
113
 
113
114
  class OnModuleLocationSequenceComponent(BaseModel):
@@ -1,5 +1,5 @@
1
1
  """Protocol engine types to do with positions inside wells."""
2
- from enum import Enum
2
+ from enum import Enum, auto
3
3
  from typing import Union, Literal
4
4
 
5
5
  from pydantic import BaseModel, Field
@@ -52,6 +52,15 @@ class DropTipWellOrigin(str, Enum):
52
52
  DEFAULT = "default"
53
53
 
54
54
 
55
+ class WellLocationFunction(int, Enum):
56
+ """The type of well location object to be created."""
57
+
58
+ BASE = auto()
59
+ LIQUID_HANDLING = auto()
60
+ PICK_UP_TIP = auto()
61
+ DROP_TIP = auto()
62
+
63
+
55
64
  # This is deliberately a separate type from Vec3f to let components default to 0.
56
65
  class WellOffset(BaseModel):
57
66
  """An offset vector in (x, y, z)."""
@@ -105,3 +114,11 @@ class DropTipWellLocation(BaseModel):
105
114
 
106
115
  origin: DropTipWellOrigin = DropTipWellOrigin.DEFAULT
107
116
  offset: WellOffset = Field(default_factory=WellOffset)
117
+
118
+
119
+ WellLocationType = Union[
120
+ WellLocation,
121
+ LiquidHandlingWellLocation,
122
+ PickUpTipWellLocation,
123
+ DropTipWellLocation,
124
+ ]
@@ -48,7 +48,7 @@ def raise_if_location_inside_liquid(
48
48
  liquid_height_from_bottom = well_core.current_liquid_height()
49
49
  except LiquidHeightUnknownError:
50
50
  liquid_height_from_bottom = None
51
- if isinstance(liquid_height_from_bottom, (int, float)):
51
+ if liquid_height_from_bottom is not None:
52
52
  if liquid_height_from_bottom + well_core.get_bottom(0).z > location.point.z:
53
53
  raise RuntimeError(
54
54
  f"{location_check_descriptors.location_type.capitalize()} location {location} is"
@@ -4,7 +4,7 @@ import logging
4
4
  import json
5
5
  import os
6
6
  from pathlib import Path
7
- from typing import Any, AnyStr, Dict, Mapping, Optional, Union, List, Sequence, Literal
7
+ from typing import Mapping, Optional, Union, List, Sequence, Literal
8
8
 
9
9
  import jsonschema # type: ignore
10
10
 
@@ -147,46 +147,51 @@ def save_definition(
147
147
 
148
148
 
149
149
  def verify_definition( # noqa: C901
150
- contents: Union[AnyStr, LabwareDefinition, Dict[str, Any]]
150
+ contents: str | bytes | LabwareDefinition | object,
151
151
  ) -> LabwareDefinition:
152
152
  """Verify that an input string is a labware definition and return it.
153
153
 
154
- If the definition is invalid, an exception is raised; otherwise parse the
155
- json and return the valid definition.
154
+ :param contents: The untrusted input to parse and validate. If str or bytes, it's
155
+ parsed as JSON. Otherwise, it should be the output of json.load().
156
156
 
157
157
  :raises NotALabwareError:
158
- :returns: The parsed definition
158
+
159
+ :returns: The parsed and validated definition
159
160
  """
160
161
  schemata_by_version = {
161
162
  2: json.loads(load_shared_data("labware/schemas/2.json").decode("utf-8")),
162
163
  3: json.loads(load_shared_data("labware/schemas/3.json").decode("utf-8")),
163
164
  }
164
165
 
165
- if isinstance(contents, dict):
166
- to_return = contents
167
- else:
168
- try:
169
- to_return = json.loads(contents)
170
- except json.JSONDecodeError as e:
171
- raise NotALabwareError("invalid-json", [e]) from e
172
166
  try:
173
- schema_version = to_return["schemaVersion"]
174
- except KeyError as e:
175
- raise NotALabwareError("no-schema-id", [e]) from e
167
+ parsed_json: object = (
168
+ json.loads(contents) if isinstance(contents, (str, bytes)) else contents
169
+ )
170
+ except json.JSONDecodeError as e:
171
+ raise NotALabwareError("invalid-json", [e]) from e
172
+
173
+ if isinstance(parsed_json, dict):
174
+ try:
175
+ schema_version: object = parsed_json["schemaVersion"]
176
+ except KeyError as e:
177
+ raise NotALabwareError("no-schema-id", [e]) from e
178
+ else:
179
+ raise NotALabwareError("no-schema-id", [])
176
180
 
177
181
  try:
178
- schema = schemata_by_version[schema_version]
182
+ # we can type ignore this because we handle the KeyError below
183
+ schema = schemata_by_version[schema_version] # type: ignore[index]
179
184
  except KeyError as e:
180
185
  raise NotALabwareError("bad-schema-id", [e]) from e
181
186
 
182
187
  try:
183
- jsonschema.validate(to_return, schema)
188
+ jsonschema.validate(parsed_json, schema)
184
189
  except jsonschema.ValidationError as e:
185
190
  raise NotALabwareError("schema-mismatch", [e]) from e
186
191
 
187
192
  # we can type ignore this because if it passes the jsonschema it has
188
193
  # the correct structure
189
- return to_return # type: ignore[return-value]
194
+ return parsed_json # type: ignore[return-value]
190
195
 
191
196
 
192
197
  def _get_labware_definition_from_bundle(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: opentrons
3
- Version: 8.4.0a2
3
+ Version: 8.4.0a4
4
4
  Summary: The Opentrons API is a simple framework designed to make writing automated biology lab protocols easy.
5
5
  Author: Opentrons
6
6
  Author-email: engineering@opentrons.com
@@ -21,7 +21,7 @@ Classifier: Programming Language :: Python :: 3.10
21
21
  Classifier: Topic :: Scientific/Engineering
22
22
  Requires-Python: >=3.10
23
23
  License-File: ../LICENSE
24
- Requires-Dist: opentrons-shared-data (==8.4.0a2)
24
+ Requires-Dist: opentrons-shared-data (==8.4.0a4)
25
25
  Requires-Dist: aionotify (==0.3.1)
26
26
  Requires-Dist: anyio (<4.0.0,>=3.6.1)
27
27
  Requires-Dist: jsonschema (<4.18.0,>=3.0.1)
@@ -35,9 +35,9 @@ Requires-Dist: pyusb (==1.2.1)
35
35
  Requires-Dist: packaging (>=21.0)
36
36
  Requires-Dist: importlib-metadata (>=1.0) ; python_version < "3.8"
37
37
  Provides-Extra: flex-hardware
38
- Requires-Dist: opentrons-hardware[flex] (==8.4.0a2) ; extra == 'flex-hardware'
38
+ Requires-Dist: opentrons-hardware[flex] (==8.4.0a4) ; extra == 'flex-hardware'
39
39
  Provides-Extra: ot2-hardware
40
- Requires-Dist: opentrons-hardware (==8.4.0a2) ; extra == 'ot2-hardware'
40
+ Requires-Dist: opentrons-hardware (==8.4.0a4) ; extra == 'ot2-hardware'
41
41
 
42
42
  .. _Full API Documentation: http://docs.opentrons.com
43
43