【发布时间】:2021-05-25 04:15:34
【问题描述】:
我开发了一种模式,用于通过cmd 模块外壳使用eventloop 命令python 守护程序。但是,它还没有准备好,因为我无法弄清楚如何优雅地退出这两个应用程序(我还在学习asyncio 并且无法弄清楚以下问题)。当cmd 模块被命令退出时,我得到:
(Cmd) exit
Shutting down client...
Traceback (most recent call last):
File "client_loop.py", line 10, in <module>
loop.run_until_complete(test.cmdloop())
File "...\asyncio\base_events.py", line 467, in run_until_complete
future = tasks.ensure_future(future, loop=self)
File "...\lib\asyncio\tasks.py", line 526, in ensure_future
raise TypeError('An asyncio.Future, a coroutine or an awaitable is '
TypeError: An asyncio.Future, a coroutine or an awaitable is required
我不擅长asyncio,我做错了什么?很抱歉这个问题很长,但文件/错误使它很长,希望它更容易调试。
以下是支持文件:
shell.py
# implementation of the (Cmd) prompt with history functionality
# standard imports
import cmd as cmd
class Shell(cmd.Cmd):
def __init__(self, **kwargs):
cmd.Cmd.__init__(self, **kwargs)
self.eventloop = None
self.shutdown_client = None
self.tcp_echo_client = None
def set_eventloop(self, loop):
self.eventloop = loop
def set_funcs(self, tcp_echo_client, shutdown_client):
self.tcp_echo_client = tcp_echo_client
self.shutdown_client = shutdown_client
def do_exit(self,*args):
"""
Exits the shell gracefully
:param args:
:return:
"""
print('Shutting down client...')
self.shutdown_client(self.eventloop)
return True
def default(self, line):
try:
self.eventloop.run_until_complete(self.tcp_echo_client(line, self.eventloop))
except SystemExit:
pass
server.py
# server logic to parse arguments coming over the TCP socket and echo it back
# standard imports
import asyncio
async def 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()
loop = asyncio.get_event_loop()
coro = asyncio.start_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()
except KeyboardInterrupt:
pass
finally:
# Close the server
for task in asyncio.Task.all_tasks():
loop.run_until_complete(task)
server.close()
loop.run_until_complete(server.wait_closed())
loop.stop()
loop.close()
exit(0)
client.py
# client functions to send message over TCP and process response
# standard imports
import asyncio
# user imports
import shell
async def tcp_echo_client(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 shutdown_client(loop):
loop.stop()
# Find all running tasks:
pending = asyncio.Task.all_tasks()
# Run loop until tasks done:
loop.run_until_complete(asyncio.gather(*pending))
loop = asyncio.get_event_loop()
test = shell.Shell()
test.set_eventloop(loop)
test.set_funcs(tcp_echo_client, shutdown_client)
loop.run_until_complete(test.cmdloop())
loop.close()
【问题讨论】:
-
您能否将您的代码缩短为一个最小 示例,该示例仍然显示您所询问的异步问题?很难遵循程序的抽象层,这可能与错误无关。 (代码似乎也不完整;您调用
test.cmdloop(),但似乎没有在任何地方定义cmdloop。) -
cmdloop() 来自 cmd.Cmd。老实说,它尽可能地小。它的客户端/服务器,其中客户端是一个 shell,它们相互发送 argsparse 命令。这是我所追求并作为基础的模式。我可以删除 arg.py 但这几乎没有什么收获。您是否尝试过自己运行程序?您可能会惊讶于它是多么容易拿起它。
-
好的,我接受了你的建议。我让它变得更简单,但仍然会产生同样的问题。请看一下。如果您启动客户端/服务器并开始在彼此之间发送消息,也应该可以工作。
-
我真的不想听起来要求很高,也许其他人不会介意问题的格式,所以我写这个只是为了澄清:我指的是这样一个事实示例包含四个源文件和抽象,如
cmd.Cmd显然与 asyncio 无关。提供一个最小示例的想法是您将代码范围缩小到尽可能接近问题所在的位置,以便更容易理解和解决它。即使在缩短之后,您的示例仍然有多个源文件并使用Cmd,因此它至少似乎不是最小的。 -
入口点看起来很可疑 - 它应该如何工作,因为
cmdloop不是异步的并且您将它传递给run_until_complete()?此外,您正在其中运行run_until_complete()的其他实例。也许根本问题是cmd.Cmd与 asyncio 不兼容?异步程序通常需要从头开始使用异步库,至少在调用任何可能阻塞的东西时。
标签: python-3.x shell tcp python-asyncio event-loop