Skip to content

Instantly share code, notes, and snippets.

Created July 27, 2016 13:34
Show Gist options
  • Save nvgoldin/30cea3c04ee0796ebd0489aa62bcf00a to your computer and use it in GitHub Desktop.
Save nvgoldin/30cea3c04ee0796ebd0489aa62bcf00a to your computer and use it in GitHub Desktop.
Python 3.5 asyncio - shutdown all tasks safely using signal handler
import signal
import functools
async def looping_task(loop, task_num):
while True:
print('{0}:in looping_task'.format(task_num))
await asyncio.sleep(5.0, loop=loop)
except asyncio.CancelledError:
return "{0}: I was cancelled!".format(task_num)
async def shutdown(sig, loop):
print('caught {0}'.format(
tasks = [task for task in asyncio.Task.all_tasks() if task is not
list(map(lambda task: task.cancel(), tasks))
results = await asyncio.gather(*tasks, return_exceptions=True)
print('finished awaiting cancelled tasks, results: {0}'.format(results))
loop = asyncio.get_event_loop()
for i in range(5):
asyncio.ensure_future(looping_task(loop, i), loop=loop)
shutdown(signal.SIGTERM, loop)))
Copy link

Thank you, works like a charm.

Copy link

For catching CTRL+C catch signal.SIGINT

Copy link

Is there a similar method we might be able to utilize in further python versions? eg: >= 3.8?
This code now raises the following error:

/usr/local/Cellar/python@3.8/3.8.6/Frameworks/Python.framework/Versions/3.8/lib/python3.8/asyncio/ RuntimeWarning: coroutine 'shutdown' was never awaited
del self._signal_handlers[sig]
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
Traceback (most recent call last):
File "/Users/rjanuary/git/stc-ds-sfbridge/", line 31, in
File "/usr/local/Cellar/python@3.8/3.8.6/Frameworks/Python.framework/Versions/3.8/lib/python3.8/asyncio/", line 570, in run_forever
File "/usr/local/Cellar/python@3.8/3.8.6/Frameworks/Python.framework/Versions/3.8/lib/python3.8/asyncio/", line 1823, in _run_once
event_list =
File "/usr/local/Cellar/python@3.8/3.8.6/Frameworks/Python.framework/Versions/3.8/lib/python3.8/", line 558, in select
kev_list = self._selector.control(None, max_ev, timeout)

Process finished with exit code 130 (interrupted by signal 2: SIGINT)

Copy link

@rjjanuary Ever figure it out? Exact same thing for me.

Copy link

vsinha commented Nov 17, 2022

Something like this works for me in 3.10. I like nvgoldin's functional style but i find it clunky to read in python and with python-black it's one less line to just do it the imperative way

def add_signal_handlers():
    loop = asyncio.get_event_loop()

    async def shutdown(sig: signal.Signals) -> None:
        Cancel all running async tasks (other than this one) when called.
        By catching asyncio.CancelledError, any running task can perform
        any necessary cleanup when it's cancelled.
        tasks = []
        for task in asyncio.all_tasks(loop):
            if task is not asyncio.current_task(loop):
        results = await asyncio.gather(*tasks, return_exceptions=True)
        print("Finished awaiting cancelled tasks, results: {0}".format(results))

    for sig in [signal.SIGINT, signal.SIGTERM]:
        loop.add_signal_handler(sig, lambda: asyncio.create_task(shutdown(sig)))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment