After the asyncio.wait_for timeout, the task was not cancelled
The script below is the minimized script to reproduce it. The tcp server just sent two chars after 100 seconds later after client connected
import sys
import asyncio
import socket
async def test_single_call():
reader, writer = await asyncio.open_connection(host='127.0.0.1', port=8888)
try:
msg = await asyncio.wait_for(reader.read(1), timeout=3)
print("Unexcepted message received:" , msg, file=sys.stderr)
assert False
except asyncio.TimeoutError:
pass
msg = await reader.read(1)
loop = asyncio.get_event_loop()
loop.run_until_complete(test_single_call())
loop.close()
The tcpclient(code above) is expected to timeout 3 seconds later, and read again after that; but it seems the task was not cancelled after it was timeout. My python version is 3.6.9
Traceback (most recent call last):
File "tcpclient.py", line 17, in <module>
loop.run_until_complete(test_single_call())
File "/usr/lib/python3.6/asyncio/base_events.py", line 484, in run_until_complete
return future.result()
File "tcpclient.py", line 14, in test_single_call
msg = await reader.read(1)
File "/usr/lib/python3.6/asyncio/streams.py", line 634, in read
yield from self._wait_for_data('read')
File "/usr/lib/python3.6/asyncio/streams.py", line 452, in _wait_for_data
'already waiting for incoming data' % func_name)
RuntimeError: read() called while another coroutine is already waiting for incoming data
I also uploaded the tcp server here
CodePudding user response:
I can not duplicate your problem in Python 3.6 and Python 3.9. You can try the following test code if it does not work, specify you event loop, I used ProactorEventLoop for Python 3.9 and _WindowsSelectorEventLoop for Python 3.6 since I use Windows:
import asyncio
# MAKE SLEEP more than 5 to get another results
SLEEP = 3
async def main():
try:
reader, writer = await asyncio.open_connection(host="localhost", port=8888)
# choose sleep number more than 3 to simulate slow connection
try_again = False
try:
data = await asyncio.wait_for(reader.read(8192), SLEEP)
except asyncio.TimeoutError:
try_again = True
print("Timeout")
else:
print(f"Without timeout data {data.decode('utf8')}")
# add proper error handling
if try_again:
data = await asyncio.wait_for(reader.read(8192), 30)
print(f"After timeout data {data.decode('utf8')}")
if writer.can_write_eof():
writer.write_eof() # tell server that we sent all data
except ConnectionAbortedError:
# if our client was too slow
print("Server timed out connection")
writer.close()
if __name__ == '__main__':
loop = asyncio.get_event_loop()
try:
loop.run_until_complete(main())
finally:
loop.close()
CodePudding user response:
For Linux Python 3.6, it had this issue. two options:
- Upgrade to Python 3.8 or 3.9
- Replace the
open_connection
andStreamReader
withloop.sock_connet
,loop.sock_recv
,loop.sock_sendall
eg:
import sys
import asyncio
import socket
async def test_single_call(loop):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock = await loop.sock_connect(sock, server_address)
try:
msg = await asyncio.wait_for(loop.sock_recv(sock, 1), timeout=3)
print("Unexcepted message received:" , msg, file=sys.stderr)
assert False
except asyncio.TimeoutError:
pass
msg = await loop.sock_recv(sock, 1)
loop = asyncio.get_event_loop()
loop.run_until_complete(test_single_call(loop))
loop.close()