Python websocket运行异步函数,但返回错误:无法从正在运行的事件循环调用asyncio.run()
我正在尝试使用django Channel 2创建WebSocket。我需要运行一个异步方法,该方法应该返回命令的输出,这样我就可以将数据传递回我网站上的用户。我的问题是,它不允许我运行它,结果出现错误:Python websocket运行异步函数,但返回错误:无法从正在运行的事件循环调用asyncio.run(),python,django,python-3.x,django-channels,Python,Django,Python 3.x,Django Channels,我正在尝试使用django Channel 2创建WebSocket。我需要运行一个异步方法,该方法应该返回命令的输出,这样我就可以将数据传递回我网站上的用户。我的问题是,它不允许我运行它,结果出现错误: asyncio.run() cannot be called from a running event loop 我做错了什么?我能做些什么 消费者.py class ChatConsumer(AsyncConsumer): async def websocket_connect(
asyncio.run() cannot be called from a running event loop
我做错了什么?我能做些什么
消费者.py
class ChatConsumer(AsyncConsumer):
async def websocket_connect(self, event):
await self.send({
"type": "websocket.accept"
})
user = self.scope['user']
get_task_id = self.scope['url_route']['kwargs']['task_id']
await asyncio.run(self.run("golemcli tasks show {}".format(get_task_id)))
await self.send({
"type": "websocket.send",
"text": "hey"
})
async def websocket_receive(self, event):
print("receive", event)
async def websocket_disconnect(self, event):
print("disconnected", event)
async def run(self, cmd):
proc = await asyncio.create_subprocess_shell(
cmd,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE)
stdout, stderr = await proc.communicate()
print(f'[{cmd!r} exited with {proc.returncode}]')
if stdout:
print(f'[stdout]\n{stdout.decode()}')
if stderr:
print(f'[stderr]\n{stderr.decode()}')
回溯:
Exception inside application: asyncio.run() cannot be called from a running event loop
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/channels/sessions.py", line 183, in __call__
return await self.inner(receive, self.send)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/channels/middleware.py", line 41, in coroutine_call
await inner_instance(receive, send)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/channels/consumer.py", line 62, in __call__
await await_many_dispatch([receive], self.dispatch)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/channels/utils.py", line 52, in await_many_dispatch
await dispatch(result)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/channels/consumer.py", line 73, in dispatch
await handler(message)
File "/Users/golemgrid/Documents/GitHub/GolemGrid/overview/consumers.py", line 19, in websocket_connect
await asyncio.run(self.run("golemcli tasks show {}".format(get_task_id)))
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncio/runners.py", line 34, in run
"asyncio.run() cannot be called from a running event loop")
asyncio.run() cannot be called from a running event loop
更新2
使用以下代码段时:
await self.run("golemcli tasks show {}".format(get_task_id)
它返回以下回溯:
Exception inside application: Cannot add child handler, the child watcher does not have a loop attached
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/channels/sessions.py", line 183, in __call__
return await self.inner(receive, self.send)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/channels/middleware.py", line 41, in coroutine_call
await inner_instance(receive, send)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/channels/consumer.py", line 62, in __call__
await await_many_dispatch([receive], self.dispatch)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/channels/utils.py", line 52, in await_many_dispatch
await dispatch(result)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/channels/consumer.py", line 73, in dispatch
await handler(message)
File "/Users/golemgrid/Documents/GitHub/GolemGrid/overview/consumers.py", line 19, in websocket_connect
await self.run("golemcli tasks show {}".format(get_task_id))
File "/Users/golemgrid/Documents/GitHub/GolemGrid/overview/consumers.py", line 37, in run
stderr=asyncio.subprocess.PIPE)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncio/subprocess.py", line 202, in create_subprocess_shell
stderr=stderr, **kwds)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncio/base_events.py", line 1503, in subprocess_shell
protocol, cmd, True, stdin, stdout, stderr, bufsize, **kwargs)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncio/unix_events.py", line 193, in _make_subprocess_transport
self._child_watcher_callback, transp)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncio/unix_events.py", line 924, in add_child_handler
"Cannot add child handler, "
Cannot add child handler, the child watcher does not have a loop attached
您已经在一个异步运行循环中,因此根据您想要执行的操作,您可以选择 在使用者运行循环中处理
run
调用
(由于存在错误,需要python 3.8)
wait self.run(“golemcli tasks show{}.format(get_task_id))
无需嵌套运行循环
这意味着,如果在子流程
执行其工作时有任何新消息到达使用者,则这些消息将排队等待,直到子流程
完成后才会执行。(公平地说,这是最简单的解决办法)
这意味着您的hey
消息将在run
方法完成后发送
创建嵌套的运行循环
(由于存在错误,需要python 3.8)
如果您希望您的消费者能够在您的子流程
运行时处理新消息(发送给它)。(这要复杂得多,除非您的websocket连接需要此功能,否则不应该这样做)
async def websocket_connect(self,event):
...
#开始跑步
self.run\u task=asyncio.create\u任务(self.run(…)
...
异步def websocket_接收(自身、事件):
打印(“接收”,事件)
异步def websocket_断开(自,事件):
打印(“断开”,事件)
如果不是self.run_task.done():
#清理我们创建的队列的任务
self.run_task.cancel()
尝试:
#让我们从嵌套循环中获取任何异常
等待self.run\u任务
除取消错误外:
#忽略此错误,因为我们刚刚触发了它
通过
其他:
#从此嵌套循环中抛出任何错误
self.run_task.result()
作为阻止任务运行,但在线程池中运行
将运行更改为同步任务
#2。在自定义线程池中运行:
loop=asyncio.get\u running\u loop()
将concurrent.futures.ThreadPoolExecutor()作为池:
结果=等待循环。在执行器中运行(
pool,self.run,“你的命令”)
打印('自定义线程池',结果)
作为一个单独的安全注意事项,您的代码可以很容易地让某人在您的服务器上运行bash命令,因为您从url中的任务id获取原始字符串。我建议在这里添加一些验证,如果您在数据库中有一个任务条目,使用url值查找数据库中的任务,然后在生成命令时使用记录中的id值,或者,如果这至少是不可能的,请确保任务id具有非常严格的正则表达式验证,以确保它不能包含任何您不期望的字符。Hi!我尝试了你的两个建议,它返回了与
child…
相同的错误。关于安全性-我将检查用户的输入。当前的代码是一个简化的演示,只是为了看看它是如何工作的,而我正试图弄明白这个异步/websockets的东西。顺便说一句,我用traceback更新了我的帖子。解决这个问题的更好方法是我可以使用芹菜在后台“卸载”任务,然后获得输出吗?关于shell保护,请看一下python文档中的子进程命令,它们提到了保护自己的方法。这是一个实际错误Python本身就是子输出的东西!我升级到3.8.1,解决了这个问题。我现在可以运行等待asyncio.run(self.run…)
!谢谢你的帮助,马特豪斯!谢谢你的建议。现在,我将使用等待self.run
并查看在完成我的方法后结果如何。