Update
Originally I was recommending Greg's answer below:
import asyncio
import sys
if sys.platform:
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
Turned out, using WindowsSelectorEventLoop
has functionality issues such as:
- Can't support more than 512 sockets
- Can't use pipe
- Can't use subprocesses
due to the fact that Windows uses I/O completion Ports unlike *nix - Therefore SelectorEventLoop
is not designed for Windows nor is implemented as full.
If those limitations matters to you - You might be better off using lengthy workaround in this answer.
Check out more about differences at documents.
Or alternatively, consider using Trio over asyncio, which is much more stable and consistent.
import trio
async def task():
await trio.sleep(5)
trio.run(task)
Original post
I've finally figured out how to keep ProactorEventLoop
running, preventing unsuccessful IO closure.
Really not sure why windows' Event loop is so faulty, as this also happens for asyncio.open_connection
and asyncio.start_server
.
To workaround this, you need to run event loop in forever loop and close manually.
Following code will cover both windows and other environments.
import asyncio
from aiocfscrape import CloudflareScraper
async def nested(url):
async with CloudflareScraper() as session:
async with session.get(url) as resp:
return await resp.text()
async def main():
await nested("https://www.binance.com/api/v3/exchangeInfo")
try:
assert isinstance(loop := asyncio.new_event_loop(), asyncio.ProactorEventLoop)
# No ProactorEventLoop is in asyncio on other OS, will raise AttributeError in that case.
except (AssertionError, AttributeError):
asyncio.run(main())
else:
async def proactor_wrap(loop_: asyncio.ProactorEventLoop, fut: asyncio.coroutines):
await fut
loop_.stop()
loop.create_task(proactor_wrap(loop, main()))
loop.run_forever()
This code will check if new EventLoop
is ProactorEventLoop
.
If so, keep loop forever until proactor_wrap
awaits main
and schedules loop stop.
Else - possibly all other OS than Windows - doesn't need these additional steps, simply call asyncio.run()
instead.
IDE like Pycharm will complain about passing AbstractEventLoop
to ProactorEventLoop
parameter, safe to ignore.