diff --git a/temporalio/client.py b/temporalio/client.py index f781774c1..1ae8de106 100644 --- a/temporalio/client.py +++ b/temporalio/client.py @@ -1292,6 +1292,7 @@ async def start_activity( search_attributes: temporalio.common.TypedSearchAttributes | None = None, summary: str | None = None, priority: temporalio.common.Priority = temporalio.common.Priority.default, + start_delay: timedelta | None = None, rpc_metadata: Mapping[str, str | bytes] = {}, rpc_timeout: timedelta | None = None, ) -> ActivityHandle[ReturnType]: ... @@ -1314,6 +1315,7 @@ async def start_activity( search_attributes: temporalio.common.TypedSearchAttributes | None = None, summary: str | None = None, priority: temporalio.common.Priority = temporalio.common.Priority.default, + start_delay: timedelta | None = None, rpc_metadata: Mapping[str, str | bytes] = {}, rpc_timeout: timedelta | None = None, ) -> ActivityHandle[ReturnType]: ... @@ -1337,6 +1339,7 @@ async def start_activity( search_attributes: temporalio.common.TypedSearchAttributes | None = None, summary: str | None = None, priority: temporalio.common.Priority = temporalio.common.Priority.default, + start_delay: timedelta | None = None, rpc_metadata: Mapping[str, str | bytes] = {}, rpc_timeout: timedelta | None = None, ) -> ActivityHandle[ReturnType]: ... @@ -1360,6 +1363,7 @@ async def start_activity( search_attributes: temporalio.common.TypedSearchAttributes | None = None, summary: str | None = None, priority: temporalio.common.Priority = temporalio.common.Priority.default, + start_delay: timedelta | None = None, rpc_metadata: Mapping[str, str | bytes] = {}, rpc_timeout: timedelta | None = None, ) -> ActivityHandle[ReturnType]: ... @@ -1383,6 +1387,7 @@ async def start_activity( search_attributes: temporalio.common.TypedSearchAttributes | None = None, summary: str | None = None, priority: temporalio.common.Priority = temporalio.common.Priority.default, + start_delay: timedelta | None = None, rpc_metadata: Mapping[str, str | bytes] = {}, rpc_timeout: timedelta | None = None, ) -> ActivityHandle[ReturnType]: ... @@ -1406,6 +1411,7 @@ async def start_activity( search_attributes: temporalio.common.TypedSearchAttributes | None = None, summary: str | None = None, priority: temporalio.common.Priority = temporalio.common.Priority.default, + start_delay: timedelta | None = None, rpc_metadata: Mapping[str, str | bytes] = {}, rpc_timeout: timedelta | None = None, ) -> ActivityHandle[ReturnType]: ... @@ -1431,6 +1437,7 @@ async def start_activity( search_attributes: temporalio.common.TypedSearchAttributes | None = None, summary: str | None = None, priority: temporalio.common.Priority = temporalio.common.Priority.default, + start_delay: timedelta | None = None, rpc_metadata: Mapping[str, str | bytes] = {}, rpc_timeout: timedelta | None = None, ) -> ActivityHandle[Any]: ... @@ -1457,6 +1464,7 @@ async def start_activity( search_attributes: temporalio.common.TypedSearchAttributes | None = None, summary: str | None = None, priority: temporalio.common.Priority = temporalio.common.Priority.default, + start_delay: timedelta | None = None, rpc_metadata: Mapping[str, str | bytes] = {}, rpc_timeout: timedelta | None = None, ) -> ActivityHandle[ReturnType]: @@ -1485,6 +1493,8 @@ async def start_activity( summary: A single-line fixed summary for this activity that may appear in the UI/CLI. This can be in single-line Temporal markdown format. priority: Priority of the activity execution. + start_delay: Time to wait before dispatching the activity. + This delay is not applied to retry attempts. rpc_metadata: Headers used on the RPC call. rpc_timeout: Optional RPC deadline to set for the RPC call. @@ -1510,6 +1520,7 @@ async def start_activity( retry_policy=retry_policy, search_attributes=search_attributes, summary=summary, + start_delay=start_delay, headers={}, rpc_metadata=rpc_metadata, rpc_timeout=rpc_timeout, @@ -1535,6 +1546,7 @@ async def execute_activity( search_attributes: temporalio.common.TypedSearchAttributes | None = None, summary: str | None = None, priority: temporalio.common.Priority = temporalio.common.Priority.default, + start_delay: timedelta | None = None, rpc_metadata: Mapping[str, str | bytes] = {}, rpc_timeout: timedelta | None = None, ) -> ReturnType: ... @@ -1557,6 +1569,7 @@ async def execute_activity( search_attributes: temporalio.common.TypedSearchAttributes | None = None, summary: str | None = None, priority: temporalio.common.Priority = temporalio.common.Priority.default, + start_delay: timedelta | None = None, rpc_metadata: Mapping[str, str | bytes] = {}, rpc_timeout: timedelta | None = None, ) -> ReturnType: ... @@ -1580,6 +1593,7 @@ async def execute_activity( search_attributes: temporalio.common.TypedSearchAttributes | None = None, summary: str | None = None, priority: temporalio.common.Priority = temporalio.common.Priority.default, + start_delay: timedelta | None = None, rpc_metadata: Mapping[str, str | bytes] = {}, rpc_timeout: timedelta | None = None, ) -> ReturnType: ... @@ -1603,6 +1617,7 @@ async def execute_activity( search_attributes: temporalio.common.TypedSearchAttributes | None = None, summary: str | None = None, priority: temporalio.common.Priority = temporalio.common.Priority.default, + start_delay: timedelta | None = None, rpc_metadata: Mapping[str, str | bytes] = {}, rpc_timeout: timedelta | None = None, ) -> ReturnType: ... @@ -1626,6 +1641,7 @@ async def execute_activity( search_attributes: temporalio.common.TypedSearchAttributes | None = None, summary: str | None = None, priority: temporalio.common.Priority = temporalio.common.Priority.default, + start_delay: timedelta | None = None, rpc_metadata: Mapping[str, str | bytes] = {}, rpc_timeout: timedelta | None = None, ) -> ReturnType: ... @@ -1649,6 +1665,7 @@ async def execute_activity( search_attributes: temporalio.common.TypedSearchAttributes | None = None, summary: str | None = None, priority: temporalio.common.Priority = temporalio.common.Priority.default, + start_delay: timedelta | None = None, rpc_metadata: Mapping[str, str | bytes] = {}, rpc_timeout: timedelta | None = None, ) -> ReturnType: ... @@ -1674,6 +1691,7 @@ async def execute_activity( search_attributes: temporalio.common.TypedSearchAttributes | None = None, summary: str | None = None, priority: temporalio.common.Priority = temporalio.common.Priority.default, + start_delay: timedelta | None = None, rpc_metadata: Mapping[str, str | bytes] = {}, rpc_timeout: timedelta | None = None, ) -> Any: ... @@ -1700,6 +1718,7 @@ async def execute_activity( search_attributes: temporalio.common.TypedSearchAttributes | None = None, summary: str | None = None, priority: temporalio.common.Priority = temporalio.common.Priority.default, + start_delay: timedelta | None = None, rpc_metadata: Mapping[str, str | bytes] = {}, rpc_timeout: timedelta | None = None, ) -> ReturnType: @@ -1734,6 +1753,7 @@ async def execute_activity( search_attributes=search_attributes, summary=summary, priority=priority, + start_delay=start_delay, rpc_metadata=rpc_metadata, rpc_timeout=rpc_timeout, ) @@ -1757,6 +1777,7 @@ async def start_activity_class( search_attributes: temporalio.common.TypedSearchAttributes | None = None, summary: str | None = None, priority: temporalio.common.Priority = temporalio.common.Priority.default, + start_delay: timedelta | None = None, rpc_metadata: Mapping[str, str | bytes] = {}, rpc_timeout: timedelta | None = None, ) -> ActivityHandle[ReturnType]: ... @@ -1779,6 +1800,7 @@ async def start_activity_class( search_attributes: temporalio.common.TypedSearchAttributes | None = None, summary: str | None = None, priority: temporalio.common.Priority = temporalio.common.Priority.default, + start_delay: timedelta | None = None, rpc_metadata: Mapping[str, str | bytes] = {}, rpc_timeout: timedelta | None = None, ) -> ActivityHandle[ReturnType]: ... @@ -1802,6 +1824,7 @@ async def start_activity_class( search_attributes: temporalio.common.TypedSearchAttributes | None = None, summary: str | None = None, priority: temporalio.common.Priority = temporalio.common.Priority.default, + start_delay: timedelta | None = None, rpc_metadata: Mapping[str, str | bytes] = {}, rpc_timeout: timedelta | None = None, ) -> ActivityHandle[ReturnType]: ... @@ -1825,6 +1848,7 @@ async def start_activity_class( search_attributes: temporalio.common.TypedSearchAttributes | None = None, summary: str | None = None, priority: temporalio.common.Priority = temporalio.common.Priority.default, + start_delay: timedelta | None = None, rpc_metadata: Mapping[str, str | bytes] = {}, rpc_timeout: timedelta | None = None, ) -> ActivityHandle[ReturnType]: ... @@ -1848,6 +1872,7 @@ async def start_activity_class( search_attributes: temporalio.common.TypedSearchAttributes | None = None, summary: str | None = None, priority: temporalio.common.Priority = temporalio.common.Priority.default, + start_delay: timedelta | None = None, rpc_metadata: Mapping[str, str | bytes] = {}, rpc_timeout: timedelta | None = None, ) -> ActivityHandle[ReturnType]: ... @@ -1871,6 +1896,7 @@ async def start_activity_class( search_attributes: temporalio.common.TypedSearchAttributes | None = None, summary: str | None = None, priority: temporalio.common.Priority = temporalio.common.Priority.default, + start_delay: timedelta | None = None, rpc_metadata: Mapping[str, str | bytes] = {}, rpc_timeout: timedelta | None = None, ) -> ActivityHandle[ReturnType]: ... @@ -1894,6 +1920,7 @@ async def start_activity_class( search_attributes: temporalio.common.TypedSearchAttributes | None = None, summary: str | None = None, priority: temporalio.common.Priority = temporalio.common.Priority.default, + start_delay: timedelta | None = None, rpc_metadata: Mapping[str, str | bytes] = {}, rpc_timeout: timedelta | None = None, ) -> ActivityHandle[Any]: @@ -1921,6 +1948,7 @@ async def start_activity_class( search_attributes=search_attributes, summary=summary, priority=priority, + start_delay=start_delay, rpc_metadata=rpc_metadata, rpc_timeout=rpc_timeout, ) @@ -1943,6 +1971,7 @@ async def execute_activity_class( search_attributes: temporalio.common.TypedSearchAttributes | None = None, summary: str | None = None, priority: temporalio.common.Priority = temporalio.common.Priority.default, + start_delay: timedelta | None = None, rpc_metadata: Mapping[str, str | bytes] = {}, rpc_timeout: timedelta | None = None, ) -> ReturnType: ... @@ -1965,6 +1994,7 @@ async def execute_activity_class( search_attributes: temporalio.common.TypedSearchAttributes | None = None, summary: str | None = None, priority: temporalio.common.Priority = temporalio.common.Priority.default, + start_delay: timedelta | None = None, rpc_metadata: Mapping[str, str | bytes] = {}, rpc_timeout: timedelta | None = None, ) -> ReturnType: ... @@ -1988,6 +2018,7 @@ async def execute_activity_class( search_attributes: temporalio.common.TypedSearchAttributes | None = None, summary: str | None = None, priority: temporalio.common.Priority = temporalio.common.Priority.default, + start_delay: timedelta | None = None, rpc_metadata: Mapping[str, str | bytes] = {}, rpc_timeout: timedelta | None = None, ) -> ReturnType: ... @@ -2011,6 +2042,7 @@ async def execute_activity_class( search_attributes: temporalio.common.TypedSearchAttributes | None = None, summary: str | None = None, priority: temporalio.common.Priority = temporalio.common.Priority.default, + start_delay: timedelta | None = None, rpc_metadata: Mapping[str, str | bytes] = {}, rpc_timeout: timedelta | None = None, ) -> ReturnType: ... @@ -2034,6 +2066,7 @@ async def execute_activity_class( search_attributes: temporalio.common.TypedSearchAttributes | None = None, summary: str | None = None, priority: temporalio.common.Priority = temporalio.common.Priority.default, + start_delay: timedelta | None = None, rpc_metadata: Mapping[str, str | bytes] = {}, rpc_timeout: timedelta | None = None, ) -> ReturnType: ... @@ -2057,6 +2090,7 @@ async def execute_activity_class( search_attributes: temporalio.common.TypedSearchAttributes | None = None, summary: str | None = None, priority: temporalio.common.Priority = temporalio.common.Priority.default, + start_delay: timedelta | None = None, rpc_metadata: Mapping[str, str | bytes] = {}, rpc_timeout: timedelta | None = None, ) -> ReturnType: ... @@ -2080,6 +2114,7 @@ async def execute_activity_class( search_attributes: temporalio.common.TypedSearchAttributes | None = None, summary: str | None = None, priority: temporalio.common.Priority = temporalio.common.Priority.default, + start_delay: timedelta | None = None, rpc_metadata: Mapping[str, str | bytes] = {}, rpc_timeout: timedelta | None = None, ) -> Any: @@ -2107,6 +2142,7 @@ async def execute_activity_class( search_attributes=search_attributes, summary=summary, priority=priority, + start_delay=start_delay, rpc_metadata=rpc_metadata, rpc_timeout=rpc_timeout, ) @@ -2129,6 +2165,7 @@ async def start_activity_method( search_attributes: temporalio.common.TypedSearchAttributes | None = None, summary: str | None = None, priority: temporalio.common.Priority = temporalio.common.Priority.default, + start_delay: timedelta | None = None, rpc_metadata: Mapping[str, str | bytes] = {}, rpc_timeout: timedelta | None = None, ) -> ActivityHandle[ReturnType]: ... @@ -2152,6 +2189,7 @@ async def start_activity_method( search_attributes: temporalio.common.TypedSearchAttributes | None = None, summary: str | None = None, priority: temporalio.common.Priority = temporalio.common.Priority.default, + start_delay: timedelta | None = None, rpc_metadata: Mapping[str, str | bytes] = {}, rpc_timeout: timedelta | None = None, ) -> ActivityHandle[ReturnType]: ... @@ -2177,6 +2215,7 @@ async def start_activity_method( search_attributes: temporalio.common.TypedSearchAttributes | None = None, summary: str | None = None, priority: temporalio.common.Priority = temporalio.common.Priority.default, + start_delay: timedelta | None = None, rpc_metadata: Mapping[str, str | bytes] = {}, rpc_timeout: timedelta | None = None, ) -> ActivityHandle[ReturnType]: ... @@ -2200,6 +2239,7 @@ async def start_activity_method( search_attributes: temporalio.common.TypedSearchAttributes | None = None, summary: str | None = None, priority: temporalio.common.Priority = temporalio.common.Priority.default, + start_delay: timedelta | None = None, rpc_metadata: Mapping[str, str | bytes] = {}, rpc_timeout: timedelta | None = None, ) -> ActivityHandle[ReturnType]: ... @@ -2223,6 +2263,7 @@ async def start_activity_method( search_attributes: temporalio.common.TypedSearchAttributes | None = None, summary: str | None = None, priority: temporalio.common.Priority = temporalio.common.Priority.default, + start_delay: timedelta | None = None, rpc_metadata: Mapping[str, str | bytes] = {}, rpc_timeout: timedelta | None = None, ) -> ActivityHandle[Any]: @@ -2250,6 +2291,7 @@ async def start_activity_method( search_attributes=search_attributes, summary=summary, priority=priority, + start_delay=start_delay, rpc_metadata=rpc_metadata, rpc_timeout=rpc_timeout, ) @@ -2272,6 +2314,7 @@ async def execute_activity_method( search_attributes: temporalio.common.TypedSearchAttributes | None = None, summary: str | None = None, priority: temporalio.common.Priority = temporalio.common.Priority.default, + start_delay: timedelta | None = None, rpc_metadata: Mapping[str, str | bytes] = {}, rpc_timeout: timedelta | None = None, ) -> ReturnType: ... @@ -2295,6 +2338,7 @@ async def execute_activity_method( search_attributes: temporalio.common.TypedSearchAttributes | None = None, summary: str | None = None, priority: temporalio.common.Priority = temporalio.common.Priority.default, + start_delay: timedelta | None = None, rpc_metadata: Mapping[str, str | bytes] = {}, rpc_timeout: timedelta | None = None, ) -> ReturnType: ... @@ -2320,6 +2364,7 @@ async def execute_activity_method( search_attributes: temporalio.common.TypedSearchAttributes | None = None, summary: str | None = None, priority: temporalio.common.Priority = temporalio.common.Priority.default, + start_delay: timedelta | None = None, rpc_metadata: Mapping[str, str | bytes] = {}, rpc_timeout: timedelta | None = None, ) -> ReturnType: ... @@ -2343,6 +2388,7 @@ async def execute_activity_method( search_attributes: temporalio.common.TypedSearchAttributes | None = None, summary: str | None = None, priority: temporalio.common.Priority = temporalio.common.Priority.default, + start_delay: timedelta | None = None, rpc_metadata: Mapping[str, str | bytes] = {}, rpc_timeout: timedelta | None = None, ) -> ReturnType: ... @@ -2366,6 +2412,7 @@ async def execute_activity_method( search_attributes: temporalio.common.TypedSearchAttributes | None = None, summary: str | None = None, priority: temporalio.common.Priority = temporalio.common.Priority.default, + start_delay: timedelta | None = None, rpc_metadata: Mapping[str, str | bytes] = {}, rpc_timeout: timedelta | None = None, ) -> Any: @@ -2393,6 +2440,7 @@ async def execute_activity_method( search_attributes=search_attributes, summary=summary, priority=priority, + start_delay=start_delay, rpc_metadata=rpc_metadata, rpc_timeout=rpc_timeout, ) @@ -7441,6 +7489,7 @@ class StartActivityInput: priority: temporalio.common.Priority search_attributes: temporalio.common.TypedSearchAttributes | None summary: str | None + start_delay: timedelta | None headers: Mapping[str, temporalio.api.common.v1.Payload] rpc_metadata: Mapping[str, str | bytes] rpc_timeout: timedelta | None @@ -8400,6 +8449,8 @@ async def start_activity(self, input: StartActivityInput) -> ActivityHandle[Any] raise ValueError( "Activity must have start_to_close_timeout or schedule_to_close_timeout" ) + if input.start_delay is not None and input.start_delay < timedelta(0): + raise ValueError("start_delay must be non-negative") req = await self._build_start_activity_execution_request(input) resp: temporalio.api.workflowservice.v1.StartActivityExecutionResponse @@ -8476,6 +8527,8 @@ async def _build_start_activity_execution_request( req.schedule_to_start_timeout.FromTimedelta(input.schedule_to_start_timeout) if input.heartbeat_timeout is not None: req.heartbeat_timeout.FromTimedelta(input.heartbeat_timeout) + if input.start_delay is not None: + req.start_delay.FromTimedelta(input.start_delay) if input.retry_policy is not None: input.retry_policy.apply_to_proto(req.retry_policy) diff --git a/tests/test_activity.py b/tests/test_activity.py index 8e851c8b1..8ed4729ec 100644 --- a/tests/test_activity.py +++ b/tests/test_activity.py @@ -220,6 +220,7 @@ async def test_start_activity_calls_interceptor( activity_id = str(uuid.uuid4()) task_queue = str(uuid.uuid4()) + start_delay = timedelta(seconds=3) await intercepted_client.start_activity( increment, @@ -227,6 +228,7 @@ async def test_start_activity_calls_interceptor( id=activity_id, task_queue=task_queue, start_to_close_timeout=timedelta(seconds=5), + start_delay=start_delay, ) assert len(interceptor.start_activity_calls) == 1 @@ -234,6 +236,7 @@ async def test_start_activity_calls_interceptor( assert call.id == activity_id assert call.task_queue == task_queue assert call.activity_type == "increment" + assert call.start_delay == start_delay async def test_describe_activity_calls_interceptor( @@ -413,6 +416,18 @@ async def test_count_activities_calls_interceptor( assert call.query == query +async def test_start_activity_rejects_negative_start_delay(client: Client): + with pytest.raises(ValueError, match="start_delay must be non-negative"): + await client.start_activity( + increment, + args=(1,), + id=str(uuid.uuid4()), + task_queue=str(uuid.uuid4()), + start_to_close_timeout=timedelta(seconds=5), + start_delay=timedelta(seconds=-1), + ) + + async def test_get_result(client: Client, env: WorkflowEnvironment): if env.supports_time_skipping: pytest.skip(