opentrons 8.7.0a2__py3-none-any.whl → 8.7.0a3__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 (119) hide show
  1. opentrons/_version.py +2 -2
  2. opentrons/drivers/thermocycler/abstract.py +0 -1
  3. opentrons/drivers/thermocycler/driver.py +4 -33
  4. opentrons/drivers/thermocycler/simulator.py +0 -2
  5. opentrons/hardware_control/api.py +5 -24
  6. opentrons/hardware_control/backends/controller.py +2 -8
  7. opentrons/hardware_control/backends/ot3controller.py +0 -3
  8. opentrons/hardware_control/backends/ot3simulator.py +1 -2
  9. opentrons/hardware_control/backends/simulator.py +1 -2
  10. opentrons/hardware_control/backends/subsystem_manager.py +2 -5
  11. opentrons/hardware_control/module_control.py +8 -82
  12. opentrons/hardware_control/modules/__init__.py +0 -3
  13. opentrons/hardware_control/modules/absorbance_reader.py +4 -11
  14. opentrons/hardware_control/modules/flex_stacker.py +9 -38
  15. opentrons/hardware_control/modules/heater_shaker.py +5 -30
  16. opentrons/hardware_control/modules/magdeck.py +4 -8
  17. opentrons/hardware_control/modules/mod_abc.py +5 -13
  18. opentrons/hardware_control/modules/tempdeck.py +5 -25
  19. opentrons/hardware_control/modules/thermocycler.py +10 -56
  20. opentrons/hardware_control/modules/types.py +1 -20
  21. opentrons/hardware_control/modules/utils.py +4 -11
  22. opentrons/hardware_control/nozzle_manager.py +0 -3
  23. opentrons/hardware_control/ot3api.py +5 -26
  24. opentrons/hardware_control/scripts/update_module_fw.py +0 -5
  25. opentrons/hardware_control/types.py +2 -31
  26. opentrons/legacy_commands/protocol_commands.py +0 -20
  27. opentrons/legacy_commands/types.py +0 -42
  28. opentrons/motion_planning/waypoints.py +29 -15
  29. opentrons/protocol_api/__init__.py +0 -5
  30. opentrons/protocol_api/_types.py +1 -6
  31. opentrons/protocol_api/core/common.py +1 -3
  32. opentrons/protocol_api/core/engine/_default_labware_versions.py +11 -32
  33. opentrons/protocol_api/core/engine/labware.py +1 -8
  34. opentrons/protocol_api/core/engine/module_core.py +0 -4
  35. opentrons/protocol_api/core/engine/protocol.py +43 -23
  36. opentrons/protocol_api/core/legacy/legacy_module_core.py +0 -2
  37. opentrons/protocol_api/core/legacy/legacy_protocol_core.py +1 -11
  38. opentrons/protocol_api/core/legacy_simulator/legacy_protocol_core.py +2 -14
  39. opentrons/protocol_api/core/module.py +0 -1
  40. opentrons/protocol_api/core/protocol.py +2 -11
  41. opentrons/protocol_api/module_contexts.py +0 -1
  42. opentrons/protocol_api/protocol_context.py +4 -26
  43. opentrons/protocol_api/robot_context.py +21 -38
  44. opentrons/protocol_api/validation.py +1 -6
  45. opentrons/protocol_engine/actions/__init__.py +2 -4
  46. opentrons/protocol_engine/actions/actions.py +9 -22
  47. opentrons/protocol_engine/clients/sync_client.py +7 -6
  48. opentrons/protocol_engine/commands/__init__.py +0 -42
  49. opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +15 -2
  50. opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +15 -2
  51. opentrons/protocol_engine/commands/aspirate.py +0 -1
  52. opentrons/protocol_engine/commands/command.py +0 -1
  53. opentrons/protocol_engine/commands/command_unions.py +0 -39
  54. opentrons/protocol_engine/commands/dispense.py +0 -1
  55. opentrons/protocol_engine/commands/drop_tip.py +8 -32
  56. opentrons/protocol_engine/commands/movement_common.py +0 -2
  57. opentrons/protocol_engine/commands/pick_up_tip.py +11 -21
  58. opentrons/protocol_engine/commands/thermocycler/run_extended_profile.py +0 -6
  59. opentrons/protocol_engine/commands/thermocycler/run_profile.py +0 -8
  60. opentrons/protocol_engine/commands/thermocycler/set_target_block_temperature.py +1 -17
  61. opentrons/protocol_engine/commands/touch_tip.py +1 -1
  62. opentrons/protocol_engine/commands/unsafe/unsafe_place_labware.py +22 -6
  63. opentrons/protocol_engine/errors/__init__.py +0 -4
  64. opentrons/protocol_engine/errors/exceptions.py +0 -55
  65. opentrons/protocol_engine/execution/__init__.py +0 -2
  66. opentrons/protocol_engine/execution/command_executor.py +0 -8
  67. opentrons/protocol_engine/execution/create_queue_worker.py +1 -5
  68. opentrons/protocol_engine/execution/labware_movement.py +12 -9
  69. opentrons/protocol_engine/execution/movement.py +0 -2
  70. opentrons/protocol_engine/execution/queue_worker.py +0 -4
  71. opentrons/protocol_engine/execution/run_control.py +0 -8
  72. opentrons/protocol_engine/protocol_engine.py +33 -67
  73. opentrons/protocol_engine/resources/__init__.py +0 -2
  74. opentrons/protocol_engine/resources/deck_configuration_provider.py +0 -7
  75. opentrons/protocol_engine/resources/labware_validation.py +6 -10
  76. opentrons/protocol_engine/state/_labware_origin_math.py +636 -0
  77. opentrons/protocol_engine/state/_well_math.py +18 -60
  78. opentrons/protocol_engine/state/addressable_areas.py +0 -2
  79. opentrons/protocol_engine/state/commands.py +7 -7
  80. opentrons/protocol_engine/state/geometry.py +374 -204
  81. opentrons/protocol_engine/state/labware.py +102 -52
  82. opentrons/protocol_engine/state/module_substates/thermocycler_module_substate.py +0 -37
  83. opentrons/protocol_engine/state/modules.py +8 -21
  84. opentrons/protocol_engine/state/motion.py +0 -44
  85. opentrons/protocol_engine/state/state.py +0 -14
  86. opentrons/protocol_engine/state/state_summary.py +0 -2
  87. opentrons/protocol_engine/state/tips.py +258 -177
  88. opentrons/protocol_engine/state/update_types.py +9 -16
  89. opentrons/protocol_engine/types/__init__.py +3 -9
  90. opentrons/protocol_engine/types/deck_configuration.py +1 -5
  91. opentrons/protocol_engine/types/instrument.py +1 -8
  92. opentrons/protocol_engine/types/labware.py +13 -1
  93. opentrons/protocol_engine/types/module.py +0 -10
  94. opentrons/protocol_engine/types/tip.py +0 -9
  95. opentrons/protocol_runner/create_simulating_orchestrator.py +2 -29
  96. opentrons/protocol_runner/run_orchestrator.py +2 -18
  97. opentrons/protocols/api_support/definitions.py +1 -1
  98. opentrons/protocols/api_support/types.py +1 -2
  99. opentrons/simulate.py +15 -48
  100. opentrons/system/camera.py +1 -1
  101. {opentrons-8.7.0a2.dist-info → opentrons-8.7.0a3.dist-info}/METADATA +4 -4
  102. {opentrons-8.7.0a2.dist-info → opentrons-8.7.0a3.dist-info}/RECORD +105 -118
  103. opentrons/protocol_api/core/engine/tasks.py +0 -35
  104. opentrons/protocol_api/core/legacy/tasks.py +0 -19
  105. opentrons/protocol_api/core/legacy_simulator/tasks.py +0 -19
  106. opentrons/protocol_api/core/tasks.py +0 -31
  107. opentrons/protocol_api/tasks.py +0 -48
  108. opentrons/protocol_engine/commands/create_timer.py +0 -83
  109. opentrons/protocol_engine/commands/set_tip_state.py +0 -97
  110. opentrons/protocol_engine/commands/wait_for_tasks.py +0 -98
  111. opentrons/protocol_engine/execution/task_handler.py +0 -157
  112. opentrons/protocol_engine/resources/concurrency_provider.py +0 -27
  113. opentrons/protocol_engine/state/labware_origin_math/errors.py +0 -94
  114. opentrons/protocol_engine/state/labware_origin_math/stackup_origin_to_labware_origin.py +0 -1331
  115. opentrons/protocol_engine/state/tasks.py +0 -139
  116. opentrons/protocol_engine/types/tasks.py +0 -38
  117. {opentrons-8.7.0a2.dist-info → opentrons-8.7.0a3.dist-info}/WHEEL +0 -0
  118. {opentrons-8.7.0a2.dist-info → opentrons-8.7.0a3.dist-info}/entry_points.txt +0 -0
  119. {opentrons-8.7.0a2.dist-info → opentrons-8.7.0a3.dist-info}/licenses/LICENSE +0 -0
@@ -1,31 +0,0 @@
1
- from abc import abstractmethod, ABC
2
- from datetime import datetime
3
- from typing import TypeVar
4
-
5
-
6
- class AbstractTaskCore(ABC):
7
- @abstractmethod
8
- def get_created_at_timestamp(self) -> datetime:
9
- """Get the createdAt timestamp of the task."""
10
- ...
11
-
12
- @abstractmethod
13
- def is_done(self) -> bool:
14
- """Returns ``True`` if the task is done."""
15
- ...
16
-
17
- @abstractmethod
18
- def is_started(self) -> bool:
19
- """Returns ``True`` if the task has started."""
20
- ...
21
-
22
- @abstractmethod
23
- def get_finished_at_timestamp(self) -> datetime | None:
24
- """The timestamp of the when the task finished.
25
-
26
- Returns ``None`` if the task hasn't finished yet.
27
- """
28
- ...
29
-
30
-
31
- TaskCoreType = TypeVar("TaskCoreType", bound=AbstractTaskCore)
@@ -1,48 +0,0 @@
1
- """Data for concurrent protocol tasks."""
2
- from typing import TYPE_CHECKING
3
- from datetime import datetime
4
- from opentrons.protocols.api_support.util import requires_version
5
- from opentrons.protocols.api_support.types import APIVersion
6
-
7
- if TYPE_CHECKING:
8
- from .core.common import TaskCore
9
-
10
-
11
- class Task:
12
- """A concurrent protocol task created by a protocol API function.
13
-
14
- .. versionadded:: 2.27
15
- """
16
-
17
- def __init__(self, core: "TaskCore", api_version: APIVersion) -> None:
18
- """Initialize a Task."""
19
- self._core = core
20
- self._api_version = api_version
21
-
22
- @property
23
- @requires_version(2, 27)
24
- def created_at(self) -> datetime:
25
- """The timestamp of when the task was created."""
26
- return self._core.get_created_at_timestamp()
27
-
28
- @property
29
- @requires_version(2, 27)
30
- def done(self) -> bool:
31
- """Returns ``True`` if the task is done."""
32
- return self._core.is_done()
33
-
34
- @property
35
- @requires_version(2, 27)
36
- def started(self) -> bool:
37
- """Returns ``True`` if the task has started."""
38
- return self._core.is_started()
39
- ...
40
-
41
- @property
42
- @requires_version(2, 27)
43
- def finished_at(self) -> datetime | None:
44
- """The timestamp of the when the task finished.
45
-
46
- Returns ``None`` if the task hasn't finished yet.
47
- """
48
- return self._core.get_finished_at_timestamp()
@@ -1,83 +0,0 @@
1
- """CreateTimer command request, result, and implementation models."""
2
-
3
- from __future__ import annotations
4
- from pydantic import BaseModel, Field
5
- from typing import Optional, Type, TYPE_CHECKING
6
- from typing_extensions import Literal
7
-
8
- from .command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
9
- from ..errors.error_occurrence import ErrorOccurrence
10
-
11
- if TYPE_CHECKING:
12
- from ..execution import TaskHandler, RunControlHandler
13
-
14
-
15
- CreateTimerCommandType = Literal["createTimer"]
16
-
17
-
18
- class CreateTimerParams(BaseModel):
19
- """Payload required to annotate execution with a CreateTimer."""
20
-
21
- time: float = Field(
22
- ...,
23
- description="The time before the timer should elapse in seconds. This is the minimum time before the timer elapses; it may in practice take longer than this.",
24
- )
25
- task_id: str | None = Field(
26
- None,
27
- description="The id of the timer task",
28
- )
29
-
30
-
31
- class CreateTimerResult(BaseModel):
32
- """Result data from the execution of a CreateTimer command."""
33
-
34
- task_id: str = Field(..., description="The id of the timer task")
35
- time: float = Field(..., description="The same time as the parameter.")
36
-
37
-
38
- class CreateTimerImplementation(
39
- AbstractCommandImpl[CreateTimerParams, SuccessData[CreateTimerResult]]
40
- ):
41
- """CreateTimer command implementation."""
42
-
43
- def __init__(
44
- self,
45
- task_handler: TaskHandler,
46
- run_control: RunControlHandler,
47
- **kwargs: object,
48
- ) -> None:
49
- self._task_handler = task_handler
50
- self._run_control = run_control
51
-
52
- async def execute(
53
- self, params: CreateTimerParams
54
- ) -> SuccessData[CreateTimerResult]:
55
- """No operation taken other than capturing message in command."""
56
-
57
- async def timer(task_handler: TaskHandler) -> None:
58
- async with task_handler.synchronize_concurrent("createTimer"):
59
- await self._run_control.wait_for_duration(params.time)
60
-
61
- task = await self._task_handler.create_task(timer, params.task_id)
62
- return SuccessData(
63
- public=CreateTimerResult(task_id=task.id, time=params.time),
64
- )
65
-
66
-
67
- class CreateTimer(BaseCommand[CreateTimerParams, CreateTimerResult, ErrorOccurrence]):
68
- """CreateTimer command model."""
69
-
70
- commandType: CreateTimerCommandType = "createTimer"
71
- params: CreateTimerParams
72
- result: Optional[CreateTimerResult] = None
73
-
74
- _ImplementationCls: Type[CreateTimerImplementation] = CreateTimerImplementation
75
-
76
-
77
- class CreateTimerCreate(BaseCommandCreate[CreateTimerParams]):
78
- """CreateTimer command request model."""
79
-
80
- commandType: CreateTimerCommandType = "createTimer"
81
- params: CreateTimerParams
82
-
83
- _CommandCls: Type[CreateTimer] = CreateTimer
@@ -1,97 +0,0 @@
1
- """Set tip state command request, result, and implementation models."""
2
-
3
- from __future__ import annotations
4
- from pydantic import BaseModel, Field
5
- from typing import TYPE_CHECKING, Optional, Type, List
6
- from typing_extensions import Literal
7
-
8
- from ..errors.error_occurrence import ErrorOccurrence
9
- from ..state.update_types import StateUpdate
10
- from ..types import TipRackWellState
11
-
12
-
13
- from .command import (
14
- AbstractCommandImpl,
15
- BaseCommand,
16
- BaseCommandCreate,
17
- SuccessData,
18
- )
19
-
20
- if TYPE_CHECKING:
21
- from ..state.state import StateView
22
-
23
-
24
- SetTipStateCommandType = Literal["setTipState"]
25
-
26
-
27
- class SetTipStateParams(BaseModel):
28
- """Payload needed to set tip wells of a tip rack to the requested state."""
29
-
30
- labwareId: str = Field(
31
- ..., description="Identifier of tip rack labware to set tip wells in."
32
- )
33
- wellNames: List[str] = Field(
34
- ..., description="Names of the well to set tip well state for."
35
- )
36
- tipWellState: TipRackWellState = Field(
37
- ..., description="State to set tip wells to."
38
- )
39
-
40
-
41
- class SetTipStateResult(BaseModel):
42
- """Result data from the execution of a setTipState command."""
43
-
44
- pass
45
-
46
-
47
- class SetTipStateImplementation(
48
- AbstractCommandImpl[SetTipStateParams, SuccessData[SetTipStateResult]]
49
- ):
50
- """Set tip state command implementation."""
51
-
52
- def __init__(
53
- self,
54
- state_view: StateView,
55
- **kwargs: object,
56
- ) -> None:
57
- self._state_view = state_view
58
-
59
- async def execute(
60
- self, params: SetTipStateParams
61
- ) -> SuccessData[SetTipStateResult]:
62
- """Set the tip rack wells to the requested state."""
63
- labware_id = params.labwareId
64
- well_names = params.wellNames
65
-
66
- self._state_view.labware.raise_if_not_tip_rack(labware_id=labware_id)
67
- self._state_view.labware.raise_if_wells_are_invalid(
68
- labware_id=labware_id, well_names=well_names
69
- )
70
-
71
- return SuccessData(
72
- public=SetTipStateResult(),
73
- state_update=StateUpdate().update_tip_rack_well_state(
74
- tip_state=params.tipWellState,
75
- labware_id=labware_id,
76
- well_names=well_names,
77
- ),
78
- )
79
-
80
-
81
- class SetTipState(BaseCommand[SetTipStateParams, SetTipStateResult, ErrorOccurrence]):
82
- """Set tip state command model."""
83
-
84
- commandType: SetTipStateCommandType = "setTipState"
85
- params: SetTipStateParams
86
- result: Optional[SetTipStateResult] = None
87
-
88
- _ImplementationCls: Type[SetTipStateImplementation] = SetTipStateImplementation
89
-
90
-
91
- class SetTipStateCreate(BaseCommandCreate[SetTipStateParams]):
92
- """Set tip state command creation request model."""
93
-
94
- commandType: SetTipStateCommandType = "setTipState"
95
- params: SetTipStateParams
96
-
97
- _CommandCls: Type[SetTipState] = SetTipState
@@ -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)