opentrons 8.7.0a0__py3-none-any.whl → 8.7.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/_version.py CHANGED
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '8.7.0a0'
32
- __version_tuple__ = version_tuple = (8, 7, 0, 'a0')
31
+ __version__ = version = '8.7.0a1'
32
+ __version_tuple__ = version_tuple = (8, 7, 0, 'a1')
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -28,6 +28,8 @@ DefaultLiquidClassVersions: TypeAlias = dict[APIVersion, dict[str, int]]
28
28
  # [any] [anything else] 1
29
29
  DEFAULT_LIQUID_CLASS_VERSIONS: DefaultLiquidClassVersions = {
30
30
  APIVersion(2, 26): {
31
+ "ethanol_80": 2,
32
+ "glycerol_50": 2,
31
33
  "water": 2,
32
34
  },
33
35
  }
@@ -21,7 +21,11 @@ from opentrons.protocol_engine import (
21
21
  OnLabwareLocation,
22
22
  DropTipWellLocation,
23
23
  )
24
- from opentrons.protocol_engine.types import StagingSlotLocation, WellLocationType
24
+ from opentrons.protocol_engine.types import (
25
+ StagingSlotLocation,
26
+ WellLocationType,
27
+ LoadedModule,
28
+ )
25
29
  from opentrons.types import DeckSlotName, StagingSlotName, Point
26
30
  from . import point_calculations
27
31
 
@@ -136,22 +140,47 @@ def check_safe_for_pipette_movement( # noqa: C901
136
140
  f" will result in collision with thermocycler lid in deck slot A1."
137
141
  )
138
142
 
143
+ def _check_for_column_4_module_collision(slot: DeckSlotName) -> None:
144
+ slot_module = engine_state.modules.get_by_slot(slot)
145
+ if (
146
+ slot_module
147
+ and engine_state.modules.is_column_4_module(slot_module.model)
148
+ and _slot_has_potential_colliding_object(
149
+ engine_state=engine_state,
150
+ pipette_bounds=pipette_bounds_at_well_location,
151
+ surrounding_location=slot_module,
152
+ )
153
+ ):
154
+ raise PartialTipMovementNotAllowedError(
155
+ f"Moving to {engine_state.labware.get_display_name(labware_id)} in slot"
156
+ f" {slot} with {primary_nozzle} nozzle partial configuration will"
157
+ f" result in collision with items on {slot_module.model} mounted in {slot}."
158
+ )
159
+
160
+ # We check the labware slot for a module that is mounted in the same cutout
161
+ # as the labwares slot but does not occupy the same heirarchy (like the stacker).
162
+ _check_for_column_4_module_collision(labware_slot)
163
+
139
164
  for regular_slot in surrounding_slots.regular_slots:
140
165
  if _slot_has_potential_colliding_object(
141
166
  engine_state=engine_state,
142
167
  pipette_bounds=pipette_bounds_at_well_location,
143
- surrounding_slot=regular_slot,
168
+ surrounding_location=regular_slot,
144
169
  ):
145
170
  raise PartialTipMovementNotAllowedError(
146
171
  f"Moving to {engine_state.labware.get_display_name(labware_id)} in slot"
147
172
  f" {labware_slot} with {primary_nozzle} nozzle partial configuration"
148
173
  f" will result in collision with items in deck slot {regular_slot}."
149
174
  )
175
+
176
+ # Check for Column 4 Modules that may be descendants of a given surrounding slot
177
+ _check_for_column_4_module_collision(regular_slot)
178
+
150
179
  for staging_slot in surrounding_slots.staging_slots:
151
180
  if _slot_has_potential_colliding_object(
152
181
  engine_state=engine_state,
153
182
  pipette_bounds=pipette_bounds_at_well_location,
154
- surrounding_slot=staging_slot,
183
+ surrounding_location=staging_slot,
155
184
  ):
156
185
  raise PartialTipMovementNotAllowedError(
157
186
  f"Moving to {engine_state.labware.get_display_name(labware_id)} in slot"
@@ -178,18 +207,45 @@ def _get_critical_point_to_use(
178
207
  def _slot_has_potential_colliding_object(
179
208
  engine_state: StateView,
180
209
  pipette_bounds: Tuple[Point, Point, Point, Point],
181
- surrounding_slot: Union[DeckSlotName, StagingSlotName],
210
+ surrounding_location: Union[DeckSlotName, StagingSlotName, LoadedModule],
182
211
  ) -> bool:
183
- """Return the slot, if any, that has an item that the pipette might collide into."""
184
- # Check if slot overlaps with pipette position
185
- slot_pos = engine_state.addressable_areas.get_addressable_area_position(
186
- addressable_area_name=surrounding_slot.id,
187
- do_compatibility_check=False,
188
- )
189
- slot_bounds = engine_state.addressable_areas.get_addressable_area_bounding_box(
190
- addressable_area_name=surrounding_slot.id,
191
- do_compatibility_check=False,
192
- )
212
+ """Return the slot, if any, that has an item that the pipette might collide into.
213
+ Can be provided a Deck Slot, Staging Slot, or Column 4 Module.
214
+ """
215
+ if isinstance(surrounding_location, LoadedModule):
216
+ if (
217
+ engine_state.modules.is_column_4_module(surrounding_location.model)
218
+ and surrounding_location.location is not None
219
+ ):
220
+ module_area = (
221
+ engine_state.modules.ensure_and_convert_module_fixture_location(
222
+ surrounding_location.location.slotName, surrounding_location.model
223
+ )
224
+ )
225
+ slot_pos = engine_state.addressable_areas.get_addressable_area_position(
226
+ addressable_area_name=module_area,
227
+ do_compatibility_check=False,
228
+ )
229
+ slot_bounds = (
230
+ engine_state.addressable_areas.get_addressable_area_bounding_box(
231
+ addressable_area_name=module_area,
232
+ do_compatibility_check=False,
233
+ )
234
+ )
235
+ else:
236
+ raise ValueError(
237
+ f"Error during collision validation, Module {surrounding_location.model} must be in Column 4."
238
+ )
239
+ else:
240
+ # Check if slot overlaps with pipette position
241
+ slot_pos = engine_state.addressable_areas.get_addressable_area_position(
242
+ addressable_area_name=surrounding_location.id,
243
+ do_compatibility_check=False,
244
+ )
245
+ slot_bounds = engine_state.addressable_areas.get_addressable_area_bounding_box(
246
+ addressable_area_name=surrounding_location.id,
247
+ do_compatibility_check=False,
248
+ )
193
249
  slot_back_left_coords = Point(slot_pos.x, slot_pos.y + slot_bounds.y, slot_pos.z)
194
250
  slot_front_right_coords = Point(slot_pos.x + slot_bounds.x, slot_pos.y, slot_pos.z)
195
251
 
@@ -199,13 +255,17 @@ def _slot_has_potential_colliding_object(
199
255
  rectangle2=(slot_back_left_coords, slot_front_right_coords),
200
256
  ):
201
257
  # Check z-height of items in overlapping slot
202
- if isinstance(surrounding_slot, DeckSlotName):
258
+ if isinstance(surrounding_location, DeckSlotName):
203
259
  slot_highest_z = engine_state.geometry.get_highest_z_in_slot(
204
- DeckSlotLocation(slotName=surrounding_slot)
260
+ DeckSlotLocation(slotName=surrounding_location)
261
+ )
262
+ elif isinstance(surrounding_location, LoadedModule):
263
+ slot_highest_z = engine_state.geometry.get_highest_z_of_column_4_module(
264
+ surrounding_location
205
265
  )
206
266
  else:
207
267
  slot_highest_z = engine_state.geometry.get_highest_z_in_slot(
208
- StagingSlotLocation(slotName=surrounding_slot)
268
+ StagingSlotLocation(slotName=surrounding_location)
209
269
  )
210
270
  return slot_highest_z >= pipette_bounds[0].z
211
271
  return False
@@ -274,12 +274,20 @@ class GeometryView:
274
274
  try:
275
275
  labware_id = self._labware.get_id_by_module(module_id=module_id)
276
276
  except LabwareNotLoadedOnModuleError:
277
- return self._modules.get_module_highest_z(
278
- module_id=module_id,
279
- addressable_areas=self._addressable_areas,
280
- )
277
+ # For the time being we will ignore column 4 modules in this check to avoid conflating results
278
+ if self._modules.is_column_4_module(slot_item.model) is False:
279
+ return self._modules.get_module_highest_z(
280
+ module_id=module_id,
281
+ addressable_areas=self._addressable_areas,
282
+ )
281
283
  else:
282
- return self.get_highest_z_of_labware_stack(labware_id)
284
+ # For the time being we will ignore column 4 modules in this check to avoid conflating results
285
+ if self._modules.is_column_4_module(slot_item.model) is False:
286
+ return self.get_highest_z_of_labware_stack(labware_id)
287
+ # todo (cb, 2025-09-15): For now we skip column 4 modules and handle them seperately in
288
+ # get_highest_z_of_column_4_module, so this will return 0. In the future we may want to consolidate
289
+ # this to make it more apparently at this point in the query process.
290
+ return 0
283
291
  elif isinstance(slot_item, LoadedLabware):
284
292
  # get stacked heights of all labware in the slot
285
293
  return self.get_highest_z_of_labware_stack(slot_item.id)
@@ -301,6 +309,26 @@ class GeometryView:
301
309
  return self.get_labware_highest_z(labware_id)
302
310
  return self.get_highest_z_of_labware_stack(stacked_labware_id)
303
311
 
312
+ def get_highest_z_of_column_4_module(self, module: LoadedModule) -> float:
313
+ """Get the highest Z-point of the topmost labware in the stack of labware on the given column 4 module.
314
+
315
+ If there is no labware on the given module, returns highest z of the module.
316
+ """
317
+ if self._modules.is_column_4_module(module.model):
318
+ try:
319
+ labware_id = self._labware.get_id_by_module(module_id=module.id)
320
+ except LabwareNotLoadedOnModuleError:
321
+ return self._modules.get_module_highest_z(
322
+ module_id=module.id,
323
+ addressable_areas=self._addressable_areas,
324
+ )
325
+ else:
326
+ return self.get_highest_z_of_labware_stack(labware_id)
327
+ else:
328
+ raise ValueError(
329
+ "Module must be a Column 4 Module to determine maximum z height."
330
+ )
331
+
304
332
  def get_min_travel_z(
305
333
  self,
306
334
  pipette_id: str,
@@ -1330,6 +1330,12 @@ class ModuleView:
1330
1330
  f"Module {module.model} is already present at {location}."
1331
1331
  )
1332
1332
 
1333
+ def is_column_4_module(self, model: ModuleModel) -> bool:
1334
+ """Determine whether or not a module is a Column 4 Module."""
1335
+ if model in _COLUMN_4_MODULES:
1336
+ return True
1337
+ return False
1338
+
1333
1339
  def get_default_gripper_offsets(
1334
1340
  self, module_id: str
1335
1341
  ) -> Optional[LabwareMovementOffsetData]:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: opentrons
3
- Version: 8.7.0a0
3
+ Version: 8.7.0a1
4
4
  Summary: The Opentrons API is a simple framework designed to make writing automated biology lab protocols easy.
5
5
  Project-URL: opentrons.com, https://www.opentrons.com
6
6
  Project-URL: Source Code On Github, https://github.com/Opentrons/opentrons/tree/edge/api
@@ -24,7 +24,7 @@ Requires-Dist: click<9,>=8.0.0
24
24
  Requires-Dist: importlib-metadata>=1.0; python_version < '3.8'
25
25
  Requires-Dist: jsonschema<4.18.0,>=3.0.1
26
26
  Requires-Dist: numpy<2,>=1.20.0
27
- Requires-Dist: opentrons-shared-data==8.7.0a0
27
+ Requires-Dist: opentrons-shared-data==8.7.0a1
28
28
  Requires-Dist: packaging>=21.0
29
29
  Requires-Dist: pydantic-settings<3,>=2
30
30
  Requires-Dist: pydantic<3,>=2.0.0
@@ -32,6 +32,6 @@ Requires-Dist: pyserial>=3.5
32
32
  Requires-Dist: pyusb==1.2.1
33
33
  Requires-Dist: typing-extensions<5,>=4.0.0
34
34
  Provides-Extra: flex-hardware
35
- Requires-Dist: opentrons-hardware[flex]==8.7.0a0; extra == 'flex-hardware'
35
+ Requires-Dist: opentrons-hardware[flex]==8.7.0a1; extra == 'flex-hardware'
36
36
  Provides-Extra: ot2-hardware
37
- Requires-Dist: opentrons-hardware==8.7.0a0; extra == 'ot2-hardware'
37
+ Requires-Dist: opentrons-hardware==8.7.0a1; extra == 'ot2-hardware'
@@ -1,5 +1,5 @@
1
1
  opentrons/__init__.py,sha256=TQ_Ca_zzAM3iLzAysWKkFkQHG8-imihxDPQbLCYrf-E,4533
2
- opentrons/_version.py,sha256=d3ve2LJJuyjX8J_41fIn96U4XlDfRGAll6nNIi4dBwY,712
2
+ opentrons/_version.py,sha256=SN78vIsTT8t_rXBBup1Jg0Zmzddm97urqUZr263vZBg,712
3
3
  opentrons/execute.py,sha256=Y88qICDiHWQjU0L4Ou7DI5OXXu7zZcdkUvNUYmZqIfc,29282
4
4
  opentrons/legacy_broker.py,sha256=XnuEBBlrHCThc31RFW2UR0tGqctqWZ-CZ9vSC4L9whU,1553
5
5
  opentrons/ordered_set.py,sha256=g-SB3qA14yxHu9zjGyc2wC7d2TUCBE6fKZlHAtbPzI8,4082
@@ -252,7 +252,7 @@ opentrons/protocol_api/core/well.py,sha256=Lf89YYEyq-ahRSRIFJw42vxIP8Fw6kzIUh9K1
252
252
  opentrons/protocol_api/core/well_grid.py,sha256=BU28DKaBgEU_JdZ6pEzrwNxmuh6TkO4zlg7Pq1Rf5Xk,1516
253
253
  opentrons/protocol_api/core/engine/__init__.py,sha256=B_5T7zgkWDb1mXPg4NbT-wBkQaK-WVokMMnJRNu7xiM,582
254
254
  opentrons/protocol_api/core/engine/_default_labware_versions.py,sha256=hLKAp33c_eNLpvT5qUt8AOYy6PHhM_JnkdOXaMl80jA,7541
255
- opentrons/protocol_api/core/engine/_default_liquid_class_versions.py,sha256=MCPKf-7fAULQ4oPVYxiyKHr4AYRAevwA9uhkdur3-kQ,1513
255
+ opentrons/protocol_api/core/engine/_default_liquid_class_versions.py,sha256=be-eczhfOsWm4Q3fSVcjWqxteg_IGnwC-pozJUZ5u4U,1564
256
256
  opentrons/protocol_api/core/engine/deck_conflict.py,sha256=j3AJ-sYr5e-06UgeGueUldx9B3fn7AhN738kJxZO_vw,14693
257
257
  opentrons/protocol_api/core/engine/exceptions.py,sha256=aZgNrmYEeuPZm21nX_KZYtvyjv5h_zPjxxgPkEV7_bw,725
258
258
  opentrons/protocol_api/core/engine/instrument.py,sha256=lDrCqOZm7ceu1E50EYiSh7qTDMci5cgw4bM-AvLyMjE,98890
@@ -260,7 +260,7 @@ opentrons/protocol_api/core/engine/labware.py,sha256=-2oygShyRZo1-R0UOoBtXniFOXf
260
260
  opentrons/protocol_api/core/engine/load_labware_params.py,sha256=CxSbCBXVXHtBszP-3Ko8hsQTjvvogKRo0CCzCmMfIic,3003
261
261
  opentrons/protocol_api/core/engine/module_core.py,sha256=qxxcqpH-A4Zil9hHDxnvh4H8JuPvK6-sgkODU5YdjZQ,39537
262
262
  opentrons/protocol_api/core/engine/overlap_versions.py,sha256=PyGvQtQUg1wzNtkuGZtxwXm019PoIjq7em2JiWaxbXc,675
263
- opentrons/protocol_api/core/engine/pipette_movement_conflict.py,sha256=x54RIYtkL1rdzvkSp2JgusLY3EEvY8bX3OwipeVKdsE,15511
263
+ opentrons/protocol_api/core/engine/pipette_movement_conflict.py,sha256=9BmtG1kg3vyrssBXflCNZrJIukGp6y5UJ_XqhIz73rI,18148
264
264
  opentrons/protocol_api/core/engine/point_calculations.py,sha256=C2eF0fvJQGMqQv3DzNhc1-m8HTAXTyTsHPJEPrEUEmo,2502
265
265
  opentrons/protocol_api/core/engine/protocol.py,sha256=MQNMYNII1OxwaQGsKbOgVtmEH54mjDahzD-WHst9MH8,46247
266
266
  opentrons/protocol_api/core/engine/robot.py,sha256=bzUt23NG-clD-9-QFsV_6nm3fMgSmvYEG9DyyZI1xgw,5366
@@ -462,12 +462,12 @@ opentrons/protocol_engine/state/commands.py,sha256=y5WE2pKmnMalgHFHEiBnBurO2TZ9w
462
462
  opentrons/protocol_engine/state/config.py,sha256=7jSGxC6Vqj1eA8fqZ2I3zjlxVXg8pxvcBYMztRIx9Mg,1515
463
463
  opentrons/protocol_engine/state/files.py,sha256=w8xxxg8HY0RqKKEGSfHWfrjV54Gb02O3dwtisJ-9j8E,1753
464
464
  opentrons/protocol_engine/state/fluid_stack.py,sha256=uwkf0qYk1UX5iU52xmk-e3yLPK8OG-TtMCcBqrkVFpM,5932
465
- opentrons/protocol_engine/state/geometry.py,sha256=o8tefXS_Jekdt82dW4HHVJSnsxCsTFJuG6ogtri2wTY,104065
465
+ opentrons/protocol_engine/state/geometry.py,sha256=X-xyaF0orHMrNzZfzyGeOonazk0F67toJPbfGQmtmfM,105687
466
466
  opentrons/protocol_engine/state/inner_well_math_utils.py,sha256=UhemsPpcuKwVc-iGXI2-v--miOGNunAnAVznJTVADlQ,20598
467
467
  opentrons/protocol_engine/state/labware.py,sha256=vwagsqSBAIkVj4eupu0pIK6F3idnqqoRWDjVbp4ZwKQ,62977
468
468
  opentrons/protocol_engine/state/liquid_classes.py,sha256=u_z75UYdiFAKG0yB3mr1il4T3qaS0Sotq8sL7KLODP8,2990
469
469
  opentrons/protocol_engine/state/liquids.py,sha256=NoesktcQdJUjIVmet1uqqJPf-rzbo4SGemXwQC295W0,2338
470
- opentrons/protocol_engine/state/modules.py,sha256=SM_6j3YfZV4ZXWTGjwxQIVH-jtHEI-YFuJtoMyPBReo,61599
470
+ opentrons/protocol_engine/state/modules.py,sha256=UbvbJcx45n5hZRbxQMTmxrA1rFichsWNUj6SpPAsBIU,61816
471
471
  opentrons/protocol_engine/state/motion.py,sha256=pWt28-vBaO6dz_rtvoliUsGDvls_ML6sRfDOwmD0F7g,15086
472
472
  opentrons/protocol_engine/state/pipettes.py,sha256=nXXQqeBIArPu0rEz-Y6OqA_F23A7uxPHVC2JfaJFH_g,38186
473
473
  opentrons/protocol_engine/state/state.py,sha256=X8Sb2HN01nmNBY1Rw6qeLOjSuIWTbiq4g5otsd4u6A0,15600
@@ -595,8 +595,8 @@ opentrons/util/linal.py,sha256=IlKAP9HkNBBgULeSf4YVwSKHdx9jnCjSr7nvDvlRALg,5753
595
595
  opentrons/util/logging_config.py,sha256=7et4YYuQdWdq_e50U-8vFS_QyNBRgdnqPGAQJm8qrIo,9954
596
596
  opentrons/util/logging_queue_handler.py,sha256=ZsSJwy-oV8DXwpYiZisQ1PbYwmK2cOslD46AcyJ1E4I,2484
597
597
  opentrons/util/performance_helpers.py,sha256=ew7H8XD20iS6-2TJAzbQeyzStZkkE6PzHt_Adx3wbZQ,5172
598
- opentrons-8.7.0a0.dist-info/METADATA,sha256=U7gGnD5DR1XRNN5e9BqM9v9_NnJFeqsqVZI51jyMnoM,1607
599
- opentrons-8.7.0a0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
600
- opentrons-8.7.0a0.dist-info/entry_points.txt,sha256=fTa6eGCYkvOtv0ov-KVE8LLGetgb35LQLF9x85OWPVw,106
601
- opentrons-8.7.0a0.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
602
- opentrons-8.7.0a0.dist-info/RECORD,,
598
+ opentrons-8.7.0a1.dist-info/METADATA,sha256=VJsAOF5Ya8ZdUbGhic3GWkxexsL2BjOSVmMxJF3zeYY,1607
599
+ opentrons-8.7.0a1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
600
+ opentrons-8.7.0a1.dist-info/entry_points.txt,sha256=fTa6eGCYkvOtv0ov-KVE8LLGetgb35LQLF9x85OWPVw,106
601
+ opentrons-8.7.0a1.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
602
+ opentrons-8.7.0a1.dist-info/RECORD,,