mirror of
https://gitee.com/openharmony/third_party_jinja2
synced 2024-11-23 07:10:31 +00:00
Merge remote-tracking branch 'origin/3.0.x'
This commit is contained in:
commit
35c69e9ec3
@ -9,7 +9,12 @@ Unreleased
|
||||
Version 3.0.2
|
||||
-------------
|
||||
|
||||
Unreleased
|
||||
- Fix a loop scoping bug that caused assignments in nested loops
|
||||
to still be referenced outside of it. :issue:`1427`
|
||||
- Make ``compile_templates`` deterministic for filter and import
|
||||
names. :issue:`1452, 1453`
|
||||
- Revert an unintended change that caused ``Undefined`` to act like
|
||||
``StrictUndefined`` for the ``in`` operator. :issue:`1448`
|
||||
|
||||
|
||||
Version 3.0.1
|
||||
|
@ -92,7 +92,7 @@ First time setup
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
git remote add fork https://github.com/{username}/jinja
|
||||
$ git remote add fork https://github.com/{username}/jinja
|
||||
|
||||
- Create a virtualenv.
|
||||
|
||||
@ -107,6 +107,12 @@ First time setup
|
||||
|
||||
> env\Scripts\activate
|
||||
|
||||
- Upgrade pip and setuptools.
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ python -m pip install --upgrade pip setuptools
|
||||
|
||||
- Install the development dependencies, then install Jinja in editable
|
||||
mode.
|
||||
|
||||
@ -138,7 +144,7 @@ Start coding
|
||||
.. code-block:: text
|
||||
|
||||
$ git fetch origin
|
||||
$ git checkout -b your-branch-name origin/1.1.x
|
||||
$ git checkout -b your-branch-name origin/3.0.x
|
||||
|
||||
If you're submitting a feature addition or change, branch off of the
|
||||
"main" branch.
|
||||
|
@ -35,7 +35,7 @@ Install and update using `pip`_:
|
||||
|
||||
$ pip install -U Jinja2
|
||||
|
||||
.. _pip: https://pip.pypa.io/en/stable/quickstart/
|
||||
.. _pip: https://pip.pypa.io/en/stable/getting-started/
|
||||
|
||||
|
||||
In A Nutshell
|
||||
|
@ -556,7 +556,7 @@ class CodeGenerator(NodeVisitor):
|
||||
visitor.tests,
|
||||
"tests",
|
||||
):
|
||||
for name in names:
|
||||
for name in sorted(names):
|
||||
if name not in id_map:
|
||||
id_map[name] = self.temporary_identifier()
|
||||
|
||||
@ -1290,6 +1290,11 @@ class CodeGenerator(NodeVisitor):
|
||||
self.write(", loop)")
|
||||
self.end_write(frame)
|
||||
|
||||
# at the end of the iteration, clear any assignments made in the
|
||||
# loop from the top level
|
||||
if self._assign_stack:
|
||||
self._assign_stack[-1].difference_update(loop_frame.symbols.stores)
|
||||
|
||||
def visit_If(self, node: nodes.If, frame: Frame) -> None:
|
||||
if_frame = frame.soft()
|
||||
self.writeline("if ", node)
|
||||
|
@ -149,7 +149,7 @@ class Symbols:
|
||||
node: t.Optional["Symbols"] = self
|
||||
|
||||
while node is not None:
|
||||
for name in node.stores:
|
||||
for name in sorted(node.stores):
|
||||
if name not in rv:
|
||||
rv[name] = self.find_ref(name) # type: ignore
|
||||
|
||||
|
@ -915,7 +915,7 @@ class Undefined:
|
||||
__floordiv__ = __rfloordiv__ = _fail_with_undefined_error
|
||||
__mod__ = __rmod__ = _fail_with_undefined_error
|
||||
__pos__ = __neg__ = _fail_with_undefined_error
|
||||
__call__ = __getitem__ = __contains__ = _fail_with_undefined_error
|
||||
__call__ = __getitem__ = _fail_with_undefined_error
|
||||
__lt__ = __le__ = __gt__ = __ge__ = _fail_with_undefined_error
|
||||
__int__ = __float__ = __complex__ = _fail_with_undefined_error
|
||||
__pow__ = __rpow__ = _fail_with_undefined_error
|
||||
@ -1091,6 +1091,7 @@ class StrictUndefined(Undefined):
|
||||
__slots__ = ()
|
||||
__iter__ = __str__ = __len__ = Undefined._fail_with_undefined_error
|
||||
__eq__ = __ne__ = __bool__ = __hash__ = Undefined._fail_with_undefined_error
|
||||
__contains__ = Undefined._fail_with_undefined_error
|
||||
|
||||
|
||||
# Remove slots attributes, after the metaclass is applied they are
|
||||
|
@ -316,7 +316,7 @@ class TestUndefined:
|
||||
assert env.from_string("{{ foo.missing }}").render(foo=42) == ""
|
||||
assert env.from_string("{{ not missing }}").render() == "True"
|
||||
pytest.raises(UndefinedError, env.from_string("{{ missing - 1}}").render)
|
||||
pytest.raises(UndefinedError, env.from_string("{{ 'foo' in missing }}").render)
|
||||
assert env.from_string("{{ 'foo' in missing }}").render() == "False"
|
||||
und1 = Undefined(name="x")
|
||||
und2 = Undefined(name="y")
|
||||
assert und1 == und2
|
||||
@ -375,6 +375,7 @@ class TestUndefined:
|
||||
pytest.raises(UndefinedError, env.from_string("{{ missing }}").render)
|
||||
pytest.raises(UndefinedError, env.from_string("{{ missing.attribute }}").render)
|
||||
pytest.raises(UndefinedError, env.from_string("{{ missing|list }}").render)
|
||||
pytest.raises(UndefinedError, env.from_string("{{ 'foo' in missing }}").render)
|
||||
assert env.from_string("{{ missing is not defined }}").render() == "True"
|
||||
pytest.raises(
|
||||
UndefinedError, env.from_string("{{ foo.missing }}").render, foo=42
|
||||
|
28
tests/test_compile.py
Normal file
28
tests/test_compile.py
Normal file
@ -0,0 +1,28 @@
|
||||
import os
|
||||
import re
|
||||
|
||||
from jinja2.environment import Environment
|
||||
from jinja2.loaders import DictLoader
|
||||
|
||||
|
||||
def test_filters_deterministic(tmp_path):
|
||||
src = "".join(f"{{{{ {i}|filter{i} }}}}" for i in range(10))
|
||||
env = Environment(loader=DictLoader({"foo": src}))
|
||||
env.filters.update(dict.fromkeys((f"filter{i}" for i in range(10)), lambda: None))
|
||||
env.compile_templates(tmp_path, zip=None)
|
||||
name = os.listdir(tmp_path)[0]
|
||||
content = (tmp_path / name).read_text("utf8")
|
||||
expect = [f"filters['filter{i}']" for i in range(10)]
|
||||
found = re.findall(r"filters\['filter\d']", content)
|
||||
assert found == expect
|
||||
|
||||
|
||||
def test_import_as_with_context_deterministic(tmp_path):
|
||||
src = "\n".join(f'{{% import "bar" as bar{i} with context %}}' for i in range(10))
|
||||
env = Environment(loader=DictLoader({"foo": src}))
|
||||
env.compile_templates(tmp_path, zip=None)
|
||||
name = os.listdir(tmp_path)[0]
|
||||
content = (tmp_path / name).read_text("utf8")
|
||||
expect = [f"'bar{i}': " for i in range(10)]
|
||||
found = re.findall(r"'bar\d': ", content)[:10]
|
||||
assert found == expect
|
@ -746,6 +746,13 @@ End"""
|
||||
tmpl = env.get_template("base")
|
||||
assert tmpl.render() == "42 y"
|
||||
|
||||
def test_nested_loop_scoping(self, env):
|
||||
tmpl = env.from_string(
|
||||
"{% set output %}{% for x in [1,2,3] %}hello{% endfor %}"
|
||||
"{% endset %}{{ output }}"
|
||||
)
|
||||
assert tmpl.render() == "hellohellohello"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("unicode_char", ["\N{FORM FEED}", "\x85"])
|
||||
def test_unicode_whitespace(env, unicode_char):
|
||||
|
Loading…
Reference in New Issue
Block a user