1
1
import asyncio
2
2
import time
3
- import traceback
4
3
from concurrent .futures import ThreadPoolExecutor
5
4
from datetime import timedelta
6
5
from typing import NoReturn
7
6
8
7
from temporalio import activity , workflow
9
8
from temporalio .client import Client , WorkflowFailureError
10
- from temporalio .exceptions import CancelledError
9
+ from temporalio .exceptions import ActivityError , CancelledError
11
10
from temporalio .worker import Worker
12
11
13
12
@@ -20,8 +19,10 @@ def never_complete_activity() -> NoReturn:
20
19
print ("Heartbeating activity" )
21
20
activity .heartbeat ()
22
21
time .sleep (1 )
23
- except CancelledError :
24
- print ("Activity cancelled" )
22
+ except CancelledError as err :
23
+ print (
24
+ f"Got expected exception in activity. Cause chain is { format_exception_cause_chain (err )} "
25
+ )
25
26
raise
26
27
27
28
@@ -43,6 +44,11 @@ async def run(self) -> None:
43
44
# Always set a heartbeat timeout for long-running activities
44
45
heartbeat_timeout = timedelta (seconds = 2 ),
45
46
)
47
+ except ActivityError as err :
48
+ print (
49
+ f"Got expected exception in workflow. Cause chain is { format_exception_cause_chain (err )} "
50
+ )
51
+ raise
46
52
finally :
47
53
await workflow .execute_activity (
48
54
cleanup_activity , start_to_close_timeout = timedelta (seconds = 5 )
@@ -61,7 +67,6 @@ async def main():
61
67
activities = [never_complete_activity , cleanup_activity ],
62
68
activity_executor = ThreadPoolExecutor (5 ),
63
69
):
64
-
65
70
# While the worker is running, use the client to start the workflow.
66
71
# Note, in many production setups, the client would be in a completely
67
72
# separate process from the worker.
@@ -80,8 +85,17 @@ async def main():
80
85
try :
81
86
await handle .result ()
82
87
raise RuntimeError ("Should not succeed" )
83
- except WorkflowFailureError :
84
- print ("Got expected exception: " , traceback .format_exc ())
88
+ except WorkflowFailureError as err :
89
+ print (
90
+ f"Got expected exception in client. Cause chain is { format_exception_cause_chain (err )} "
91
+ )
92
+
93
+
94
+ def format_exception_cause_chain (err : BaseException ) -> str :
95
+ causes = [err ]
96
+ while cause := causes [- 1 ].__cause__ :
97
+ causes .append (cause )
98
+ return " -> " .join ([f"{ e .__class__ .__name__ } " for e in causes ])
85
99
86
100
87
101
if __name__ == "__main__" :
0 commit comments