aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Lord <davidism@gmail.com>2020-02-27 11:16:01 -0800
committerDavid Lord <davidism@gmail.com>2020-02-27 11:16:01 -0800
commit45a76a3794a91e6d7077ced88c814a96cc87d5c2 (patch)
treeeabc964389f550d464285db680a4c6a3c48e91e5
parente08dadd220563609a67bc32d4d1c0abe91699231 (diff)
parentbe0e0b02f591d07cfb3e46fb33397f1d819a30c1 (diff)
downloadjinja-45a76a3794a91e6d7077ced88c814a96cc87d5c2.tar.gz
Merge branch '2.11.x'
-rw-r--r--CHANGES.rst3
-rw-r--r--docs/api.rst59
-rw-r--r--src/jinja2/filters.py2
-rw-r--r--tests/test_filters.py7
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() == "&lt;hehe&gt;"