Codebase list jinja2 / 415997b
Update upstream source from tag 'upstream/3.0.3' Update to upstream version '3.0.3' with Debian dir c5effc9e732ea93b64c585df82836429d457b66a Piotr Ożarowski 2 years ago
40 changed file(s) with 566 addition(s) and 422 deletion(s). Raw diff Collapse all Expand all
00 .. currentmodule:: jinja2
1
2 Version 3.0.3
3 -------------
4
5 Released 2021-11-09
6
7 - Fix traceback rewriting internals for Python 3.10 and 3.11.
8 :issue:`1535`
9 - Fix how the native environment treats leading and trailing spaces
10 when parsing values on Python 3.10. :pr:`1537`
11 - Improve async performance by avoiding checks for common types.
12 :issue:`1514`
13 - Revert change to ``hash(Node)`` behavior. Nodes are hashed by id
14 again :issue:`1521`
15 - ``PackageLoader`` works when the package is a single module file.
16 :issue:`1512`
17
18
19 Version 3.0.2
20 -------------
21
22 Released 2021-10-04
23
24 - Fix a loop scoping bug that caused assignments in nested loops
25 to still be referenced outside of it. :issue:`1427`
26 - Make ``compile_templates`` deterministic for filter and import
27 names. :issue:`1452, 1453`
28 - Revert an unintended change that caused ``Undefined`` to act like
29 ``StrictUndefined`` for the ``in`` operator. :issue:`1448`
30 - Imported macros have access to the current template globals in async
31 environments. :issue:`1494`
32 - ``PackageLoader`` will not include a current directory (.) path
33 segment. This allows loading templates from the root of a zip
34 import. :issue:`1467`
35
136
237 Version 3.0.1
338 -------------
389424 possible. For more information and a discussion see :issue:`641`
390425 - Resolved an issue where ``block scoped`` would not take advantage of
391426 the new scoping rules. In some more exotic cases a variable
392 overriden in a local scope would not make it into a block.
427 overridden in a local scope would not make it into a block.
393428 - Change the code generation of the ``with`` statement to be in line
394429 with the new scoping rules. This resolves some unlikely bugs in edge
395430 cases. This also introduces a new internal ``With`` node that can be
00 Metadata-Version: 2.1
11 Name: Jinja2
2 Version: 3.0.1
2 Version: 3.0.3
33 Summary: A very fast and expressive template engine.
44 Home-page: https://palletsprojects.com/p/jinja/
55 Author: Armin Ronacher
1414 Project-URL: Issue Tracker, https://github.com/pallets/jinja/issues/
1515 Project-URL: Twitter, https://twitter.com/PalletsTeam
1616 Project-URL: Chat, https://discord.gg/pallets
17 Description: Jinja
18 =====
19
20 Jinja is a fast, expressive, extensible templating engine. Special
21 placeholders in the template allow writing code similar to Python
22 syntax. Then the template is passed data to render the final document.
23
24 It includes:
25
26 - Template inheritance and inclusion.
27 - Define and import macros within templates.
28 - HTML templates can use autoescaping to prevent XSS from untrusted
29 user input.
30 - A sandboxed environment can safely render untrusted templates.
31 - AsyncIO support for generating templates and calling async
32 functions.
33 - I18N support with Babel.
34 - Templates are compiled to optimized Python code just-in-time and
35 cached, or can be compiled ahead-of-time.
36 - Exceptions point to the correct line in templates to make debugging
37 easier.
38 - Extensible filters, tests, functions, and even syntax.
39
40 Jinja's philosophy is that while application logic belongs in Python if
41 possible, it shouldn't make the template designer's job difficult by
42 restricting functionality too much.
43
44
45 Installing
46 ----------
47
48 Install and update using `pip`_:
49
50 .. code-block:: text
51
52 $ pip install -U Jinja2
53
54 .. _pip: https://pip.pypa.io/en/stable/quickstart/
55
56
57 In A Nutshell
58 -------------
59
60 .. code-block:: jinja
61
62 {% extends "base.html" %}
63 {% block title %}Members{% endblock %}
64 {% block content %}
65 <ul>
66 {% for user in users %}
67 <li><a href="{{ user.url }}">{{ user.username }}</a></li>
68 {% endfor %}
69 </ul>
70 {% endblock %}
71
72
73 Donate
74 ------
75
76 The Pallets organization develops and supports Jinja and other popular
77 packages. In order to grow the community of contributors and users, and
78 allow the maintainers to devote more time to the projects, `please
79 donate today`_.
80
81 .. _please donate today: https://palletsprojects.com/donate
82
83
84 Links
85 -----
86
87 - Documentation: https://jinja.palletsprojects.com/
88 - Changes: https://jinja.palletsprojects.com/changes/
89 - PyPI Releases: https://pypi.org/project/Jinja2/
90 - Source Code: https://github.com/pallets/jinja/
91 - Issue Tracker: https://github.com/pallets/jinja/issues/
92 - Website: https://palletsprojects.com/p/jinja/
93 - Twitter: https://twitter.com/PalletsTeam
94 - Chat: https://discord.gg/pallets
95
9617 Platform: UNKNOWN
9718 Classifier: Development Status :: 5 - Production/Stable
9819 Classifier: Environment :: Web Environment
10526 Requires-Python: >=3.6
10627 Description-Content-Type: text/x-rst
10728 Provides-Extra: i18n
29 License-File: LICENSE.rst
30
31 Jinja
32 =====
33
34 Jinja is a fast, expressive, extensible templating engine. Special
35 placeholders in the template allow writing code similar to Python
36 syntax. Then the template is passed data to render the final document.
37
38 It includes:
39
40 - Template inheritance and inclusion.
41 - Define and import macros within templates.
42 - HTML templates can use autoescaping to prevent XSS from untrusted
43 user input.
44 - A sandboxed environment can safely render untrusted templates.
45 - AsyncIO support for generating templates and calling async
46 functions.
47 - I18N support with Babel.
48 - Templates are compiled to optimized Python code just-in-time and
49 cached, or can be compiled ahead-of-time.
50 - Exceptions point to the correct line in templates to make debugging
51 easier.
52 - Extensible filters, tests, functions, and even syntax.
53
54 Jinja's philosophy is that while application logic belongs in Python if
55 possible, it shouldn't make the template designer's job difficult by
56 restricting functionality too much.
57
58
59 Installing
60 ----------
61
62 Install and update using `pip`_:
63
64 .. code-block:: text
65
66 $ pip install -U Jinja2
67
68 .. _pip: https://pip.pypa.io/en/stable/getting-started/
69
70
71 In A Nutshell
72 -------------
73
74 .. code-block:: jinja
75
76 {% extends "base.html" %}
77 {% block title %}Members{% endblock %}
78 {% block content %}
79 <ul>
80 {% for user in users %}
81 <li><a href="{{ user.url }}">{{ user.username }}</a></li>
82 {% endfor %}
83 </ul>
84 {% endblock %}
85
86
87 Donate
88 ------
89
90 The Pallets organization develops and supports Jinja and other popular
91 packages. In order to grow the community of contributors and users, and
92 allow the maintainers to devote more time to the projects, `please
93 donate today`_.
94
95 .. _please donate today: https://palletsprojects.com/donate
96
97
98 Links
99 -----
100
101 - Documentation: https://jinja.palletsprojects.com/
102 - Changes: https://jinja.palletsprojects.com/changes/
103 - PyPI Releases: https://pypi.org/project/Jinja2/
104 - Source Code: https://github.com/pallets/jinja/
105 - Issue Tracker: https://github.com/pallets/jinja/issues/
106 - Website: https://palletsprojects.com/p/jinja/
107 - Twitter: https://twitter.com/PalletsTeam
108 - Chat: https://discord.gg/pallets
109
110
3434
3535 $ pip install -U Jinja2
3636
37 .. _pip: https://pip.pypa.io/en/stable/quickstart/
37 .. _pip: https://pip.pypa.io/en/stable/getting-started/
3838
3939
4040 In A Nutshell
3737 ]
3838 }
3939 html_sidebars = {
40 "index": ["project.html", "localtoc.html", "searchbox.html"],
41 "**": ["localtoc.html", "relations.html", "searchbox.html"],
40 "index": ["project.html", "localtoc.html", "searchbox.html", "ethicalads.html"],
41 "**": ["localtoc.html", "relations.html", "searchbox.html", "ethicalads.html"],
4242 }
43 singlehtml_sidebars = {"index": ["project.html", "localtoc.html"]}
43 singlehtml_sidebars = {"index": ["project.html", "localtoc.html", "ethicalads.html"]}
4444 html_static_path = ["_static"]
4545 html_favicon = "_static/jinja-logo-sidebar.png"
4646 html_logo = "_static/jinja-logo-sidebar.png"
122122 :ref:`the template documentation <i18n-in-templates>`.
123123
124124 .. _gettext: https://docs.python.org/3/library/gettext.html
125 .. _Babel: http://babel.pocoo.org/
125 .. _Babel: https://babel.pocoo.org/
126126
127127
128128 Whitespace Trimming
5959
6060 - `Babel`_ provides translation support in templates.
6161
62 .. _Babel: http://babel.pocoo.org/
62 .. _Babel: https://babel.pocoo.org/
2020 echo.may add the Sphinx directory to PATH.
2121 echo.
2222 echo.If you don't have Sphinx installed, grab it from
23 echo.http://sphinx-doc.org/
23 echo.https://www.sphinx-doc.org/
2424 exit /b 1
2525 )
2626
586586 Template Objects
587587 ~~~~~~~~~~~~~~~~
588588
589 .. versionchanged:: 2.4
590
591 If a template object was passed in the template context, you can
592 extend from that object as well. Assuming the calling code passes
593 a layout template as `layout_template` to the environment, this
594 code works::
595
596 {% extends layout_template %}
597
598 Previously, the `layout_template` variable had to be a string with
599 the layout template's filename for this to work.
589 ``extends``, ``include``, and ``import`` can take a template object
590 instead of the name of a template to load. This could be useful in some
591 advanced situations, since you can use Python code to load a template
592 first and pass it in to ``render``.
593
594 .. code-block:: python
595
596 if debug_mode:
597 layout = env.get_template("debug_layout.html")
598 else:
599 layout = env.get_template("layout.html")
600
601 user_detail = env.get_template("user/detail.html", layout=layout)
602
603 .. code-block:: jinja
604
605 {% extends layout %}
606
607 Note how ``extends`` is passed the variable with the template object
608 that was passed to ``render``, instead of a string.
600609
601610
602611 HTML Escaping
913922 `arguments`
914923 A tuple of the names of arguments the macro accepts.
915924
916 `defaults`
917 A tuple of default values.
918
919925 `catch_kwargs`
920926 This is `true` if the macro accepts extra keyword arguments (i.e.: accesses
921927 the special `kwargs` variable).
13371343 ``{{ '=' * 80 }}`` would print a bar of 80 equal signs.
13381344
13391345 ``**``
1340 Raise the left operand to the power of the right operand. ``{{ 2**3 }}``
1341 would return ``8``.
1346 Raise the left operand to the power of the right operand.
1347 ``{{ 2**3 }}`` would return ``8``.
1348
1349 Unlike Python, chained pow is evaluated left to right.
1350 ``{{ 3**3**3 }}`` is evaluated as ``(3**3)**3`` in Jinja, but would
1351 be evaluated as ``3**(3**3)`` in Python. Use parentheses in Jinja
1352 to be explicit about what order you want. It is usually preferable
1353 to do extended math in Python and pass the results to ``render``
1354 rather than doing it in the template.
1355
1356 This behavior may be changed in the future to match Python, if it's
1357 possible to introduce an upgrade path.
1358
13421359
13431360 Comparisons
13441361 ~~~~~~~~~~~
00 #
1 # This file is autogenerated by pip-compile
1 # This file is autogenerated by pip-compile with python 3.10
22 # To update, run:
33 #
44 # pip-compile requirements/dev.in
55 #
66 alabaster==0.7.12
77 # via sphinx
8 appdirs==1.4.4
9 # via virtualenv
108 attrs==21.2.0
119 # via pytest
1210 babel==2.9.1
1311 # via sphinx
14 certifi==2020.12.5
12 backports.entry-points-selectable==1.1.0
13 # via virtualenv
14 certifi==2021.10.8
1515 # via requests
16 cfgv==3.2.0
16 cfgv==3.3.1
1717 # via pre-commit
18 chardet==4.0.0
18 charset-normalizer==2.0.7
1919 # via requests
20 click==8.0.0
20 click==8.0.3
2121 # via pip-tools
22 distlib==0.3.1
22 distlib==0.3.3
2323 # via virtualenv
2424 docutils==0.17.1
2525 # via sphinx
26 filelock==3.0.12
26 filelock==3.3.2
2727 # via
2828 # tox
2929 # virtualenv
30 identify==2.2.4
30 identify==2.3.3
3131 # via pre-commit
32 idna==2.10
32 idna==3.3
3333 # via requests
3434 imagesize==1.2.0
3535 # via sphinx
3636 iniconfig==1.1.1
3737 # via pytest
38 jinja2==3.0.0
38 jinja2==3.0.2
3939 # via sphinx
40 markupsafe==2.0.0
40 markupsafe==2.0.1
4141 # via jinja2
42 mypy==0.910
43 # via -r requirements/typing.in
4244 mypy-extensions==0.4.3
4345 # via mypy
44 mypy==0.812
45 # via -r requirements/typing.in
4646 nodeenv==1.6.0
4747 # via pre-commit
48 packaging==20.9
48 packaging==21.2
4949 # via
5050 # pallets-sphinx-themes
5151 # pytest
5252 # sphinx
5353 # tox
54 pallets-sphinx-themes==2.0.0
54 pallets-sphinx-themes==2.0.1
5555 # via -r requirements/docs.in
56 pep517==0.10.0
56 pep517==0.12.0
5757 # via pip-tools
58 pip-tools==6.1.0
58 pip-tools==6.4.0
5959 # via -r requirements/dev.in
60 pluggy==0.13.1
60 platformdirs==2.4.0
61 # via virtualenv
62 pluggy==1.0.0
6163 # via
6264 # pytest
6365 # tox
64 pre-commit==2.12.1
66 pre-commit==2.15.0
6567 # via -r requirements/dev.in
66 py==1.10.0
68 py==1.11.0
6769 # via
6870 # pytest
6971 # tox
70 pygments==2.9.0
72 pygments==2.10.0
7173 # via sphinx
7274 pyparsing==2.4.7
7375 # via packaging
74 pytest==6.2.4
76 pytest==6.2.5
7577 # via -r requirements/tests.in
76 pytz==2021.1
78 pytz==2021.3
7779 # via babel
78 pyyaml==5.4.1
80 pyyaml==6.0
7981 # via pre-commit
80 requests==2.25.1
82 requests==2.26.0
8183 # via sphinx
8284 six==1.16.0
8385 # via
8587 # virtualenv
8688 snowballstemmer==2.1.0
8789 # via sphinx
88 sphinx-issues==1.2.0
89 # via -r requirements/docs.in
90 git+https://github.com/sphinx-doc/sphinx.git@96dbe5e3
90 sphinx==4.2.0
9191 # via
9292 # -r requirements/docs.in
9393 # pallets-sphinx-themes
9494 # sphinx-issues
9595 # sphinxcontrib-log-cabinet
96 sphinx-issues==1.2.0
97 # via -r requirements/docs.in
9698 sphinxcontrib-applehelp==1.0.2
9799 # via sphinx
98100 sphinxcontrib-devhelp==1.0.2
99101 # via sphinx
100 sphinxcontrib-htmlhelp==1.0.3
102 sphinxcontrib-htmlhelp==2.0.0
101103 # via sphinx
102104 sphinxcontrib-jsmath==1.0.1
103105 # via sphinx
105107 # via -r requirements/docs.in
106108 sphinxcontrib-qthelp==1.0.3
107109 # via sphinx
108 sphinxcontrib-serializinghtml==1.1.4
110 sphinxcontrib-serializinghtml==1.1.5
109111 # via sphinx
110112 toml==0.10.2
111113 # via
112 # pep517
114 # mypy
113115 # pre-commit
114116 # pytest
115117 # tox
116 tox==3.23.1
118 tomli==1.2.2
119 # via pep517
120 tox==3.24.4
117121 # via -r requirements/dev.in
118 typed-ast==1.4.3
122 typing-extensions==3.10.0.2
119123 # via mypy
120 typing-extensions==3.10.0.0
121 # via mypy
122 urllib3==1.26.4
124 urllib3==1.26.7
123125 # via requests
124 virtualenv==20.4.6
126 virtualenv==20.10.0
125127 # via
126128 # pre-commit
127129 # tox
130 wheel==0.37.0
131 # via pip-tools
128132
129133 # The following packages are considered to be unsafe in a requirements file:
130134 # pip
00 #
1 # This file is autogenerated by pip-compile
1 # This file is autogenerated by pip-compile with python 3.10
22 # To update, run:
33 #
44 # pip-compile requirements/docs.in
77 # via sphinx
88 babel==2.9.1
99 # via sphinx
10 certifi==2020.12.5
10 certifi==2021.10.8
1111 # via requests
12 chardet==4.0.0
12 charset-normalizer==2.0.7
1313 # via requests
1414 docutils==0.17.1
1515 # via sphinx
16 idna==2.10
16 idna==3.3
1717 # via requests
1818 imagesize==1.2.0
1919 # via sphinx
20 jinja2==3.0.0
20 jinja2==3.0.2
2121 # via sphinx
22 markupsafe==2.0.0
22 markupsafe==2.0.1
2323 # via jinja2
24 packaging==20.9
24 packaging==21.2
2525 # via
2626 # pallets-sphinx-themes
2727 # sphinx
28 pallets-sphinx-themes==2.0.0
28 pallets-sphinx-themes==2.0.1
2929 # via -r requirements/docs.in
30 pygments==2.9.0
30 pygments==2.10.0
3131 # via sphinx
3232 pyparsing==2.4.7
3333 # via packaging
34 pytz==2021.1
34 pytz==2021.3
3535 # via babel
36 requests==2.25.1
36 requests==2.26.0
3737 # via sphinx
3838 snowballstemmer==2.1.0
3939 # via sphinx
40 sphinx-issues==1.2.0
41 # via -r requirements/docs.in
42 git+https://github.com/sphinx-doc/sphinx.git@96dbe5e3
40 sphinx==4.2.0
4341 # via
4442 # -r requirements/docs.in
4543 # pallets-sphinx-themes
4644 # sphinx-issues
4745 # sphinxcontrib-log-cabinet
46 sphinx-issues==1.2.0
47 # via -r requirements/docs.in
4848 sphinxcontrib-applehelp==1.0.2
4949 # via sphinx
5050 sphinxcontrib-devhelp==1.0.2
5151 # via sphinx
52 sphinxcontrib-htmlhelp==1.0.3
52 sphinxcontrib-htmlhelp==2.0.0
5353 # via sphinx
5454 sphinxcontrib-jsmath==1.0.1
5555 # via sphinx
5757 # via -r requirements/docs.in
5858 sphinxcontrib-qthelp==1.0.3
5959 # via sphinx
60 sphinxcontrib-serializinghtml==1.1.4
60 sphinxcontrib-serializinghtml==1.1.5
6161 # via sphinx
62 urllib3==1.26.4
62 urllib3==1.26.7
6363 # via requests
6464
6565 # The following packages are considered to be unsafe in a requirements file:
00 #
1 # This file is autogenerated by pip-compile
1 # This file is autogenerated by pip-compile with python 3.10
22 # To update, run:
33 #
44 # pip-compile requirements/tests.in
77 # via pytest
88 iniconfig==1.1.1
99 # via pytest
10 packaging==20.9
10 packaging==21.2
1111 # via pytest
12 pluggy==0.13.1
12 pluggy==1.0.0
1313 # via pytest
14 py==1.10.0
14 py==1.11.0
1515 # via pytest
1616 pyparsing==2.4.7
1717 # via packaging
18 pytest==6.2.4
18 pytest==6.2.5
1919 # via -r requirements/tests.in
2020 toml==0.10.2
2121 # via pytest
00 #
1 # This file is autogenerated by pip-compile
1 # This file is autogenerated by pip-compile with python 3.10
22 # To update, run:
33 #
44 # pip-compile requirements/typing.in
55 #
6 mypy==0.910
7 # via -r requirements/typing.in
68 mypy-extensions==0.4.3
79 # via mypy
8 mypy==0.812
9 # via -r requirements/typing.in
10 typed-ast==1.4.3
10 toml==0.10.2
1111 # via mypy
12 typing-extensions==3.10.0.0
12 typing-extensions==3.10.0.2
1313 # via mypy
4545 testpaths = tests
4646 filterwarnings =
4747 error
48 ignore:The loop argument:DeprecationWarning:asyncio[.]base_events:542
4849
4950 [coverage:run]
5051 branch = True
00 Metadata-Version: 2.1
11 Name: Jinja2
2 Version: 3.0.1
2 Version: 3.0.3
33 Summary: A very fast and expressive template engine.
44 Home-page: https://palletsprojects.com/p/jinja/
55 Author: Armin Ronacher
1414 Project-URL: Issue Tracker, https://github.com/pallets/jinja/issues/
1515 Project-URL: Twitter, https://twitter.com/PalletsTeam
1616 Project-URL: Chat, https://discord.gg/pallets
17 Description: Jinja
18 =====
19
20 Jinja is a fast, expressive, extensible templating engine. Special
21 placeholders in the template allow writing code similar to Python
22 syntax. Then the template is passed data to render the final document.
23
24 It includes:
25
26 - Template inheritance and inclusion.
27 - Define and import macros within templates.
28 - HTML templates can use autoescaping to prevent XSS from untrusted
29 user input.
30 - A sandboxed environment can safely render untrusted templates.
31 - AsyncIO support for generating templates and calling async
32 functions.
33 - I18N support with Babel.
34 - Templates are compiled to optimized Python code just-in-time and
35 cached, or can be compiled ahead-of-time.
36 - Exceptions point to the correct line in templates to make debugging
37 easier.
38 - Extensible filters, tests, functions, and even syntax.
39
40 Jinja's philosophy is that while application logic belongs in Python if
41 possible, it shouldn't make the template designer's job difficult by
42 restricting functionality too much.
43
44
45 Installing
46 ----------
47
48 Install and update using `pip`_:
49
50 .. code-block:: text
51
52 $ pip install -U Jinja2
53
54 .. _pip: https://pip.pypa.io/en/stable/quickstart/
55
56
57 In A Nutshell
58 -------------
59
60 .. code-block:: jinja
61
62 {% extends "base.html" %}
63 {% block title %}Members{% endblock %}
64 {% block content %}
65 <ul>
66 {% for user in users %}
67 <li><a href="{{ user.url }}">{{ user.username }}</a></li>
68 {% endfor %}
69 </ul>
70 {% endblock %}
71
72
73 Donate
74 ------
75
76 The Pallets organization develops and supports Jinja and other popular
77 packages. In order to grow the community of contributors and users, and
78 allow the maintainers to devote more time to the projects, `please
79 donate today`_.
80
81 .. _please donate today: https://palletsprojects.com/donate
82
83
84 Links
85 -----
86
87 - Documentation: https://jinja.palletsprojects.com/
88 - Changes: https://jinja.palletsprojects.com/changes/
89 - PyPI Releases: https://pypi.org/project/Jinja2/
90 - Source Code: https://github.com/pallets/jinja/
91 - Issue Tracker: https://github.com/pallets/jinja/issues/
92 - Website: https://palletsprojects.com/p/jinja/
93 - Twitter: https://twitter.com/PalletsTeam
94 - Chat: https://discord.gg/pallets
95
9617 Platform: UNKNOWN
9718 Classifier: Development Status :: 5 - Production/Stable
9819 Classifier: Environment :: Web Environment
10526 Requires-Python: >=3.6
10627 Description-Content-Type: text/x-rst
10728 Provides-Extra: i18n
29 License-File: LICENSE.rst
30
31 Jinja
32 =====
33
34 Jinja is a fast, expressive, extensible templating engine. Special
35 placeholders in the template allow writing code similar to Python
36 syntax. Then the template is passed data to render the final document.
37
38 It includes:
39
40 - Template inheritance and inclusion.
41 - Define and import macros within templates.
42 - HTML templates can use autoescaping to prevent XSS from untrusted
43 user input.
44 - A sandboxed environment can safely render untrusted templates.
45 - AsyncIO support for generating templates and calling async
46 functions.
47 - I18N support with Babel.
48 - Templates are compiled to optimized Python code just-in-time and
49 cached, or can be compiled ahead-of-time.
50 - Exceptions point to the correct line in templates to make debugging
51 easier.
52 - Extensible filters, tests, functions, and even syntax.
53
54 Jinja's philosophy is that while application logic belongs in Python if
55 possible, it shouldn't make the template designer's job difficult by
56 restricting functionality too much.
57
58
59 Installing
60 ----------
61
62 Install and update using `pip`_:
63
64 .. code-block:: text
65
66 $ pip install -U Jinja2
67
68 .. _pip: https://pip.pypa.io/en/stable/getting-started/
69
70
71 In A Nutshell
72 -------------
73
74 .. code-block:: jinja
75
76 {% extends "base.html" %}
77 {% block title %}Members{% endblock %}
78 {% block content %}
79 <ul>
80 {% for user in users %}
81 <li><a href="{{ user.url }}">{{ user.username }}</a></li>
82 {% endfor %}
83 </ul>
84 {% endblock %}
85
86
87 Donate
88 ------
89
90 The Pallets organization develops and supports Jinja and other popular
91 packages. In order to grow the community of contributors and users, and
92 allow the maintainers to devote more time to the projects, `please
93 donate today`_.
94
95 .. _please donate today: https://palletsprojects.com/donate
96
97
98 Links
99 -----
100
101 - Documentation: https://jinja.palletsprojects.com/
102 - Changes: https://jinja.palletsprojects.com/changes/
103 - PyPI Releases: https://pypi.org/project/Jinja2/
104 - Source Code: https://github.com/pallets/jinja/
105 - Issue Tracker: https://github.com/pallets/jinja/issues/
106 - Website: https://palletsprojects.com/p/jinja/
107 - Twitter: https://twitter.com/PalletsTeam
108 - Chat: https://discord.gg/pallets
109
110
7575 tests/test_async.py
7676 tests/test_async_filters.py
7777 tests/test_bytecode_cache.py
78 tests/test_compile.py
7879 tests/test_core_tags.py
7980 tests/test_debug.py
8081 tests/test_ext.py
8687 tests/test_lexnparse.py
8788 tests/test_loader.py
8889 tests/test_nativetypes.py
90 tests/test_nodes.py
8991 tests/test_regression.py
9092 tests/test_runtime.py
9193 tests/test_security.py
4141 from .utils import pass_eval_context as pass_eval_context
4242 from .utils import select_autoescape as select_autoescape
4343
44 __version__ = "3.0.1"
44 __version__ = "3.0.3"
4343 return decorator
4444
4545
46 _common_primitives = {int, float, bool, str, list, dict, tuple, type(None)}
47
48
4649 async def auto_await(value: t.Union[t.Awaitable["V"], "V"]) -> "V":
50 # Avoid a costly call to isawaitable
51 if type(value) in _common_primitives:
52 return t.cast("V", value)
53
4754 if inspect.isawaitable(value):
4855 return await t.cast("t.Awaitable[V]", value)
4956
153153 hash = sha1(name.encode("utf-8"))
154154
155155 if filename is not None:
156 hash.update(f"|{filename}".encode("utf-8"))
156 hash.update(f"|{filename}".encode())
157157
158158 return hash.hexdigest()
159159
555555 visitor.tests,
556556 "tests",
557557 ):
558 for name in names:
558 for name in sorted(names):
559559 if name not in id_map:
560560 id_map[name] = self.temporary_identifier()
561561
10891089 self.write(
10901090 f"{f_name}(context.get_all(), True, {self.dump_local_context(frame)})"
10911091 )
1092 elif self.environment.is_async:
1093 self.write("_get_default_module_async()")
1094 else:
1095 self.write("_get_default_module(context)")
1092 else:
1093 self.write(f"_get_default_module{self.choose_async('_async')}(context)")
10961094
10971095 def visit_Import(self, node: nodes.Import, frame: Frame) -> None:
10981096 """Visit regular imports."""
12891287 self.write(", loop)")
12901288 self.end_write(frame)
12911289
1290 # at the end of the iteration, clear any assignments made in the
1291 # loop from the top level
1292 if self._assign_stack:
1293 self._assign_stack[-1].difference_update(loop_frame.symbols.stores)
1294
12921295 def visit_If(self, node: nodes.If, frame: Frame) -> None:
12931296 if_frame = frame.soft()
12941297 self.writeline("if ", node)
101101 "__jinja_exception__": exc_value,
102102 }
103103 # Raise an exception at the correct line number.
104 code = compile("\n" * (lineno - 1) + "raise __jinja_exception__", filename, "exec")
104 code: CodeType = compile(
105 "\n" * (lineno - 1) + "raise __jinja_exception__", filename, "exec"
106 )
105107
106108 # Build a new code object that points to the template file and
107109 # replaces the location with a block name.
108 try:
109 location = "template"
110
111 if tb is not None:
112 function = tb.tb_frame.f_code.co_name
113
114 if function == "root":
115 location = "top-level template code"
116 elif function.startswith("block_"):
117 location = f"block {function[6:]!r}"
118
119 # Collect arguments for the new code object. CodeType only
120 # accepts positional arguments, and arguments were inserted in
121 # new Python versions.
122 code_args = []
123
124 for attr in (
125 "argcount",
126 "posonlyargcount", # Python 3.8
127 "kwonlyargcount",
128 "nlocals",
129 "stacksize",
130 "flags",
131 "code", # codestring
132 "consts", # constants
133 "names",
134 "varnames",
135 ("filename", filename),
136 ("name", location),
137 "firstlineno",
138 "lnotab",
139 "freevars",
140 "cellvars",
141 "linetable", # Python 3.10
142 ):
143 if isinstance(attr, tuple):
144 # Replace with given value.
145 code_args.append(attr[1])
146 continue
147
148 try:
149 # Copy original value if it exists.
150 code_args.append(getattr(code, "co_" + t.cast(str, attr)))
151 except AttributeError:
152 # Some arguments were added later.
153 continue
154
155 code = CodeType(*code_args)
156 except Exception:
157 # Some environments such as Google App Engine don't support
158 # modifying code objects.
159 pass
110 location = "template"
111
112 if tb is not None:
113 function = tb.tb_frame.f_code.co_name
114
115 if function == "root":
116 location = "top-level template code"
117 elif function.startswith("block_"):
118 location = f"block {function[6:]!r}"
119
120 if sys.version_info >= (3, 8):
121 code = code.replace(co_name=location)
122 else:
123 code = CodeType(
124 code.co_argcount,
125 code.co_kwonlyargcount,
126 code.co_nlocals,
127 code.co_stacksize,
128 code.co_flags,
129 code.co_code,
130 code.co_consts,
131 code.co_names,
132 code.co_varnames,
133 code.co_filename,
134 location,
135 code.co_firstlineno,
136 code.co_lnotab,
137 code.co_freevars,
138 code.co_cellvars,
139 )
160140
161141 # Execute the new code, which is guaranteed to raise, and return
162142 # the new traceback without this frame.
11141114
11151115
11161116 class Template:
1117 """The central template object. This class represents a compiled template
1118 and is used to evaluate it.
1119
1120 Normally the template object is generated from an :class:`Environment` but
1121 it also has a constructor that makes it possible to create a template
1122 instance directly using the constructor. It takes the same arguments as
1123 the environment constructor but it's not possible to specify a loader.
1124
1125 Every template object has a few methods and members that are guaranteed
1126 to exist. However it's important that a template object should be
1127 considered immutable. Modifications on the object are not supported.
1128
1129 Template objects created from the constructor rather than an environment
1130 do have an `environment` attribute that points to a temporary environment
1131 that is probably shared with other templates created with the constructor
1132 and compatible settings.
1133
1134 >>> template = Template('Hello {{ name }}!')
1135 >>> template.render(name='John Doe') == u'Hello John Doe!'
1136 True
1137 >>> stream = template.stream(name='John Doe')
1138 >>> next(stream) == u'Hello John Doe!'
1139 True
1140 >>> next(stream)
1141 Traceback (most recent call last):
1142 ...
1143 StopIteration
1117 """A compiled template that can be rendered.
1118
1119 Use the methods on :class:`Environment` to create or load templates.
1120 The environment is used to configure how templates are compiled and
1121 behave.
1122
1123 It is also possible to create a template object directly. This is
1124 not usually recommended. The constructor takes most of the same
1125 arguments as :class:`Environment`. All templates created with the
1126 same environment arguments share the same ephemeral ``Environment``
1127 instance behind the scenes.
1128
1129 A template object should be considered immutable. Modifications on
1130 the object are not supported.
11441131 """
11451132
11461133 #: Type of environment to create when creating a template directly
13491349 rv = list(value)
13501350 rv.reverse()
13511351 return rv
1352 except TypeError:
1353 raise FilterArgumentError("argument must be iterable")
1352 except TypeError as e:
1353 raise FilterArgumentError("argument must be iterable") from e
13541354
13551355
13561356 @pass_environment
16901690 name = args[0]
16911691 args = args[1:]
16921692 except LookupError:
1693 raise FilterArgumentError("map requires a filter argument")
1693 raise FilterArgumentError("map requires a filter argument") from None
16941694
16951695 def func(item: t.Any) -> t.Any:
16961696 return context.environment.call_filter(
17111711 try:
17121712 attr = args[0]
17131713 except LookupError:
1714 raise FilterArgumentError("Missing parameter for attribute name")
1714 raise FilterArgumentError("Missing parameter for attribute name") from None
17151715
17161716 transfunc = make_attrgetter(context.environment, attr)
17171717 off = 1
148148 node: t.Optional["Symbols"] = self
149149
150150 while node is not None:
151 for name in node.stores:
151 for name in sorted(node.stores):
152152 if name not in rv:
153153 rv[name] = self.find_ref(name) # type: ignore
154154
654654 )
655655 except Exception as e:
656656 msg = str(e).split(":")[-1].strip()
657 raise TemplateSyntaxError(msg, lineno, name, filename)
657 raise TemplateSyntaxError(msg, lineno, name, filename) from e
658658 elif token == TOKEN_INTEGER:
659659 value = int(value_str.replace("_", ""), 0)
660660 elif token == TOKEN_FLOAT:
269269 package_path: "str" = "templates",
270270 encoding: str = "utf-8",
271271 ) -> None:
272 package_path = os.path.normpath(package_path).rstrip(os.path.sep)
273
274 # normpath preserves ".", which isn't valid in zip paths.
272275 if package_path == os.path.curdir:
273276 package_path = ""
274277 elif package_path[:2] == os.path.curdir + os.path.sep:
275278 package_path = package_path[2:]
276279
277 package_path = os.path.normpath(package_path).rstrip(os.path.sep)
278280 self.package_path = package_path
279281 self.package_name = package_name
280282 self.encoding = encoding
294296 self._archive = loader.archive
295297 pkgdir = next(iter(spec.submodule_search_locations)) # type: ignore
296298 template_root = os.path.join(pkgdir, package_path)
297 elif spec.submodule_search_locations:
298 # This will be one element for regular packages and multiple
299 # for namespace packages.
300 for root in spec.submodule_search_locations:
299 else:
300 roots: t.List[str] = []
301
302 # One element for regular packages, multiple for namespace
303 # packages, or None for single module file.
304 if spec.submodule_search_locations:
305 roots.extend(spec.submodule_search_locations)
306 # A single module file, use the parent directory instead.
307 elif spec.origin is not None:
308 roots.append(os.path.dirname(spec.origin))
309
310 for root in roots:
301311 root = os.path.join(root, package_path)
302312
303313 if os.path.isdir(root):
335345 # Package is a zip file.
336346 try:
337347 source = self._loader.get_data(p) # type: ignore
338 except OSError:
339 raise TemplateNotFound(template)
348 except OSError as e:
349 raise TemplateNotFound(template) from e
340350
341351 # Could use the zip's mtime for all template mtimes, but
342352 # would need to safely reload the module if it's out of
475485 try:
476486 prefix, name = template.split(self.delimiter, 1)
477487 loader = self.mapping[prefix]
478 except (ValueError, KeyError):
479 raise TemplateNotFound(template)
488 except (ValueError, KeyError) as e:
489 raise TemplateNotFound(template) from e
480490 return loader, name
481491
482492 def get_source(
485495 loader, name = self.get_loader(template)
486496 try:
487497 return loader.get_source(environment, name)
488 except TemplateNotFound:
498 except TemplateNotFound as e:
489499 # re-raise the exception with the correct filename here.
490500 # (the one that includes the prefix)
491 raise TemplateNotFound(template)
501 raise TemplateNotFound(template) from e
492502
493503 @internalcode
494504 def load(
500510 loader, local_name = self.get_loader(name)
501511 try:
502512 return loader.load(environment, local_name, globals)
503 except TemplateNotFound:
513 except TemplateNotFound as e:
504514 # re-raise the exception with the correct filename here.
505515 # (the one that includes the prefix)
506 raise TemplateNotFound(name)
516 raise TemplateNotFound(name) from e
507517
508518 def list_templates(self) -> t.List[str]:
509519 result = []
626636 if mod is None:
627637 try:
628638 mod = __import__(module, None, None, ["root"])
629 except ImportError:
630 raise TemplateNotFound(name)
639 except ImportError as e:
640 raise TemplateNotFound(name) from e
631641
632642 # remove the entry from sys.modules, we only want the attribute
633643 # on the module object we have stored on the loader.
00 import typing as t
11 from ast import literal_eval
2 from ast import parse
23 from itertools import chain
34 from itertools import islice
45
3233 raw = "".join([str(v) for v in chain(head, values)])
3334
3435 try:
35 return literal_eval(raw)
36 return literal_eval(
37 # In Python 3.10+ ast.literal_eval removes leading spaces/tabs
38 # from the given string. For backwards compatibility we need to
39 # parse the string ourselves without removing leading spaces/tabs.
40 parse(raw, mode="eval")
41 )
3642 except (ValueError, SyntaxError, MemoryError):
3743 return raw
3844
240240
241241 return tuple(self.iter_fields()) == tuple(other.iter_fields())
242242
243 def __hash__(self) -> int:
244 return hash(tuple(self.iter_fields()))
243 __hash__ = object.__hash__
245244
246245 def __repr__(self) -> str:
247246 args_str = ", ".join(f"{a}={getattr(self, a, None)!r}" for a in self.fields)
506505 f = _binop_to_func[self.operator]
507506 try:
508507 return f(self.left.as_const(eval_ctx), self.right.as_const(eval_ctx))
509 except Exception:
510 raise Impossible()
508 except Exception as e:
509 raise Impossible() from e
511510
512511
513512 class UnaryExpr(Expr):
530529 f = _uaop_to_func[self.operator]
531530 try:
532531 return f(self.node.as_const(eval_ctx))
533 except Exception:
534 raise Impossible()
532 except Exception as e:
533 raise Impossible() from e
535534
536535
537536 class Name(Expr):
722721 if node.dyn_args is not None:
723722 try:
724723 args.extend(node.dyn_args.as_const(eval_ctx))
725 except Exception:
726 raise Impossible()
724 except Exception as e:
725 raise Impossible() from e
727726
728727 if node.dyn_kwargs is not None:
729728 try:
730729 kwargs.update(node.dyn_kwargs.as_const(eval_ctx))
731 except Exception:
732 raise Impossible()
730 except Exception as e:
731 raise Impossible() from e
733732
734733 return args, kwargs
735734
778777
779778 try:
780779 return func(*args, **kwargs)
781 except Exception:
782 raise Impossible()
780 except Exception as e:
781 raise Impossible() from e
783782
784783
785784 class Filter(_FilterTestCommon):
846845 return eval_ctx.environment.getitem(
847846 self.node.as_const(eval_ctx), self.arg.as_const(eval_ctx)
848847 )
849 except Exception:
850 raise Impossible()
848 except Exception as e:
849 raise Impossible() from e
851850
852851
853852 class Getattr(Expr):
868867
869868 try:
870869 return eval_ctx.environment.getattr(self.node.as_const(eval_ctx), self.attr)
871 except Exception:
872 raise Impossible()
870 except Exception as e:
871 raise Impossible() from e
873872
874873
875874 class Slice(Expr):
928927 return False
929928
930929 value = new_value
931 except Exception:
932 raise Impossible()
930 except Exception as e:
931 raise Impossible() from e
933932
934933 return result
935934
955954
956955
957956 class FloorDiv(BinExpr):
958 """Divides the left by the right node and truncates conver the
957 """Divides the left by the right node and converts the
959958 result into an integer by truncating.
960959 """
961960
914914 __floordiv__ = __rfloordiv__ = _fail_with_undefined_error
915915 __mod__ = __rmod__ = _fail_with_undefined_error
916916 __pos__ = __neg__ = _fail_with_undefined_error
917 __call__ = __getitem__ = __contains__ = _fail_with_undefined_error
917 __call__ = __getitem__ = _fail_with_undefined_error
918918 __lt__ = __le__ = __gt__ = __ge__ = _fail_with_undefined_error
919919 __int__ = __float__ = __complex__ = _fail_with_undefined_error
920920 __pow__ = __rpow__ = _fail_with_undefined_error
10901090 __slots__ = ()
10911091 __iter__ = __str__ = __len__ = Undefined._fail_with_undefined_error
10921092 __eq__ = __ne__ = __bool__ = __hash__ = Undefined._fail_with_undefined_error
1093 __contains__ = Undefined._fail_with_undefined_error
10931094
10941095
10951096 # Remove slots attributes, after the metaclass is applied they are
823823 try:
824824 return self.__attrs[name]
825825 except KeyError:
826 raise AttributeError(name)
826 raise AttributeError(name) from None
827827
828828 def __setitem__(self, name: str, value: t.Any) -> None:
829829 self.__attrs[name] = value
315315 assert env.from_string("{{ foo.missing }}").render(foo=42) == ""
316316 assert env.from_string("{{ not missing }}").render() == "True"
317317 pytest.raises(UndefinedError, env.from_string("{{ missing - 1}}").render)
318 pytest.raises(UndefinedError, env.from_string("{{ 'foo' in missing }}").render)
318 assert env.from_string("{{ 'foo' in missing }}").render() == "False"
319319 und1 = Undefined(name="x")
320320 und2 = Undefined(name="y")
321321 assert und1 == und2
374374 pytest.raises(UndefinedError, env.from_string("{{ missing }}").render)
375375 pytest.raises(UndefinedError, env.from_string("{{ missing.attribute }}").render)
376376 pytest.raises(UndefinedError, env.from_string("{{ missing|list }}").render)
377 pytest.raises(UndefinedError, env.from_string("{{ 'foo' in missing }}").render)
377378 assert env.from_string("{{ missing is not defined }}").render() == "True"
378379 pytest.raises(
379380 UndefinedError, env.from_string("{{ foo.missing }}").render, foo=42
188188 assert m.variable == 42
189189 assert not hasattr(m, "notthere")
190190
191 def test_import_with_globals(self, test_env_async):
192 t = test_env_async.from_string(
193 '{% import "module" as m %}{{ m.test() }}', globals={"foo": 42}
194 )
195 assert t.render() == "[42|23]"
196
197 t = test_env_async.from_string('{% import "module" as m %}{{ m.test() }}')
198 assert t.render() == "[|23]"
199
200 def test_import_with_globals_override(self, test_env_async):
201 t = test_env_async.from_string(
202 '{% set foo = 41 %}{% import "module" as m %}{{ m.test() }}',
203 globals={"foo": 42},
204 )
205 assert t.render() == "[42|23]"
206
207 def test_from_import_with_globals(self, test_env_async):
208 t = test_env_async.from_string(
209 '{% from "module" import test %}{{ test() }}',
210 globals={"foo": 42},
211 )
212 assert t.render() == "[42|23]"
213
191214
192215 class TestAsyncIncludes:
193216 def test_context_include(self, test_env_async):
0 import os
1 import re
2
3 from jinja2.environment import Environment
4 from jinja2.loaders import DictLoader
5
6
7 def test_filters_deterministic(tmp_path):
8 src = "".join(f"{{{{ {i}|filter{i} }}}}" for i in range(10))
9 env = Environment(loader=DictLoader({"foo": src}))
10 env.filters.update(dict.fromkeys((f"filter{i}" for i in range(10)), lambda: None))
11 env.compile_templates(tmp_path, zip=None)
12 name = os.listdir(tmp_path)[0]
13 content = (tmp_path / name).read_text("utf8")
14 expect = [f"filters['filter{i}']" for i in range(10)]
15 found = re.findall(r"filters\['filter\d']", content)
16 assert found == expect
17
18
19 def test_import_as_with_context_deterministic(tmp_path):
20 src = "\n".join(f'{{% import "bar" as bar{i} with context %}}' for i in range(10))
21 env = Environment(loader=DictLoader({"foo": src}))
22 env.compile_templates(tmp_path, zip=None)
23 name = os.listdir(tmp_path)[0]
24 content = (tmp_path / name).read_text("utf8")
25 expect = [f"'bar{i}': " for i in range(10)]
26 found = re.findall(r"'bar\d': ", content)[:10]
27 assert found == expect
3535 test,
3636 r"""
3737 File ".*?broken.html", line 2, in (top-level template code|<module>)
38 \{\{ fail\(\) \}\}
38 \{\{ fail\(\) \}\}(
39 \^{12})?
3940 File ".*debug?.pyc?", line \d+, in <lambda>
40 tmpl\.render\(fail=lambda: 1 / 0\)
41 tmpl\.render\(fail=lambda: 1 / 0\)(
42 ~~\^~~)?
4143 ZeroDivisionError: (int(eger)? )?division (or modulo )?by zero
4244 """,
4345 )
6567 test,
6668 r"""
6769 File ".*debug.pyc?", line \d+, in test
68 raise TemplateSyntaxError\("wtf", 42\)
70 raise TemplateSyntaxError\("wtf", 42\)(
71 \^{36})?
6972 (jinja2\.exceptions\.)?TemplateSyntaxError: wtf
7073 line 42""",
7174 )
9898 t.render()
9999
100100 def test_import_with_globals(self, test_env):
101 env = Environment(
102 loader=DictLoader(
103 {
104 "macros": "{% macro test() %}foo: {{ foo }}{% endmacro %}",
105 "test": "{% import 'macros' as m %}{{ m.test() }}",
106 "test1": "{% import 'macros' as m %}{{ m.test() }}",
107 }
108 )
109 )
110 tmpl = env.get_template("test", globals={"foo": "bar"})
111 assert tmpl.render() == "foo: bar"
112
113 tmpl = env.get_template("test1")
114 assert tmpl.render() == "foo: "
101 t = test_env.from_string(
102 '{% import "module" as m %}{{ m.test() }}', globals={"foo": 42}
103 )
104 assert t.render() == "[42|23]"
105
106 t = test_env.from_string('{% import "module" as m %}{{ m.test() }}')
107 assert t.render() == "[|23]"
115108
116109 def test_import_with_globals_override(self, test_env):
117 env = Environment(
118 loader=DictLoader(
119 {
120 "macros": "{% set foo = '42' %}{% macro test() %}"
121 "foo: {{ foo }}{% endmacro %}",
122 "test": "{% from 'macros' import test %}{{ test() }}",
123 }
124 )
125 )
126 tmpl = env.get_template("test", globals={"foo": "bar"})
127 assert tmpl.render() == "foo: 42"
110 t = test_env.from_string(
111 '{% set foo = 41 %}{% import "module" as m %}{{ m.test() }}',
112 globals={"foo": 42},
113 )
114 assert t.render() == "[42|23]"
128115
129116 def test_from_import_with_globals(self, test_env):
130 env = Environment(
131 loader=DictLoader(
132 {
133 "macros": "{% macro testing() %}foo: {{ foo }}{% endmacro %}",
134 "test": "{% from 'macros' import testing %}{{ testing() }}",
135 }
136 )
137 )
138 tmpl = env.get_template("test", globals={"foo": "bar"})
139 assert tmpl.render() == "foo: bar"
117 t = test_env.from_string(
118 '{% from "module" import test %}{{ test() }}',
119 globals={"foo": 42},
120 )
121 assert t.render() == "[42|23]"
140122
141123
142124 class TestIncludes:
3636 {% block block1 %}
3737 {% if false %}
3838 {% block block2 %}
39 this should workd
39 this should work
4040 {% endblock %}
4141 {% endif %}
4242 {% endblock %}
4848 {% block block1 %}
4949 {% if false %}
5050 {% block block2 %}
51 this should workd
51 this should work
5252 {% endblock %}
5353 {% endif %}
5454 {% endblock %}
313313
314314
315315 @pytest.fixture()
316 def package_file_loader(monkeypatch):
317 monkeypatch.syspath_prepend(Path(__file__).parent / "res")
318 return PackageLoader("__init__")
319
320
321 @pytest.mark.parametrize(
322 ("template", "expect"), [("foo/test.html", "FOO"), ("test.html", "BAR")]
323 )
324 def test_package_file_source(package_file_loader, template, expect):
325 source, name, up_to_date = package_file_loader.get_source(None, template)
326 assert source.rstrip() == expect
327 assert name.endswith(os.path.join(*split_template_path(template)))
328 assert up_to_date()
329
330
331 def test_package_file_list(package_file_loader):
332 templates = package_file_loader.list_templates()
333 assert "foo/test.html" in templates
334 assert "test.html" in templates
335
336
337 @pytest.fixture()
316338 def package_zip_loader(monkeypatch):
317339 package_zip = (Path(__file__) / ".." / "res" / "package.zip").resolve()
318340 monkeypatch.syspath_prepend(package_zip)
338360 assert package_zip_loader.list_templates() == ["foo/test.html", "test.html"]
339361
340362
363 @pytest.mark.parametrize("package_path", ["", ".", "./"])
364 def test_package_zip_omit_curdir(package_zip_loader, package_path):
365 """PackageLoader should not add or include "." or "./" in the root
366 path, it is invalid in zip paths.
367 """
368 loader = PackageLoader("t_pack", package_path)
369 assert loader.package_path == ""
370 source, _, _ = loader.get_source(None, "templates/foo/test.html")
371 assert source.rstrip() == "FOO"
372
373
341374 def test_pep_451_import_hook():
342375 class ImportHook(importlib.abc.MetaPathFinder, importlib.abc.Loader):
343376 def find_spec(self, name, path=None, target=None):
146146 def test_spontaneous_env():
147147 t = NativeTemplate("{{ true }}")
148148 assert isinstance(t.environment, NativeEnvironment)
149
150
151 def test_leading_spaces(env):
152 t = env.from_string(" {{ True }}")
153 result = t.render()
154 assert result == " True"
0 def test_template_hash(env):
1 template = env.parse("hash test")
2 hash(template)
745745 tmpl = env.get_template("base")
746746 assert tmpl.render() == "42 y"
747747
748 def test_nested_loop_scoping(self, env):
749 tmpl = env.from_string(
750 "{% set output %}{% for x in [1,2,3] %}hello{% endfor %}"
751 "{% endset %}{{ output }}"
752 )
753 assert tmpl.render() == "hellohellohello"
754
748755
749756 @pytest.mark.parametrize("unicode_char", ["\N{FORM FEED}", "\x85"])
750757 def test_unicode_whitespace(env, unicode_char):
00 [tox]
11 envlist =
2 py{39,38,37,36,py3}
2 py{311,310,39,38,37,36,py37}
33 style
44 typing
55 docs