@@ -30,7 +30,7 @@ def create_task(self, func, *args) -> Task:
3030 exceptions. because asyncio Tasks do not raise exceptions
3131 """
3232 task = asyncio .create_task (func (* args ))
33- task .add_done_callback (self .handle_exception )
33+ task .add_done_callback (self .handle_task_exception )
3434
3535 # Allow the event loop to run the scheduled task
3636 # await asyncio.sleep(0)
@@ -39,20 +39,13 @@ def create_task(self, func, *args) -> Task:
3939 self .tasks .append (task )
4040 return task
4141
42- def handle_exception (self , task ):
43- """
44- in asyncmodules we use Async.Task to run some of the functions
45- If an exception occurs in a coroutine that was wrapped in a Task
46- (e.g., asyncio.create_task), the exception does not crash the program
47- but remains in the task.
48- This function is used to handle the exception in the task
49- """
42+ def handle_task_exception (self , task : asyncio .Task ):
5043 try :
51- # Access task result to raise the exception if it occurred
52- task . result ()
53- except ( KeyboardInterrupt , asyncio . exceptions . CancelledError ):
54- pass
55- except Exception :
44+ exception = task . exception ()
45+ except asyncio . CancelledError :
46+ return # Task was cancelled, not an error
47+ if exception :
48+ self . print ( f"Unhandled exception in task: { exception } " )
5649 self .print_traceback ()
5750
5851 async def main (self ): ...
@@ -74,28 +67,45 @@ def run_async_function(self, func: Callable):
7467 Returns the Future’s result or raise its exception.
7568 """
7669 loop = asyncio .get_event_loop ()
77- loop .set_exception_handler (self .handle_exception )
70+ loop .set_exception_handler (self .handle_loop_exception )
7871 return loop .run_until_complete (func ())
7972
73+ def handle_loop_exception (self , loop , context ):
74+ """A common loop exception handler"""
75+ exception = context .get ("exception" )
76+ future = context .get ("future" )
77+
78+ if future :
79+ try :
80+ future .result ()
81+ except Exception :
82+ self .print_traceback ()
83+ elif exception :
84+ self .print (f"Unhandled loop exception: { exception } " )
85+ else :
86+ self .print (f"Unhandled loop error: { context .get ('message' )} " )
87+
8088 def run (self ):
81- loop = asyncio .new_event_loop ()
82- asyncio .set_event_loop (loop )
89+ asyncio .run (self ._run_pre_main_and_main ())
90+
91+ async def _run_pre_main_and_main (self ):
92+ """
93+ runs pre_main() once, then runs main() in a loop
94+ """
95+ loop = asyncio .get_event_loop ()
96+ loop .set_exception_handler (self .handle_loop_exception )
8397
8498 try :
8599 error : bool = self .pre_main ()
86100 if error or self .should_stop ():
87- self .run_async_function (
88- self .gather_tasks_and_shutdown_gracefully
89- )
101+ await self .gather_tasks_and_shutdown_gracefully ()
90102 return
91103 except KeyboardInterrupt :
92- self .run_async_function ( self . gather_tasks_and_shutdown_gracefully )
104+ await self .gather_tasks_and_shutdown_gracefully ( )
93105 return
94106 except RuntimeError as e :
95107 if "Event loop stopped before Future completed" in str (e ):
96- self .run_async_function (
97- self .gather_tasks_and_shutdown_gracefully
98- )
108+ await self .gather_tasks_and_shutdown_gracefully ()
99109 return
100110 except Exception :
101111 self .print_traceback ()
@@ -104,18 +114,14 @@ def run(self):
104114 while True :
105115 try :
106116 if self .should_stop ():
107- self .run_async_function (
108- self .gather_tasks_and_shutdown_gracefully
109- )
117+ await self .gather_tasks_and_shutdown_gracefully ()
110118 return
111119
112120 # if a module's main() returns 1, it means there's an
113121 # error and it needs to stop immediately
114- error : bool = self .run_async_function ( self . main )
122+ error : bool | None = await self .main ( )
115123 if error :
116- self .run_async_function (
117- self .gather_tasks_and_shutdown_gracefully
118- )
124+ await self .gather_tasks_and_shutdown_gracefully ()
119125 return
120126
121127 except KeyboardInterrupt :
@@ -128,9 +134,7 @@ def run(self):
128134 continue
129135 except RuntimeError as e :
130136 if "Event loop stopped before Future completed" in str (e ):
131- self .run_async_function (
132- self .gather_tasks_and_shutdown_gracefully
133- )
137+ await self .gather_tasks_and_shutdown_gracefully ()
134138 return
135139 except Exception :
136140 self .print_traceback ()
0 commit comments