From 275d9acc634e60d2b39c51852549a6304c20b399 Mon Sep 17 00:00:00 2001 From: Thomas Hardy Date: Mon, 9 Feb 2026 11:11:25 -0800 Subject: [PATCH 1/3] plumb keep-original-wf-id --- temporalio/client.py | 9 +++++++++ tests/test_client.py | 21 +++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/temporalio/client.py b/temporalio/client.py index 7d0ea9f6a..91cb89425 100644 --- a/temporalio/client.py +++ b/temporalio/client.py @@ -4336,12 +4336,20 @@ class SchedulePolicy: exhausted. """ + keep_original_workflow_id: bool = False + """Whether a scheduled workflow keeps its original workflow ID. + + If false, a timestamp may be appended to the scheduled workflow ID for + uniqueness. + """ + @staticmethod def _from_proto(pol: temporalio.api.schedule.v1.SchedulePolicies) -> SchedulePolicy: return SchedulePolicy( overlap=ScheduleOverlapPolicy(int(pol.overlap_policy)), catchup_window=pol.catchup_window.ToTimedelta(), pause_on_failure=pol.pause_on_failure, + keep_original_workflow_id=pol.keep_original_workflow_id, ) def _to_proto(self) -> temporalio.api.schedule.v1.SchedulePolicies: @@ -4353,6 +4361,7 @@ def _to_proto(self) -> temporalio.api.schedule.v1.SchedulePolicies: ), catchup_window=catchup_window, pause_on_failure=self.pause_on_failure, + keep_original_workflow_id=self.keep_original_workflow_id, ) diff --git a/tests/test_client.py b/tests/test_client.py index 833c97fb0..02c18f425 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -14,6 +14,8 @@ from google.protobuf import json_format import temporalio.api.common.v1 +import temporalio.api.enums.v1 +import temporalio.api.schedule.v1 import temporalio.api.workflowservice.v1 import temporalio.common import temporalio.exceptions @@ -826,6 +828,7 @@ async def test_schedule_basics( overlap=ScheduleOverlapPolicy.BUFFER_ONE, catchup_window=timedelta(minutes=5), pause_on_failure=True, + keep_original_workflow_id=True, ), state=ScheduleState( note="sched note 1", paused=True, limited_actions=True, remaining_actions=30 @@ -879,6 +882,7 @@ async def test_schedule_basics( assert desc.schedule == schedule assert "memoval2" == await desc.memo_value("memokey2") + # Update to just change the schedule workflow's task timeout def update_schedule_simple(input: ScheduleUpdateInput) -> ScheduleUpdate: assert input.description.schedule == schedule @@ -1022,6 +1026,23 @@ async def list_ids() -> list[str]: await assert_no_schedules(client) +def test_schedule_policy_keep_original_workflow_id_round_trip() -> None: + policy = SchedulePolicy( + overlap=ScheduleOverlapPolicy.BUFFER_ONE, + catchup_window=timedelta(minutes=5), + pause_on_failure=True, + keep_original_workflow_id=True, + ) + proto = policy._to_proto() + assert proto.keep_original_workflow_id + assert SchedulePolicy._from_proto(proto) == policy + assert not SchedulePolicy._from_proto( + temporalio.api.schedule.v1.SchedulePolicies( + overlap_policy=temporalio.api.enums.v1.ScheduleOverlapPolicy.SCHEDULE_OVERLAP_POLICY_SKIP + ) + ).keep_original_workflow_id + + async def test_schedule_calendar_spec_defaults( client: Client, worker: ExternalWorker, env: WorkflowEnvironment ): From 0a216a60b54da3a1084027547141832eed509de0 Mon Sep 17 00:00:00 2001 From: Thomas Hardy Date: Mon, 9 Feb 2026 15:13:27 -0800 Subject: [PATCH 2/3] improve tests --- tests/test_client.py | 88 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 71 insertions(+), 17 deletions(-) diff --git a/tests/test_client.py b/tests/test_client.py index 02c18f425..c68e5d347 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -14,8 +14,6 @@ from google.protobuf import json_format import temporalio.api.common.v1 -import temporalio.api.enums.v1 -import temporalio.api.schedule.v1 import temporalio.api.workflowservice.v1 import temporalio.common import temporalio.exceptions @@ -828,7 +826,6 @@ async def test_schedule_basics( overlap=ScheduleOverlapPolicy.BUFFER_ONE, catchup_window=timedelta(minutes=5), pause_on_failure=True, - keep_original_workflow_id=True, ), state=ScheduleState( note="sched note 1", paused=True, limited_actions=True, remaining_actions=30 @@ -1026,21 +1023,78 @@ async def list_ids() -> list[str]: await assert_no_schedules(client) -def test_schedule_policy_keep_original_workflow_id_round_trip() -> None: - policy = SchedulePolicy( - overlap=ScheduleOverlapPolicy.BUFFER_ONE, - catchup_window=timedelta(minutes=5), - pause_on_failure=True, - keep_original_workflow_id=True, +async def test_schedule_can_create_with_keep_original_workflow_id_policy( + client: Client, worker: ExternalWorker, env: WorkflowEnvironment +): + if env.supports_time_skipping: + pytest.skip("Java test server doesn't support schedules") + + schedule_id = f"can-create-schedule-with-keep-original-workflow-id-{uuid.uuid4()}" + handle = await client.create_schedule( + schedule_id, + Schedule( + spec=ScheduleSpec(intervals=[ScheduleIntervalSpec(every=timedelta(hours=1))]), + action=ScheduleActionStartWorkflow( + "kitchen_sink", + KSWorkflowParams(actions=[KSAction(result=KSResultAction("some result"))]), + id=f"{schedule_id}-workflow", + task_queue=worker.task_queue, + ), + policy=SchedulePolicy(keep_original_workflow_id=True), + ), ) - proto = policy._to_proto() - assert proto.keep_original_workflow_id - assert SchedulePolicy._from_proto(proto) == policy - assert not SchedulePolicy._from_proto( - temporalio.api.schedule.v1.SchedulePolicies( - overlap_policy=temporalio.api.enums.v1.ScheduleOverlapPolicy.SCHEDULE_OVERLAP_POLICY_SKIP - ) - ).keep_original_workflow_id + + try: + desc = await handle.describe() + assert desc.schedule.policy.keep_original_workflow_id + assert desc.raw_description.schedule.policies.keep_original_workflow_id + finally: + await handle.delete() + + +async def test_schedule_can_update_keep_original_workflow_id_policy( + client: Client, worker: ExternalWorker, env: WorkflowEnvironment +): + if env.supports_time_skipping: + pytest.skip("Java test server doesn't support schedules") + + schedule_id = f"can-update-keep-original-workflow-id-{uuid.uuid4()}" + handle = await client.create_schedule( + schedule_id, + Schedule( + spec=ScheduleSpec(intervals=[ScheduleIntervalSpec(every=timedelta(hours=5))]), + action=ScheduleActionStartWorkflow( + "kitchen_sink", + KSWorkflowParams(actions=[KSAction(result=KSResultAction("some result"))]), + id=f"{schedule_id}-workflow", + task_queue=worker.task_queue, + ), + ), + ) + + try: + desc = await handle.describe() + assert not desc.schedule.policy.keep_original_workflow_id + + def update_enable(input: ScheduleUpdateInput) -> ScheduleUpdate: + input.description.schedule.policy.keep_original_workflow_id = True + return ScheduleUpdate(schedule=input.description.schedule) + + await handle.update(update_enable) + desc = await handle.describe() + assert desc.schedule.policy.keep_original_workflow_id + assert desc.raw_description.schedule.policies.keep_original_workflow_id + + def update_disable(input: ScheduleUpdateInput) -> ScheduleUpdate: + input.description.schedule.policy.keep_original_workflow_id = False + return ScheduleUpdate(schedule=input.description.schedule) + + await handle.update(update_disable) + desc = await handle.describe() + assert not desc.schedule.policy.keep_original_workflow_id + assert not desc.raw_description.schedule.policies.keep_original_workflow_id + finally: + await handle.delete() async def test_schedule_calendar_spec_defaults( From e9fdef104972575718f71a84589c40f022495588 Mon Sep 17 00:00:00 2001 From: Thomas Hardy Date: Mon, 9 Feb 2026 15:54:51 -0800 Subject: [PATCH 3/3] formatting --- tests/test_client.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/tests/test_client.py b/tests/test_client.py index c68e5d347..eda7a9f8c 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -879,7 +879,6 @@ async def test_schedule_basics( assert desc.schedule == schedule assert "memoval2" == await desc.memo_value("memokey2") - # Update to just change the schedule workflow's task timeout def update_schedule_simple(input: ScheduleUpdateInput) -> ScheduleUpdate: assert input.description.schedule == schedule @@ -1033,10 +1032,14 @@ async def test_schedule_can_create_with_keep_original_workflow_id_policy( handle = await client.create_schedule( schedule_id, Schedule( - spec=ScheduleSpec(intervals=[ScheduleIntervalSpec(every=timedelta(hours=1))]), + spec=ScheduleSpec( + intervals=[ScheduleIntervalSpec(every=timedelta(hours=1))] + ), action=ScheduleActionStartWorkflow( "kitchen_sink", - KSWorkflowParams(actions=[KSAction(result=KSResultAction("some result"))]), + KSWorkflowParams( + actions=[KSAction(result=KSResultAction("some result"))] + ), id=f"{schedule_id}-workflow", task_queue=worker.task_queue, ), @@ -1062,10 +1065,14 @@ async def test_schedule_can_update_keep_original_workflow_id_policy( handle = await client.create_schedule( schedule_id, Schedule( - spec=ScheduleSpec(intervals=[ScheduleIntervalSpec(every=timedelta(hours=5))]), + spec=ScheduleSpec( + intervals=[ScheduleIntervalSpec(every=timedelta(hours=5))] + ), action=ScheduleActionStartWorkflow( "kitchen_sink", - KSWorkflowParams(actions=[KSAction(result=KSResultAction("some result"))]), + KSWorkflowParams( + actions=[KSAction(result=KSResultAction("some result"))] + ), id=f"{schedule_id}-workflow", task_queue=worker.task_queue, ),