Python's [asyncio]( enables a variety of use cases and workflows. In this post we'll explore the ability to send a SIGHUP signal so one async task (or more) can reload its configuration and continue. It's similar in concept to the ``nginx -s reload`` command where nginx reloads its configuration without restarting. The POSIX world makes use of [signals]( to notify a process or a thread within a process of an event. There are a variety of signals, each with its own purpose. One of the most interesting signals is [SIGHUP](, which is sent when the [controlling terminal]( of a process is closed. More recently it has been used as a way to notify the process to reload its configuration (for example, [Reload configuration files on SIGHUP signal]( This blog post has a much smaller scope than [Graceful Shutdowns with asyncio]( by Lynn Root but you should read that one first (or maybe only read that series because it's great). I have tried to pare down to the smallest details to explain the concept as I imperfectly understand it. Here's the code, import asyncio import signal async def main(): with open("/path/to/config/file") as f: lines = f.readlines() try: while True: print(lines) await asyncio.sleep(3) except asyncio.CancelledError: print("main() is cancelled") async def sighup(): pending = [t for t in asyncio.all_tasks() if t is not asyncio.current_task()] for task in pending: task.cancel() await asyncio.gather(*pending) print("sighup() is complete") def start(): loop = asyncio.get_event_loop() loop.add_signal_handler(signal.SIGHUP, lambda: asyncio.create_task(sighup())) while True: loop.run_until_complete(main()) print("run_until_complete() is done") if __name__ == "__main__": start() Let's begin with the _start()_ function. Use the default event loop and add a signal handler, aptly named _sighup()_, to it. Later, in an infinite loop, call the _main()_ function, which does the bulk of the work. It's this function which we will reload with the SIGHUP signal. The _sighup()_ function makes a list of all the tasks in the event loop, except itself. It calls _cancel_ on all these tasks and waits for them to complete. The contrived _main()_ function reads a configuration file and then does some work. In this case its in an infinite loop of printing lines in config file and async sleeping for a little bit. When it receives a cancel it raises an exception, _asyncio.CancelledError_. Run this code and then send it a SIGHUP, ``kill -SIGHUP "${PID}"``. The event loop calls _sighup()_ which sends a cancel to _main()_. _main()_ completes and control is passed to _start()_ where _loop.run_until_complete(main())_ completes. Finally, _sighup()_ completes. This is visible from the output of the script but I don't know for sure that this is how it's designed. In this case don't handle SIGTERM or SIGINT signals. If desire is to keep running the process for better uptime then use a process supervisor like [supervisord](, [systemd](, or [Kubernetes]( to start a new process when this dies. Thanks to Lynn Root for writing the amazing series of blog posts. I'm sure I'll be referencing that work even more in the future.