discord.ext.tasks -- asyncio.Task ヘルパー

バージョン 1.1.0 で追加.

ボットを作成するときの最も一般的な操作の1つは、指定した間隔でバックグラウンドでループを実行させることです。このパターンは非常に一般的ですが、注意すべきことがたくさんあります。

  • asyncio.CancelledError はどのように処理するべきですか?

  • インターネット接続が切れた場合はどうするべきですか?

  • スリープできる最大時間は何秒ですか?

discord.pyの拡張機能の目的は、こういった苦労の種を抽象化することです。

レシピ

Cog におけるシンプルなバックグラウンドタスク:

from discord.ext import tasks, commands

class MyCog(commands.Cog):
    def __init__(self):
        self.index = 0
        self.printer.start()

    def cog_unload(self):
        self.printer.cancel()

    @tasks.loop(seconds=5.0)
    async def printer(self):
        print(self.index)
        self.index += 1

再接続中に処理する例外を追加します:

import asyncpg
from discord.ext import tasks, commands

class MyCog(commands.Cog):
    def __init__(self, bot):
        self.bot = bot
        self.data = []
        self.batch_update.add_exception_type(asyncpg.PostgresConnectionError)
        self.batch_update.start()

    def cog_unload(self):
        self.batch_update.cancel()

    @tasks.loop(minutes=5.0)
    async def batch_update(self):
        async with self.bot.pool.acquire() as con:
            # batch update here...
            pass

特定の回数ループさせる:

from discord.ext import tasks
import discord

@tasks.loop(seconds=5.0, count=5)
async def slow_count():
    print(slow_count.current_loop)

@slow_count.after_loop
async def after_slow_count():
    print('done!')

class MyClient(discord.Client):
    async def setup_hook(self):
        slow_count.start()

ループが始まる前に、Botの準備が整うまで待機する:

from discord.ext import tasks, commands

class MyCog(commands.Cog):
    def __init__(self, bot):
        self.index = 0
        self.bot = bot
        self.printer.start()

    def cog_unload(self):
        self.printer.cancel()

    @tasks.loop(seconds=5.0)
    async def printer(self):
        print(self.index)
        self.index += 1

    @printer.before_loop
    async def before_printer(self):
        print('waiting...')
        await self.bot.wait_until_ready()

キャンセルする場合、その間に何らかの処理を行う:

from discord.ext import tasks, commands
import asyncio

class MyCog(commands.Cog):
    def __init__(self, bot):
        self.bot = bot
        self._batch = []
        self.lock = asyncio.Lock()
        self.bulker.start()

    async def cog_unload(self):
        self.bulker.cancel()

    async def do_bulk(self):
        # bulk insert data here
        ...

    @tasks.loop(seconds=10.0)
    async def bulker(self):
        async with self.lock:
            await self.do_bulk()

    @bulker.after_loop
    async def on_bulker_cancel(self):
        if self.bulker.is_being_cancelled() and len(self._batch) != 0:
            # if we're cancelled and we have some data left...
            # let's insert it to our database
            await self.do_bulk()

毎日特定の時間に何かを行う:

import datetime
from discord.ext import commands, tasks

utc = datetime.timezone.utc

# If no tzinfo is given then UTC is assumed.
time = datetime.time(hour=8, minute=30, tzinfo=utc)

class MyCog(commands.Cog):
    def __init__(self, bot):
        self.bot = bot
        self.my_task.start()

    def cog_unload(self):
        self.my_task.cancel()

    @tasks.loop(time=time)
    async def my_task(self):
        print("My task is running!")

毎日複数の特定の時間に何かを行う:

import datetime
from discord.ext import commands, tasks

utc = datetime.timezone.utc

# If no tzinfo is given then UTC is assumed.
times = [
    datetime.time(hour=8, tzinfo=utc),
    datetime.time(hour=12, minute=30, tzinfo=utc),
    datetime.time(hour=16, minute=40, second=30, tzinfo=utc)
]

class MyCog(commands.Cog):
    def __init__(self, bot):
        self.bot = bot
        self.my_task.start()

    def cog_unload(self):
        self.my_task.cancel()

    @tasks.loop(time=times)
    async def my_task(self):
        print("My task is running!")

APIリファレンス

class discord.ext.tasks.Loop

ループと再接続処理を抽象化するバックグラウンドタスクのヘルパー。

loop() はこれを作成するための主要なインタフェースです。

@after_loop

ループが終了した後に呼び出されるようにコルーチンを登録するデコレータ。

コルーチンは、引数をとりません。(クラス内での self は除く)

注釈

このコルーチンはキャンセル中も呼び出されます。キャンセルされたかを判断したい場合は、 is_being_cancelled()True か確認してください。

パラメータ

coro (coroutine) -- ループが終了した後に登録するコルーチン。

例外

TypeError -- 渡された関数がコルーチンでない場合に発生します。

@before_loop

ループが始まる前に呼び出されるようにコルーチンを登録するデコレータ。

discord.Client.wait_until_ready() などを用いて、ループ開始前にボットの状態が整うまで待つのに便利です。

コルーチンは、引数をとりません。(クラス内での self は除く)

バージョン 2.0 で変更: このコルーチンで stop() を呼び出すと、最初のループの実行前にループが停止します。

パラメータ

coro (coroutine) -- ループを実行する前に登録するコルーチンを示します。

例外

TypeError -- 渡された関数がコルーチンでない場合に発生します。

@error

タスクが未処理の例外に遭遇した場合に呼び出されるコルーチンを登録するデコレータ。

コルーチンは、送出された例外を引数としてとります。(クラス内での self は除く)

デフォルトではライブラリロガーに出力しますが、他の実装をするために上書きすることもできます。

バージョン 1.4 で追加.

バージョン 2.0 で変更: sys.stderr に出力する代わりに、ライブラリロガーを使用するようになりました。

パラメータ

coro (coroutine) -- 処理されない例外が発生した場合に登録するコルーチンを示します。

例外

TypeError -- 渡された関数がコルーチンでない場合に発生します。

property seconds

各ループ間の秒数を示す読み取り専用の値です。代わりに、明示的な time が渡された場合は None となります。

バージョン 2.0 で追加.

Optional[float]

property minutes

各ループ間の分数を示す読み取り専用の値です。代わりに、明示的な time が渡された場合は None となります。

バージョン 2.0 で追加.

Optional[float]

property hours

各ループ間の時間数を示す読み取り専用の値です。代わりに、明示的な time が渡された場合は None となります。

バージョン 2.0 で追加.

Optional[float]

property time

このループが実行する正確な時刻の読み取り専用のリスト。 相対的な時間が代わりに渡された場合は None

バージョン 2.0 で追加.

Optional[List[datetime.time]]

property current_loop

ループの現在の実行回数。

int

property next_iteration

次のループがいつ発火するか。

バージョン 1.3 で追加.

Optional[datetime.datetime]

await __call__(*args, **kwargs)

This function is a coroutine.

タスク内のコールバックを呼び出します。

バージョン 1.6 で追加.

パラメータ
  • *args -- 使用する引数。

  • **kwargs -- 使用するキーワード引数。

start(*args, **kwargs)

イベントループでタスクを開始します。

パラメータ
  • *args -- 使用する引数。

  • **kwargs -- 使用するキーワード引数。

例外

RuntimeError -- タスクはすでに実行されていて実行中である。

戻り値

作成されたタスク。

戻り値の型

asyncio.Task

stop()

タスクを正常に停止させます。

cancel() とは違い、現在のループが終了してからタスクを終了します。

注釈

もし終了前に内部の関数が処理可能な例外を送出した場合は成功するまで再試行します。

この動作が望ましくない場合は、停止前に clear_exception_types() を用いてエラー処理を除去するか、代わりに cancel() を使用してください。

バージョン 2.0 で変更: before_loop() にてこのメソッドを呼び出すと、最初のループの実行前にループが停止します。

バージョン 1.2 で追加.

cancel()

実行中の場合、内部のタスクをキャンセルします。

restart(*args, **kwargs)

内部タスクを再起動する便利なメソッド。

注釈

この関数の動作により、 start() とは異なりタスクは返されません。

パラメータ
  • *args -- 使用する引数。

  • **kwargs -- 使用するキーワード引数。

add_exception_type(*exceptions)

再接続中に処理する例外を追加します。

デフォルトでは、 discord.Client.connect() によって処理される例外は、多くのインターネット切断エラーを含んでいます。

独自の例外を送出するサードパーティー・ライブラリーを使用している場合に便利です。

パラメータ

*exceptions (Type[BaseException]) -- 処理するべき例外のクラスの引数リスト。

例外

TypeError -- 渡された例外はクラスでないか、 BaseException を継承していません。

clear_exception_types()

処理すべき例外タイプをすべて除去します。

注釈

この動作は当然ながら元に戻せません!

remove_exception_type(*exceptions)

再接続中に処理する例外を除去します:

パラメータ

*exceptions (Type[BaseException]) -- 処理するべき例外のクラスの引数リスト。

戻り値

すべての例外が正常に除去されたかどうか。

戻り値の型

bool

get_task()

Optional[asyncio.Task]: 内部タスクを取得します。実行中のものがない場合は None を返します。

is_being_cancelled()

タスクがキャンセルされているかどうか。

failed()

bool: 内部タスクが失敗したかどうか。

バージョン 1.2 で追加.

is_running()

bool: タスクが現在実行されているか確認します。

バージョン 1.4 で追加.

change_interval(*, seconds=0, minutes=0, hours=0, time=...)

ループ間の間隔を変更します。

バージョン 1.2 で追加.

パラメータ
  • seconds (float) -- 各繰り返しの間の秒数。

  • minutes (float) -- 各繰り返しの間の 分数。

  • hours (float) -- 各繰り返しの間の時間数。

  • time (Union[datetime.time, Sequence[datetime.time]]) --

    ループを実行する正確な時刻。空でないリストか、 datetime.time が単独で渡されるべきです。これは、相対的な時間を指定するパラメーターと同時に使用できません。

    バージョン 2.0 で追加.

    注釈

    重複された時刻は無視され、一回だけ実行されます。

例外
  • ValueError -- 無効な値が渡された場合。

  • TypeError -- time パラメーターに無効な値が渡され、または time パラメーターと相対的な時間を指定するパラメーターが同時に使用された場合。

@discord.ext.tasks.loop(*, seconds=..., minutes=..., hours=..., time=..., count=None, reconnect=True)

バックグラウンドでタスクをスケジュールし、オプションで再接続ロジックを扱うデコレータ。デコレータは Loop を返します。

パラメータ
  • seconds (float) -- 各繰り返しの間の秒数。

  • minutes (float) -- 各繰り返しの間の 分数。

  • hours (float) -- 各繰り返しの間の時間数。

  • time (Union[datetime.time, Sequence[datetime.time]]) --

    ループを実行する正確な時刻。空でないリストか、 datetime.time が単独で渡されるべきです。タイムゾーンが使用できます。タイムゾーンが設定されてない場合はUTCと推定されます。

    これは、相対的な時間を指定するパラメーターと同時に使用できません。

    注釈

    重複された時刻は無視され、一回だけ実行されます。

    バージョン 2.0 で追加.

  • count (Optional[int]) -- 実行するループの回数、無限ループの場合は None です。

  • reconnect (bool) -- エラーを処理し、 discord.Client.connect() で使用されたものと同様のエクスポネンシャルバックオフ アルゴリズムを用いてタスクを再起動するべきか。

例外
  • ValueError -- 無効な値が渡された場合。

  • TypeError -- 関数がコルーチンではない場合、 time パラメーターに無効な値が渡された場合、または time パラメーターと相対的な時間を指定するパラメーターが同時に使用された場合。