Jinja2 の async support を試した
Jinja2 にバージョン 2.9 で async support が入ったとのこと。
Environment
作成時に引数enable_async=True
を渡すことで有効になるテンプレート中の関数呼び出しが、 (結果が coroutine であれば?) 自動で await されるようになる
Template.render_async()
を呼ぶことで、テンプレートをレンダリングする coroutine が得られる
ということで動かしてみる:
import asyncio
import jinja2
# テキトウに非同期な関数を用意する
async def do_something(value: int) -> int:
await asyncio.sleep(1)
return value + 1
async def main():
# `enable_async=True` を渡して Environment 作成
env = jinja2.Environment(enable_async=True)
env.globals.update({
'do_something': do_something,
})
tmpl = env.from_string('''
{#- テンプレート中では、非同期関数は普通の関数と同じように呼び出せる #}
result: {{ do_something(1) }}
''')
# .render_async() で coroutine が得られる
coro = tmpl.render_async()
print(repr(coro))
# coroutine を await することでレンダリング結果が手に入る
txt = await coro
print(txt)
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
実行結果:
<coroutine object Template.render_async at 0x7ff2038688e0>
result: 2
さて、ドキュメントには asyncio
がどうのこうのと書いてあるが、 これは Template.render()
などの同期的なメソッドが呼ばれた際に、 内部で asyncio
の event loop を取得して coroutine
を実行し、結果を得ているという話っぽい。
なので、 .render_async()
などを呼んでいる分には、 asyncio
以外の event loop でも使えそう。
ということで twisted と組み合わせて動かしてみる:
import jinja2
from twisted.internet import defer
def sleep(delay: int) -> defer.Deferred:
d = defer.Deferred()
from twisted.internet import reactor
reactor.callLater(delay, d.callback, None)
return d
# テキトウに非同期な関数を用意する
async def do_something(value: int) -> int:
await sleep(1)
return value + 1
# Deferred を返す版も試しておく
def do_something_deferred(value: int) -> defer.Deferred:
d = sleep(1)
d.addCallback(lambda _: value + 1)
return d
async def main():
# `enable_async=True` を渡して Environment 作成
env = jinja2.Environment(enable_async=True)
env.globals.update({
'do_something': do_something,
'do_something_deferred': do_something_deferred,
})
tmpl = env.from_string('''
{#- テンプレート中では、非同期関数は普通の関数と同じように呼び出せる #}
result: {{ do_something(1) }}
{#- Deferred を返す関数も同様に、普通に呼び出せる #}
result from deferred: {{ do_something_deferred(1) }}
''')
# .render_async() で coroutine が得られる
coro = tmpl.render_async()
print(repr(coro))
# coroutine を await することでレンダリング結果が手に入る
txt = await coro
print(txt)
if __name__ == '__main__':
from twisted.internet import task
task.react(lambda _: defer.ensureDeferred(main()))
実行結果:
<coroutine object Template.render_async at 0x7f16bbea5ca8>
result: 2
result from deferred: 2