python-trio: AttributeError: sendall

python-trio: AttributeError: sendall

我只是想 运行 echo-client-low-level.py 来自 python-trio docs:

# echo-client-low-level.py

import sys
import trio

# arbitrary, but:
# - must be in between 1024 and 65535
# - can't be in use by some other program on your computer
# - must match what we set in our echo server
PORT = 12345
# How much memory to spend (at most) on each call to recv. Pretty arbitrary,
# but shouldn't be too big or too small.
BUFSIZE = 16384

async def sender(client_sock):
    print("sender: started!")
    while True:
        data = b"async can sometimes be confusing, but I believe in you!"
        print("sender: sending {!r}".format(data))
        await client_sock.sendall(data)
        await trio.sleep(1)

async def receiver(client_sock):
    print("receiver: started!")
    while True:
        data = await client_sock.recv(BUFSIZE)
        print("receiver: got data {!r}".format(data))
        if not data:
            print("receiver: connection closed")
            sys.exit()

async def parent():
    print("parent: connecting to 127.0.0.1:{}".format(PORT))
    with trio.socket.socket() as client_sock:
        await client_sock.connect(("127.0.0.1", PORT))
        async with trio.open_nursery() as nursery:
            print("parent: spawning sender...")
            nursery.start_soon(sender, client_sock)

            print("parent: spawning receiver...")
            nursery.start_soon(receiver, client_sock)

trio.run(parent)

然而他们的例子产生了一个令人讨厌的 AttributeError: sendall 错误:

$ python echo-client-low-level.py 
parent: connecting to 127.0.0.1:12345
parent: spawning sender...
parent: spawning receiver...
sender: started!
sender: sending b'async can sometimes be confusing, but I believe in you!'
receiver: started!
Traceback (most recent call last):
  File "echo-client-low-level.py", line 43, in <module>
    trio.run(parent)
  File "/usr/lib/python3.6/site-packages/trio/_core/_run.py", line 1225, in run
    return result.unwrap()
  File "/usr/lib/python3.6/site-packages/trio/_core/_result.py", line 119, in unwrap
    raise self.error
  File "/usr/lib/python3.6/site-packages/trio/_core/_run.py", line 1334, in run_impl
    msg = task.coro.send(next_send)
  File "/usr/lib/python3.6/site-packages/trio/_core/_run.py", line 923, in init
    self.entry_queue.spawn()
  File "/usr/lib/python3.6/site-packages/trio/_util.py", line 109, in __aexit__
    await self._agen.asend(None)
  File "/usr/lib/python3.6/site-packages/async_generator/_impl.py", line 274, in asend
    return await self._do_it(self._it.send, value)
  File "/usr/lib/python3.6/site-packages/async_generator/_impl.py", line 290, in _do_it
    return await ANextIter(self._it, start_fn, *args)
  File "/usr/lib/python3.6/site-packages/async_generator/_impl.py", line 202, in send
    return self._invoke(self._it.send, value)
  File "/usr/lib/python3.6/site-packages/async_generator/_impl.py", line 209, in _invoke
    result = fn(*args)
  File "/usr/lib/python3.6/site-packages/trio/_core/_run.py", line 318, in open_nursery
    await nursery._nested_child_finished(nested_child_exc)
  File "/usr/lib/python3.6/contextlib.py", line 99, in __exit__
    self.gen.throw(type, value, traceback)
  File "/usr/lib/python3.6/site-packages/trio/_core/_run.py", line 203, in open_cancel_scope
    yield scope
  File "/usr/lib/python3.6/site-packages/trio/_core/_multierror.py", line 144, in __exit__
    raise filtered_exc
  File "/usr/lib/python3.6/site-packages/trio/_core/_run.py", line 203, in open_cancel_scope
    yield scope
  File "/usr/lib/python3.6/site-packages/trio/_core/_run.py", line 318, in open_nursery
    await nursery._nested_child_finished(nested_child_exc)
  File "/usr/lib/python3.6/site-packages/trio/_core/_run.py", line 427, in _nested_child_finished
    raise MultiError(self._pending_excs)
  File "/usr/lib/python3.6/site-packages/trio/_core/_run.py", line 1334, in run_impl
    msg = task.coro.send(next_send)
  File "echo-client-low-level.py", line 41, in parent
    nursery.start_soon(receiver, client_sock)
  File "/usr/lib/python3.6/site-packages/trio/_util.py", line 109, in __aexit__
    await self._agen.asend(None)
  File "/usr/lib/python3.6/site-packages/async_generator/_impl.py", line 274, in asend
    return await self._do_it(self._it.send, value)
  File "/usr/lib/python3.6/site-packages/async_generator/_impl.py", line 290, in _do_it
    return await ANextIter(self._it, start_fn, *args)
  File "/usr/lib/python3.6/site-packages/async_generator/_impl.py", line 202, in send
    return self._invoke(self._it.send, value)
  File "/usr/lib/python3.6/site-packages/async_generator/_impl.py", line 209, in _invoke
    result = fn(*args)
  File "/usr/lib/python3.6/site-packages/trio/_core/_run.py", line 318, in open_nursery
    await nursery._nested_child_finished(nested_child_exc)
  File "/usr/lib/python3.6/contextlib.py", line 99, in __exit__
    self.gen.throw(type, value, traceback)
  File "/usr/lib/python3.6/site-packages/trio/_core/_run.py", line 203, in open_cancel_scope
    yield scope
  File "/usr/lib/python3.6/site-packages/trio/_core/_multierror.py", line 144, in __exit__
    raise filtered_exc
  File "/usr/lib/python3.6/site-packages/trio/_core/_run.py", line 203, in open_cancel_scope
    yield scope
  File "/usr/lib/python3.6/site-packages/trio/_core/_run.py", line 318, in open_nursery
    await nursery._nested_child_finished(nested_child_exc)
  File "/usr/lib/python3.6/site-packages/trio/_core/_run.py", line 427, in _nested_child_finished
    raise MultiError(self._pending_excs)
  File "/usr/lib/python3.6/site-packages/trio/_core/_run.py", line 1334, in run_impl
    msg = task.coro.send(next_send)
  File "echo-client-low-level.py", line 20, in sender
    await client_sock.sendall(data)
  File "/usr/lib/python3.6/site-packages/trio/_socket.py", line 426, in __getattr__
    raise AttributeError(name)
AttributeError: sendall

检查 GitHub 后,似乎 sendallintentionally omitted

我有点糊涂了,我是不是漏掉了什么?

In [3]: trio.__version__
Out[3]: '0.3.0'

糟糕,这是文档中的一个错误 – 感谢您的发现。 sockets 上曾经有一个 sendall 方法,但是 it had problems 并且是介于低层和高层之间的一个奇怪的特性,所以它在 0.3.0 中被删除了。但是我错过了在那里更新文档。

我真的需要重写该部分以使用新的高级 APIs! (Bug filed.) 但现在这里是将示例快速翻译成新的(更好、更高级别)API:

客户:

# echo-client.py

import sys
import trio

# arbitrary, but:
# - must be in between 1024 and 65535
# - can't be in use by some other program on your computer
# - must match what we set in our echo server
PORT = 12345
# How much memory to spend (at most) on each call to recv. Pretty arbitrary,
# but shouldn't be too big or too small.
BUFSIZE = 16384

async def sender(client_stream):
    print("sender: started!")
    while True:
        data = b"async can sometimes be confusing, but I believe in you!"
        print("sender: sending {!r}".format(data))
        await client_stream.send_all(data)
        await trio.sleep(1)

async def receiver(client_stream):
    print("receiver: started!")
    while True:
        data = await client_stream.receive_some(BUFSIZE)
        print("receiver: got data {!r}".format(data))
        if not data:
            print("receiver: connection closed")
            sys.exit()

async def parent():
    print("parent: connecting to 127.0.0.1:{}".format(PORT))
    client_stream = await trio.open_tcp_stream("127.0.0.1", PORT)
    async with client_stream:
        async with trio.open_nursery() as nursery:
            print("parent: spawning sender...")
            nursery.start_soon(sender, client_stream)

            print("parent: spawning receiver...")
            nursery.start_soon(receiver, client_stream)

trio.run(parent)

服务器

# echo-server.py

import trio
from itertools import count

# Port is arbitrary, but:
# - must be in between 1024 and 65535
# - can't be in use by some other program on your computer
# - must match what we set in our echo client
PORT = 12345
# How much memory to spend (at most) on each call to recv. Pretty arbitrary,
# but shouldn't be too big or too small.
BUFSIZE = 16384

CONNECTION_COUNTER = count()

async def echo_server(server_stream):
    # Assign each connection a unique number to make our logging easier to
    # understand
    ident = next(CONNECTION_COUNTER)
    print("echo_server {}: started".format(ident))
    try:
        while True:
            data = await server_stream.receive_some(BUFSIZE)
            print("echo_server {}: received data {!r}".format(ident, data))
            if not data:
                print("echo_server {}: connection closed".format(ident))
                return
            print("echo_server {}: sending data {!r}".format(ident, data))
            await server_stream.send_all(data)
    except Exception as exc:
        # Unhandled exceptions will propagate into our parent and take
        # down the whole program. If the exception is KeyboardInterrupt,
        # that's what we want, but otherwise maybe not...
        print("echo_server {}: crashed: {!r}".format(ident, exc))

async def main():
    await trio.serve_tcp(echo_server, PORT)

# We could also just write 'trio.run(serve_tcp, echo_server, PORT)', but real
# programs almost always end up doing other stuff too and then we'd have to go
# back and factor it out into a separate function anyway. So it's simplest to
# just make it a standalone function from the beginning.
trio.run(main)

它们与文本不太匹配,抱歉!但希望他们能给你一些机会弄清楚发生了什么,直到我真正修复文档。