python 3.6 coroutine was never awaited - python

So when ever I run my program and connect to it with the echo client it gives me this error.
Starting server
Serving on ('127.0.0.1', 8881)
Exception in callback UVTransport._call_connection_made
handle: <Handle UVTransport._call_connection_made>
Traceback (most recent call last):
File "uvloop/cbhandles.pyx", line 52, in uvloop.loop.Handle._run (uvloop/loop.c:48414)
File "uvloop/handles/tcp.pyx", line 141, in uvloop.loop.TCPTransport._call_connection_made (uvloop/loop.c:80488)
File "uvloop/handles/basetransport.pyx", line 140, in uvloop.loop.UVBaseTransport._call_connection_made (uvloop/loop.c:65774)
File "uvloop/handles/basetransport.pyx", line 137, in uvloop.loop.UVBaseTransport._call_connection_made (uvloop/loop.c:65671)
AttributeError: 'coroutine' object has no attribute 'connection_made'
/home/kenton/Programming/bridal/bridal-middle/middle/lib/server.py:16:RuntimeWarning: coroutine 'handle_request' was never awaited
loop.run_forever()
As far as I know I have everything that should be awaited awaited.
Here is the code:
class Server:
def __init__(self, port):
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
loop = asyncio.get_event_loop()
server = loop.run_until_complete(self.init(loop))
print("Serving on {}".format(server.sockets[0].getsockname()))
try:
loop.run_forever()
except KeyboardInterrupt:
print("\rclosing the server")
pass
server.close()
loop.run_until_complete(server.wait_closed())
loop.close()
async def init(self, loop):
server = await loop.create_server(self.handle_request, '127.0.0.1', 8881)
return server
async def handle_request(self):
print(datetime.datetime.now())
reader = asyncio.StreamReader()
writer = asyncio.StreamWriter()
data = await reader.read(100)
message = data.decode()
addr = writer.get_extra_info('peername')
code = message.partition('-')
if code[0].startswith("1") or code[0].startswith("5"):
accounts = lib.settings.database.accounts
if code[0] == "101":
result = await self.login_101(code, accounts, writer)
if code[0] == "501":
result = await accounts.find_one({"username":code[2]})
print("looking up", code[0])
#code logic to keep asking for a valid username if one exists
if result is None:
username = code[2]
print(username, " does not exist. Creating")
writer.write(b"0")
await writer.drain()
data = await reader.read(100)
message = data.decode()
code = message.partition('-')
post = {"username":username,"password":code[0],"email":code[2]}
post_id = await accounts.insert_one(post).inserted_id
writer.write(b(post_id))
await writer.drain()
print("Closed the client socket")
writer.close()
print(datetime.datetime.now())

Regarding your error message, the actual error is:
AttributeError: 'coroutine' object has no attribute 'connection_made'
And the line below is just a warning (RuntimeWarning: coroutine 'handle_request' was never awaited).
You might be mixing asyncio.start_server with loop.create_server().
loop.create_server()'s first parameter is protocol_factory which is a callable that returns an instance of a Protocol (and not a coroutine as in your code above):
import asyncio
class MyProtocol(asyncio.Protocol):
def connection_made(self, transport):
print("Connection made", transport)
def data_received(self, data):
print("Data received", data)
loop = asyncio.get_event_loop()
# Each client connection will create a new protocol instance
coro = loop.create_server(MyProtocol, '127.0.0.1', 8888)
server = loop.run_until_complete(coro)
loop.run_forever()
See full echo server example here.

Related

Python asyncio socket server eating errors (not displaying any errors)

Edited my question to provide more information and a reproducible example.
I'm not sure if anybody could help me here. But I'm having some problems with my asyncio socket server.
As you can see, in the server.py on line 18 there is a deliberate line that will cause a value error, as you cannot change "" to an int. This is intentional, as for some reason the error on this line is never output to the console.
When the client connects to the server the print("here1") line runs, but the lines after that do not due to the error on line 18. I need this error to be output into console, but it appears nowhere. I am very confused and cannot find anything online about why asyncio could be eating the errors and not displaying them.
I have attempted to see any errors by using the logging module, but even this doesn't show any errors..
import logging
logging.basicConfig(level=logging.ERROR)
The line which causes the error is definitely running.
I have extracted all of my code into a smaller reproducible set of files below.
server.py
import asyncio
class Server:
def __init__(self, host: str, port: int):
self.host = host
self.port = port
async def start_server(self):
print("Server online")
server = await asyncio.start_server(self.handle_events, self.host, self.port)
async with server:
await server.serve_forever()
async def handle_events(self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter):
while True:
print("here1")
int("")
print("here2")
data = await reader.read(1024)
if not data:
continue
print(f"received: {data}")
async def start():
host = "127.0.0.1"
port = 55551
server = Server(host=host, port=port)
server_task = asyncio.create_task(server.start_server())
await asyncio.gather(server_task)
if __name__ == "__main__":
asyncio.run(start())
client.py
import asyncio
class Client:
def __init__(self, host: str, port: int):
super().__init__()
self.host = host
self.port = port
self.writer = None
self.reader = None
async def start_connection(self):
self.reader, self.writer = await asyncio.open_connection(host=self.host, port=self.port)
await self.message_handler()
async def message_handler(self):
while True:
await asyncio.sleep(0)
data = await self.reader.read(1024)
if not data:
continue
await self.writer.drain()
async def start():
host = "127.0.0.1"
port = 55551
server = Client(host=host, port=port)
server_task = asyncio.create_task(server.start_connection())
await asyncio.gather(server_task)
if __name__ == "__main__":
asyncio.run(start())
Cause it's not even calling that handle_events func.
import asyncio
async def tcp_echo_client(message):
reader, writer = await asyncio.open_connection(
'127.0.0.1', 55555)
print(f'Send: {message!r}')
writer.write(message.encode())
await writer.drain()
data = await reader.read(100)
print(f'Received: {data.decode()!r}')
print('Close the connection')
writer.close()
asyncio.run(tcp_echo_client('Hello World!'))
I just copied this from the doc so I can test it.

Why does getting a contextvar here not work?

So here's the basic code (sorry it's long)
import argparse
import asyncio
from contextvars import ContextVar
import sys
# This thing is the offender
message_var = ContextVar("message")
class ServerProtocol(asyncio.Protocol):
def connection_made(self, transport):
peername = transport.get_extra_info("peername")
print("Server: Connection from {}".format(peername))
self.transport = transport
def data_received(self, data):
message = data.decode()
print("Server: Data received: {!r}".format(message))
print("Server: Send: {!r}".format(message))
self.transport.write(data)
print("Server: Close the client socket")
self.transport.close()
class ClientProtocol(asyncio.Protocol):
def __init__(self, on_conn_lost):
self.on_conn_lost = on_conn_lost
self.transport = None
self.is_connected: bool = False
def connection_made(self, transport):
self.transport = transport
self.is_connected = True
def data_received(self, data):
# reading back supposed contextvar
message = message_var.get()
print(f"{message} : {data.decode()}")
def connection_lost(self, exc):
print("The server closed the connection")
self.is_connected = False
self.on_conn_lost.set_result(True)
def send(self, message: str):
# Setting context var
message_var.set(message)
if self.transport:
self.transport.write(message.encode())
def close(self):
self.transport.close()
self.is_connected = False
if not self.on_conn_lost.done():
self.on_conn_lost.set_result(True)
async def get_input(client: ClientProtocol):
loop = asyncio.get_running_loop()
while client.is_connected:
message = await loop.run_in_executor(None, input, ">>>")
if message == "q":
client.close()
return
client.send(message)
async def main(args):
host = "127.0.0.1"
port = 5001
loop = asyncio.get_running_loop()
if args.server:
server = await loop.create_server(lambda: ServerProtocol(), host, port)
async with server:
await server.serve_forever()
return
on_conn_lost = loop.create_future()
client = ClientProtocol(on_conn_lost)
await loop.create_connection(lambda: client, host, port)
await get_input(client)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument(
"--server", "-s", default=False, action="store_true", help="Start server"
)
arguments = parser.parse_args(sys.argv[1:])
asyncio.run(main(args=arguments))
This crashes with the following exception:
Exception in callback _ProactorReadPipeTransport._loop_reading(<_OverlappedF...shed result=4>)
handle: <Handle _ProactorReadPipeTransport._loop_reading(<_OverlappedF...shed result=4>)>
Traceback (most recent call last):
File "C:\Users\brent\AppData\Local\Programs\Python\Python310\lib\asyncio\events.py", line 80, in _run
self._context.run(self._callback, *self._args)
File "C:\Users\brent\AppData\Local\Programs\Python\Python310\lib\asyncio\proactor_events.py", line 320, in _loop_reading
self._data_received(data, length)
File "C:\Users\brent\AppData\Local\Programs\Python\Python310\lib\asyncio\proactor_events.py", line 270, in _data_received
self._protocol.data_received(data)
File "E:\Development\Python\ibcs2023\_prep\experimental\asyncio_context.py", line 40, in data_received
message = message_var.get()
LookupError: <ContextVar name='message' at 0x0000023F30A54FE0>
The server closed the connection
Why does calling message = message_var.get() cause a crash? Why can't Python find the context var? Why is data_received not in the same context as send? How can I keep them in the same context?
I'm working on a larger project with the main branch of Textual and it uses a contextvar that loses context every time a message is received using a modified version of the code above.
Keeping a separated "context" for each task is exactly what contextvars are about. You could only assert that the send and data_received methods were called within the same context if you had control over the "uperlying" (as opposed to 'underlying') driver of your Protocol class - that is not the case, and both are called in different contexts. I mean, the answer to "How can I keep them in the same context?" is: you can't unless you write your own implementation of the code which makes this work inside asyncio.
There is no way you can keep track of metadata from a message, and retrieve this metadata on getting the reply, unless there is a marker on the message itself, that will survive the round-trip. That is: your networking/communication protocol itself have to spec a way to identify messages It might be as simple as a sequential integer number prefixing every string, for example - or, in this case where you simply echo the message back, it could be the message itself. Once you have that, a simple dictionary having these message IDs as keys, will work for what you seem to intend in this example.

Python asyncio http server

I try to build a base http server with the following code.
async def handle_client(client, address):
print('connection start')
data = await loop.sock_recv(client, 1024)
resp = b'HTTP/1.1 404 NOT FOUND\r\n\r\n<h1>404 NOT FOUND</h1>'
await loop.sock_sendall(client, resp)
client.close()
async def run_server():
while True:
client, address = await loop.sock_accept(server)
print('start')
loop.create_task(handle_client(client,address))
print(client)
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('localhost', 3006))
server.listen(8)
print(1)
loop = asyncio.get_event_loop()
loop.run_until_complete(run_server())
The output I expect to get is
1
start
connection start
But the actual result of running is
1
start
start
start
It seems that the function in loop.create_task() is not being run, so now I got confuesed., what is the correct way to use loop.create_task()?
You need to await the task that is created via loop.create_task(), otherwise run_server() will schedule the task and then just exit before the result has been returned.
Try changing run_server() to the following:
async def run_server():
while True:
client, address = await loop.sock_accept(server)
print('start')
await loop.create_task(handle_client(client,address))
print(client)

Python 3 asyncio: run_until_complete() blocks when waiting for ProcessPoolExecutor job done

I'm trying to combine TCP echo client and server for testing automation into single module using ProcessPoolExecutor() and it works as expected.
The only issue is I can't finish event loop. I can see debug output of last line of executor's target run_client(), but looks like executor itself still blocks.
The code:
import asyncio
import concurrent.futures
from concurrent.futures import ProcessPoolExecutor
async def server_handle_echo(reader, writer):
data = await reader.read(100)
message = data.decode()
addr = writer.get_extra_info('peername')
print("Received %r from %r" % (message, addr))
print("Send: %r" % message)
writer.write(data)
await writer.drain()
print("Close the client socket")
writer.close()
async def echo_client_handler(message, loop):
reader, writer = await asyncio.open_connection('127.0.0.1', 8888,
loop=loop)
print('Send: %r' % message)
writer.write(message.encode())
data = await reader.read(100)
print('Received: %r' % data.decode())
print('Close the socket')
writer.close()
def run_client():
message = 'Hello World!'
loop = asyncio.get_event_loop()
loop.run_until_complete(echo_client_handler(message, loop))
loop.close()
print('run_client last line')
executor = ProcessPoolExecutor(1)
loop = asyncio.get_event_loop()
coro = asyncio.start_server(server_handle_echo, '127.0.0.1', 8888, loop=loop)
server = loop.run_until_complete(coro)
# Serve requests until Ctrl+C is pressed
print('Serving on {}'.format(server.sockets[0].getsockname()))
try:
#loop.run_forever()
client = asyncio.ensure_future(loop.run_in_executor(executor, run_client))
loop.run_until_complete(client)
except KeyboardInterrupt:
pass
# Close the server
server.close()
loop.run_until_complete(server.wait_closed())
loop.close()
Output:
Serving on ('127.0.0.1', 8888)
Send: 'Hello World!'
Received 'Hello World!' from ('127.0.0.1', 51157)
Send: 'Hello World!'
Close the client socket
Received: 'Hello World!'
Close the socket
run_client last line
After this output it hands in msg loop waiting for IO.
Looking forward for your help. Sorry, I'm one-day-asyncioist :)
You can't use the same event loop for your client running in a subprocess, you need a new loop:
def run_client():
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
[...]

How can I close a DatagramTransport as soon one datagram is sent?

I'm trying to close the transport right after sending the UDP packet and I'm getting an Exception in callback _SelectorDatagramTransport._read_ready()
import asyncio
class MyProtocol:
def __init__(self, message, loop):
self.message = message
self.loop = loop
self.transport = None
def connection_made(self, transport):
self.transport = transport
print("Send:", self.message)
self.transport.sendto(self.message.encode())
self.transport.close() # <----------
def error_received(self, exc):
print('Error received', exc)
def connection_lost(self, exc):
print("Socket closed, stop the event loop")
self.loop.stop()
loop = asyncio.get_event_loop()
message = "hello"
connect = loop.create_datagram_endpoint(lambda: MyProtocol(message, loop), remote_addr=('127.0.0.1', 2222))
transport, protocol = loop.run_until_complete(connect)
loop.run_forever()
The full stack trace that I get is while running the snippet above in CPython 3.5.1 is:
Socket closed, stop the event loop
Exception in callback _SelectorDatagramTransport._read_ready()
handle: <Handle _SelectorDatagramTransport._read_ready()>
Traceback (most recent call last):
File "/home/ecerulm/.pyenv/versions/3.5.1/lib/python3.5/asyncio/selector_events.py", line 1002, in _read_ready
data, addr = self._sock.recvfrom(self.max_size)
AttributeError: 'NoneType' object has no attribute 'recvfrom'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/ecerulm/.pyenv/versions/3.5.1/lib/python3.5/asyncio/events.py", line 125, in _run
self._callback(*self._args)
File "/home/ecerulm/.pyenv/versions/3.5.1/lib/python3.5/asyncio/selector_events.py", line 1008, in _read_ready
self._fatal_error(exc, 'Fatal read error on datagram transport')
File "/home/ecerulm/.pyenv/versions/3.5.1/lib/python3.5/asyncio/selector_events.py", line 587, in _fatal_error
self._loop.call_exception_handler({
AttributeError: 'NoneType' object has no attribute 'call_exception_handler'
I believe the exception is only generated if the UDP packet is actively refused, with an ICMP Destination Unreachable (which I'm not interested in).
So the question is what is the right way of doing this. I'm not interested in this connection anymore after sending so I want to get rid of the transport as soon as possible. The documentation for DatagramTransport.sendto() just says that the methods doesn't block. But how do I know when the sending is completed? (And by complete I mean when is handed over to the OS, not delivered to the remote).
Is there any other asyncio coroutine to send an UDP packet asynchronously and simple await (maybe even skipping the whole create_datagram_endpoint) ?
Is there any other asyncio coroutine to send an UDP packet asynchronously and simple await?
I would, base on DatagramTransport source, wrap it in Future to be yieldable/awaitable. It will raise exception on error and return True on success. The example PoC code:
import asyncio
import socket
class UDPClient():
def __init__(self, host, port, loop=None):
self._loop = asyncio.get_event_loop() if loop is None else loop
self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self._sock.setblocking(False)
self._addr = (host, port)
self._future = None
self._data = None
def sendto(self, data):
self._future = asyncio.Future(loop=self._loop)
self.data = data if isinstance(data, bytes) else str(data).encode('utf-8')
loop.add_writer(self._sock.fileno(), self._sendto)
return self._future
def _sendto(self):
try:
self._sock.sendto(self.data, self._addr)
except (BlockingIOError, InterruptedError):
return
except OSError as exc:
self.abort(exc)
except Exception as exc:
self.abort(exc)
else:
self.close()
self._future.set_result(True)
def abort(self, exc):
self.close()
self._future.set_exception(exc)
def close(self):
self._loop.remove_writer(self._sock.fileno())
self._sock.close()
Than simple example would look like:
#asyncio.coroutine
def test():
yield from UDPClient('127.0.0.1', 1234).sendto('ok')
# or 3.5+ syntax
# async def test():
# await UDPClient('127.0.0.1', 1234).sendto('ok')
loop = asyncio.get_event_loop()
loop.run_until_complete(test())

Categories

Resources