Skip to content

Commit be50780

Browse files
committed
Try to prototype new decorator
1 parent 377ec89 commit be50780

File tree

1 file changed

+43
-29
lines changed

1 file changed

+43
-29
lines changed

Diff for: update/job_runner_I1_native.py

+43-29
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22
from contextlib import asynccontextmanager
33
from dataclasses import dataclass
44
from datetime import datetime, timedelta
5-
import inspect
65
import logging
76
from typing import Awaitable, Callable, Optional, Type
87

98
from temporalio import common, workflow, activity
109
from temporalio.client import Client, WorkflowHandle
1110
from temporalio.worker import Worker
11+
from temporalio.workflow import UpdateMethodMultiParam
1212

1313

1414
JobID = str
@@ -42,6 +42,13 @@ class JobOutput:
4242
UpdateID = str
4343
Workflow = Type
4444

45+
46+
@dataclass
47+
class Update:
48+
id: UpdateID
49+
arg: I
50+
51+
4552
_sdk_internals_pending_tasks_count = 0
4653
_sdk_internals_handler_mutex = asyncio.Lock()
4754

@@ -67,41 +74,44 @@ async def _sdk_internals__track_pending__wait_until_ready__serialize_execution(
6774
_sdk_internals_pending_tasks_count -= 1
6875

6976

70-
class SDKInternals:
71-
# Here, the SDK is wrapping the user's update handlers with the required wait-until-ready,
72-
# pending tasks tracking, and synchronization functionality. This is a fake implementation: the
73-
# real implementation will automatically inspect and wrap the user's declared update handlers.
77+
_original_workflow_update_decorator = workflow.update
7478

75-
def ready_to_execute(self, arg: I) -> bool:
76-
# Implemented by user
77-
return True
7879

79-
@workflow.update
80-
async def run_shell_script_job(self, arg: I) -> O:
81-
handler = getattr(self, "_" + inspect.currentframe().f_code.co_name)
82-
async with _sdk_internals__track_pending__wait_until_ready__serialize_execution(
83-
lambda: self.ready_to_execute(arg)
84-
):
85-
return await handler(arg)
80+
def _new_workflow_update_decorator(
81+
execute_condition: Callable[[Workflow, Update], bool], **kwargs
82+
) -> Callable[
83+
[Callable[[Workflow, I], Awaitable[O]]],
84+
UpdateMethodMultiParam[[Workflow, I], O],
85+
]:
86+
def decorator(
87+
handler: Callable[[Workflow, I], Awaitable[O]]
88+
) -> UpdateMethodMultiParam:
89+
async def wrapped_handler(self: Workflow, arg: I):
90+
async with _sdk_internals__track_pending__wait_until_ready__serialize_execution(
91+
lambda: execute_condition(self, Update(arg.id, arg))
92+
):
93+
return await handler(self, arg)
94+
95+
dec = (
96+
_original_workflow_update_decorator(**kwargs)
97+
if kwargs
98+
else _original_workflow_update_decorator
99+
)
100+
return dec(wrapped_handler)
86101

87-
@workflow.update
88-
async def run_python_job(self, arg: I) -> O:
89-
handler = getattr(self, "_" + inspect.currentframe().f_code.co_name)
90-
async with _sdk_internals__track_pending__wait_until_ready__serialize_execution(
91-
lambda: self.ready_to_execute(arg)
92-
):
93-
return await handler(arg)
102+
return decorator
94103

95104

96105
# Monkey-patch proposed new public API
97106
setattr(workflow, "all_handlers_completed", _sdk_internals_all_handlers_completed)
107+
setattr(workflow, "update", _new_workflow_update_decorator)
98108
##
99109
## END SDK internals prototype
100110
##
101111

102112

103113
@workflow.defn
104-
class JobRunner(SDKInternals):
114+
class JobRunner:
105115
"""
106116
Jobs must be executed in order dictated by job dependency graph (see `job.depends_on`) and
107117
not before `job.after_time`.
@@ -115,12 +125,13 @@ async def run(self):
115125
await workflow.wait_condition(
116126
lambda: (
117127
workflow.info().is_continue_as_new_suggested()
118-
and self.all_handlers_completed()
128+
and workflow.all_handlers_completed()
119129
)
120130
)
121131
workflow.continue_as_new()
122132

123-
def ready_to_execute(self, job: Job) -> bool:
133+
def ready_to_execute(self, update: Update) -> bool:
134+
job = update.arg
124135
if not set(job.depends_on) <= self.completed_tasks:
125136
return False
126137
if after_time := job.after_time:
@@ -131,8 +142,11 @@ def ready_to_execute(self, job: Job) -> bool:
131142
# These are the real handler functions. When we implement SDK support, these will use the
132143
# @workflow.update decorator and will not use an underscore prefix.
133144

134-
# @workflow.update
135-
async def _run_shell_script_job(self, job: Job) -> JobOutput:
145+
# @workflow.update(
146+
# execute_condition=lambda self, update: self.ready_to_execute(update)
147+
# )
148+
@_new_workflow_update_decorator(execute_condition=ready_to_execute)
149+
async def run_shell_script_job(self, job: Job) -> JobOutput:
136150
try:
137151
if security_errors := await workflow.execute_activity(
138152
run_shell_script_security_linter,
@@ -148,8 +162,8 @@ async def _run_shell_script_job(self, job: Job) -> JobOutput:
148162
# FIXME: unbounded memory usage
149163
self.completed_tasks.add(job.id)
150164

151-
# @workflow.update
152-
async def _run_python_job(self, job: Job) -> JobOutput:
165+
@_new_workflow_update_decorator(execute_condition=ready_to_execute)
166+
async def run_python_job(self, job: Job) -> JobOutput:
153167
try:
154168
if not await workflow.execute_activity(
155169
check_python_interpreter_version,

0 commit comments

Comments
 (0)