Edited at

python async/await curio

More than 1 year has passed since last update.

python curio を試してみたよ。

基本的な考え方としては「async/await を使う」。つまり「タスクを作って回す」になる。タスクは async キーワードを付けた関数の形で宣言する。interactive shell では「タスクを回す」ものを用意して実行することになる。昔から asyncio があったけれども、curio でもよい。あるいは coroutine method を呼び出してもよい。

>>> async def hello():

... return "Hello"
...
>>> import curio
>>> curio.run(hello())
'Hello'
>>> import asyncio
>>> asyncio.get_event_loop().run_until_complete(hello())
'Hello'
>>> try:
... hello().send(None)
... except StopIteration as e:
... e.value
...
'Hello'

curio だと短くていいね。

こうなると asynciocurio の比較になってくる。asyncio は async/await 構文ができる前からあることもあって、async/await に最適な設計というわけではないようだcurio に慣れていくと、asyncio はガードレールのない高速道路のような感覚がしてくる。uvloop速くなるよ と言われても、躊躇する。

asyncio 作者。https://www.youtube.com/watch?v=m28fiN9y_r8

curio 作者。Live coding が鮮やか https://www.youtube.com/watch?v=ZzfHjytDceU https://www.youtube.com/watch?v=MCs5OvhV9S4

一方で、async/await それ自体の癖も相当ある。標準ライブラリは暗黙的に socket オブジェクトを中で使うけれども、普通に使うと I/O がブロッキングモードになって、組み合わせるときに苦労する。標準ライブラリとは別に、socket を取り除いてあって、プロトコル部分に絞ってステートマシン的に使えるライブラリもある。例えば h11 や hyper h2 は async/await の中では比較的組み合わせやすい。

h2 作者。https://www.youtube.com/watch?v=7cC3_jGwl_U

この方向でいくつか試していくと、monkey_patch しない gevent, eventlet のような感じになってくる。curio との比較で言うと、monkey_patch で生じる激甚な副作用と便利さを秤にかける感じになるだろうか。

最後の最後まで問題になるのは database 接続。SQLAlchemy 使うユースケースはまだ模索中…。構造を持った大きなファイルを seek して回るパターンも良いプロトコルパーサを作るのは難しい。

ちなみに開発者ドキュメントに Please, Don’t Use Curio! と書いてあって、笑ってしまった。そうですねぇ……geventがPEP492な __await__ を実装してくれれば、鬼に金棒なのだけど。asyncio で事故らない自信が持てないんだよなぁ。


TIPS

async/await 使う場合は await 漏れを検出できるのはありがたい。

if __name__=="__main__":

import logging
logging.getLogger("asyncio").setLevel(logging.DEBUG)
import gc
gc.set_debug(gc.DEBUG_UNCOLLECTABLE)
loop = asyncio.get_event_loop()
loop.set_debug(True)
try:
loop.run_forever()
finally:
loop.close()