LoginSignup
0
0

Python async/awaitのエラーをシンプルに出す。

Posted at

Pythonのasync/await。入門するとエラーがわかりづらい。

  • SyntaxError: 'await' outside function
  • RuntimeWarning: coroutine 'main' was never awaited
  • TypeError: object async_generator can't be used in 'await' expression
  • TypeError: object generator can't be used in 'await' expression
  • await value <generator object 関数名 at アドレス>がエターナル
  • await value <async_generator object 関数名 at アドレス>がエターナル
  • TypeError: 'async_generator' object is not iterable
  • TypeError: 'async for' requires an object with __aiter__ method, got generator
  • RuntimeWarning: coroutine 'sleep' was never awaited
  • RuntimeError: Task got bad yield: 0

そこで上記のエラーが出るシンプルなコード例(エラーの出ない例も少し)を作ってみることにしました。

この記事には、本質的な同期/非同期の話はありません。単にasync/await周りのエラーはこうすると出ますという、逆引き風の例です。

余談ですが、この例を作り終わったあと、昨晩ハマっていたいくつかのasync/await課題はコードを見ただけでウソのように直せるようになっていました。

SyntaxError: 'await' outside function

構文エラーで、「"await" は非同期関数内でのみ許可されます」というPylanceのメッセージが出ます。

エラー例1

import asyncio


async def simple():
    return 49


await simple()

エラー例2

import asyncio


async def simple():
    return 49


def main():  # ★これが非同期関数ではない
    ret_value = await simple()
    print(f"await value {ret_value}")


main()

main()をasync defにして実行すると次のRuntimeWarningが出ます。 asyncのコルーチンは関数のように呼び出しても実行されないのがPythonのasync/awaitのわかりにくいところですね。コルーチンを生成してくれるだけ(コンストラクタ)です。

RuntimeWarning: coroutine 'main' was never awaited

これはワーニングが出る例ですが、プログラムとして実行もされません。

ワーニング例

import asyncio


async def simple():
    return 49

    
async def main():
    ret_value = await simple()
    print(f"await value {ret_value}")


main() # 非同期関数の呼び出しはコルーチンを生成するだけ、実行されない。

動く例

import asyncio


async def simple():
    return 49


async def main():
    ret_value = await simple()
    print(f"await value {ret_value}")


asyncio.run(main()) # 非同期関数を実行する。

TypeError: object async_generator can't be used in 'await' expression

エラー例

import asyncio


async def simple():  ## async付きでyieldする関数は非同期ジェネレータ
    for i in range(10):
        print(f"good_yield({i})")
        yield i


async def main():
    ret_value = await simple()
    print(f"await value {ret_value}")


asyncio.run(main())

非同期ジェネレータはawaitしてもダメです。
ジェネレータ(generator)は、for文で呼び出すのが普通です。

for value in <ジェネレータ>:
   valueを使う。

世界的に有名なジェネレータは range() でしょうか。

TypeError: object generator can't be used in 'await' expression

先のものと似ていますが、 simple()は async_generator ではなく、普通の generator です。

エラー例

import asyncio


def simple():  # これは普通のジェネレータ
    for i in range(10):
        print(f"good_yield({i})")
        yield i


async def main():
    ret_value = await simple()
    print(f"await value {ret_value}")


asyncio.run(main())

await value <generator object 関数 at アドレス>がエターナル

エターナル例

import asyncio


def simple():  # yieldで戻る関数はジェネレータオブジェクト(Generator)
    for i in range(10):
        print(f"good_yield({i})")
        yield i

        
async def main():
    while True:
        ret_value = simple() # これはジェネレータを生成しているだけ
        print(f"await value {ret_value}")


asyncio.run(main())

ちなみにジェネレータは先に書いた通り、for文で使います。

for value in sample():
   print(value)

await value <async_generator object 関数名 at アドレス>がエターナル

先の例のgeneratorがasync_generatorになっただけです。

エターナル例

import asyncio


async def simple(): # さっきの例にasyncを入れただけ。async_generatorに変わった
    for i in range(10):
        print(f"good_yield({i})")
        yield i


async def main():
    while True:
        ret_value = simple()
        print(f"await value {ret_value}")


asyncio.run(main())

非同期ジェネレータはasync forで使います。

async for value in sample():
  print(value)

TypeError: 'async_generator' object is not iterable

非同期ジェネレータオブジェクトがiterableではない。。。ジェネレータがiterableではないというメッセージはちょっと理不尽な気もしますね。

エラー例

import asyncio


async def simple(): # ★ここをasyncにした。
    for i in range(10):
        print(f"good_yield({i})")
        yield i


async def main():
    for ret_value in simple(): # async forにしないと理不尽なことを言われる。
        print(f"await value {ret_value}")


asyncio.run(main())

理不尽でない例

import asyncio


async def simple(): 
    for i in range(10):
        print(f"good_yield({i})")
        yield i


async def main():
    async for ret_value in simple(): # ここにasyncを入れる
        print(f"await value {ret_value}")


asyncio.run(main())

TypeError: 'async for' requires an object with __aiter__ method, got generator

今度は async for で普通のジェネレータを呼んでしまった場合。 普通ジェネレータには__iter__が、非同期ジェネレータには__aiter__メソッドが付きます。

エラー例

import asyncio


def simple():  # ここのasyncを外すと __iter__ はあるけど __aiter__ がない。
    for i in range(10):
        print(f"good_yield({i})")
        yield i


async def main():
    async for ret_value in simple(): # ここにasyncがあるため要 __aiter__
        print(f"await value {ret_value}")


asyncio.run(main())

RuntimeWarning: coroutine 'sleep' was never awaited

asyncio.sleep()に awaitを忘れた場合(sleepしてくれない)。

ワーニング例

import asyncio


async def whild_simple():
    i = 0
    while True:
        i += 1
        print(f"while simple {i}")
        asyncio.sleep(1)  # ここにawaitが抜けている
        yield i


async def main():

    async for ret_value in whild_simple():
        print(f"main ret_value = {ret_value}")


asyncio.run(main())

RuntimeError: Task got bad yield: 0

ジェネレータをtaskで実行しちゃった場合

エラー例

import asyncio
import time


def whild_simple():  # 普通のジェネレータ
    i = 0
    while True:
        time.sleep(1)
        i += 1
        if i % 2 == 0:
            print(f"while yield {i}")
            yield i
        else:
            print(f"while not yield {i}")


async def main():

    task = asyncio.create_task(whild_simple()) # taskとして実行
    await task


asyncio.run(main())

動く例

import asyncio
import time


async def while_simple():
    i = 0
    while True:
        time.sleep(1)
        i += 1
        if i % 2 == 0:
            print(f"while yield {i}")
            yield i
        else:
            print(f"while not yield {i}")


async def main():

    async for value in while_simple(): # ジェネレータは async for で呼ぶ
        print(value)


asyncio.run(main())

TypeError: a coroutine was expected, got <async_generator object 関数 at アドレス>

非同期ジェネレータをtaskとして実行しちゃった場合

エラー例

import asyncio
import time


async def while_simple(): # 非同期ジェネレータ
    i = 0
    while True:
        time.sleep(1)
        i += 1
        if i % 2 == 0:
            print(f"while yield {i}")
            yield i
        else:
            print(f"while not yield {i}")


async def main():

    task = asyncio.create_task(while_simple()) # 非同期ジェネレータをtaskで実行
    await task


asyncio.run(main())

以上。お疲れ様でした。 

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0