If a KeyboardInterrupt et al occur in main, it should check the status of the pending tasks and cancel them appropriately and maybe print what was being waited on. Otherwise you get a big list of opaque blobs like:
KeyboardInterrupt
ERROR:asyncio:Task was destroyed but it is pending!
task: <Task pending coro=<Pushl.process_feed() done, defined at /Users/fluffy/projects/Pushl/pushl/__init__.py:35> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x10f41d0a8>()]> cb=[_wait.<locals>._on_completion() at /usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncio/tasks.py:436]>
ERROR:asyncio:Task was destroyed but it is pending!
task: <Task pending coro=<Pushl.process_entry() done, defined at /Users/fluffy/projects/Pushl/pushl/__init__.py:80> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x1113b27c8>()]> cb=[_wait.<locals>._on_completion() at /usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncio/tasks.py:436]>
ERROR:asyncio:Task was destroyed but it is pending!
task: <Task pending coro=<Pushl.process_entry() done, defined at /Users/fluffy/projects/Pushl/pushl/__init__.py:80> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x111391678>()]> cb=[_wait.<locals>._on_completion() at /usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncio/tasks.py:436]>
ERROR:asyncio:Task was destroyed but it is pending!
task: <Task pending coro=<Pushl.process_feed() done, defined at /Users/fluffy/projects/Pushl/pushl/__init__.py:35> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x1114811f8>()]> cb=[_wait.<locals>._on_completion() at /usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncio/tasks.py:436]>
ERROR:asyncio:Task was destroyed but it is pending!
task: <Task pending coro=<Pushl.process_feed() done, defined at /Users/fluffy/projects/Pushl/pushl/__init__.py:35> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x1113b2be8>()]> cb=[_wait.<locals>._on_completion() at /usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncio/tasks.py:436]>
ERROR:asyncio:Task was destroyed but it is pending!
task: <Task pending coro=<Pushl.process_entry() done, defined at /Users/fluffy/projects/Pushl/pushl/__init__.py:80> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x112175558>()]> cb=[_wait.<locals>._on_completion() at /usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncio/tasks.py:436]>
ERROR:asyncio:Task was destroyed but it is pending!
task: <Task pending coro=<Pushl.process_entry() done, defined at /Users/fluffy/projects/Pushl/pushl/__init__.py:80> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x111ec0f18>()]> cb=[_wait.<locals>._on_completion() at /usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncio/tasks.py:436]>
ERROR:asyncio:Task was destroyed but it is pending!
task: <Task pending coro=<Pushl.send_webmention() done, defined at /Users/fluffy/projects/Pushl/pushl/__init__.py:110> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x112380e88>()]> cb=[_wait.<locals>._on_completion() at /usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncio/tasks.py:436]>
ERROR:asyncio:Task was destroyed but it is pending!
task: <Task pending coro=<Pushl.send_webmention() done, defined at /Users/fluffy/projects/Pushl/pushl/__init__.py:110> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x111ec0348>()]> cb=[_wait.<locals>._on_completion() at /usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncio/tasks.py:436]>
If a KeyboardInterrupt et al occur in main, it should check the status of the pending tasks and cancel them appropriately and maybe print what was being waited on. Otherwise you get a big list of opaque blobs like: