uvloop closes underlying sockets that were established outside upon end of asyncio.run()
This seem to be version/OS unrelated. Sample:
import asyncio
import socket
import uvloop
b = bytes('{"msg": "test"}', 'utf-8')
async def main_async(sock: socket.socket):
_, writer = await asyncio.open_unix_connection(sock=sock)
writer.write(b)
await writer.drain()
if __name__ == '__main__':
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.connect('data/sockets/echo_server.sock')
sock.sendall(b)
uvloop.install()
asyncio.run(main_async(sock)) # sock gets wrongly closed on asyncio.run() termination
sock.sendall(b) # Exception here OSError: [Errno 9] Bad file descriptor
Last line result in exception "OSError: [Errno 9] Bad file descriptor" because sock gets closed right after asyncio.run() terminates. It is wrong behavior, because sock was created outside of asyncio loop and has to be left untouched. The issue doesn't occur using regular asyncio loop.
To work around this issue, you might consider a few alternative approaches:
-
Use Regular
asyncioEvent Loop: As you mentioned, the issue doesn't occur with the regularasyncioloop. If this is acceptable for your use case, you can consider sticking with the regularasyncioevent loop instead of usinguvloop. -
Wrap
socketin a Context Manager: You can create a custom context manager that wraps the socket and ensures it's not prematurely closed. Here's an example of how you could do this:
import asyncio
import socket
import uvloop
from contextlib import contextmanager
b = bytes('{"msg": "test"}', 'utf-8')
@contextmanager
def open_socket():
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.connect('data/sockets/echo_server.sock')
yield sock
sock.close()
async def main_async(sock: socket.socket):
_, writer = await asyncio.open_unix_connection(sock=sock)
writer.write(b)
await writer.drain()
if __name__ == '__main__':
uvloop.install()
with open_socket() as sock:
asyncio.run(main_async(sock))
sock.sendall(b) # Should not result in "OSError: [Errno 9] Bad file descriptor"
Using a context manager ensures that the socket is properly closed when you're done with it, avoiding potential issues with the socket's lifecycle.
-
Check for Updates: Sometimes, issues like this can be due to specific versions of libraries or platforms. Make sure you're using the latest versions of
uvloop,asyncio, and other relevant libraries. If the issue is a known bug, there might be a fix available in newer versions.