Skip to content

Commit

Permalink
use asyncio.run
Browse files Browse the repository at this point in the history
  • Loading branch information
davidism committed May 18, 2021
1 parent 94a6423 commit 7d0b7ac
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 15 deletions.
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

0 comments on commit 7d0b7ac

Please sign in to comment.