New submission from Andreas H. <ahanga...@gmx.net>:

The issue is that the main task (which was supplied to asyncio.run) has no 
chance to clean up its "own" sub-tasks and handle 
possible exceptions that occur during the sub-task clean up. It prevents a 
graceful shutdown.

There is no way to prevent the current printing of the "unhandled" exeption, 
even though the sub-task exception was catched by the main task. (See example 
below)


-- Current behavior --

When asyncio.run() receives an (unhanded) exception, all tasks are cancelled 
simultaneously. 
        
If any task generates an exception during its clean-up phase this is printed to 
the log, even though this exception is handled by the main task.
        

-- Expected behavior --
        
asyncio.run() should first cancel the main task, wait for it to complete its 
shutdown (and possible cancel its own sub-tasks, with exception catching), and 
*afterwards* cancel the remaining tasks.
        
        
-- Example Code --

For instance realize a graceful shutdown of a webserver when SIGTERM signal 
handler raises a SystemExit exception.




import os
import asyncio
import logging


async def main():

    logging.basicConfig(level=logging.INFO)

    async def sub_task():
        logging.info('sub_task: enter')    
        try:    
            while True:
                await asyncio.sleep(1)
                logging.info('some_task: action')
        finally:
            logging.info('sub_task: cleanup')    
            await asyncio.sleep(3)
            logging.info('sub_task: cleanup generates exception')    
            raise ValueError()
            logging.info('sub_task: cleanup end')    
    
    task = asyncio.create_task(sub_task())
                         
    try:
        while True:
            await asyncio.sleep(1)
    except Exception as e:
        logging.info(f"Main: exception {repr(e)} received: something went 
wrong: cancelling sub-task")
        task.cancel()
    finally:
        logging.info("Main: cleanup")
        try:
            await task
        except Exception as e:
            logging.info(f"Main: catched exception {repr(e)} from await 
sub_task")

try:
    asyncio.run( main() )
except KeyboardInterrupt:
    pass

-- Script Output with Ctrl+C manually generating an KeyboardInterrupt exception 
--


INFO:root:sub_task: enter
INFO:root:some_task: action    
        <--- CtrlC pressed here
INFO:root:Main: exception CancelledError() received: something went wrong: 
cancelling sub-task
INFO:root:Main: cleanup
INFO:root:sub_task: cleanup
INFO:root:sub_task: cleanup generates exception
INFO:root:Main: catched exception ValueError() from await sub_task
ERROR:asyncio:unhandled exception during asyncio.run() shutdown
task: <Task finished coro=<main.<locals>.sub_task() done, defined at 
D:\Benutzer\projekte\iep\apps\data_player\_signals_test\test.py:10> 
exception=ValueError()>
Traceback (most recent call last):
  File 
"C:\Users\z0013xar\AppData\Local\Continuum\anaconda3\lib\asyncio\runners.py", 
line 43, in run
    return loop.run_until_complete(main)
  File 
"C:\Users\z0013xar\AppData\Local\Continuum\anaconda3\lib\asyncio\base_events.py",
 line 574, in run_until_complete
    self.run_forever()
  File 
"C:\Users\z0013xar\AppData\Local\Continuum\anaconda3\lib\asyncio\base_events.py",
 line 541, in run_forever
    self._run_once()
  File 
"C:\Users\z0013xar\AppData\Local\Continuum\anaconda3\lib\asyncio\base_events.py",
 line 1750, in _run_once
    event_list = self._selector.select(timeout)
  File "C:\Users\z0013xar\AppData\Local\Continuum\anaconda3\lib\selectors.py", 
line 323, in select
    r, w, _ = self._select(self._readers, self._writers, [], timeout)
  File "C:\Users\z0013xar\AppData\Local\Continuum\anaconda3\lib\selectors.py", 
line 314, in _select
    r, w, x = select.select(r, w, w, timeout)
KeyboardInterrupt

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "D:\Benutzer\projekte\iep\apps\data_player\_signals_test\test.py", line 
14, in sub_task
    await asyncio.sleep(1)
  File 
"C:\Users\z0013xar\AppData\Local\Continuum\anaconda3\lib\asyncio\tasks.py", 
line 595, in sleep
    return await future
concurrent.futures._base.CancelledError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "D:\Benutzer\projekte\iep\apps\data_player\_signals_test\test.py", line 
34, in main
    await task
  File "D:\Benutzer\projekte\iep\apps\data_player\_signals_test\test.py", line 
20, in sub_task
    raise ValueError()
ValueError

-- Expected Output --

Same as above but without

  "ERROR:asyncio:unhandled exception during asyncio.run() shutdown"

and following traceback

----------
components: asyncio
messages: 398638
nosy: andreash, asvetlov, yselivanov
priority: normal
severity: normal
status: open
title: asyncio.run does not allow for graceful shutdown of main task
type: behavior

_______________________________________
Python tracker <rep...@bugs.python.org>
<https://bugs.python.org/issue44795>
_______________________________________
_______________________________________________
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com

Reply via email to