在 asyncio.Protocol.data_received() 中调用协程并获得未来?

Calling coroutine and getting future in asyncio.Protocol.data_received()?

我需要在 asyncio 循环中获取未来的结果,它类似于

但是 PY35 和 PY34 中的 asyncio 完全不同,这里是可以在 PY34 中正确 运行 的代码,但在 PY35 中它会在 yield from 处暂停并且永远不会 return.

# PY34
class RelayClient(asyncio.Protocol):
    pass

class Server(asyncio.Protocol):
    def data_received(self, data):
        # I need to connect to another address, and get future result at current function.
        # Also I could not run loop.run_until_complete().
        loop = asyncio.get_event_loop()
        result = yield from loop.create_connection(RelayClient, 'www.google.com', 443)
        do_some_thing_with_result(result)

那么,如何在 python 3.5 中做到这一点?

如有任何建议,我们将不胜感激。

您不能等待来自非协程的函数的协程。 data_received 不是协程,因此正如评论中提到的,您需要使用 ensure_future 帮助器从协程创建一个 "background" 任务。

但是不需要开始使用回调:

async def do_stuff(data):
        result = await loop.create_connection(RelayClient, 'www.google.com', 443)
       await do_some_thing_with_result(result)

class Server(asyncio.Protocol):
    def data_received(self, data):
        asyncio.ensure_future(do_stuff(data))

不过,我要指出的是,asyncio 不保证 data_received 将使用您期望的完整数据进行调用。通常你在 Protocol 中看到的模式看起来很像这样:

async def process_line(line):
    ...

class Server(asyncio.Protocol):
    def __init__(self):
        self.buffer = b''

    def data_received(self, data):
        self.buffer += data

        if b'\n' not in self.buffer:
            return

        line, self.buffer = self.buffer.split(b'\n')
        fut = asyncio.ensure_future(process_line(line))
        fut.add_done_callback(self._handle_exception)

    def _handle_exception(self, fut):
        if fut.exception() is not None:
            print('Processing failed', fut.exception())

(这只是一个示例,它复制缓冲区的方式过多,在大多数生产用例中效率非常低)