有没有一种方法可以使用atexit运行异步协程?

时间:2018-12-05 21:24:17

标签: python asynchronous python-asyncio discord.py

我正在使用`discord.py在机器人上工作。该漫游器建立/删除多个通道,并连接到SQLite数据库。如果漫游器崩溃了,我希望它

  1. 销毁它创建的所有临时语音通道。
  2. 断开与SQL数据库的连接。

这是关机协程:

async def shutdown(self):
    print("Shutting down Canvas...")
    for ch in self.active_channels:
        await client.delete_channel(ch)
    self.db.close()

我尝试过的事情:

# Canv is the interface between the bot and the data we're collecting
atexit.register(canv.shutdown) 
bot.run(TOKEN)

try:
    bot.loop.run_until_complete(bot.start(TOKEN))
except KeyboardInterrupt or InterruptedError:
    bot.loop.run_until_complete(canv.shutdown())
finally:
    bot.loop.close()

from async_generator import asynccontextmanager

@asynccontextmanager
async def cleanup_context_manager():
    try:
        yield
    finally:
        await canv.shutdown()

with cleanup_context_manager():
    bot.run(TOKEN)

这些都不运行canv.shutdown(),它是asyncio.coroutine。如何确保此代码在每种类型的出口上运行?

我使用this post来获取一些信息,我认为它与我想要的最接近。

2 个答案:

答案 0 :(得分:1)

尝试:

try:
    bot.loop.run_until_complete(bot.start(TOKEN))
finally:
    bot.loop.run_until_complete(canv.shutdown())
    bot.loop.close()

您想在每次关闭脚本时都删除通道并关闭数据库,不仅是崩溃时,对吗?

否则,请尝试:

try:
    bot.loop.run_until_complete(bot.start(TOKEN))
except Exception:
    bot.loop.run_until_complete(canv.shutdown())
    raise
finally:
    bot.loop.close()

更新:

根据您提供的链接:

  

已散发的异常信息以及异常本身可以   通过对sys.exc_info()的标准调用来检索。

让我们尝试一下:

import sys

try:
    bot.loop.run_until_complete(bot.start(TOKEN))
finally:
    if sys.exc_info() != (None, None, None):
        bot.loop.run_until_complete(canv.shutdown())
    bot.loop.close()

答案 1 :(得分:1)

您可以尝试这样的事情

import asyncio
import atexit


@atexit.register
def shutdown(self):
    print("Shutting down Canvas...")
    loop = asyncio.get_event_loop()
    loop.run_until_complete(await_delete_channels())
    self.db.close()


async def await_delete_channels(self):
    # # Works but it is better to do it asynchronously
    # for ch in self.active_channels:
    #    await client.delete_channel(ch)
    #
    # Doing delete_channels() asynchronously
    delete_channels = [client.delete_channel(ch) for ch in self.active_channels]
    await asyncio.wait(delete_channels, return_when=asyncio.ALL_COMPLETED)