在信号处理程序、Asyncio 中调用异步函数

Jar*_*hen 8 python python-asyncio discord.py

我正在使用discord.py 制作一个Discord 机器人,有时我用来运行它的脚本需要关闭该机器人。当我在不使用信号处理程序的情况下关闭它时,会出现很多关于循环未关闭的错误,因此我添加了一个信号处理程序(使用下面的代码),并且在内部我需要调用client.close()and client.logout(),但问题是这些是异步函数和因此要求我等待它们,但我无法等待这些函数,因为信号处理程序不能是异步函数。

这是代码:

def handler():
    print("Logging out of Discord Bot")
    client.logout()
    client.close()
    sys.exit()

@client.event
async def on_ready():
    print('We have logged in as {0.user}'.format(client))

    for signame in ('SIGINT', 'SIGTERM'):
        client.loop.add_signal_handler(getattr(signal, signame),
                                lambda: asyncio.ensure_future(handler()))
Run Code Online (Sandbox Code Playgroud)

有没有办法使用信号处理程序正确注销,或者至少只是沉默代码内部的警告和错误,这样控制台中就不会打印任何错误。

use*_*342 7

您的方法处于正确的轨道上 - 由于add_signal_handler需要普通函数而不是异步函数,因此您确实需要调用ensure_future(或其表兄弟create_task)来提交异步函数以在事件循环中运行。下一步是实际进行handler异步及其await调用的协程:

async def handler():
    print("Logging out of Discord Bot")
    await client.logout()
    await client.close()
    asyncio.get_event_loop().stop()
Run Code Online (Sandbox Code Playgroud)

请注意,我更改为显式停止事件循环,因为 asyncio 对从回调中间调用的sys.exit()反应不佳(它捕获异常并抱怨未检索的异常)。sys.exit()SystemExit

由于我没有不和谐的测试,我通过更改注销并关闭睡眠来测试它:

import asyncio, signal

async def handler():
    print("sleeping a bit...")
    await asyncio.sleep(0.2)
    print('exiting')
    asyncio.get_event_loop().stop()

def setup():
    loop = asyncio.get_event_loop()
    for signame in ('SIGINT', 'SIGTERM'):
        loop.add_signal_handler(getattr(signal, signame),
                                lambda: asyncio.create_task(handler()))

setup()
asyncio.get_event_loop().run_forever()
Run Code Online (Sandbox Code Playgroud)

如果您使用 以外的其他内容启动事件循环run_forever,例如asyncio.run(some_function()),那么您将需要替换loop.stop()为设置主协程等待的任何事件的代码。例如,如果它server.serve_forever()在服务器上等待,那么您可以将服务器对象传递给handler并调用server.close(),依此类推。