From 65162c4a4fc48a20f58301e906f7b91dac302692 Mon Sep 17 00:00:00 2001 From: Simon Emms Date: Tue, 23 Sep 2025 08:32:54 +0000 Subject: [PATCH 1/2] fix(python): move to a DI model for activities --- python/src/activities.py | 15 ++++++++++----- python/src/worker.py | 6 ++++-- python/src/workflows.py | 4 ++-- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/python/src/activities.py b/python/src/activities.py index 5bff4fa..3df24bc 100644 --- a/python/src/activities.py +++ b/python/src/activities.py @@ -1,9 +1,14 @@ from temporalio import activity -@activity.defn -async def sayName(name: str) -> str: - if name == "": - name = "anonymous human" +class Activities: + # If you wish to connect any dependencies (eg, database), add in here + # def __init__(self, db: DB): + # self.db = db - return f"Hello {name}!" + @activity.defn + async def sayName(self, name: str) -> str: + if name == "": + name = "anonymous human" + + return f"Hello {name}!" diff --git a/python/src/worker.py b/python/src/worker.py index 8a6130c..febf5d3 100644 --- a/python/src/worker.py +++ b/python/src/worker.py @@ -3,7 +3,7 @@ from temporalio.worker import Worker from temporalio.envconfig import ClientConfigProfile from workflows import HelloWorldWorkflow -from activities import sayName +from activities import Activities import shared @@ -12,11 +12,13 @@ async def main(): connect_config = default_profile.to_client_connect_config() client = await Client.connect(**connect_config) + activities = Activities() + worker = Worker( client, task_queue=shared.TASK_QUEUE_NAME, workflows=[HelloWorldWorkflow], - activities=[sayName], + activities=[activities.sayName], ) print("Worker started.") diff --git a/python/src/workflows.py b/python/src/workflows.py index f20602a..57d86a1 100644 --- a/python/src/workflows.py +++ b/python/src/workflows.py @@ -1,6 +1,6 @@ from datetime import timedelta from temporalio import workflow -from activities import sayName +from activities import Activities @workflow.defn @@ -8,7 +8,7 @@ class HelloWorldWorkflow: @workflow.run async def run(self, name: str) -> str: return await workflow.execute_activity( - sayName, + Activities.sayName, name, schedule_to_close_timeout=timedelta(seconds=10), ) From bebee6e62edca484700e4fc1b8fec133afe3dde5 Mon Sep 17 00:00:00 2001 From: Simon Emms Date: Tue, 23 Sep 2025 08:39:45 +0000 Subject: [PATCH 2/2] feat(python): auto-discover the activities in the class --- python/src/worker.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/python/src/worker.py b/python/src/worker.py index febf5d3..09e633b 100644 --- a/python/src/worker.py +++ b/python/src/worker.py @@ -5,6 +5,7 @@ from workflows import HelloWorldWorkflow from activities import Activities import shared +import inspect async def main(): @@ -12,18 +13,39 @@ async def main(): connect_config = default_profile.to_client_connect_config() client = await Client.connect(**connect_config) + + # Register the activities - you may need to inject dependencies in here activities = Activities() worker = Worker( client, task_queue=shared.TASK_QUEUE_NAME, workflows=[HelloWorldWorkflow], - activities=[activities.sayName], + activities=find_activities(activities), ) print("Worker started.") await worker.run() +# ---- auto-discovery helper ---- +_ACTIVITY_ATTR = "__temporal_activity_definition" # set by @activity.defn + + +def find_activities(obj): + """Return a list of bound methods on `obj` that are Temporal activities.""" + acts = [] + for _, member in inspect.getmembers(obj): + if callable(member) and is_activity_callable(member): + acts.append(member) # bound method carries injected deps + return acts + + +def is_activity_callable(attr) -> bool: + # Works for functions and bound methods + func = attr.__func__ if inspect.ismethod(attr) else attr + return hasattr(func, _ACTIVITY_ATTR) + + if __name__ == "__main__": asyncio.run(main())