opentrons 8.7.0a7__py3-none-any.whl → 8.7.0a9__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 (147) hide show
  1. opentrons/_version.py +2 -2
  2. opentrons/drivers/asyncio/communication/serial_connection.py +55 -129
  3. opentrons/drivers/flex_stacker/driver.py +6 -1
  4. opentrons/drivers/heater_shaker/abstract.py +0 -5
  5. opentrons/drivers/heater_shaker/driver.py +0 -10
  6. opentrons/drivers/heater_shaker/simulator.py +0 -4
  7. opentrons/drivers/thermocycler/abstract.py +0 -6
  8. opentrons/drivers/thermocycler/driver.py +10 -61
  9. opentrons/drivers/thermocycler/simulator.py +0 -6
  10. opentrons/hardware_control/api.py +5 -24
  11. opentrons/hardware_control/backends/controller.py +2 -8
  12. opentrons/hardware_control/backends/flex_protocol.py +1 -0
  13. opentrons/hardware_control/backends/ot3controller.py +3 -3
  14. opentrons/hardware_control/backends/ot3simulator.py +2 -2
  15. opentrons/hardware_control/backends/simulator.py +1 -2
  16. opentrons/hardware_control/backends/subsystem_manager.py +2 -5
  17. opentrons/hardware_control/emulation/abstract_emulator.py +4 -6
  18. opentrons/hardware_control/emulation/connection_handler.py +5 -8
  19. opentrons/hardware_control/emulation/heater_shaker.py +3 -12
  20. opentrons/hardware_control/emulation/settings.py +1 -1
  21. opentrons/hardware_control/emulation/thermocycler.py +15 -67
  22. opentrons/hardware_control/module_control.py +8 -82
  23. opentrons/hardware_control/modules/__init__.py +0 -3
  24. opentrons/hardware_control/modules/absorbance_reader.py +4 -11
  25. opentrons/hardware_control/modules/flex_stacker.py +9 -38
  26. opentrons/hardware_control/modules/heater_shaker.py +5 -42
  27. opentrons/hardware_control/modules/magdeck.py +4 -8
  28. opentrons/hardware_control/modules/mod_abc.py +5 -13
  29. opentrons/hardware_control/modules/tempdeck.py +5 -25
  30. opentrons/hardware_control/modules/thermocycler.py +11 -68
  31. opentrons/hardware_control/modules/types.py +1 -20
  32. opentrons/hardware_control/modules/utils.py +4 -11
  33. opentrons/hardware_control/nozzle_manager.py +0 -3
  34. opentrons/hardware_control/ot3api.py +7 -26
  35. opentrons/hardware_control/poller.py +8 -22
  36. opentrons/hardware_control/protocols/gripper_controller.py +1 -0
  37. opentrons/hardware_control/scripts/update_module_fw.py +0 -5
  38. opentrons/hardware_control/types.py +2 -31
  39. opentrons/legacy_commands/module_commands.py +0 -23
  40. opentrons/legacy_commands/protocol_commands.py +0 -20
  41. opentrons/legacy_commands/types.py +0 -80
  42. opentrons/motion_planning/deck_conflict.py +12 -17
  43. opentrons/motion_planning/waypoints.py +29 -15
  44. opentrons/protocol_api/__init__.py +1 -5
  45. opentrons/protocol_api/_types.py +1 -6
  46. opentrons/protocol_api/core/common.py +1 -3
  47. opentrons/protocol_api/core/engine/_default_labware_versions.py +11 -32
  48. opentrons/protocol_api/core/engine/labware.py +1 -8
  49. opentrons/protocol_api/core/engine/module_core.py +8 -75
  50. opentrons/protocol_api/core/engine/protocol.py +1 -18
  51. opentrons/protocol_api/core/engine/well.py +0 -8
  52. opentrons/protocol_api/core/legacy/legacy_module_core.py +4 -24
  53. opentrons/protocol_api/core/legacy/legacy_protocol_core.py +1 -11
  54. opentrons/protocol_api/core/legacy/legacy_well_core.py +0 -4
  55. opentrons/protocol_api/core/legacy_simulator/legacy_protocol_core.py +2 -14
  56. opentrons/protocol_api/core/module.py +4 -37
  57. opentrons/protocol_api/core/protocol.py +2 -11
  58. opentrons/protocol_api/core/well.py +0 -4
  59. opentrons/protocol_api/labware.py +0 -5
  60. opentrons/protocol_api/module_contexts.py +61 -122
  61. opentrons/protocol_api/protocol_context.py +4 -26
  62. opentrons/protocol_api/robot_context.py +21 -38
  63. opentrons/protocol_api/validation.py +1 -6
  64. opentrons/protocol_engine/actions/__init__.py +2 -4
  65. opentrons/protocol_engine/actions/actions.py +9 -22
  66. opentrons/protocol_engine/clients/sync_client.py +7 -42
  67. opentrons/protocol_engine/commands/__init__.py +0 -42
  68. opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +15 -2
  69. opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +15 -2
  70. opentrons/protocol_engine/commands/aspirate.py +0 -1
  71. opentrons/protocol_engine/commands/command.py +0 -1
  72. opentrons/protocol_engine/commands/command_unions.py +0 -49
  73. opentrons/protocol_engine/commands/dispense.py +0 -1
  74. opentrons/protocol_engine/commands/drop_tip.py +8 -32
  75. opentrons/protocol_engine/commands/heater_shaker/__init__.py +0 -14
  76. opentrons/protocol_engine/commands/heater_shaker/set_and_wait_for_shake_speed.py +4 -5
  77. opentrons/protocol_engine/commands/heater_shaker/set_target_temperature.py +5 -31
  78. opentrons/protocol_engine/commands/movement_common.py +0 -2
  79. opentrons/protocol_engine/commands/pick_up_tip.py +11 -21
  80. opentrons/protocol_engine/commands/temperature_module/set_target_temperature.py +7 -38
  81. opentrons/protocol_engine/commands/thermocycler/__init__.py +0 -16
  82. opentrons/protocol_engine/commands/thermocycler/run_extended_profile.py +0 -6
  83. opentrons/protocol_engine/commands/thermocycler/run_profile.py +0 -8
  84. opentrons/protocol_engine/commands/thermocycler/set_target_block_temperature.py +6 -40
  85. opentrons/protocol_engine/commands/thermocycler/set_target_lid_temperature.py +5 -29
  86. opentrons/protocol_engine/commands/touch_tip.py +1 -1
  87. opentrons/protocol_engine/commands/unsafe/unsafe_place_labware.py +22 -6
  88. opentrons/protocol_engine/errors/__init__.py +0 -4
  89. opentrons/protocol_engine/errors/exceptions.py +0 -55
  90. opentrons/protocol_engine/execution/__init__.py +0 -2
  91. opentrons/protocol_engine/execution/command_executor.py +0 -8
  92. opentrons/protocol_engine/execution/create_queue_worker.py +1 -5
  93. opentrons/protocol_engine/execution/labware_movement.py +21 -10
  94. opentrons/protocol_engine/execution/movement.py +0 -2
  95. opentrons/protocol_engine/execution/queue_worker.py +0 -4
  96. opentrons/protocol_engine/execution/run_control.py +0 -8
  97. opentrons/protocol_engine/protocol_engine.py +34 -75
  98. opentrons/protocol_engine/resources/__init__.py +0 -2
  99. opentrons/protocol_engine/resources/deck_configuration_provider.py +0 -7
  100. opentrons/protocol_engine/resources/labware_validation.py +6 -10
  101. opentrons/protocol_engine/state/_labware_origin_math.py +636 -0
  102. opentrons/protocol_engine/state/_well_math.py +18 -60
  103. opentrons/protocol_engine/state/addressable_areas.py +0 -2
  104. opentrons/protocol_engine/state/commands.py +11 -14
  105. opentrons/protocol_engine/state/geometry.py +374 -213
  106. opentrons/protocol_engine/state/labware.py +102 -52
  107. opentrons/protocol_engine/state/module_substates/thermocycler_module_substate.py +0 -37
  108. opentrons/protocol_engine/state/modules.py +8 -21
  109. opentrons/protocol_engine/state/motion.py +0 -44
  110. opentrons/protocol_engine/state/state.py +0 -14
  111. opentrons/protocol_engine/state/state_summary.py +0 -2
  112. opentrons/protocol_engine/state/tips.py +258 -177
  113. opentrons/protocol_engine/state/update_types.py +9 -16
  114. opentrons/protocol_engine/types/__init__.py +3 -9
  115. opentrons/protocol_engine/types/deck_configuration.py +1 -5
  116. opentrons/protocol_engine/types/instrument.py +1 -8
  117. opentrons/protocol_engine/types/labware.py +13 -1
  118. opentrons/protocol_engine/types/module.py +0 -10
  119. opentrons/protocol_engine/types/tip.py +0 -9
  120. opentrons/protocol_runner/create_simulating_orchestrator.py +2 -29
  121. opentrons/protocol_runner/run_orchestrator.py +2 -18
  122. opentrons/protocols/api_support/definitions.py +1 -1
  123. opentrons/protocols/api_support/types.py +1 -2
  124. opentrons/simulate.py +15 -48
  125. opentrons/system/camera.py +1 -1
  126. {opentrons-8.7.0a7.dist-info → opentrons-8.7.0a9.dist-info}/METADATA +4 -4
  127. {opentrons-8.7.0a7.dist-info → opentrons-8.7.0a9.dist-info}/RECORD +130 -146
  128. opentrons/protocol_api/core/engine/tasks.py +0 -48
  129. opentrons/protocol_api/core/legacy/tasks.py +0 -19
  130. opentrons/protocol_api/core/legacy_simulator/tasks.py +0 -19
  131. opentrons/protocol_api/core/tasks.py +0 -31
  132. opentrons/protocol_api/tasks.py +0 -48
  133. opentrons/protocol_engine/commands/create_timer.py +0 -83
  134. opentrons/protocol_engine/commands/heater_shaker/common.py +0 -20
  135. opentrons/protocol_engine/commands/heater_shaker/set_shake_speed.py +0 -136
  136. opentrons/protocol_engine/commands/set_tip_state.py +0 -97
  137. opentrons/protocol_engine/commands/thermocycler/start_run_extended_profile.py +0 -191
  138. opentrons/protocol_engine/commands/wait_for_tasks.py +0 -98
  139. opentrons/protocol_engine/execution/task_handler.py +0 -157
  140. opentrons/protocol_engine/resources/concurrency_provider.py +0 -27
  141. opentrons/protocol_engine/state/labware_origin_math/errors.py +0 -94
  142. opentrons/protocol_engine/state/labware_origin_math/stackup_origin_to_labware_origin.py +0 -1331
  143. opentrons/protocol_engine/state/tasks.py +0 -139
  144. opentrons/protocol_engine/types/tasks.py +0 -38
  145. {opentrons-8.7.0a7.dist-info → opentrons-8.7.0a9.dist-info}/WHEEL +0 -0
  146. {opentrons-8.7.0a7.dist-info → opentrons-8.7.0a9.dist-info}/entry_points.txt +0 -0
  147. {opentrons-8.7.0a7.dist-info → opentrons-8.7.0a9.dist-info}/licenses/LICENSE +0 -0
@@ -1,191 +0,0 @@
1
- """StartRunProfile command request, result, and implementation models."""
2
-
3
- from __future__ import annotations
4
- from pydantic import BaseModel, Field
5
- from typing import List, Optional, TYPE_CHECKING, overload, Union, Any
6
- from typing_extensions import Literal, Type
7
- from pydantic.json_schema import SkipJsonSchema
8
-
9
- from opentrons.hardware_control.modules.types import ThermocyclerStep, ThermocyclerCycle
10
-
11
- from ..command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
12
- from ...errors.error_occurrence import ErrorOccurrence
13
- from .run_extended_profile import ProfileStep, ProfileCycle
14
-
15
- if TYPE_CHECKING:
16
- from opentrons.protocol_engine.state.state import StateView
17
- from opentrons.protocol_engine.execution import (
18
- TaskHandler,
19
- EquipmentHandler,
20
- )
21
- from opentrons.protocol_engine.state.module_substates.thermocycler_module_substate import (
22
- ThermocyclerModuleSubState,
23
- )
24
-
25
- StartRunExtendedProfileCommandType = Literal["thermocycler/startRunExtendedProfile"]
26
-
27
-
28
- def _remove_default(s: dict[str, Any]) -> None:
29
- s.pop("default", None)
30
-
31
-
32
- class StartRunExtendedProfileStepParams(BaseModel):
33
- """Input parameters for an individual Thermocycler profile step."""
34
-
35
- celsius: float = Field(..., description="Target temperature in °C.")
36
- holdSeconds: float = Field(
37
- ..., description="Time to hold target temperature at in seconds."
38
- )
39
- rampRate: float | SkipJsonSchema[None] = Field(
40
- None,
41
- description="How quickly to change temperature in °C/second.",
42
- json_schema_extra=_remove_default,
43
- )
44
-
45
-
46
- class StartRunExtendedProfileParams(BaseModel):
47
- """Input parameters to run a Thermocycler profile."""
48
-
49
- moduleId: str = Field(..., description="Unique ID of the Thermocycler.")
50
- profileElements: List[Union[ProfileStep, ProfileCycle]] = Field(
51
- ...,
52
- description="Elements of the profile. Each can be either a step or a cycle.",
53
- )
54
- blockMaxVolumeUl: float | SkipJsonSchema[None] = Field(
55
- None,
56
- description="Amount of liquid in uL of the most-full well"
57
- " in labware loaded onto the thermocycler.",
58
- json_schema_extra=_remove_default,
59
- )
60
- taskId: str | None = Field(None, description="The id of the profile task")
61
-
62
-
63
- class StartRunExtendedProfileResult(BaseModel):
64
- """Result data from running a Thermocycler profile."""
65
-
66
- taskId: str = Field(..., description="The id of the profile task")
67
-
68
-
69
- def _transform_profile_step(
70
- step: ProfileStep, thermocycler_state: ThermocyclerModuleSubState
71
- ) -> ThermocyclerStep:
72
- return ThermocyclerStep(
73
- temperature=thermocycler_state.validate_target_block_temperature(step.celsius),
74
- hold_time_seconds=step.holdSeconds,
75
- ramp_rate=thermocycler_state.validate_ramp_rate(step.rampRate, step.celsius),
76
- )
77
-
78
-
79
- @overload
80
- def _transform_profile_element(
81
- element: ProfileStep, thermocycler_state: ThermocyclerModuleSubState
82
- ) -> ThermocyclerStep:
83
- ...
84
-
85
-
86
- @overload
87
- def _transform_profile_element(
88
- element: ProfileCycle, thermocycler_state: ThermocyclerModuleSubState
89
- ) -> ThermocyclerCycle:
90
- ...
91
-
92
-
93
- def _transform_profile_element(
94
- element: Union[ProfileStep, ProfileCycle],
95
- thermocycler_state: ThermocyclerModuleSubState,
96
- ) -> Union[ThermocyclerStep, ThermocyclerCycle]:
97
- if isinstance(element, ProfileStep):
98
- return _transform_profile_step(element, thermocycler_state)
99
- else:
100
- return ThermocyclerCycle(
101
- steps=[
102
- _transform_profile_step(step, thermocycler_state)
103
- for step in element.steps
104
- ],
105
- repetitions=element.repetitions,
106
- )
107
-
108
-
109
- class StartRunExtendedProfileImpl(
110
- AbstractCommandImpl[
111
- StartRunExtendedProfileParams, SuccessData[StartRunExtendedProfileResult]
112
- ]
113
- ):
114
- """Execution implementation of a Thermocycler's run profile command."""
115
-
116
- def __init__(
117
- self,
118
- state_view: StateView,
119
- equipment: EquipmentHandler,
120
- task_handler: TaskHandler,
121
- **unused_dependencies: object,
122
- ) -> None:
123
- self._state_view = state_view
124
- self._equipment = equipment
125
- self._task_handler = task_handler
126
-
127
- async def execute(
128
- self, params: StartRunExtendedProfileParams
129
- ) -> SuccessData[StartRunExtendedProfileResult]:
130
- """Run a Thermocycler profile."""
131
- thermocycler_state = self._state_view.modules.get_thermocycler_module_substate(
132
- params.moduleId
133
- )
134
- thermocycler_hardware = self._equipment.get_module_hardware_api(
135
- thermocycler_state.module_id
136
- )
137
-
138
- profile = [
139
- _transform_profile_element(element, thermocycler_state)
140
- for element in params.profileElements
141
- ]
142
- target_volume: Optional[float]
143
- if params.blockMaxVolumeUl is not None:
144
- target_volume = thermocycler_state.validate_max_block_volume(
145
- params.blockMaxVolumeUl
146
- )
147
- else:
148
- target_volume = None
149
-
150
- async def start_run_profile(task_handler: TaskHandler) -> None:
151
- if thermocycler_hardware is not None:
152
- async with task_handler.synchronize_cancel_latest(
153
- thermocycler_state.module_id
154
- ):
155
- await thermocycler_hardware.execute_profile(
156
- profile=profile, volume=target_volume
157
- )
158
-
159
- task = await self._task_handler.create_task(
160
- task_function=start_run_profile, id=params.taskId
161
- )
162
-
163
- return SuccessData(
164
- public=StartRunExtendedProfileResult(taskId=task.id),
165
- )
166
-
167
-
168
- class StartRunExtendedProfile(
169
- BaseCommand[
170
- StartRunExtendedProfileParams, StartRunExtendedProfileResult, ErrorOccurrence
171
- ]
172
- ):
173
- """A command to start the execution of a Thermocycler profile run without waiting for its completion."""
174
-
175
- commandType: StartRunExtendedProfileCommandType = (
176
- "thermocycler/startRunExtendedProfile"
177
- )
178
- params: StartRunExtendedProfileParams
179
-
180
- _ImplementationCls: Type[StartRunExtendedProfileImpl] = StartRunExtendedProfileImpl
181
-
182
-
183
- class StartRunExtendedProfileCreate(BaseCommandCreate[StartRunExtendedProfileParams]):
184
- """A request to execute a Thermocycler profile run."""
185
-
186
- commandType: StartRunExtendedProfileCommandType = (
187
- "thermocycler/startRunExtendedProfile"
188
- )
189
- params: StartRunExtendedProfileParams
190
-
191
- _CommandCls: Type[StartRunExtendedProfile] = StartRunExtendedProfile
@@ -1,98 +0,0 @@
1
- """WaitForTasks command request, result, and implementation models."""
2
- from __future__ import annotations
3
- from pydantic import BaseModel, Field
4
- from typing import Optional, Type, TYPE_CHECKING
5
- from typing_extensions import Literal
6
-
7
- from .command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
8
- from ..errors.error_occurrence import ErrorOccurrence, ProtocolCommandFailedError
9
- from ..errors.exceptions import TaskFailedError
10
-
11
- if TYPE_CHECKING:
12
- from ..execution import TaskHandler, RunControlHandler
13
- from ..state.state import StateView
14
-
15
-
16
- WaitForTasksCommandType = Literal["waitForTasks"]
17
-
18
-
19
- class WaitForTasksParams(BaseModel):
20
- """Payload required to annotate execution with a WaitForTasks."""
21
-
22
- task_ids: list[str] = Field(
23
- ...,
24
- description="The list of task ids to wait for.",
25
- )
26
-
27
-
28
- class WaitForTasksResult(BaseModel):
29
- """Result data from the execution of a WaitForTasks command."""
30
-
31
- task_ids: list[str] = Field(
32
- ...,
33
- description="The list of completed task ids.",
34
- )
35
-
36
-
37
- class WaitForTasksImplementation(
38
- AbstractCommandImpl[WaitForTasksParams, SuccessData[WaitForTasksResult]]
39
- ):
40
- """WaitForTasks command implementation."""
41
-
42
- def __init__(
43
- self,
44
- task_handler: TaskHandler,
45
- run_control: RunControlHandler,
46
- state_view: StateView,
47
- **kwargs: object,
48
- ) -> None:
49
- self._task_handler = task_handler
50
- self._run_control = run_control
51
- self._state_view = state_view
52
-
53
- async def execute(
54
- self, params: WaitForTasksParams
55
- ) -> SuccessData[WaitForTasksResult]:
56
- """Checks for existance of task id and then asynchronously waits for the valid, specified tasks to finish."""
57
- # Raises the exception if we don't have a valid task id.
58
- for task_id in params.task_ids:
59
- _ = self._state_view.tasks.get(task_id)
60
-
61
- await self._run_control.wait_for_tasks(params.task_ids)
62
-
63
- failed_tasks = self._state_view.tasks.get_failed_tasks(params.task_ids)
64
- if failed_tasks:
65
- raise TaskFailedError(
66
- message=f"{len(failed_tasks)} tasks failed.",
67
- details={"failed_task_ids": failed_tasks},
68
- wrapping=[
69
- ProtocolCommandFailedError(
70
- original_error=self._state_view.tasks.get_finished(
71
- task_id
72
- ).error
73
- )
74
- for task_id in failed_tasks
75
- ],
76
- )
77
- return SuccessData(public=WaitForTasksResult(task_ids=params.task_ids))
78
-
79
-
80
- class WaitForTasks(
81
- BaseCommand[WaitForTasksParams, WaitForTasksResult, ErrorOccurrence]
82
- ):
83
- """WaitForTasks command model."""
84
-
85
- commandType: WaitForTasksCommandType = "waitForTasks"
86
- params: WaitForTasksParams
87
- result: Optional[WaitForTasksResult] = None
88
-
89
- _ImplementationCls: Type[WaitForTasksImplementation] = WaitForTasksImplementation
90
-
91
-
92
- class WaitForTasksCreate(BaseCommandCreate[WaitForTasksParams]):
93
- """WaitForTasks command request model."""
94
-
95
- commandType: WaitForTasksCommandType = "waitForTasks"
96
- params: WaitForTasksParams
97
-
98
- _CommandCls: Type[WaitForTasks] = WaitForTasks
@@ -1,157 +0,0 @@
1
- """Task handling."""
2
-
3
- from __future__ import annotations
4
- import logging
5
- from typing import Protocol, AsyncIterator
6
- from ..state.state import StateStore
7
- from ..resources import ModelUtils, ConcurrencyProvider
8
- from ..types import Task
9
- import asyncio
10
- import contextlib
11
- from ..actions import ActionDispatcher, FinishTaskAction, StartTaskAction
12
- from ..errors import ErrorOccurrence
13
- from opentrons_shared_data.errors.exceptions import EnumeratedError, PythonException
14
-
15
- log = logging.getLogger(__name__)
16
-
17
-
18
- class TaskFunction(Protocol):
19
- """The function run inside a task protocol."""
20
-
21
- async def __call__(self, task_handler: TaskHandler) -> None:
22
- """The function called inside a task."""
23
- ...
24
-
25
-
26
- class TaskHandler:
27
- """Implementation logic for fast concurrency."""
28
-
29
- _state_store: StateStore
30
- _model_utils: ModelUtils
31
- _concurrency_provider: ConcurrencyProvider
32
-
33
- def __init__(
34
- self,
35
- state_store: StateStore,
36
- action_dispatcher: ActionDispatcher,
37
- model_utils: ModelUtils | None = None,
38
- concurrency_provider: ConcurrencyProvider | None = None,
39
- ) -> None:
40
- """Initialize a TaskHandler instance."""
41
- self._state_store = state_store
42
- self._model_utils = model_utils or ModelUtils()
43
- self._concurrency_provider = concurrency_provider or ConcurrencyProvider()
44
- self._action_dispatcher = action_dispatcher
45
-
46
- async def create_task(
47
- self, task_function: TaskFunction, id: str | None = None
48
- ) -> Task:
49
- """Create a task and immediately schedules it."""
50
- task_id = self._model_utils.ensure_id(id)
51
- asyncio_task = asyncio.create_task(
52
- task_function(task_handler=self), name=f"engine-task-{task_id}"
53
- )
54
-
55
- def _done_callback(task: asyncio.Task[None]) -> None:
56
- try:
57
- maybe_exception = task.exception()
58
- except asyncio.CancelledError as e:
59
- maybe_exception = e
60
- if isinstance(maybe_exception, EnumeratedError):
61
- occurence: ErrorOccurrence | None = ErrorOccurrence.from_failed(
62
- id=self._model_utils.generate_id(),
63
- createdAt=self._model_utils.get_timestamp(),
64
- error=maybe_exception,
65
- )
66
- elif isinstance(maybe_exception, BaseException):
67
- occurence = ErrorOccurrence.from_failed(
68
- id=self._model_utils.generate_id(),
69
- createdAt=self._model_utils.get_timestamp(),
70
- error=PythonException(maybe_exception),
71
- )
72
- else:
73
- occurence = None
74
- try:
75
- self._action_dispatcher.dispatch(
76
- FinishTaskAction(
77
- task_id=task_id,
78
- finished_at=self._model_utils.get_timestamp(),
79
- error=occurence,
80
- ),
81
- )
82
- except BaseException:
83
- log.exception("Exception in task finish dispatch.")
84
-
85
- asyncio_task.add_done_callback(_done_callback)
86
- task = Task(
87
- id=task_id,
88
- createdAt=self._model_utils.get_timestamp(),
89
- asyncioTask=asyncio_task,
90
- )
91
- self._action_dispatcher.dispatch(StartTaskAction(task))
92
- return task
93
-
94
- @staticmethod
95
- def _empty_queue(
96
- queue: "asyncio.Queue[asyncio.Task[None]]", this_task: asyncio.Task[None]
97
- ) -> None:
98
- """Empties the queue."""
99
- try:
100
- while True:
101
- task = queue.get_nowait()
102
- if task is this_task:
103
- break
104
- except asyncio.QueueEmpty:
105
- pass
106
-
107
- @contextlib.asynccontextmanager
108
- async def synchronize_cancel_latest(self, group_id: str) -> AsyncIterator[None]:
109
- """Cancel current task."""
110
- lock = self._concurrency_provider.lock_for_group(group_id)
111
- if lock.locked():
112
- raise asyncio.CancelledError()
113
- async with lock:
114
- yield
115
-
116
- @contextlib.asynccontextmanager
117
- async def synchronize_cancel_previous(self, group_id: str) -> AsyncIterator[None]:
118
- """Cancel previous run."""
119
- queue = self._concurrency_provider.queue_for_group(group_id)
120
- while not queue.empty():
121
- task = queue.get_nowait()
122
- task.cancel()
123
- this_task = asyncio.current_task()
124
- assert this_task is not None
125
- queue.put_nowait(this_task)
126
- try:
127
- yield
128
- except asyncio.CancelledError:
129
- raise
130
- except BaseException:
131
- self._empty_queue(queue, this_task)
132
- raise
133
- else:
134
- self._empty_queue(queue, this_task)
135
-
136
- @contextlib.asynccontextmanager
137
- async def synchronize_sequential(self, group_id: str) -> AsyncIterator[None]:
138
- """Run tasks one after the other."""
139
- lock = self._concurrency_provider.lock_for_group(group_id)
140
- async with lock:
141
- yield
142
-
143
- @contextlib.asynccontextmanager
144
- async def synchronize_concurrent(self, group_id: str) -> AsyncIterator[None]:
145
- """Run a list of tasks at the same time."""
146
- yield
147
-
148
- def cancel_all(self, message: str | None = None) -> None:
149
- """Cancel all asyncio tasks immediately.
150
-
151
- Do not call this more than once synchronously because
152
- that could lead to tasks cancelling more than once.
153
- It can be called if there are no current tasks. In that case
154
- nothing will happen.
155
- """
156
- for task in self._state_store.tasks.get_all_current():
157
- task.asyncioTask.cancel(msg=message)
@@ -1,27 +0,0 @@
1
- """Concurrency primitives providers."""
2
- import asyncio
3
-
4
-
5
- class ConcurrencyProvider:
6
- """Concurrency primitives for engine tasks."""
7
-
8
- def __init__(self) -> None:
9
- """Build a concurrency provider."""
10
- self._locks: dict[str, asyncio.Lock] = {}
11
- self._queues: dict[str, "asyncio.Queue[asyncio.Task[None]]"] = {}
12
-
13
- def lock_for_group(self, group_id: str) -> asyncio.Lock:
14
- """Returns the lock for specified group id."""
15
- try:
16
- return self._locks[group_id]
17
- except KeyError:
18
- self._locks[group_id] = asyncio.Lock()
19
- return self._locks[group_id]
20
-
21
- def queue_for_group(self, group_id: str) -> "asyncio.Queue[asyncio.Task[None]]":
22
- """Returns the queue for specified group id."""
23
- try:
24
- return self._queues[group_id]
25
- except KeyError:
26
- self._queues[group_id] = asyncio.Queue()
27
- return self._queues[group_id]
@@ -1,94 +0,0 @@
1
- """Labware origin math errors."""
2
-
3
- from typing import Any, Dict, Optional, Sequence
4
-
5
- from opentrons.protocol_engine.errors import ProtocolEngineError
6
- from opentrons_shared_data.errors import ErrorCodes
7
- from opentrons_shared_data.errors.exceptions import EnumeratedError
8
-
9
-
10
- class LabwareLocatingFeatureError(ProtocolEngineError):
11
- """Base class for errors related to labware locating features."""
12
-
13
- def __init__(
14
- self,
15
- message: Optional[str] = None,
16
- details: Optional[Dict[str, Any]] = None,
17
- wrapping: Optional[Sequence[EnumeratedError]] = None,
18
- ) -> None:
19
- """Build a LabwareLocatingFeatureError."""
20
- super().__init__(
21
- ErrorCodes.LABWARE_LOCATING_FEATURE_ERROR, message, details, wrapping
22
- )
23
-
24
-
25
- class MissingLocatingFeatureError(LabwareLocatingFeatureError):
26
- """Raised when a labware definition is missing a required locating feature."""
27
-
28
- def __init__(
29
- self,
30
- labware_name: str,
31
- required_feature: str,
32
- message: Optional[str] = None,
33
- details: Optional[Dict[str, Any]] = None,
34
- wrapping: Optional[Sequence[EnumeratedError]] = None,
35
- ) -> None:
36
- """Build a MissingLocatingFeatureError."""
37
- if message is None:
38
- message = f"Expected {labware_name} to have {required_feature} feature"
39
-
40
- if details is None:
41
- details = {
42
- "labware_name": labware_name,
43
- "required_feature": required_feature,
44
- }
45
-
46
- super().__init__(message, details, wrapping)
47
-
48
-
49
- class InvalidLabwarePlacementError(LabwareLocatingFeatureError):
50
- """Raised when a labware cannot be placed in the specified location due to locating feature constraints."""
51
-
52
- def __init__(
53
- self,
54
- feature_name: str,
55
- invalid_placement: str,
56
- message: Optional[str] = None,
57
- details: Optional[Dict[str, Any]] = None,
58
- wrapping: Optional[Sequence[EnumeratedError]] = None,
59
- ) -> None:
60
- """Build an InvalidLabwarePlacementError."""
61
- if message is None:
62
- message = f"{feature_name} feature does not support placement: {invalid_placement}"
63
-
64
- if details is None:
65
- details = {
66
- "feature_name": feature_name,
67
- "invalid_placement": invalid_placement,
68
- }
69
-
70
- super().__init__(message, details, wrapping)
71
-
72
-
73
- class IncompatibleLocatingFeatureError(LabwareLocatingFeatureError):
74
- """Raised when parent and child labware have incompatible locating features."""
75
-
76
- def __init__(
77
- self,
78
- parent_feature: str,
79
- child_feature: str,
80
- message: Optional[str] = None,
81
- details: Optional[Dict[str, Any]] = None,
82
- wrapping: Optional[Sequence[EnumeratedError]] = None,
83
- ) -> None:
84
- """Build an IncompatibleLocatingFeatureError."""
85
- if message is None:
86
- message = f"Incompatible labware features: parent {parent_feature}, child {child_feature}"
87
-
88
- if details is None:
89
- details = {
90
- "parent_feature": parent_feature,
91
- "child_feature": child_feature,
92
- }
93
-
94
- super().__init__(message, details, wrapping)