![](/img/trans.png)
[英]I'm trying to make an async function and came across this error saying 'asyncio.run() cannot be called from an running event loop'
[英]websocket run async function but returns error: asyncio.run() cannot be called from a running event loop
我正在尝试使用 django-channels 2 来创建 websocket。 我需要运行一个异步方法,该方法应该返回命令的输出,以便我可以将数据传递回我网站上的用户。 我的问题是它不会让我运行它并出现错误:
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
调用(由于bug需要 python 3.8)
await self.run("golemcli tasks show {}".format(get_task_id))
不需要嵌套的 runloop。
这意味着如果在subprocess
执行其工作时有任何新消息到达消费者,它们将排队等候并且在subprocess
完成之前不会被执行。 (这是最简单的解决方案)。
这意味着您的hey
消息将在run
方法完成后发送。
(由于bug需要 python 3.8)
如果您希望您的消费者能够在您的subprocess
处理新消息(发送给它)。 (这要复杂得多,除非您的 websocket 连接需要此功能,否则您不应该这样做)。
async def websocket_connect(self, event):
...
# start the run
self.run_task = asyncio.create_task(self.run(...))
...
async def websocket_receive(self, event):
print("receive", event)
async def websocket_disconnect(self, event):
print("disconnected", event)
if not self.run_task.done():
# Clean up the task for the queue we created
self.run_task.cancel()
try:
# let us get any exceptions from the nested loop
await self.run_task
except CancelledError:
# Ignore this error as we just triggered it
pass
else:
# throw any error from this nested loop
self.run_task.result()
将运行更改为同步任务。
# 2. Run in a custom thread pool:
loop = asyncio.get_running_loop()
with concurrent.futures.ThreadPoolExecutor() as pool:
result = await loop.run_in_executor(
pool, self.run, "your cmd")
print('custom thread pool', result)
作为单独的安全说明,您的代码很容易让某人在您的服务器上运行 bash 命令,因为您从 url 中的任务 id 获取原始字符串。 我建议在此处添加一些验证,如果您的数据库中有一个任务条目,则可能使用 url 值查找数据库中的任务,然后在构建命令时使用记录中的 id 值,或者如果这至少是不可能的确保 task_id 具有非常严格的正则表达式验证,以确保它不能包含您不期望的任何字符。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.