Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

use asyncio.run #1444

Merged
merged 1 commit into from May 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGES.rst
Expand Up @@ -13,6 +13,9 @@ Unreleased
extensions shows more relevant context. :issue:`1429`
- Fixed calling deprecated ``jinja2.Markup`` without an argument.
Use ``markupsafe.Markup`` instead. :issue:`1438`
- Calling sync ``render`` for an async template uses ``asyncio.run``
on Python >= 3.7. This fixes a deprecation that Python 3.10
introduces. :issue:`1443`


Version 3.0.0
Expand Down
6 changes: 3 additions & 3 deletions docs/api.rst
Expand Up @@ -513,12 +513,12 @@ handle async and sync code in an asyncio event loop. This has the
following implications:

- Template rendering requires an event loop to be available to the
current thread. :func:`asyncio.get_event_loop` must return an event
loop.
current thread. :func:`asyncio.get_running_loop` must return an
event loop.
- The compiled code uses ``await`` for functions and attributes, and
uses ``async for`` loops. In order to support using both async and
sync functions in this context, a small wrapper is placed around
all calls and access, which add overhead compared to purely async
all calls and access, which adds overhead compared to purely async
code.
- Sync methods and filters become wrappers around their corresponding
async implementations where needed. For example, ``render`` invokes
Expand Down
36 changes: 27 additions & 9 deletions src/jinja2/environment.py
Expand Up @@ -2,6 +2,7 @@
options.
"""
import os
import sys
import typing
import typing as t
import weakref
Expand Down Expand Up @@ -1278,8 +1279,22 @@ def render(self, *args: t.Any, **kwargs: t.Any) -> str:
if self.environment.is_async:
import asyncio

loop = asyncio.get_event_loop()
return loop.run_until_complete(self.render_async(*args, **kwargs))
close = False

if sys.version_info < (3, 7):
loop = asyncio.get_event_loop()
else:
try:
loop = asyncio.get_running_loop()
except RuntimeError:
loop = asyncio.new_event_loop()
close = True

try:
return loop.run_until_complete(self.render_async(*args, **kwargs))
finally:
if close:
loop.close()

ctx = self.new_context(dict(*args, **kwargs))

Expand Down Expand Up @@ -1326,14 +1341,17 @@ def generate(self, *args: t.Any, **kwargs: t.Any) -> t.Iterator[str]:
if self.environment.is_async:
import asyncio

loop = asyncio.get_event_loop()
async_gen = self.generate_async(*args, **kwargs)
async def to_list() -> t.List[str]:
return [x async for x in self.generate_async(*args, **kwargs)]

try:
while True:
yield loop.run_until_complete(async_gen.__anext__())
except StopAsyncIteration:
return
if sys.version_info < (3, 7):
loop = asyncio.get_event_loop()
out = loop.run_until_complete(to_list())
else:
out = asyncio.run(to_list())

yield from out
return

ctx = self.new_context(dict(*args, **kwargs))

Expand Down
15 changes: 12 additions & 3 deletions tests/test_async.py
@@ -1,4 +1,5 @@
import asyncio
import sys

import pytest

Expand All @@ -13,9 +14,17 @@
from jinja2.nativetypes import NativeEnvironment


def run(coro):
loop = asyncio.get_event_loop()
return loop.run_until_complete(coro)
if sys.version_info < (3, 7):

def run(coro):
loop = asyncio.get_event_loop()
return loop.run_until_complete(coro)


else:

def run(coro):
return asyncio.run(coro)


def test_basic_async():
Expand Down