diff options
author | David Lord <davidism@gmail.com> | 2020-02-27 11:16:01 -0800 |
---|---|---|
committer | David Lord <davidism@gmail.com> | 2020-02-27 11:16:01 -0800 |
commit | 45a76a3794a91e6d7077ced88c814a96cc87d5c2 (patch) | |
tree | eabc964389f550d464285db680a4c6a3c48e91e5 | |
parent | e08dadd220563609a67bc32d4d1c0abe91699231 (diff) | |
parent | be0e0b02f591d07cfb3e46fb33397f1d819a30c1 (diff) | |
download | jinja-45a76a3794a91e6d7077ced88c814a96cc87d5c2.tar.gz |
Merge branch '2.11.x'
-rw-r--r-- | CHANGES.rst | 3 | ||||
-rw-r--r-- | docs/api.rst | 59 | ||||
-rw-r--r-- | src/jinja2/filters.py | 2 | ||||
-rw-r--r-- | tests/test_filters.py | 7 |
4 files changed, 40 insertions, 31 deletions
diff --git a/CHANGES.rst b/CHANGES.rst index 20358b6f..e7542118 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -19,7 +19,8 @@ Unreleased - Fix a bug that caused callable objects with ``__getattr__``, like :class:`~unittest.mock.Mock` to be treated as a :func:`contextfunction`. :issue:`1145` - +- Update ``wordcount`` filter to trigger :class:`Undefined` methods + by wrapping the input in :func:`soft_str`. :pr:`1160` Version 2.11.1 -------------- diff --git a/docs/api.rst b/docs/api.rst index 075bcc6a..c92c383e 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -477,37 +477,38 @@ Builtin bytecode caches: Async Support ------------- -Starting with version 2.9, Jinja also supports the Python `async` and -`await` constructs. As far as template designers go this feature is -entirely opaque to them however as a developer you should be aware of how -it's implemented as it influences what type of APIs you can safely expose -to the template environment. - -First you need to be aware that by default async support is disabled as -enabling it will generate different template code behind the scenes which -passes everything through the asyncio event loop. This is important to -understand because it has some impact to what you are doing: - -* template rendering will require an event loop to be set for the - current thread (``asyncio.get_event_loop`` needs to return one) -* all template generation code internally runs async generators which - means that you will pay a performance penalty even if the non sync - methods are used! -* The sync methods are based on async methods if the async mode is - enabled which means that `render` for instance will internally invoke - `render_async` and run it as part of the current event loop until the - execution finished. +.. versionadded:: 2.9 + +Jinja supports the Python ``async`` and ``await`` syntax. For the +template designer, this support (when enabled) is entirely transparent, +templates continue to look exactly the same. However, developers should +be aware of the implementation as it affects what types of APIs you can +use. + +By default, async support is disabled. Enabling it will cause the +environment to compile different code behind the scenes in order to +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. +- 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 + code. +- Sync methods and filters become wrappers around their corresponding + async implementations where needed. For example, ``render`` invokes + ``async_render``, and ``|map`` supports async iterables. Awaitable objects can be returned from functions in templates and any -function call in a template will automatically await the result. This -means that you can provide a method that asynchronously loads data -from a database if you so desire and from the template designer's point of -view this is just another function they can call. This means that the -``await`` you would normally issue in Python is implied. However this -only applies to function calls. If an attribute for instance would be an -awaitable object then this would not result in the expected behavior. - -Likewise iterations with a `for` loop support async iterators. +function call in a template will automatically await the result. The +``await`` you would normally add in Python is implied. For example, you +can provide a method that asynchronously loads data from a database, and +from the template designer's point of view it can be called like any +other function. + .. _policies: diff --git a/src/jinja2/filters.py b/src/jinja2/filters.py index f39d389f..c257d4c5 100644 --- a/src/jinja2/filters.py +++ b/src/jinja2/filters.py @@ -741,7 +741,7 @@ def do_wordwrap( def do_wordcount(s): """Count the words in that string.""" - return len(_word_re.findall(s)) + return len(_word_re.findall(soft_str(s))) def do_int(value, default=0, base=10): diff --git a/tests/test_filters.py b/tests/test_filters.py index ab6e93d4..8087a248 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -5,6 +5,8 @@ import pytest from jinja2 import Environment from jinja2 import Markup +from jinja2 import StrictUndefined +from jinja2 import UndefinedError class Magic: @@ -363,6 +365,11 @@ class TestFilter: tmpl = env.from_string('{{ "foo bar baz"|wordcount }}') assert tmpl.render() == "3" + strict_env = Environment(undefined=StrictUndefined) + t = strict_env.from_string("{{ s|wordcount }}") + with pytest.raises(UndefinedError): + t.render() + def test_block(self, env): tmpl = env.from_string("{% filter lower|escape %}<HEHE>{% endfilter %}") assert tmpl.render() == "<hehe>" |