使用 discord.py 调用 python asyncio loop.run_until_complete() 不起作用?
Calling python asyncio loop.run_until_complete() with discord.py not working?
使用 discord.py 制作 Discord 机器人,这是我第一次使用 asyncio,也可能是我第一次在 Python.
中遇到如此令人沮丧的事情
这个问题的重点不是教我如何使用asyncio,而是教我如何避免使用它,即使它不是正确的做事方式。
所以我需要 运行 来自常规 def
函数的不和谐客户端协程。经过几个小时的搜索,我发现了这个:asyncio.get_event_loop().run_until_complete(...)
。我设置了一个小脚本来测试它:
import asyncio
async def test():
print('Success')
asyncio.get_event_loop().run_until_complete(test())
而且效果很好。所以我继续尝试在不和谐的机器人中使用它:
import discord
import asyncio
client = discord.Client()
@client.event
async def on_ready():
test()
def test():
asyncio.get_event_loop().run_until_complete(run())
async def run():
print('Success')
client.run('TOKEN_HERE')
我得到一个错误... Stacktrace/Output:
Success
Ignoring exception in on_ready
Traceback (most recent call last):
File "C:\Program Files\Python36\lib\site-packages\discord\client.py", line 307, in _run_event
yield from getattr(self, event)(*args, **kwargs)
File "C:/Users/OverclockedSanic/PyCharm Projects/asyncio test/test.py", line 8, in on_ready
test()
File "C:/Users/OverclockedSanic/PyCharm Projects/asyncio test/test.py", line 11, in test
asyncio.get_event_loop().run_until_complete(run())
File "C:\Program Files\Python36\lib\asyncio\base_events.py", line 454, in run_until_complete
self.run_forever()
File "C:\Program Files\Python36\lib\asyncio\base_events.py", line 408, in run_forever
raise RuntimeError('This event loop is already running')
RuntimeError: This event loop is already running
奇怪的是最后的 "Success" 部分......我尝试了一些其他测试,看看我是否可以 return 来自协程的数据或执行更多的东西,但它不能.
我什至尝试用 client.loop
替换 asyncio.get_event_loop()
,但也没用。
找了2天了,还是没有解决。有什么想法吗?
编辑: 按照评论中的建议用 new_event_loop()
替换 get_event_loop()
提出了这个问题:
Ignoring exception in on_ready
Traceback (most recent call last):
File "C:\Program Files\Python36\lib\site-packages\discord\client.py", line 307, in _run_event
yield from getattr(self, event)(*args, **kwargs)
File "C:/Users/USER/PyCharm Projects/asyncio test/test.py", line 8, in on_ready
test()
File "C:/Users/USER/PyCharm Projects/asyncio test/test.py", line 11, in test
asyncio.new_event_loop().run_until_complete(run())
File "C:\Program Files\Python36\lib\asyncio\base_events.py", line 454, in run_until_complete
self.run_forever()
File "C:\Program Files\Python36\lib\asyncio\base_events.py", line 411, in run_forever
'Cannot run the event loop while another loop is running')
RuntimeError: Cannot run the event loop while another loop is running
您的问题似乎本质上与混合同步和异步代码有关。有两种可能:
1) 如果您的 non-async 例程不需要阻塞,只是为了安排一些异步任务(例如 send_message
)在 运行 之后,那么他们可以简单地调用get_event_loop().create_task()
。如果您希望在异步操作完成时调用其他 (non-async) 例程,您甚至可以对返回的任务使用 add_done_callback
。 (如果套路要运行也是non-async,那就用get_event_loop().call_soon()
。)
2) 如果您的 non-async 例程绝对必须阻塞(包括可能等待异步例程),并且不能安排稍后的阻塞操作,那么您不应该 运行 它们来自同一个线程作为主事件循环。您可以使用 concurrent.futures.ThreadPoolExecutor
创建一个线程池,并使用 asyncio.run_in_executor()
来安排您的 non-async 例程,然后等待结果。如果他们反过来需要调用异步例程,那么 run_until_complete()
应该可以工作,因为现在你不在一个已经有事件循环的线程中 运行。 (但要注意线程安全问题。如果您需要在主事件循环中等待某些东西到 运行,您可能需要类似 run_coroutine_threadsafe
的东西。)
如果有帮助,asgiref
包中包含可以为您简化此过程的例程。它们的设计用途略有不同(Web 服务器),但也可能适合您。当您想从异步例程调用 non-async 例程时,您可以使用 await asgiref.sync.sync_to_async(func)(args)
,这将 运行 线程池中的例程,然后在您想要时使用 asgiref.sync.async_to_sync(func)(args)
从线程池中 运行 的 non-async 例程调用异步例程。
使用 discord.py 制作 Discord 机器人,这是我第一次使用 asyncio,也可能是我第一次在 Python.
中遇到如此令人沮丧的事情这个问题的重点不是教我如何使用asyncio,而是教我如何避免使用它,即使它不是正确的做事方式。
所以我需要 运行 来自常规 def
函数的不和谐客户端协程。经过几个小时的搜索,我发现了这个:asyncio.get_event_loop().run_until_complete(...)
。我设置了一个小脚本来测试它:
import asyncio
async def test():
print('Success')
asyncio.get_event_loop().run_until_complete(test())
而且效果很好。所以我继续尝试在不和谐的机器人中使用它:
import discord
import asyncio
client = discord.Client()
@client.event
async def on_ready():
test()
def test():
asyncio.get_event_loop().run_until_complete(run())
async def run():
print('Success')
client.run('TOKEN_HERE')
我得到一个错误... Stacktrace/Output:
Success
Ignoring exception in on_ready
Traceback (most recent call last):
File "C:\Program Files\Python36\lib\site-packages\discord\client.py", line 307, in _run_event
yield from getattr(self, event)(*args, **kwargs)
File "C:/Users/OverclockedSanic/PyCharm Projects/asyncio test/test.py", line 8, in on_ready
test()
File "C:/Users/OverclockedSanic/PyCharm Projects/asyncio test/test.py", line 11, in test
asyncio.get_event_loop().run_until_complete(run())
File "C:\Program Files\Python36\lib\asyncio\base_events.py", line 454, in run_until_complete
self.run_forever()
File "C:\Program Files\Python36\lib\asyncio\base_events.py", line 408, in run_forever
raise RuntimeError('This event loop is already running')
RuntimeError: This event loop is already running
奇怪的是最后的 "Success" 部分......我尝试了一些其他测试,看看我是否可以 return 来自协程的数据或执行更多的东西,但它不能.
我什至尝试用 client.loop
替换 asyncio.get_event_loop()
,但也没用。
找了2天了,还是没有解决。有什么想法吗?
编辑: 按照评论中的建议用 new_event_loop()
替换 get_event_loop()
提出了这个问题:
Ignoring exception in on_ready
Traceback (most recent call last):
File "C:\Program Files\Python36\lib\site-packages\discord\client.py", line 307, in _run_event
yield from getattr(self, event)(*args, **kwargs)
File "C:/Users/USER/PyCharm Projects/asyncio test/test.py", line 8, in on_ready
test()
File "C:/Users/USER/PyCharm Projects/asyncio test/test.py", line 11, in test
asyncio.new_event_loop().run_until_complete(run())
File "C:\Program Files\Python36\lib\asyncio\base_events.py", line 454, in run_until_complete
self.run_forever()
File "C:\Program Files\Python36\lib\asyncio\base_events.py", line 411, in run_forever
'Cannot run the event loop while another loop is running')
RuntimeError: Cannot run the event loop while another loop is running
您的问题似乎本质上与混合同步和异步代码有关。有两种可能:
1) 如果您的 non-async 例程不需要阻塞,只是为了安排一些异步任务(例如 send_message
)在 运行 之后,那么他们可以简单地调用get_event_loop().create_task()
。如果您希望在异步操作完成时调用其他 (non-async) 例程,您甚至可以对返回的任务使用 add_done_callback
。 (如果套路要运行也是non-async,那就用get_event_loop().call_soon()
。)
2) 如果您的 non-async 例程绝对必须阻塞(包括可能等待异步例程),并且不能安排稍后的阻塞操作,那么您不应该 运行 它们来自同一个线程作为主事件循环。您可以使用 concurrent.futures.ThreadPoolExecutor
创建一个线程池,并使用 asyncio.run_in_executor()
来安排您的 non-async 例程,然后等待结果。如果他们反过来需要调用异步例程,那么 run_until_complete()
应该可以工作,因为现在你不在一个已经有事件循环的线程中 运行。 (但要注意线程安全问题。如果您需要在主事件循环中等待某些东西到 运行,您可能需要类似 run_coroutine_threadsafe
的东西。)
如果有帮助,asgiref
包中包含可以为您简化此过程的例程。它们的设计用途略有不同(Web 服务器),但也可能适合您。当您想从异步例程调用 non-async 例程时,您可以使用 await asgiref.sync.sync_to_async(func)(args)
,这将 运行 线程池中的例程,然后在您想要时使用 asgiref.sync.async_to_sync(func)(args)
从线程池中 运行 的 non-async 例程调用异步例程。