Reputation: 85
Here is a python script that includes an async class which awaits a task and starts a background loop at initialization:
import asyncio, aiohttp, logging
logging.basicConfig(level=logging.DEBUG, filename='test.log', filemode='w')
class Client:
async def _async_init(self):
self.session = aiohttp.ClientSession()
self.bg_task = asyncio.create_task(self._background_task())
await self.make_request()
logging.debug('async client initialized.')
return self
async def make_request(self):
async with self.session.get('https://google.com') as response:
self.info = await response.text()
async def _background_task(self):
while True:
await asyncio.sleep(1000)
async def main():
client = await Client()._async_init()
# Intentionally leaving the aiohttp client session unclosed...
# ...to log the 'unclosed client session' error.
asyncio.run(main())
Output (not desired):
# test.log
DEBUG:asyncio:Using proactor: IocpProactor
DEBUG:root:async client initialized.
What I want is to log certain asyncio errors to the test.log
file. These errors normally get printed out to the terminal if I don't use any logging. These errors are also logged on program exit since they are logged for unclosed client session
and unclosed connector
situations.
I figured that if I remove the background task from the class:
# These lines are removed from the class
...
# self.bg_task = asyncio.create_task(self._background_task())
...
# async def _background_loop(self):
# while True:
# await asyncio.sleep(1000)
...
Output (desired):
# test.log
DEBUG:asyncio:Using proactor: IocpProactor
DEBUG:root:async client initialized.
ERROR:asyncio:Unclosed client session
client_session: <aiohttp.client.ClientSession object at 0x000002043315D6D0>
ERROR:asyncio:Unclosed connector
connections: ['[(<aiohttp.client_proto.ResponseHandler object at 0x000002043312B230>, 44091.562)]', '[(<aiohttp.client_proto.ResponseHandler object at 0x000002043312B7E0>, 44092.468)]']
connector: <aiohttp.connector.TCPConnector object at 0x000002043315D710>
The question is, how can I get this output while also keeping the background task in the class?
Upvotes: 0
Views: 1532
Reputation: 85
I found one solution, which is catching the asyncio.CancelledError
that the event loop will automatically throw into the _background_task
when the loop completes:
...
async def _background_task(self):
try:
while True:
await asyncio.sleep(1000)
except asyncio.CancelledError:
pass # catch the exception that the event loop throws
...
After running the script, the desired output is logged.
# test.log
DEBUG:asyncio:Using proactor: IocpProactor
DEBUG:root:async client initialized.
ERROR:asyncio:Unclosed client session
client_session: <aiohttp.client.ClientSession object at 0x000001187C9F9C90>
ERROR:asyncio:Unclosed connector
connections: ['[(<aiohttp.client_proto.ResponseHandler object at 0x000001187C9C7690>, 24429.703)]', '[(<aiohttp.client_proto.ResponseHandler object at 0x000001187C9C7C40>, 24430.015)]']
connector: <aiohttp.connector.TCPConnector object at 0x000001187C9F9CD0>
Not sure about why it works though, but this answer is useful to learn more about task creation and termination.
Upvotes: 1