diff --git a/CHANGES.rst b/CHANGES.rst index eb46957df..efb20fed8 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -8,6 +8,7 @@ Unreleased - Use modern packaging metadata with ``pyproject.toml`` instead of ``setup.cfg``. :pr:`1793` - Use ``flit_core`` instead of ``setuptools`` as build backend. +- Avoid unclosed ``auto_aiter`` warnings. :pr:`1954` Version 3.1.3 diff --git a/src/jinja2/async_utils.py b/src/jinja2/async_utils.py index e65219e49..b0d277de7 100644 --- a/src/jinja2/async_utils.py +++ b/src/jinja2/async_utils.py @@ -6,6 +6,9 @@ from .utils import _PassArg from .utils import pass_eval_context +if t.TYPE_CHECKING: + import typing_extensions as te + V = t.TypeVar("V") @@ -67,15 +70,27 @@ async def auto_await(value: t.Union[t.Awaitable["V"], "V"]) -> "V": return t.cast("V", value) -async def auto_aiter( +class _IteratorToAsyncIterator(t.Generic[V]): + def __init__(self, iterator: "t.Iterator[V]"): + self._iterator = iterator + + def __aiter__(self) -> "te.Self": + return self + + async def __anext__(self) -> V: + try: + return next(self._iterator) + except StopIteration as e: + raise StopAsyncIteration(e.value) from e + + +def auto_aiter( iterable: "t.Union[t.AsyncIterable[V], t.Iterable[V]]", ) -> "t.AsyncIterator[V]": if hasattr(iterable, "__aiter__"): - async for item in t.cast("t.AsyncIterable[V]", iterable): - yield item + return iterable.__aiter__() else: - for item in iterable: - yield item + return _IteratorToAsyncIterator(iter(iterable)) async def auto_to_list( diff --git a/src/jinja2/compiler.py b/src/jinja2/compiler.py index ff95c807b..5551b9455 100644 --- a/src/jinja2/compiler.py +++ b/src/jinja2/compiler.py @@ -973,14 +973,18 @@ def visit_Block(self, node: nodes.Block, frame: Frame) -> None: f"yield from context.blocks[{node.name!r}][0]({context})", node ) else: + self.writeline("gen = context.blocks[{node.name!r}][0]({context})") + self.writeline("try:") + self.indent() self.writeline( - f"{self.choose_async()}for event in" - f" context.blocks[{node.name!r}][0]({context}):", + f"{self.choose_async()}for event in gen:" node, ) self.indent() self.simple_write("event", frame) self.outdent() + self.outdent() + self.write_line(f"finally: {self.choose_async('await gen.aclose()', 'gen.close()')}") self.outdent(level)