简体   繁体   English

为什么在这里获取 contextvar 不起作用?

[英]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?为什么调用message = message_var.get()会导致崩溃? Why can't Python find the context var?为什么 Python 找不到上下文变量? Why is data_received not in the same context as send ?为什么data_receivedsend不在同一上下文中? 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.我正在使用Textual的主分支开发一个更大的项目,它使用一个 contextvar,每次使用上面代码的修改版本收到消息时都会丢失上下文。

Keeping a separated "context" for each task is exactly what contextvars are about.为每个任务保留一个单独的“上下文”正是 contextvars 的意义所在。 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.如果您可以控制协议类的“上层”(而不是“底层”)驱动程序,则只能断言在同一上下文中调用了senddata_received方法 - 情况并非如此,并且两者都被调用不同的语境。 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.是:除非您编写自己的代码实现以使其在 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.一旦你有了它,一个以这些消息 ID 作为键的简单字典就可以满足你在这个例子中的意图。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 为什么 enumerate() 在这里没有按预期工作? - Why does enumerate() not work as intended here? 为什么“ url”在这里不能作为变量使用? - Why does 'url' not work as a variable here? 为什么 break 语句在这里不起作用? - Why does the break statement not work here? 为什么将列表元素分配给变量在这里以不同的方式工作? - Why does assiging a list element to a variable work in a different manner here? 将列表插入单元格 - 为什么loc在这里工作? - Inserting list into a cell - why does loc ACTUALLY work here? 为什么 f-string 文字在这里不起作用,而 %()s 格式可以? - Why does f-string literal not work here, whereas %()s formatting does? 这里默认是怎么工作的,get方法里面怎么有2 arguments,为什么最后有()? - How does the default work here and how does the get method have 2 arguments in it and why are there () at the end? 为什么一个功能起作用而另一个起作用呢? 可变性或不变性如何在这里起作用? - Why does one function work and other don't ? How is the mutability or immutability working here? 当 np.where 抛出 TypeError 时,为什么 np.vectorize 在这里工作? - why does np.vectorize work here when np.where throws a TypeError? double asterik运算符在这里如何工作? - How does the double asterik operator work here?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM