no way to explicitly shutdown async generators
graingert opened this issue · 1 comments
graingert commented
given:
import sys
import trio
from jinja2 import Template, select_autoescape
class MyModel:
class objects:
@staticmethod
async def all():
while True:
yield "hello"
template = """
<html>
<head></head>
<body>
{% for m in model.objects.all() %}
{{ m }}
{% break %}
{% endfor %}
</body>
</html>
"""
async def amain():
return await Template(
source=template, enable_async=True, extensions=["jinja2.ext.loopcontrols"]
).render_async(model=MyModel)
def main():
trio.run(amain)
if __name__ == "__main__":
sys.exit(main())
this results in:
python -Wall demo_jinja_asyncgens.py
<template>:19: ResourceWarning: Async generator 'jinja2.async_utils.auto_aiter' was garbage collected before it had been exhausted. Surround its use in 'async with aclosing(...):' to ensure that it gets cleaned up as soon as you're done using it.
ResourceWarning: Enable tracemalloc to get the object allocation traceback
/home/graingert/.virtualenvs/demo-jinja-asyncgens/lib/python3.12/site-packages/trio/_core/_asyncgens.py:204: ResourceWarning: Async generator '__main__.MyModel.objects.all' was garbage collected before it had been exhausted. Surround its use in 'async with aclosing(...):' to ensure that it gets cleaned up as soon as you're done using it.
await agen.aclose()
ResourceWarning: Enable tracemalloc to get the object allocation traceback
but there's no way to wrap generators with async with aclosing(...):
in jinja2
Environment:
- Python version: 3.12.2
- Jinja version: 3.1.3
graingert commented
I'd like to see a cmgr/closing block so I can do:
<html>
<head></head>
<body>
{% closing model.objects.all() as agen %}
{% for m in agen %}
{{ m }}
{% break %}
{% endfor %}
{% endclosing %}
</body>
</html>
but with all the changes in #1960 it's possible pass in an aclosing
function to context:
import sys
import trio
import contextlib
from jinja2 import Template, select_autoescape
class MyModel:
class objects:
@staticmethod
async def all():
while True:
yield "hello"
template = """
<html>
<head></head>
<body>
{% for m in aclosing(model.objects.all()) %}
{{ m }}
{% break %}
{% endfor %}
</body>
</html>
"""
async def amain():
async with contextlib.AsyncExitStack() as stack:
def aclosing(agen):
stack.push_async_callback(agen.aclose)
return agen
return await Template(
source=template, enable_async=True, extensions=["jinja2.ext.loopcontrols"]
).render_async(model=MyModel, aclosing=aclosing)
def main():
trio.run(amain)
if __name__ == "__main__":
sys.exit(main())