Showing preview only (979K chars total). Download the full file or copy to clipboard to get everything.
Repository: pallets/jinja
Branch: main
Commit: 5ef70112a1ff
Files: 102
Total size: 940.1 KB
Directory structure:
gitextract_8ck6zqut/
├── .devcontainer/
│ ├── devcontainer.json
│ └── on-create-command.sh
├── .editorconfig
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug-report.md
│ │ ├── config.yml
│ │ └── feature-request.md
│ ├── pull_request_template.md
│ └── workflows/
│ ├── lock.yaml
│ ├── pre-commit.yaml
│ ├── publish.yaml
│ └── tests.yaml
├── .gitignore
├── .pre-commit-config.yaml
├── .readthedocs.yaml
├── CHANGES.rst
├── LICENSE.txt
├── README.md
├── docs/
│ ├── Makefile
│ ├── api.rst
│ ├── changes.rst
│ ├── conf.py
│ ├── examples/
│ │ ├── cache_extension.py
│ │ └── inline_gettext_extension.py
│ ├── extensions.rst
│ ├── faq.rst
│ ├── index.rst
│ ├── integration.rst
│ ├── intro.rst
│ ├── license.rst
│ ├── make.bat
│ ├── nativetypes.rst
│ ├── sandbox.rst
│ ├── switching.rst
│ ├── templates.rst
│ └── tricks.rst
├── examples/
│ └── basic/
│ ├── cycle.py
│ ├── debugger.py
│ ├── inheritance.py
│ ├── templates/
│ │ ├── broken.html
│ │ └── subbroken.html
│ ├── test.py
│ ├── test_filter_and_linestatements.py
│ ├── test_loop_filter.py
│ └── translate.py
├── pyproject.toml
├── scripts/
│ └── generate_identifier_pattern.py
├── src/
│ └── jinja2/
│ ├── __init__.py
│ ├── _identifier.py
│ ├── async_utils.py
│ ├── bccache.py
│ ├── compiler.py
│ ├── constants.py
│ ├── debug.py
│ ├── defaults.py
│ ├── environment.py
│ ├── exceptions.py
│ ├── ext.py
│ ├── filters.py
│ ├── idtracking.py
│ ├── lexer.py
│ ├── loaders.py
│ ├── meta.py
│ ├── nativetypes.py
│ ├── nodes.py
│ ├── optimizer.py
│ ├── parser.py
│ ├── py.typed
│ ├── runtime.py
│ ├── sandbox.py
│ ├── tests.py
│ ├── utils.py
│ └── visitor.py
└── tests/
├── conftest.py
├── res/
│ ├── __init__.py
│ ├── templates/
│ │ ├── broken.html
│ │ ├── foo/
│ │ │ └── test.html
│ │ ├── mojibake.txt
│ │ ├── syntaxerror.html
│ │ └── test.html
│ └── templates2/
│ └── foo
├── test_api.py
├── test_async.py
├── test_async_filters.py
├── test_bytecode_cache.py
├── test_compile.py
├── test_core_tags.py
├── test_debug.py
├── test_ext.py
├── test_filters.py
├── test_idtracking.py
├── test_imports.py
├── test_inheritance.py
├── test_lexnparse.py
├── test_loader.py
├── test_nativetypes.py
├── test_nodes.py
├── test_pickle.py
├── test_regression.py
├── test_runtime.py
├── test_security.py
├── test_tests.py
└── test_utils.py
================================================
FILE CONTENTS
================================================
================================================
FILE: .devcontainer/devcontainer.json
================================================
{
"name": "pallets/jinja",
"image": "mcr.microsoft.com/devcontainers/python:3",
"customizations": {
"vscode": {
"settings": {
"python.defaultInterpreterPath": "${workspaceFolder}/.venv",
"python.terminal.activateEnvInCurrentTerminal": true,
"python.terminal.launchArgs": [
"-X",
"dev"
]
}
}
},
"onCreateCommand": ".devcontainer/on-create-command.sh"
}
================================================
FILE: .devcontainer/on-create-command.sh
================================================
#!/bin/bash
set -e
# Install uv if not already installed
if ! command -v uv &> /dev/null; then
echo "Installing uv..."
curl -LsSf https://astral.sh/uv/install.sh | sh
export PATH="$HOME/.cargo/bin:$PATH"
fi
# Create venv using uv and install dependencies
echo "Creating virtual environment and installing dependencies..."
uv sync
# Install pre-commit hooks
echo "Installing pre-commit hooks..."
pre-commit install --install-hooks
================================================
FILE: .editorconfig
================================================
root = true
[*]
indent_style = space
indent_size = 4
insert_final_newline = true
trim_trailing_whitespace = true
end_of_line = lf
charset = utf-8
max_line_length = 88
[*.{css,html,js,json,jsx,scss,ts,tsx,yaml,yml}]
indent_size = 2
================================================
FILE: .github/ISSUE_TEMPLATE/bug-report.md
================================================
---
name: Bug report
about: Report a bug in Jinja (not other projects which depend on Jinja)
---
<!--
This issue tracker is a tool to address bugs in Jinja itself. Please use
GitHub Discussions or the Pallets Discord for questions about your own code.
Replace this comment with a clear outline of what the bug is.
-->
<!--
Describe how to replicate the bug.
Include a minimal reproducible example that demonstrates the bug.
Include the full traceback if there was an exception.
-->
<!--
Describe the expected behavior that should have happened but didn't.
-->
Environment:
- Python version:
- Jinja version:
================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
contact_links:
- name: Questions on Discussions
url: https://github.com/pallets/jinja/discussions/
about: Ask questions about your own code on the Discussions tab.
- name: Questions on Chat
url: https://discord.gg/pallets
about: Ask questions about your own code on our Discord chat.
================================================
FILE: .github/ISSUE_TEMPLATE/feature-request.md
================================================
---
name: Feature request
about: Suggest a new feature for Jinja
---
<!--
Replace this comment with a description of what the feature should do.
Include details such as links to relevant specs or previous discussions.
-->
<!--
Replace this comment with an example of the problem which this feature
would resolve. Is this problem solvable without changes to Jinja, such
as by subclassing or using an extension?
-->
================================================
FILE: .github/pull_request_template.md
================================================
<!--
Before opening a PR, open a ticket describing the issue or feature the
PR will address. An issue is not required for fixing typos in
documentation, or other simple non-code changes.
Replace this comment with a description of the change. Describe how it
addresses the linked ticket.
-->
<!--
Link to relevant issues or previous PRs, one per line. Use "fixes" to
automatically close an issue.
fixes #<issue number>
-->
<!--
Ensure each step in CONTRIBUTING.rst is complete, especially the following:
- Add tests that demonstrate the correct behavior of the change. Tests
should fail without the change.
- Add or update relevant docs, in the docs folder and in code.
- Add an entry in CHANGES.rst summarizing the change and linking to the issue.
- Add `.. versionchanged::` entries in any relevant code docs.
-->
================================================
FILE: .github/workflows/lock.yaml
================================================
name: Lock inactive closed issues
# Lock closed issues that have not received any further activity for two weeks.
# This does not close open issues, only humans may do that. It is easier to
# respond to new issues with fresh examples rather than continuing discussions
# on old issues.
on:
schedule:
- cron: '0 0 * * *'
permissions:
issues: write
pull-requests: write
discussions: write
concurrency:
group: lock
jobs:
lock:
runs-on: ubuntu-latest
steps:
- uses: dessant/lock-threads@1bf7ec25051fe7c00bdd17e6a7cf3d7bfb7dc771 # v5.0.1
with:
issue-inactive-days: 14
pr-inactive-days: 14
discussion-inactive-days: 14
================================================
FILE: .github/workflows/pre-commit.yaml
================================================
name: pre-commit
on:
pull_request:
push:
branches: [main, stable]
jobs:
main:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: astral-sh/setup-uv@f0ec1fc3b38f5e7cd731bb6ce540c5af426746bb # v6.1.0
with:
enable-cache: true
prune-cache: false
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
id: setup-python
with:
python-version-file: pyproject.toml
- uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: ~/.cache/pre-commit
key: pre-commit|${{ hashFiles('pyproject.toml', '.pre-commit-config.yaml') }}
- run: uv run --locked --group pre-commit pre-commit run --show-diff-on-failure --color=always --all-files
- uses: pre-commit-ci/lite-action@5d6cc0eb514c891a40562a58a8e71576c5c7fb43 # v1.1.0
if: ${{ !cancelled() }}
================================================
FILE: .github/workflows/publish.yaml
================================================
name: Publish
on:
push:
tags: ['*']
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: astral-sh/setup-uv@f0ec1fc3b38f5e7cd731bb6ce540c5af426746bb # v6.1.0
with:
enable-cache: true
prune-cache: false
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
with:
python-version-file: pyproject.toml
- run: echo "SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct)" >> $GITHUB_ENV
- run: uv build
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
path: ./dist
create-release:
needs: [build]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
- name: create release
run: >
gh release create --draft --repo ${{ github.repository }}
${{ github.ref_name }} artifact/*
env:
GH_TOKEN: ${{ github.token }}
publish-pypi:
needs: [build]
environment:
name: publish
url: https://pypi.org/project/Jinja2/${{ github.ref_name }}
runs-on: ubuntu-latest
permissions:
id-token: write
steps:
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
- uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4
with:
packages-dir: artifact/
================================================
FILE: .github/workflows/tests.yaml
================================================
name: Tests
on:
pull_request:
paths-ignore: ['docs/**', 'README.md']
push:
branches: [main, stable]
paths-ignore: ['docs/**', 'README.md']
jobs:
tests:
name: ${{ matrix.name || matrix.python }}
runs-on: ${{ matrix.os || 'ubuntu-latest' }}
strategy:
fail-fast: false
matrix:
include:
- {python: '3.13'}
- {name: Windows, python: '3.13', os: windows-latest}
- {name: Mac, python: '3.13', os: macos-latest}
- {python: '3.12'}
- {python: '3.11'}
- {python: '3.10'}
- {name: PyPy, python: 'pypy-3.11', tox: pypy3.11}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: astral-sh/setup-uv@f0ec1fc3b38f5e7cd731bb6ce540c5af426746bb # v6.1.0
with:
enable-cache: true
prune-cache: false
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
with:
python-version: ${{ matrix.python }}
- run: uv run --locked tox run -e ${{ matrix.tox || format('py{0}', matrix.python) }}
typing:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: astral-sh/setup-uv@f0ec1fc3b38f5e7cd731bb6ce540c5af426746bb # v6.1.0
with:
enable-cache: true
prune-cache: false
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
with:
python-version-file: pyproject.toml
- name: cache mypy
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: ./.mypy_cache
key: mypy|${{ hashFiles('pyproject.toml') }}
- run: uv run --locked tox run -e typing
================================================
FILE: .gitignore
================================================
.idea/
.vscode/
__pycache__/
dist/
.coverage*
htmlcov/
.tox/
docs/_build/
================================================
FILE: .pre-commit-config.yaml
================================================
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: 76e47323a83cd9795e4ff9a1de1c0d2eef610f17 # frozen: v0.11.11
hooks:
- id: ruff
- id: ruff-format
- repo: https://github.com/astral-sh/uv-pre-commit
rev: 648bdbfd6bb1a82f132ecc2c666e0d1b2e4b0d94 # frozen: 0.7.8
hooks:
- id: uv-lock
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: cef0300fd0fc4d2a87a85fa2093c6b283ea36f4b # frozen: v5.0.0
hooks:
- id: check-merge-conflict
- id: debug-statements
- id: fix-byte-order-marker
- id: trailing-whitespace
- id: end-of-file-fixer
================================================
FILE: .readthedocs.yaml
================================================
version: 2
build:
os: ubuntu-24.04
tools:
python: '3.13'
commands:
- asdf plugin add uv
- asdf install uv latest
- asdf global uv latest
- uv run --group docs sphinx-build -W -b dirhtml docs $READTHEDOCS_OUTPUT/html
================================================
FILE: CHANGES.rst
================================================
.. currentmodule:: jinja2
Version 3.2.0
-------------
Unreleased
- Drop support for Python 3.7, 3.8, and 3.9.
- Update minimum MarkupSafe version to >= 3.0.
- Update minimum Babel version to >= 2.17.
- Deprecate the ``__version__`` attribute. Use feature detection or
``importlib.metadata.version("jinja2")`` instead.
- Use modern packaging metadata with ``pyproject.toml`` instead of ``setup.cfg``.
:pr:`1793`
- Use ``flit_core`` instead of ``setuptools`` as build backend.
Version 3.1.6
-------------
Released 2025-03-05
- The ``|attr`` filter does not bypass the environment's attribute lookup,
allowing the sandbox to apply its checks. :ghsa:`cpwx-vrp4-4pq7`
Version 3.1.5
-------------
Released 2024-12-21
- The sandboxed environment handles indirect calls to ``str.format``, such as
by passing a stored reference to a filter that calls its argument.
:ghsa:`q2x7-8rv6-6q7h`
- Escape template name before formatting it into error messages, to avoid
issues with names that contain f-string syntax.
:issue:`1792`, :ghsa:`gmj6-6f8f-6699`
- Sandbox does not allow ``clear`` and ``pop`` on known mutable sequence
types. :issue:`2032`
- Calling sync ``render`` for an async template uses ``asyncio.run``.
:pr:`1952`
- Avoid unclosed ``auto_aiter`` warnings. :pr:`1960`
- Return an ``aclose``-able ``AsyncGenerator`` from
``Template.generate_async``. :pr:`1960`
- Avoid leaving ``root_render_func()`` unclosed in
``Template.generate_async``. :pr:`1960`
- Avoid leaving async generators unclosed in blocks, includes and extends.
:pr:`1960`
- The runtime uses the correct ``concat`` function for the current environment
when calling block references. :issue:`1701`
- Make ``|unique`` async-aware, allowing it to be used after another
async-aware filter. :issue:`1781`
- ``|int`` filter handles ``OverflowError`` from scientific notation.
:issue:`1921`
- Make compiling deterministic for tuple unpacking in a ``{% set ... %}``
call. :issue:`2021`
- Fix dunder protocol (`copy`/`pickle`/etc) interaction with ``Undefined``
objects. :issue:`2025`
- Fix `copy`/`pickle` support for the internal ``missing`` object.
:issue:`2027`
- ``Environment.overlay(enable_async)`` is applied correctly. :pr:`2061`
- The error message from ``FileSystemLoader`` includes the paths that were
searched. :issue:`1661`
- ``PackageLoader`` shows a clearer error message when the package does not
contain the templates directory. :issue:`1705`
- Improve annotations for methods returning copies. :pr:`1880`
- ``urlize`` does not add ``mailto:`` to values like `@a@b`. :pr:`1870`
- Tests decorated with `@pass_context`` can be used with the ``|select``
filter. :issue:`1624`
- Using ``set`` for multiple assignment (``a, b = 1, 2``) does not fail when the
target is a namespace attribute. :issue:`1413`
- Using ``set`` in all branches of ``{% if %}{% elif %}{% else %}`` blocks
does not cause the variable to be considered initially undefined.
:issue:`1253`
Version 3.1.4
-------------
Released 2024-05-05
- The ``xmlattr`` filter does not allow keys with ``/`` solidus, ``>``
greater-than sign, or ``=`` equals sign, in addition to disallowing spaces.
Regardless of any validation done by Jinja, user input should never be used
as keys to this filter, or must be separately validated first.
:ghsa:`h75v-3vvj-5mfj`
Version 3.1.3
-------------
Released 2024-01-10
- Fix compiler error when checking if required blocks in parent templates are
empty. :pr:`1858`
- ``xmlattr`` filter does not allow keys with spaces. :ghsa:`h5c8-rqwp-cp95`
- Make error messages stemming from invalid nesting of ``{% trans %}`` blocks
more helpful. :pr:`1918`
Version 3.1.2
-------------
Released 2022-04-28
- Add parameters to ``Environment.overlay`` to match ``__init__``.
:issue:`1645`
- Handle race condition in ``FileSystemBytecodeCache``. :issue:`1654`
Version 3.1.1
-------------
Released 2022-03-25
- The template filename on Windows uses the primary path separator.
:issue:`1637`
Version 3.1.0
-------------
Released 2022-03-24
- Drop support for Python 3.6. :pr:`1534`
- Remove previously deprecated code. :pr:`1544`
- ``WithExtension`` and ``AutoEscapeExtension`` are built-in now.
- ``contextfilter`` and ``contextfunction`` are replaced by
``pass_context``. ``evalcontextfilter`` and
``evalcontextfunction`` are replaced by ``pass_eval_context``.
``environmentfilter`` and ``environmentfunction`` are replaced
by ``pass_environment``.
- ``Markup`` and ``escape`` should be imported from MarkupSafe.
- Compiled templates from very old Jinja versions may need to be
recompiled.
- Legacy resolve mode for ``Context`` subclasses is no longer
supported. Override ``resolve_or_missing`` instead of
``resolve``.
- ``unicode_urlencode`` is renamed to ``url_quote``.
- Add support for native types in macros. :issue:`1510`
- The ``{% trans %}`` tag can use ``pgettext`` and ``npgettext`` by
passing a context string as the first token in the tag, like
``{% trans "title" %}``. :issue:`1430`
- Update valid identifier characters from Python 3.6 to 3.7.
:pr:`1571`
- Filters and tests decorated with ``@async_variant`` are pickleable.
:pr:`1612`
- Add ``items`` filter. :issue:`1561`
- Subscriptions (``[0]``, etc.) can be used after filters, tests, and
calls when the environment is in async mode. :issue:`1573`
- The ``groupby`` filter is case-insensitive by default, matching
other comparison filters. Added the ``case_sensitive`` parameter to
control this. :issue:`1463`
- Windows drive-relative path segments in template names will not
result in ``FileSystemLoader`` and ``PackageLoader`` loading from
drive-relative paths. :pr:`1621`
Version 3.0.3
-------------
Released 2021-11-09
- Fix traceback rewriting internals for Python 3.10 and 3.11.
:issue:`1535`
- Fix how the native environment treats leading and trailing spaces
when parsing values on Python 3.10. :pr:`1537`
- Improve async performance by avoiding checks for common types.
:issue:`1514`
- Revert change to ``hash(Node)`` behavior. Nodes are hashed by id
again :issue:`1521`
- ``PackageLoader`` works when the package is a single module file.
:issue:`1512`
Version 3.0.2
-------------
Released 2021-10-04
- 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`
- Imported macros have access to the current template globals in async
environments. :issue:`1494`
- ``PackageLoader`` will not include a current directory (.) path
segment. This allows loading templates from the root of a zip
import. :issue:`1467`
Version 3.0.1
-------------
Released 2021-05-18
- Update MarkupSafe dependency to >= 2.0. :pr:`1418`
- Mark top-level names as exported so type checking understands
imports in user projects. :issue:`1426`
- Fix some types that weren't available in Python 3.6.0. :issue:`1433`
- The deprecation warning for unneeded ``autoescape`` and ``with_``
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.new_event_loop``
This fixes a deprecation that Python 3.10 introduces. :issue:`1443`
Version 3.0.0
-------------
Released 2021-05-11
- Drop support for Python 2.7 and 3.5.
- Bump MarkupSafe dependency to >=1.1.
- Bump Babel optional dependency to >=2.1.
- Remove code that was marked deprecated.
- Add type hinting. :pr:`1412`
- Use :pep:`451` API to load templates with
:class:`~loaders.PackageLoader`. :issue:`1168`
- Fix a bug that caused imported macros to not have access to the
current template's globals. :issue:`688`
- Add ability to ignore ``trim_blocks`` using ``+%}``. :issue:`1036`
- Fix a bug that caused custom async-only filters to fail with
constant input. :issue:`1279`
- Fix UndefinedError incorrectly being thrown on an undefined variable
instead of ``Undefined`` being returned on
``NativeEnvironment`` on Python 3.10. :issue:`1335`
- Blocks can be marked as ``required``. They must be overridden at
some point, but not necessarily by the direct child. :issue:`1147`
- Deprecate the ``autoescape`` and ``with`` extensions, they are
built-in to the compiler. :issue:`1203`
- The ``urlize`` filter recognizes ``mailto:`` links and takes
``extra_schemes`` (or ``env.policies["urlize.extra_schemes"]``) to
recognize other schemes. It tries to balance parentheses within a
URL instead of ignoring trailing characters. The parsing in general
has been updated to be more efficient and match more cases. URLs
without a scheme are linked as ``https://`` instead of ``http://``.
:issue:`522, 827, 1172`, :pr:`1195`
- Filters that get attributes, such as ``map`` and ``groupby``, can
use a false or empty value as a default. :issue:`1331`
- Fix a bug that prevented variables set in blocks or loops from
being accessed in custom context functions. :issue:`768`
- Fix a bug that caused scoped blocks from accessing special loop
variables. :issue:`1088`
- Update the template globals when calling
``Environment.get_template(globals=...)`` even if the template was
already loaded. :issue:`295`
- Do not raise an error for undefined filters in unexecuted
if-statements and conditional expressions. :issue:`842`
- Add ``is filter`` and ``is test`` tests to test if a name is a
registered filter or test. This allows checking if a filter is
available in a template before using it. Test functions can be
decorated with ``@pass_environment``, ``@pass_eval_context``,
or ``@pass_context``. :issue:`842`, :pr:`1248`
- Support ``pgettext`` and ``npgettext`` (message contexts) in i18n
extension. :issue:`441`
- The ``|indent`` filter's ``width`` argument can be a string to
indent by. :pr:`1167`
- The parser understands hex, octal, and binary integer literals.
:issue:`1170`
- ``Undefined.__contains__`` (``in``) raises an ``UndefinedError``
instead of a ``TypeError``. :issue:`1198`
- ``Undefined`` is iterable in an async environment. :issue:`1294`
- ``NativeEnvironment`` supports async mode. :issue:`1362`
- Template rendering only treats ``\n``, ``\r\n`` and ``\r`` as line
breaks. Other characters are left unchanged. :issue:`769, 952, 1313`
- ``|groupby`` filter takes an optional ``default`` argument.
:issue:`1359`
- The function and filter decorators have been renamed and unified.
The old names are deprecated. :issue:`1381`
- ``pass_context`` replaces ``contextfunction`` and
``contextfilter``.
- ``pass_eval_context`` replaces ``evalcontextfunction`` and
``evalcontextfilter``
- ``pass_environment`` replaces ``environmentfunction`` and
``environmentfilter``.
- Async support no longer requires Jinja to patch itself. It must
still be enabled with ``Environment(enable_async=True)``.
:issue:`1390`
- Overriding ``Context.resolve`` is deprecated, override
``resolve_or_missing`` instead. :issue:`1380`
Version 2.11.3
--------------
Released 2021-01-31
- Improve the speed of the ``urlize`` filter by reducing regex
backtracking. Email matching requires a word character at the start
of the domain part, and only word characters in the TLD. :pr:`1343`
Version 2.11.2
--------------
Released 2020-04-13
- 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`
- Fix a hang when displaying tracebacks on Python 32-bit.
:issue:`1162`
- Showing an undefined error for an object that raises
``AttributeError`` on access doesn't cause a recursion error.
:issue:`1177`
- Revert changes to :class:`~loaders.PackageLoader` from 2.10 which
removed the dependency on setuptools and pkg_resources, and added
limited support for namespace packages. The changes caused issues
when using Pytest. Due to the difficulty in supporting Python 2 and
:pep:`451` simultaneously, the changes are reverted until 3.0.
:pr:`1182`
- Fix line numbers in error messages when newlines are stripped.
:pr:`1178`
- The special ``namespace()`` assignment object in templates works in
async environments. :issue:`1180`
- Fix whitespace being removed before tags in the middle of lines when
``lstrip_blocks`` is enabled. :issue:`1138`
- :class:`~nativetypes.NativeEnvironment` doesn't evaluate
intermediate strings during rendering. This prevents early
evaluation which could change the value of an expression.
:issue:`1186`
Version 2.11.1
--------------
Released 2020-01-30
- Fix a bug that prevented looking up a key after an attribute
(``{{ data.items[1:] }}``) in an async template. :issue:`1141`
Version 2.11.0
--------------
Released 2020-01-27
- Drop support for Python 2.6, 3.3, and 3.4. This will be the last
version to support Python 2.7 and 3.5.
- Added a new ``ChainableUndefined`` class to support getitem and
getattr on an undefined object. :issue:`977`
- Allow ``{%+`` syntax (with NOP behavior) when ``lstrip_blocks`` is
disabled. :issue:`748`
- Added a ``default`` parameter for the ``map`` filter. :issue:`557`
- Exclude environment globals from
:func:`meta.find_undeclared_variables`. :issue:`931`
- Float literals can be written with scientific notation, like
2.56e-3. :issue:`912`, :pr:`922`
- Int and float literals can be written with the '_' separator for
legibility, like 12_345. :pr:`923`
- Fix a bug causing deadlocks in ``LRUCache.setdefault``. :pr:`1000`
- The ``trim`` filter takes an optional string of characters to trim.
:pr:`828`
- A new ``jinja2.ext.debug`` extension adds a ``{% debug %}`` tag to
quickly dump the current context and available filters and tests.
:issue:`174`, :pr:`798, 983`
- Lexing templates with large amounts of whitespace is much faster.
:issue:`857`, :pr:`858`
- Parentheses around comparisons are preserved, so
``{{ 2 * (3 < 5) }}`` outputs "2" instead of "False".
:issue:`755`, :pr:`938`
- Add new ``boolean``, ``false``, ``true``, ``integer`` and ``float``
tests. :pr:`824`
- The environment's ``finalize`` function is only applied to the
output of expressions (constant or not), not static template data.
:issue:`63`
- When providing multiple paths to ``FileSystemLoader``, a template
can have the same name as a directory. :issue:`821`
- Always return :class:`Undefined` when omitting the ``else`` clause
in a ``{{ 'foo' if bar }}`` expression, regardless of the
environment's ``undefined`` class. Omitting the ``else`` clause is a
valid shortcut and should not raise an error when using
:class:`StrictUndefined`. :issue:`710`, :pr:`1079`
- Fix behavior of ``loop`` control variables such as ``length`` and
``revindex0`` when looping over a generator. :issue:`459, 751, 794`,
:pr:`993`
- Async support is only loaded the first time an environment enables
it, in order to avoid a slow initial import. :issue:`765`
- In async environments, the ``|map`` filter will await the filter
call if needed. :pr:`913`
- In for loops that access ``loop`` attributes, the iterator is not
advanced ahead of the current iteration unless ``length``,
``revindex``, ``nextitem``, or ``last`` are accessed. This makes it
less likely to break ``groupby`` results. :issue:`555`, :pr:`1101`
- In async environments, the ``loop`` attributes ``length`` and
``revindex`` work for async iterators. :pr:`1101`
- In async environments, values from attribute/property access will
be awaited if needed. :pr:`1101`
- :class:`~loader.PackageLoader` doesn't depend on setuptools or
pkg_resources. :issue:`970`
- ``PackageLoader`` has limited support for :pep:`420` namespace
packages. :issue:`1097`
- Support :class:`os.PathLike` objects in
:class:`~loader.FileSystemLoader` and :class:`~loader.ModuleLoader`.
:issue:`870`
- :class:`~nativetypes.NativeTemplate` correctly handles quotes
between expressions. ``"'{{ a }}', '{{ b }}'"`` renders as the tuple
``('1', '2')`` rather than the string ``'1, 2'``. :issue:`1020`
- Creating a :class:`~nativetypes.NativeTemplate` directly creates a
:class:`~nativetypes.NativeEnvironment` instead of a default
:class:`Environment`. :issue:`1091`
- After calling ``LRUCache.copy()``, the copy's queue methods point to
the correct queue. :issue:`843`
- Compiling templates always writes UTF-8 instead of defaulting to the
system encoding. :issue:`889`
- ``|wordwrap`` filter treats existing newlines as separate paragraphs
to be wrapped individually, rather than creating short intermediate
lines. :issue:`175`
- Add ``break_on_hyphens`` parameter to ``|wordwrap`` filter.
:issue:`550`
- Cython compiled functions decorated as context functions will be
passed the context. :pr:`1108`
- When chained comparisons of constants are evaluated at compile time,
the result follows Python's behavior of returning ``False`` if any
comparison returns ``False``, rather than only the last one.
:issue:`1102`
- Tracebacks for exceptions in templates show the correct line numbers
and source for Python >= 3.7. :issue:`1104`
- Tracebacks for template syntax errors in Python 3 no longer show
internal compiler frames. :issue:`763`
- Add a ``DerivedContextReference`` node that can be used by
extensions to get the current context and local variables such as
``loop``. :issue:`860`
- Constant folding during compilation is applied to some node types
that were previously overlooked. :issue:`733`
- ``TemplateSyntaxError.source`` is not empty when raised from an
included template. :issue:`457`
- Passing an ``Undefined`` value to ``get_template`` (such as through
``extends``, ``import``, or ``include``), raises an
``UndefinedError`` consistently. ``select_template`` will show the
undefined message in the list of attempts rather than the empty
string. :issue:`1037`
- ``TemplateSyntaxError`` can be pickled. :pr:`1117`
Version 2.10.3
--------------
Released 2019-10-04
- Fix a typo in Babel entry point in ``setup.py`` that was preventing
installation.
Version 2.10.2
--------------
Released 2019-10-04
- Fix Python 3.7 deprecation warnings.
- Using ``range`` in the sandboxed environment uses ``xrange`` on
Python 2 to avoid memory use. :issue:`933`
- Use Python 3.7's better traceback support to avoid a core dump when
using debug builds of Python 3.7. :issue:`1050`
Version 2.10.1
--------------
Released 2019-04-06
- ``SandboxedEnvironment`` securely handles ``str.format_map`` in
order to prevent code execution through untrusted format strings.
The sandbox already handled ``str.format``.
Version 2.10
------------
Released 2017-11-08
- Added a new extension node called ``OverlayScope`` which can be used
to create an unoptimized scope that will look up all variables from
a derived context.
- Added an ``in`` test that works like the in operator. This can be
used in combination with ``reject`` and ``select``.
- Added ``previtem`` and ``nextitem`` to loop contexts, providing
access to the previous/next item in the loop. If such an item does
not exist, the value is undefined.
- Added ``changed(*values)`` to loop contexts, providing an easy way
of checking whether a value has changed since the last iteration (or
rather since the last call of the method)
- Added a ``namespace`` function that creates a special object which
allows attribute assignment using the ``set`` tag. This can be used
to carry data across scopes, e.g. from a loop body to code that
comes after the loop.
- Added a ``trimmed`` modifier to ``{% trans %}`` to strip linebreaks
and surrounding whitespace. Also added a new policy to enable this
for all ``trans`` blocks.
- The ``random`` filter is no longer incorrectly constant folded and
will produce a new random choice each time the template is rendered.
:pr:`478`
- Added a ``unique`` filter. :pr:`469`
- Added ``min`` and ``max`` filters. :pr:`475`
- Added tests for all comparison operators: ``eq``, ``ne``, ``lt``,
``le``, ``gt``, ``ge``. :pr:`665`
- ``import`` statement cannot end with a trailing comma. :pr:`617`,
:pr:`618`
- ``indent`` filter will not indent blank lines by default. :pr:`685`
- Add ``reverse`` argument for ``dictsort`` filter. :pr:`692`
- Add a ``NativeEnvironment`` that renders templates to native Python
types instead of strings. :pr:`708`
- Added filter support to the block ``set`` tag. :pr:`489`
- ``tojson`` filter marks output as safe to match documented behavior.
:pr:`718`
- Resolved a bug where getting debug locals for tracebacks could
modify template context.
- Fixed a bug where having many ``{% elif ... %}`` blocks resulted in
a "too many levels of indentation" error. These blocks now compile
to native ``elif ..:`` instead of ``else: if ..:`` :issue:`759`
Version 2.9.6
-------------
Released 2017-04-03
- Fixed custom context behavior in fast resolve mode :issue:`675`
Version 2.9.5
-------------
Released 2017-01-28
- Restored the original repr of the internal ``_GroupTuple`` because
this caused issues with ansible and it was an unintended change.
:issue:`654`
- Added back support for custom contexts that override the old
``resolve`` method since it was hard for people to spot that this
could cause a regression.
- Correctly use the buffer for the else block of for loops. This
caused invalid syntax errors to be caused on 2.x and completely
wrong behavior on Python 3 :issue:`669`
- Resolve an issue where the ``{% extends %}`` tag could not be used
with async environments. :issue:`668`
- Reduce memory footprint slightly by reducing our unicode database
dump we use for identifier matching on Python 3 :issue:`666`
- Fixed autoescaping not working for macros in async compilation mode.
:issue:`671`
Version 2.9.4
-------------
Released 2017-01-10
- Solved some warnings for string literals. :issue:`646`
- Increment the bytecode cache version which was not done due to an
oversight before.
- Corrected bad code generation and scoping for filtered loops.
:issue:`649`
- Resolved an issue where top-level output silencing after known
extend blocks could generate invalid code when blocks where
contained in if statements. :issue:`651`
- Made the ``truncate.leeway`` default configurable to improve
compatibility with older templates.
Version 2.9.3
-------------
Released 2017-01-08
- Restored the use of blocks in macros to the extend that was possible
before. On Python 3 it would render a generator repr instead of the
block contents. :issue:`645`
- Set a consistent behavior for assigning of variables in inner scopes
when the variable is also read from an outer scope. This now sets
the intended behavior in all situations however it does not restore
the old behavior where limited assignments to outer scopes was
possible. For more information and a discussion see :issue:`641`
- Resolved an issue where ``block scoped`` would not take advantage of
the new scoping rules. In some more exotic cases a variable
overridden in a local scope would not make it into a block.
- Change the code generation of the ``with`` statement to be in line
with the new scoping rules. This resolves some unlikely bugs in edge
cases. This also introduces a new internal ``With`` node that can be
used by extensions.
Version 2.9.2
-------------
Released 2017-01-08
- Fixed a regression that caused for loops to not be able to use the
same variable for the target as well as source iterator.
:issue:`640`
- Add support for a previously unknown behavior of macros. It used to
be possible in some circumstances to explicitly provide a caller
argument to macros. While badly buggy and unintended it turns out
that this is a common case that gets copy pasted around. To not
completely break backwards compatibility with the most common cases
it's now possible to provide an explicit keyword argument for caller
if it's given an explicit default. :issue:`642`
Version 2.9.1
-------------
Released 2017-01-07
- Resolved a regression with call block scoping for macros. Nested
caller blocks that used the same identifiers as outer macros could
refer to the wrong variable incorrectly.
Version 2.9
-----------
Released 2017-01-07, codename Derivation
- Change cache key definition in environment. This fixes a performance
regression introduced in 2.8.
- Added support for ``generator_stop`` on supported Python versions
(Python 3.5 and later)
- Corrected a long standing issue with operator precedence of math
operations not being what was expected.
- Added support for Python 3.6 async iterators through a new async
mode.
- Added policies for filter defaults and similar things.
- Urlize now sets "rel noopener" by default.
- Support attribute fallback for old-style classes in 2.x.
- Support toplevel set statements in extend situations.
- Restored behavior of Cycler for Python 3 users.
- Subtraction now follows the same behavior as other operators on
undefined values.
- ``map`` and friends will now give better error messages if you
forgot to quote the parameter.
- Depend on MarkupSafe 0.23 or higher.
- Improved the ``truncate`` filter to support better truncation in
case the string is barely truncated at all.
- Change the logic for macro autoescaping to be based on the runtime
autoescaping information at call time instead of macro define time.
- Ported a modified version of the ``tojson`` filter from Flask to
Jinja and hooked it up with the new policy framework.
- Block sets are now marked ``safe`` by default.
- On Python 2 the asciification of ASCII strings can now be disabled
with the ``compiler.ascii_str`` policy.
- Tests now no longer accept an arbitrary expression as first argument
but a restricted one. This means that you can now properly use
multiple tests in one expression without extra parentheses. In
particular you can now write ``foo is divisibleby 2 or foo is
divisibleby 3`` as you would expect.
- Greatly changed the scoping system to be more consistent with what
template designers and developers expect. There is now no more magic
difference between the different include and import constructs.
Context is now always propagated the same way. The only remaining
differences is the defaults for ``with context`` and ``without
context``.
- The ``with`` and ``autoescape`` tags are now built-in.
- Added the new ``select_autoescape`` function which helps configuring
better autoescaping easier.
- Fixed a runtime error in the sandbox when attributes of async
generators were accessed.
Version 2.8.1
-------------
Released 2016-12-29
- Fixed the ``for_qs`` flag for ``urlencode``.
- Fixed regression when applying ``int`` to non-string values.
- SECURITY: if the sandbox mode is used format expressions are now
sandboxed with the same rules as in Jinja. This solves various
information leakage problems that can occur with format strings.
Version 2.8
-----------
Released 2015-07-26, codename Replacement
- Added ``target`` parameter to urlize function.
- Added support for ``followsymlinks`` to the file system loader.
- The truncate filter now counts the length.
- Added equalto filter that helps with select filters.
- Changed cache keys to use absolute file names if available instead
of load names.
- Fixed loop length calculation for some iterators.
- Changed how Jinja enforces strings to be native strings in Python 2
to work when people break their default encoding.
- Added ``make_logging_undefined`` which returns an undefined
object that logs failures into a logger.
- If unmarshalling of cached data fails the template will be reloaded
now.
- Implemented a block ``set`` tag.
- Default cache size was increased to 400 from a low 50.
- Fixed ``is number`` test to accept long integers in all Python
versions.
- Changed ``is number`` to accept Decimal as a number.
- Added a check for default arguments followed by non-default
arguments. This change makes ``{% macro m(x, y=1, z) %}`` a syntax
error. The previous behavior for this code was broken anyway
(resulting in the default value being applied to ``y``).
- Add ability to use custom subclasses of
``jinja2.compiler.CodeGenerator`` and ``jinja2.runtime.Context`` by
adding two new attributes to the environment
(``code_generator_class`` and ``context_class``). :pr:`404`
- Added support for context/environment/evalctx decorator functions on
the finalize callback of the environment.
- Escape query strings for urlencode properly. Previously slashes were
not escaped in that place.
- Add 'base' parameter to 'int' filter.
Version 2.7.3
-------------
Released 2014-06-06
- Security issue: Corrected the security fix for the cache folder.
This fix was provided by RedHat.
Version 2.7.2
-------------
Released 2014-01-10
- Prefix loader was not forwarding the locals properly to inner
loaders. This is now fixed.
- Security issue: Changed the default folder for the filesystem cache
to be user specific and read and write protected on UNIX systems.
See `Debian bug 734747`_ for more information.
.. _Debian bug 734747: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=734747
Version 2.7.1
-------------
Released 2013-08-07
- Fixed a bug with ``call_filter`` not working properly on environment
and context filters.
- Fixed lack of Python 3 support for bytecode caches.
- Reverted support for defining blocks in included templates as this
broke existing templates for users.
- Fixed some warnings with hashing of undefineds and nodes if Python
is run with warnings for Python 3.
- Added support for properly hashing undefined objects.
- Fixed a bug with the title filter not working on already uppercase
strings.
Version 2.7
-----------
Released 2013-05-20, codename Translation
- Choice and prefix loaders now dispatch source and template lookup
separately in order to work in combination with module loaders as
advertised.
- Fixed filesizeformat.
- Added a non-silent option for babel extraction.
- Added ``urlencode`` filter that automatically quotes values for URL
safe usage with utf-8 as only supported encoding. If applications
want to change this encoding they can override the filter.
- Added ``keep-trailing-newline`` configuration to environments and
templates to optionally preserve the final trailing newline.
- Accessing ``last`` on the loop context no longer causes the iterator
to be consumed into a list.
- Python requirement changed: 2.6, 2.7 or >= 3.3 are required now,
supported by same source code, using the "six" compatibility
library.
- Allow ``contextfunction`` and other decorators to be applied to
``__call__``.
- Added support for changing from newline to different signs in the
``wordwrap`` filter.
- Added support for ignoring memcache errors silently.
- Added support for keeping the trailing newline in templates.
- Added finer grained support for stripping whitespace on the left
side of blocks.
- Added ``map``, ``select``, ``reject``, ``selectattr`` and
``rejectattr`` filters.
- Added support for ``loop.depth`` to figure out how deep inside a
recursive loop the code is.
- Disabled py_compile for pypy and python 3.
Version 2.6
-----------
Released 2011-07-24, codename Convolution
- Internal attributes now raise an internal attribute error now
instead of returning an undefined. This fixes problems when passing
undefined objects to Python semantics expecting APIs.
- Traceback support now works properly for PyPy. (Tested with 1.4)
- Implemented operator intercepting for sandboxed environments. This
allows application developers to disable builtin operators for
better security. (For instance limit the mathematical operators to
actual integers instead of longs)
- Groupby filter now supports dotted notation for grouping by
attributes of attributes.
- Scoped blocks now properly treat toplevel assignments and imports.
Previously an import suddenly "disappeared" in a scoped block.
- Automatically detect newer Python interpreter versions before
loading code from bytecode caches to prevent segfaults on invalid
opcodes. The segfault in earlier Jinja versions here was not a
Jinja bug but a limitation in the underlying Python interpreter. If
you notice Jinja segfaulting in earlier versions after an upgrade
of the Python interpreter you don't have to upgrade, it's enough to
flush the bytecode cache. This just no longer makes this necessary,
Jinja will automatically detect these cases now.
- The sum filter can now sum up values by attribute. This is a
backwards incompatible change. The argument to the filter previously
was the optional starting index which defaults to zero. This now
became the second argument to the function because it's rarely used.
- Like sum, sort now also makes it possible to order items by
attribute.
- Like sum and sort, join now also is able to join attributes of
objects as string.
- The internal eval context now has a reference to the environment.
- Added a mapping test to see if an object is a dict or an object with
a similar interface.
Version 2.5.5
-------------
Released 2010-10-18
- Built documentation is no longer part of release.
Version 2.5.4
-------------
Released 2010-10-17
- Fixed extensions not loading properly with overlays.
- Work around a bug in cpython for the debugger that causes segfaults
on 64bit big-endian architectures.
Version 2.5.3
-------------
Released 2010-10-17
- Fixed an operator precedence error introduced in 2.5.2. Statements
like "-foo.bar" had their implicit parentheses applied around the
first part of the expression ("(-foo).bar") instead of the more
correct "-(foo.bar)".
Version 2.5.2
-------------
Released 2010-08-18
- Improved setup.py script to better work with assumptions people
might still have from it (``--with-speedups``).
- Fixed a packaging error that excluded the new debug support.
Version 2.5.1
-------------
Released 2010-08-17
- StopIteration exceptions raised by functions called from templates
are now intercepted and converted to undefineds. This solves a lot
of debugging grief. (StopIteration is used internally to abort
template execution)
- Improved performance of macro calls slightly.
- Babel extraction can now properly extract newstyle gettext calls.
- Using the variable ``num`` in newstyle gettext for something else
than the pluralize count will no longer raise a :exc:`KeyError`.
- Removed builtin markup class and switched to markupsafe. For
backwards compatibility the pure Python implementation still exists
but is pulled from markupsafe by the Jinja developers. The debug
support went into a separate feature called "debugsupport" and is
disabled by default because it is only relevant for Python 2.4
- Fixed an issue with unary operators having the wrong precedence.
Version 2.5
-----------
Released 2010-05-29, codename Incoherence
- Improved the sort filter (should have worked like this for a long
time) by adding support for case insensitive searches.
- Fixed a bug for getattribute constant folding.
- Support for newstyle gettext translations which result in a nicer
in-template user interface and more consistent catalogs.
- It's now possible to register extensions after an environment was
created.
Version 2.4.1
-------------
Released 2010-04-20
- Fixed an error reporting bug for undefined.
Version 2.4
-----------
Released 2010-04-13, codename Correlation
- The environment template loading functions now transparently pass
through a template object if it was passed to it. This makes it
possible to import or extend from a template object that was passed
to the template.
- Added a ``ModuleLoader`` that can load templates from
precompiled sources. The environment now features a method to
compile the templates from a configured loader into a zip file or
folder.
- The _speedups C extension now supports Python 3.
- Added support for autoescaping toggling sections and support for
evaluation contexts.
- Extensions have a priority now.
Version 2.3.1
-------------
Released 2010-02-19
- Fixed an error reporting bug on all python versions
- Fixed an error reporting bug on Python 2.4
Version 2.3
-----------
Released 2010-02-10, codename 3000 Pythons
- Fixes issue with code generator that causes unbound variables to be
generated if set was used in if-blocks and other small identifier
problems.
- Include tags are now able to select between multiple templates and
take the first that exists, if a list of templates is given.
- Fixed a problem with having call blocks in outer scopes that have an
argument that is also used as local variable in an inner frame
:issue:`360`.
- Greatly improved error message reporting :pr:`339`
- Implicit tuple expressions can no longer be totally empty. This
change makes ``{% if %}`` a syntax error now. :issue:`364`
- Added support for translator comments if extracted via babel.
- Added with-statement extension.
- Experimental Python 3 support.
Version 2.2.1
-------------
Released 2009-09-14
- Fixes some smaller problems for Jinja on Jython.
Version 2.2
-----------
Released 2009-09-13, codename Kong
- Include statements can now be marked with ``ignore missing`` to skip
non existing templates.
- Priority of ``not`` raised. It's now possible to write ``not foo in
bar`` as an alias to ``foo not in bar`` like in python. Previously
the grammar required parentheses (``not (foo in bar)``) which was
odd.
- Fixed a bug that caused syntax errors when defining macros or using
the ``{% call %}`` tag inside loops.
- Fixed a bug in the parser that made ``{{ foo[1, 2] }}`` impossible.
- Made it possible to refer to names from outer scopes in included
templates that were unused in the callers frame :issue:`327`
- Fixed a bug that caused internal errors if names where used as
iteration variable and regular variable *after* the loop if that
variable was unused *before* the loop. :pr:`331`
- Added support for optional ``scoped`` modifier to blocks.
- Added support for line-comments.
- Added the ``meta`` module.
- Renamed (undocumented) attribute "overlay" to "overlayed" on the
environment because it was clashing with a method of the same name.
- Speedup extension is now disabled by default.
Version 2.1.1
-------------
Released 2008-12-25
- Fixed a translation error caused by looping over empty recursive
loops.
Version 2.1
-----------
Released 2008-11-23, codename Yasuzō
- Fixed a bug with nested loops and the special loop variable. Before
the change an inner loop overwrote the loop variable from the outer
one after iteration.
- Fixed a bug with the i18n extension that caused the explicit
pluralization block to look up the wrong variable.
- Fixed a limitation in the lexer that made ``{{ foo.0.0 }}``
impossible.
- Index based subscribing of variables with a constant value returns
an undefined object now instead of raising an index error. This was
a bug caused by eager optimizing.
- The i18n extension looks up ``foo.ugettext`` now followed by
``foo.gettext`` if an translations object is installed. This makes
dealing with custom translations classes easier.
- Fixed a confusing behavior with conditional extending. loops were
partially executed under some conditions even though they were not
part of a visible area.
- Added ``sort`` filter that works like ``dictsort`` but for arbitrary
sequences.
- Fixed a bug with empty statements in macros.
- Implemented a bytecode cache system.
- The template context is now weakref-able
- Inclusions and imports "with context" forward all variables now, not
only the initial context.
- Added a cycle helper called ``cycler``.
- Added a joining helper called ``joiner``.
- Added a ``compile_expression`` method to the environment that allows
compiling of Jinja expressions into callable Python objects.
- Fixed an escaping bug in urlize
Version 2.0
-----------
Released 2008-07-17, codename Jinjavitus
- The subscribing of objects (looking up attributes and items) changed
from slightly. It's now possible to give attributes or items a
higher priority by either using dot-notation lookup or the bracket
syntax. This also changed the AST slightly. ``Subscript`` is gone
and was replaced with ``Getitem`` and ``Getattr``.
- Added support for preprocessing and token stream filtering for
extensions. This would allow extensions to allow simplified gettext
calls in template data and something similar.
- Added ``TemplateStream.dump``.
- Added missing support for implicit string literal concatenation.
``{{ "foo" "bar" }}`` is equivalent to ``{{ "foobar" }}``
- ``else`` is optional for conditional expressions. If not given it
evaluates to ``false``.
- Improved error reporting for undefined values by providing a
position.
- ``filesizeformat`` filter uses decimal prefixes now by default and
can be set to binary mode with the second parameter.
- Fixed bug in finalizer
Version 2.0rc1
--------------
Released 2008-06-09
- First release of Jinja 2.
================================================
FILE: LICENSE.txt
================================================
Copyright 2007 Pallets
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
================================================
FILE: README.md
================================================
<div align="center"><img src="https://raw.githubusercontent.com/pallets/jinja/refs/heads/stable/docs/_static/jinja-name.svg" alt="" height="150"></div>
# Jinja
Jinja is a fast, expressive, extensible templating engine. Special
placeholders in the template allow writing code similar to Python
syntax. Then the template is passed data to render the final document.
It includes:
- Template inheritance and inclusion.
- Define and import macros within templates.
- HTML templates can use autoescaping to prevent XSS from untrusted
user input.
- A sandboxed environment can safely render untrusted templates.
- AsyncIO support for generating templates and calling async
functions.
- I18N support with Babel.
- Templates are compiled to optimized Python code just-in-time and
cached, or can be compiled ahead-of-time.
- Exceptions point to the correct line in templates to make debugging
easier.
- Extensible filters, tests, functions, and even syntax.
Jinja's philosophy is that while application logic belongs in Python if
possible, it shouldn't make the template designer's job difficult by
restricting functionality too much.
## In A Nutshell
```jinja
{% extends "base.html" %}
{% block title %}Members{% endblock %}
{% block content %}
<ul>
{% for user in users %}
<li><a href="{{ user.url }}">{{ user.username }}</a></li>
{% endfor %}
</ul>
{% endblock %}
```
## Donate
The Pallets organization develops and supports Jinja and other popular
packages. In order to grow the community of contributors and users, and
allow the maintainers to devote more time to the projects, [please
donate today][].
[please donate today]: https://palletsprojects.com/donate
## Contributing
See our [detailed contributing documentation][contrib] for many ways to
contribute, including reporting issues, requesting features, asking or answering
questions, and making PRs.
[contrib]: https://palletsprojects.com/contributing/
================================================
FILE: docs/Makefile
================================================
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
SOURCEDIR = .
BUILDDIR = _build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
================================================
FILE: docs/api.rst
================================================
API
===
.. module:: jinja2
:noindex:
:synopsis: public Jinja API
This document describes the API to Jinja and not the template language
(for that, see :doc:`/templates`). It will be most useful as reference
to those implementing the template interface to the application and not
those who are creating Jinja templates.
Basics
------
Jinja uses a central object called the template :class:`Environment`.
Instances of this class are used to store the configuration and global objects,
and are used to load templates from the file system or other locations.
Even if you are creating templates from strings by using the constructor of
:class:`Template` class, an environment is created automatically for you,
albeit a shared one.
Most applications will create one :class:`Environment` object on application
initialization and use that to load templates. In some cases however, it's
useful to have multiple environments side by side, if different configurations
are in use.
The simplest way to configure Jinja to load templates for your
application is to use :class:`~loaders.PackageLoader`.
.. code-block:: python
from jinja2 import Environment, PackageLoader, select_autoescape
env = Environment(
loader=PackageLoader("yourapp"),
autoescape=select_autoescape()
)
This will create a template environment with a loader that looks up
templates in the ``templates`` folder inside the ``yourapp`` Python
package (or next to the ``yourapp.py`` Python module). It also enables
autoescaping for HTML files. This loader only requires that ``yourapp``
is importable, it figures out the absolute path to the folder for you.
Different loaders are available to load templates in other ways or from
other locations. They're listed in the `Loaders`_ section below. You can
also write your own if you want to load templates from a source that's
more specialized to your project.
To load a template from this environment, call the :meth:`get_template`
method, which returns the loaded :class:`Template`.
.. code-block:: python
template = env.get_template("mytemplate.html")
To render it with some variables, call the :meth:`render` method.
.. code-block:: python
print(template.render(the="variables", go="here"))
Using a template loader rather than passing strings to :class:`Template`
or :meth:`Environment.from_string` has multiple advantages. Besides being
a lot easier to use it also enables template inheritance.
.. admonition:: Notes on Autoescaping
In future versions of Jinja we might enable autoescaping by default
for security reasons. As such you are encouraged to explicitly
configure autoescaping now instead of relying on the default.
High Level API
--------------
The high-level API is the API you will use in the application to load and
render Jinja templates. The :ref:`low-level-api` on the other side is only
useful if you want to dig deeper into Jinja or :ref:`develop extensions
<jinja-extensions>`.
.. autoclass:: Environment([options])
:members: from_string, get_template, select_template,
get_or_select_template, join_path, extend, compile_expression,
compile_templates, list_templates, add_extension
.. attribute:: shared
If a template was created by using the :class:`Template` constructor
an environment is created automatically. These environments are
created as shared environments which means that multiple templates
may have the same anonymous environment. For all shared environments
this attribute is `True`, else `False`.
.. attribute:: sandboxed
If the environment is sandboxed this attribute is `True`. For the
sandbox mode have a look at the documentation for the
:class:`~jinja2.sandbox.SandboxedEnvironment`.
.. attribute:: filters
A dict of filters for this environment. As long as no template was
loaded it's safe to add new filters or remove old. For custom filters
see :ref:`writing-filters`. For valid filter names have a look at
:ref:`identifier-naming`.
.. attribute:: tests
A dict of test functions for this environment. As long as no
template was loaded it's safe to modify this dict. For custom tests
see :ref:`writing-tests`. For valid test names have a look at
:ref:`identifier-naming`.
.. attribute:: globals
A dict of variables that are available in every template loaded
by the environment. As long as no template was loaded it's safe
to modify this. For more details see :ref:`global-namespace`.
For valid object names see :ref:`identifier-naming`.
.. attribute:: policies
A dictionary with :ref:`policies`. These can be reconfigured to
change the runtime behavior or certain template features. Usually
these are security related.
.. attribute:: code_generator_class
The class used for code generation. This should not be changed
in most cases, unless you need to modify the Python code a
template compiles to.
.. attribute:: context_class
The context used for templates. This should not be changed
in most cases, unless you need to modify internals of how
template variables are handled. For details, see
:class:`~jinja2.runtime.Context`.
.. automethod:: overlay([options])
.. method:: undefined([hint, obj, name, exc])
Creates a new :class:`Undefined` object for `name`. This is useful
for filters or functions that may return undefined objects for
some operations. All parameters except of `hint` should be provided
as keyword parameters for better readability. The `hint` is used as
error message for the exception if provided, otherwise the error
message will be generated from `obj` and `name` automatically. The exception
provided as `exc` is raised if something with the generated undefined
object is done that the undefined object does not allow. The default
exception is :exc:`UndefinedError`. If a `hint` is provided the
`name` may be omitted.
The most common way to create an undefined object is by providing
a name only::
return environment.undefined(name='some_name')
This means that the name `some_name` is not defined. If the name
was from an attribute of an object it makes sense to tell the
undefined object the holder object to improve the error message::
if not hasattr(obj, 'attr'):
return environment.undefined(obj=obj, name='attr')
For a more complex example you can provide a hint. For example
the :func:`first` filter creates an undefined object that way::
return environment.undefined('no first item, sequence was empty')
If it the `name` or `obj` is known (for example because an attribute
was accessed) it should be passed to the undefined object, even if
a custom `hint` is provided. This gives undefined objects the
possibility to enhance the error message.
.. autoclass:: Template
:members: module, make_module
.. attribute:: globals
A dict of variables that are available every time the template
is rendered, without needing to pass them during render. This
should not be modified, as depending on how the template was
loaded it may be shared with the environment and other
templates.
Defaults to :attr:`Environment.globals` unless extra values are
passed to :meth:`Environment.get_template`.
Globals are only intended for data that is common to every
render of the template. Specific data should be passed to
:meth:`render`.
See :ref:`global-namespace`.
.. attribute:: name
The loading name of the template. If the template was loaded from a
string this is `None`.
.. attribute:: filename
The filename of the template on the file system if it was loaded from
there. Otherwise this is `None`.
.. automethod:: render([context])
.. automethod:: generate([context])
.. automethod:: stream([context])
.. automethod:: render_async([context])
.. automethod:: generate_async([context])
.. autoclass:: jinja2.environment.TemplateStream()
:members: disable_buffering, enable_buffering, dump
Autoescaping
------------
.. versionchanged:: 2.4
Jinja now comes with autoescaping support. As of Jinja 2.9 the
autoescape extension is removed and built-in. However autoescaping is
not yet enabled by default though this will most likely change in the
future. It's recommended to configure a sensible default for
autoescaping. This makes it possible to enable and disable autoescaping
on a per-template basis (HTML versus text for instance).
.. autofunction:: jinja2.select_autoescape
Here a recommended setup that enables autoescaping for templates ending
in ``'.html'``, ``'.htm'`` and ``'.xml'`` and disabling it by default
for all other extensions. You can use the :func:`~jinja2.select_autoescape`
function for this::
from jinja2 import Environment, PackageLoader, select_autoescape
env = Environment(autoescape=select_autoescape(['html', 'htm', 'xml']),
loader=PackageLoader('mypackage'))
The :func:`~jinja.select_autoescape` function returns a function that
works roughly like this::
def autoescape(template_name):
if template_name is None:
return False
if template_name.endswith(('.html', '.htm', '.xml'))
When implementing a guessing autoescape function, make sure you also
accept `None` as valid template name. This will be passed when generating
templates from strings. You should always configure autoescaping as
defaults in the future might change.
Inside the templates the behaviour can be temporarily changed by using
the `autoescape` block (see :ref:`autoescape-overrides`).
.. _identifier-naming:
Notes on Identifiers
--------------------
Jinja uses Python naming rules. Valid identifiers can be any combination
of characters accepted by Python.
Filters and tests are looked up in separate namespaces and have slightly
modified identifier syntax. Filters and tests may contain dots to group
filters and tests by topic. For example it's perfectly valid to add a
function into the filter dict and call it `to.str`. The regular
expression for filter and test identifiers is
``[a-zA-Z_][a-zA-Z0-9_]*(\.[a-zA-Z_][a-zA-Z0-9_]*)*``.
Undefined Types
---------------
These classes can be used as undefined types. The :class:`Environment`
constructor takes an `undefined` parameter that can be one of those classes
or a custom subclass of :class:`Undefined`. Whenever the template engine is
unable to look up a name or access an attribute one of those objects is
created and returned. Some operations on undefined values are then allowed,
others fail.
The closest to regular Python behavior is the :class:`StrictUndefined` which
disallows all operations beside testing if it's an undefined object.
.. autoclass:: jinja2.Undefined()
.. attribute:: _undefined_hint
Either `None` or a string with the error message for the
undefined object.
.. attribute:: _undefined_obj
Either `None` or the owner object that caused the undefined object
to be created (for example because an attribute does not exist).
.. attribute:: _undefined_name
The name for the undefined variable / attribute or just `None`
if no such information exists.
.. attribute:: _undefined_exception
The exception that the undefined object wants to raise. This
is usually one of :exc:`UndefinedError` or :exc:`SecurityError`.
.. method:: _fail_with_undefined_error(\*args, \**kwargs)
When called with any arguments this method raises
:attr:`_undefined_exception` with an error message generated
from the undefined hints stored on the undefined object.
.. autoclass:: jinja2.ChainableUndefined()
.. autoclass:: jinja2.DebugUndefined()
.. autoclass:: jinja2.StrictUndefined()
There is also a factory function that can decorate undefined objects to
implement logging on failures:
.. autofunction:: jinja2.make_logging_undefined
Undefined objects are created by calling :attr:`undefined`.
.. admonition:: Implementation
:class:`Undefined` is implemented by overriding the special
``__underscore__`` methods. For example the default
:class:`Undefined` class implements ``__str__`` to returns an empty
string, while ``__int__`` and others fail with an exception. To
allow conversion to int by returning ``0`` you can implement your
own subclass.
.. code-block:: python
class NullUndefined(Undefined):
def __int__(self):
return 0
def __float__(self):
return 0.0
To disallow a method, override it and raise
:attr:`~Undefined._undefined_exception`. Because this is very
common there is the helper method
:meth:`~Undefined._fail_with_undefined_error` that raises the error
with the correct information. Here's a class that works like the
regular :class:`Undefined` but fails on iteration::
class NonIterableUndefined(Undefined):
def __iter__(self):
self._fail_with_undefined_error()
The Context
-----------
.. autoclass:: jinja2.runtime.Context()
:members: get, resolve, resolve_or_missing, get_exported, get_all
.. attribute:: parent
A dict of read only, global variables the template looks up. These
can either come from another :class:`Context`, from the
:attr:`Environment.globals` or :attr:`Template.globals` or points
to a dict created by combining the globals with the variables
passed to the render function. It must not be altered.
.. attribute:: vars
The template local variables. This list contains environment and
context functions from the :attr:`parent` scope as well as local
modifications and exported variables from the template. The template
will modify this dict during template evaluation but filters and
context functions are not allowed to modify it.
.. attribute:: environment
The environment that loaded the template.
.. attribute:: exported_vars
This set contains all the names the template exports. The values for
the names are in the :attr:`vars` dict. In order to get a copy of the
exported variables as dict, :meth:`get_exported` can be used.
.. attribute:: name
The load name of the template owning this context.
.. attribute:: blocks
A dict with the current mapping of blocks in the template. The keys
in this dict are the names of the blocks, and the values a list of
blocks registered. The last item in each list is the current active
block (latest in the inheritance chain).
.. attribute:: eval_ctx
The current :ref:`eval-context`.
.. automethod:: jinja2.runtime.Context.call(callable, \*args, \**kwargs)
The context is immutable, it prevents modifications, and if it is
modified somehow despite that those changes may not show up. For
performance, Jinja does not use the context as data storage for, only as
a primary data source. Variables that the template does not define are
looked up in the context, but variables the template does define are
stored locally.
Instead of modifying the context directly, a function should return
a value that can be assigned to a variable within the template itself.
.. code-block:: jinja
{% set comments = get_latest_comments() %}
.. _loaders:
Loaders
-------
Loaders are responsible for loading templates from a resource such as the
file system. The environment will keep the compiled modules in memory like
Python's `sys.modules`. Unlike `sys.modules` however this cache is limited in
size by default and templates are automatically reloaded.
All loaders are subclasses of :class:`BaseLoader`. If you want to create your
own loader, subclass :class:`BaseLoader` and override `get_source`.
.. autoclass:: jinja2.BaseLoader
:members: get_source, load
Here a list of the builtin loaders Jinja provides:
.. autoclass:: jinja2.FileSystemLoader
.. autoclass:: jinja2.PackageLoader
.. autoclass:: jinja2.DictLoader
.. autoclass:: jinja2.FunctionLoader
.. autoclass:: jinja2.PrefixLoader
.. autoclass:: jinja2.ChoiceLoader
.. autoclass:: jinja2.ModuleLoader
.. _bytecode-cache:
Bytecode Cache
--------------
Jinja 2.1 and higher support external bytecode caching. Bytecode caches make
it possible to store the generated bytecode on the file system or a different
location to avoid parsing the templates on first use.
This is especially useful if you have a web application that is initialized on
the first request and Jinja compiles many templates at once which slows down
the application.
To use a bytecode cache, instantiate it and pass it to the :class:`Environment`.
.. autoclass:: jinja2.BytecodeCache
:members: load_bytecode, dump_bytecode, clear
.. autoclass:: jinja2.bccache.Bucket
:members: write_bytecode, load_bytecode, bytecode_from_string,
bytecode_to_string, reset
.. attribute:: environment
The :class:`Environment` that created the bucket.
.. attribute:: key
The unique cache key for this bucket
.. attribute:: code
The bytecode if it's loaded, otherwise `None`.
Builtin bytecode caches:
.. autoclass:: jinja2.FileSystemBytecodeCache
.. autoclass:: jinja2.MemcachedBytecodeCache
Async Support
-------------
.. 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:
- 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 adds 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. 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:
Policies
--------
Starting with Jinja 2.9 policies can be configured on the environment
which can slightly influence how filters and other template constructs
behave. They can be configured with the
:attr:`~jinja2.Environment.policies` attribute.
Example::
env.policies['urlize.rel'] = 'nofollow noopener'
``truncate.leeway``:
Configures the leeway default for the `truncate` filter. Leeway as
introduced in 2.9 but to restore compatibility with older templates
it can be configured to `0` to get the old behavior back. The default
is `5`.
``urlize.rel``:
A string that defines the items for the `rel` attribute of generated
links with the `urlize` filter. These items are always added. The
default is `noopener`.
``urlize.target``:
The default target that is issued for links from the `urlize` filter
if no other target is defined by the call explicitly.
``urlize.extra_schemes``:
Recognize URLs that start with these schemes in addition to the
default ``http://``, ``https://``, and ``mailto:``.
``json.dumps_function``:
If this is set to a value other than `None` then the `tojson` filter
will dump with this function instead of the default one. Note that
this function should accept arbitrary extra arguments which might be
passed in the future from the filter. Currently the only argument
that might be passed is `indent`. The default dump function is
``json.dumps``.
``json.dumps_kwargs``:
Keyword arguments to be passed to the dump function. The default is
``{'sort_keys': True}``.
.. _ext-i18n-trimmed:
``ext.i18n.trimmed``:
If this is set to `True`, ``{% trans %}`` blocks of the
:ref:`i18n-extension` will always unify linebreaks and surrounding
whitespace as if the `trimmed` modifier was used.
Utilities
---------
These helper functions and classes are useful if you add custom filters or
functions to a Jinja environment.
.. autofunction:: jinja2.pass_context
.. autofunction:: jinja2.pass_eval_context
.. autofunction:: jinja2.pass_environment
.. autofunction:: jinja2.clear_caches
.. autofunction:: jinja2.is_undefined
Exceptions
----------
.. autoexception:: jinja2.TemplateError
.. autoexception:: jinja2.UndefinedError
.. autoexception:: jinja2.TemplateNotFound
.. autoexception:: jinja2.TemplatesNotFound
.. autoexception:: jinja2.TemplateSyntaxError
.. attribute:: message
The error message.
.. attribute:: lineno
The line number where the error occurred.
.. attribute:: name
The load name for the template.
.. attribute:: filename
The filename that loaded the template in the encoding of the
file system (most likely utf-8, or mbcs on Windows systems).
.. autoexception:: jinja2.TemplateRuntimeError
.. autoexception:: jinja2.TemplateAssertionError
.. _writing-filters:
Custom Filters
--------------
Filters are Python functions that take the value to the left of the
filter as the first argument and produce a new value. Arguments passed
to the filter are passed after the value.
For example, the filter ``{{ 42|myfilter(23) }}`` is called behind the
scenes as ``myfilter(42, 23)``.
Jinja comes with some :ref:`built-in filters <builtin-filters>`. To use
a custom filter, write a function that takes at least a ``value``
argument, then register it in :attr:`Environment.filters`.
Here's a filter that formats datetime objects:
.. code-block:: python
def datetime_format(value, format="%H:%M %d-%m-%y"):
return value.strftime(format)
environment.filters["datetime_format"] = datetime_format
Now it can be used in templates:
.. sourcecode:: jinja
{{ article.pub_date|datetime_format }}
{{ article.pub_date|datetime_format("%B %Y") }}
Some decorators are available to tell Jinja to pass extra information to
the filter. The object is passed as the first argument, making the value
being filtered the second argument.
- :func:`pass_environment` passes the :class:`Environment`.
- :func:`pass_eval_context` passes the :ref:`eval-context`.
- :func:`pass_context` passes the current
:class:`~jinja2.runtime.Context`.
Here's a filter that converts line breaks into HTML ``<br>`` and ``<p>``
tags. It uses the eval context to check if autoescape is currently
enabled before escaping the input and marking the output safe.
.. code-block:: python
import re
from jinja2 import pass_eval_context
from markupsafe import Markup, escape
@pass_eval_context
def nl2br(eval_ctx, value):
br = "<br>\n"
if eval_ctx.autoescape:
value = escape(value)
br = Markup(br)
result = "\n\n".join(
f"<p>{br.join(p.splitlines())}</p>"
for p in re.split(r"(?:\r\n|\r(?!\n)|\n){2,}", value)
)
return Markup(result) if eval_ctx.autoescape else result
.. _writing-tests:
Custom Tests
------------
Test are Python functions that take the value to the left of the test as
the first argument, and return ``True`` or ``False``. Arguments passed
to the test are passed after the value.
For example, the test ``{{ 42 is even }}`` is called behind the scenes
as ``is_even(42)``.
Jinja comes with some :ref:`built-in tests <builtin-tests>`. To use a
custom tests, write a function that takes at least a ``value`` argument,
then register it in :attr:`Environment.tests`.
Here's a test that checks if a value is a prime number:
.. code-block:: python
import math
def is_prime(n):
if n == 2:
return True
for i in range(2, int(math.ceil(math.sqrt(n))) + 1):
if n % i == 0:
return False
return True
environment.tests["prime"] = is_prime
Now it can be used in templates:
.. sourcecode:: jinja
{% if value is prime %}
{{ value }} is a prime number
{% else %}
{{ value }} is not a prime number
{% endif %}
Some decorators are available to tell Jinja to pass extra information to
the test. The object is passed as the first argument, making the value
being tested the second argument.
- :func:`pass_environment` passes the :class:`Environment`.
- :func:`pass_eval_context` passes the :ref:`eval-context`.
- :func:`pass_context` passes the current
:class:`~jinja2.runtime.Context`.
.. _eval-context:
Evaluation Context
------------------
The evaluation context (short eval context or eval ctx) makes it
possible to activate and deactivate compiled features at runtime.
Currently it is only used to enable and disable automatic escaping, but
it can be used by extensions as well.
The ``autoescape`` setting should be checked on the evaluation context,
not the environment. The evaluation context will have the computed value
for the current template.
Instead of ``pass_environment``:
.. code-block:: python
@pass_environment
def filter(env, value):
result = do_something(value)
if env.autoescape:
result = Markup(result)
return result
Use ``pass_eval_context`` if you only need the setting:
.. code-block:: python
@pass_eval_context
def filter(eval_ctx, value):
result = do_something(value)
if eval_ctx.autoescape:
result = Markup(result)
return result
Or use ``pass_context`` if you need other context behavior as well:
.. code-block:: python
@pass_context
def filter(context, value):
result = do_something(value)
if context.eval_ctx.autoescape:
result = Markup(result)
return result
The evaluation context must not be modified at runtime. Modifications
must only happen with a :class:`nodes.EvalContextModifier` and
:class:`nodes.ScopedEvalContextModifier` from an extension, not on the
eval context object itself.
.. autoclass:: jinja2.nodes.EvalContext
.. attribute:: autoescape
`True` or `False` depending on if autoescaping is active or not.
.. attribute:: volatile
`True` if the compiler cannot evaluate some expressions at compile
time. At runtime this should always be `False`.
.. _global-namespace:
The Global Namespace
--------------------
The global namespace stores variables and functions that should be
available without needing to pass them to :meth:`Template.render`. They
are also available to templates that are imported or included without
context. Most applications should only use :attr:`Environment.globals`.
:attr:`Environment.globals` are intended for data that is common to all
templates loaded by that environment. :attr:`Template.globals` are
intended for data that is common to all renders of that template, and
default to :attr:`Environment.globals` unless they're given in
:meth:`Environment.get_template`, etc. Data that is specific to a
render should be passed as context to :meth:`Template.render`.
Only one set of globals is used during any specific rendering. If
templates A and B both have template globals, and B extends A, then
only B's globals are used for both when using ``b.render()``.
Environment globals should not be changed after loading any templates,
and template globals should not be changed at any time after loading the
template. Changing globals after loading a template will result in
unexpected behavior as they may be shared between the environment and
other templates.
.. _low-level-api:
Low Level API
-------------
The low level API exposes functionality that can be useful to understand some
implementation details, debugging purposes or advanced :ref:`extension
<jinja-extensions>` techniques. Unless you know exactly what you are doing we
don't recommend using any of those.
.. automethod:: Environment.lex
.. automethod:: Environment.parse
.. automethod:: Environment.preprocess
.. automethod:: Template.new_context
.. method:: Template.root_render_func(context)
This is the low level render function. It's passed a :class:`Context`
that has to be created by :meth:`new_context` of the same template or
a compatible template. This render function is generated by the
compiler from the template code and returns a generator that yields
strings.
If an exception in the template code happens the template engine will
not rewrite the exception but pass through the original one. As a
matter of fact this function should only be called from within a
:meth:`render` / :meth:`generate` / :meth:`stream` call.
.. attribute:: Template.blocks
A dict of block render functions. Each of these functions works exactly
like the :meth:`root_render_func` with the same limitations.
.. attribute:: Template.is_up_to_date
This attribute is `False` if there is a newer version of the template
available, otherwise `True`.
.. admonition:: Note
The low-level API is fragile. Future Jinja versions will try not to
change it in a backwards incompatible way but modifications in the Jinja
core may shine through. For example if Jinja introduces a new AST node
in later versions that may be returned by :meth:`~Environment.parse`.
The Meta API
------------
.. versionadded:: 2.2
The meta API returns some information about abstract syntax trees that
could help applications to implement more advanced template concepts. All
the functions of the meta API operate on an abstract syntax tree as
returned by the :meth:`Environment.parse` method.
.. autofunction:: jinja2.meta.find_undeclared_variables
.. autofunction:: jinja2.meta.find_referenced_templates
================================================
FILE: docs/changes.rst
================================================
Changes
=======
.. include:: ../CHANGES.rst
================================================
FILE: docs/conf.py
================================================
from pallets_sphinx_themes import get_version
from pallets_sphinx_themes import ProjectLink
# Project --------------------------------------------------------------
project = "Jinja"
copyright = "2007 Pallets"
author = "Pallets"
release, version = get_version("Jinja2")
# General --------------------------------------------------------------
default_role = "code"
extensions = [
"sphinx.ext.autodoc",
"sphinx.ext.extlinks",
"sphinx.ext.intersphinx",
"sphinxcontrib.log_cabinet",
"pallets_sphinx_themes",
]
autodoc_member_order = "bysource"
autodoc_typehints = "description"
autodoc_preserve_defaults = True
extlinks = {
"issue": ("https://github.com/pallets/jinja/issues/%s", "#%s"),
"pr": ("https://github.com/pallets/jinja/pull/%s", "#%s"),
"ghsa": ("https://github.com/pallets/jinja/security/advisories/GHSA-%s", "GHSA-%s"),
}
intersphinx_mapping = {
"python": ("https://docs.python.org/3/", None),
}
# HTML -----------------------------------------------------------------
html_theme = "jinja"
html_theme_options = {"index_sidebar_logo": False}
html_context = {
"project_links": [
ProjectLink("Donate", "https://palletsprojects.com/donate"),
ProjectLink("PyPI Releases", "https://pypi.org/project/Jinja2/"),
ProjectLink("Source Code", "https://github.com/pallets/jinja/"),
ProjectLink("Issue Tracker", "https://github.com/pallets/jinja/issues/"),
ProjectLink("Chat", "https://discord.gg/pallets"),
]
}
html_sidebars = {
"index": ["project.html", "localtoc.html", "searchbox.html", "ethicalads.html"],
"**": ["localtoc.html", "relations.html", "searchbox.html", "ethicalads.html"],
}
singlehtml_sidebars = {"index": ["project.html", "localtoc.html", "ethicalads.html"]}
html_static_path = ["_static"]
html_favicon = "_static/jinja-icon.svg"
html_logo = "_static/jinja-logo.svg"
html_title = f"Jinja Documentation ({version})"
html_show_sourcelink = False
================================================
FILE: docs/examples/cache_extension.py
================================================
from jinja2 import nodes
from jinja2.ext import Extension
class FragmentCacheExtension(Extension):
# a set of names that trigger the extension.
tags = {"cache"}
def __init__(self, environment):
super().__init__(environment)
# add the defaults to the environment
environment.extend(fragment_cache_prefix="", fragment_cache=None)
def parse(self, parser):
# the first token is the token that started the tag. In our case
# we only listen to ``'cache'`` so this will be a name token with
# `cache` as value. We get the line number so that we can give
# that line number to the nodes we create by hand.
lineno = next(parser.stream).lineno
# now we parse a single expression that is used as cache key.
args = [parser.parse_expression()]
# if there is a comma, the user provided a timeout. If not use
# None as second parameter.
if parser.stream.skip_if("comma"):
args.append(parser.parse_expression())
else:
args.append(nodes.Const(None))
# now we parse the body of the cache block up to `endcache` and
# drop the needle (which would always be `endcache` in that case)
body = parser.parse_statements(["name:endcache"], drop_needle=True)
# now return a `CallBlock` node that calls our _cache_support
# helper method on this extension.
return nodes.CallBlock(
self.call_method("_cache_support", args), [], [], body
).set_lineno(lineno)
def _cache_support(self, name, timeout, caller):
"""Helper callback."""
key = self.environment.fragment_cache_prefix + name
# try to load the block from the cache
# if there is no fragment in the cache, render it and store
# it in the cache.
rv = self.environment.fragment_cache.get(key)
if rv is not None:
return rv
rv = caller()
self.environment.fragment_cache.add(key, rv, timeout)
return rv
================================================
FILE: docs/examples/inline_gettext_extension.py
================================================
import re
from jinja2.exceptions import TemplateSyntaxError
from jinja2.ext import Extension
from jinja2.lexer import count_newlines
from jinja2.lexer import Token
_outside_re = re.compile(r"\\?(gettext|_)\(")
_inside_re = re.compile(r"\\?[()]")
class InlineGettext(Extension):
"""This extension implements support for inline gettext blocks::
<h1>_(Welcome)</h1>
<p>_(This is a paragraph)</p>
Requires the i18n extension to be loaded and configured.
"""
def filter_stream(self, stream):
paren_stack = 0
for token in stream:
if token.type != "data":
yield token
continue
pos = 0
lineno = token.lineno
while True:
if not paren_stack:
match = _outside_re.search(token.value, pos)
else:
match = _inside_re.search(token.value, pos)
if match is None:
break
new_pos = match.start()
if new_pos > pos:
preval = token.value[pos:new_pos]
yield Token(lineno, "data", preval)
lineno += count_newlines(preval)
gtok = match.group()
if gtok[0] == "\\":
yield Token(lineno, "data", gtok[1:])
elif not paren_stack:
yield Token(lineno, "block_begin", None)
yield Token(lineno, "name", "trans")
yield Token(lineno, "block_end", None)
paren_stack = 1
else:
if gtok == "(" or paren_stack > 1:
yield Token(lineno, "data", gtok)
paren_stack += -1 if gtok == ")" else 1
if not paren_stack:
yield Token(lineno, "block_begin", None)
yield Token(lineno, "name", "endtrans")
yield Token(lineno, "block_end", None)
pos = match.end()
if pos < len(token.value):
yield Token(lineno, "data", token.value[pos:])
if paren_stack:
raise TemplateSyntaxError(
"unclosed gettext expression",
token.lineno,
stream.name,
stream.filename,
)
================================================
FILE: docs/extensions.rst
================================================
.. _jinja-extensions:
Extensions
==========
Jinja supports extensions that can add extra filters, tests, globals or even
extend the parser. The main motivation of extensions is to move often used
code into a reusable class like adding support for internationalization.
Adding Extensions
-----------------
Extensions are added to the Jinja environment at creation time. To add an
extension pass a list of extension classes or import paths to the
``extensions`` parameter of the :class:`~jinja2.Environment` constructor. The following
example creates a Jinja environment with the i18n extension loaded::
jinja_env = Environment(extensions=['jinja2.ext.i18n'])
To add extensions after creation time, use the :meth:`~jinja2.Environment.add_extension` method::
jinja_env.add_extension('jinja2.ext.debug')
.. _i18n-extension:
i18n Extension
--------------
**Import name:** ``jinja2.ext.i18n``
The i18n extension can be used in combination with `gettext`_ or
`Babel`_. When it's enabled, Jinja provides a ``trans`` statement that
marks a block as translatable and calls ``gettext``.
After enabling, an application has to provide functions for ``gettext``,
``ngettext``, and optionally ``pgettext`` and ``npgettext``, either
globally or when rendering. A ``_()`` function is added as an alias to
the ``gettext`` function.
A convenient way to provide these functions is to call one of the below
methods depending on the translation system in use. If you do not require
actual translation, use ``Environment.install_null_translations`` to
install no-op functions.
Environment Methods
~~~~~~~~~~~~~~~~~~~
After enabling the extension, the environment provides the following
additional methods:
.. method:: jinja2.Environment.install_gettext_translations(translations, newstyle=False)
Installs a translation globally for the environment. The
``translations`` object must implement ``gettext``, ``ngettext``,
and optionally ``pgettext`` and ``npgettext``.
:class:`gettext.NullTranslations`, :class:`gettext.GNUTranslations`,
and `Babel`_\s ``Translations`` are supported.
.. versionchanged:: 3.0
Added ``pgettext`` and ``npgettext``.
.. versionchanged:: 2.5
Added new-style gettext support.
.. method:: jinja2.Environment.install_null_translations(newstyle=False)
Install no-op gettext functions. This is useful if you want to
prepare the application for internationalization but don't want to
implement the full system yet.
.. versionchanged:: 2.5 Added new-style gettext support.
.. method:: jinja2.Environment.install_gettext_callables(gettext, ngettext, newstyle=False, pgettext=None, npgettext=None)
Install the given ``gettext``, ``ngettext``, ``pgettext``, and
``npgettext`` callables into the environment. They should behave
exactly like :func:`gettext.gettext`, :func:`gettext.ngettext`,
:func:`gettext.pgettext` and :func:`gettext.npgettext`.
If ``newstyle`` is activated, the callables are wrapped to work like
newstyle callables. See :ref:`newstyle-gettext` for more information.
.. versionchanged:: 3.0
Added ``pgettext`` and ``npgettext``.
.. versionadded:: 2.5
Added new-style gettext support.
.. method:: jinja2.Environment.uninstall_gettext_translations()
Uninstall the environment's globally installed translation.
.. method:: jinja2.Environment.extract_translations(source)
Extract localizable strings from the given template node or source.
For every string found this function yields a ``(lineno, function,
message)`` tuple, where:
- ``lineno`` is the number of the line on which the string was
found.
- ``function`` is the name of the ``gettext`` function used (if
the string was extracted from embedded Python code).
- ``message`` is the string itself, or a tuple of strings for
functions with multiple arguments.
If `Babel`_ is installed, see :ref:`babel-integration` to extract
the strings.
For a web application that is available in multiple languages but gives
all the users the same language (for example, multilingual forum
software installed for a French community), the translation may be
installed when the environment is created.
.. code-block:: python
translations = get_gettext_translations()
env = Environment(extensions=["jinja2.ext.i18n"])
env.install_gettext_translations(translations)
The ``get_gettext_translations`` function would return the translator
for the current configuration, for example by using ``gettext.find``.
The usage of the ``i18n`` extension for template designers is covered in
:ref:`the template documentation <i18n-in-templates>`.
.. _gettext: https://docs.python.org/3/library/gettext.html
.. _Babel: https://babel.pocoo.org/
Whitespace Trimming
~~~~~~~~~~~~~~~~~~~
.. versionadded:: 2.10
Within ``{% trans %}`` blocks, it can be useful to trim line breaks and
whitespace so that the block of text looks like a simple string with
single spaces in the translation file.
Linebreaks and surrounding whitespace can be automatically trimmed by
enabling the ``ext.i18n.trimmed`` :ref:`policy <ext-i18n-trimmed>`.
.. _newstyle-gettext:
New Style Gettext
~~~~~~~~~~~~~~~~~
.. versionadded:: 2.5
New style gettext calls are less to type, less error prone, and support
autoescaping better.
You can use "new style" gettext calls by setting
``env.newstyle_gettext = True`` or passing ``newstyle=True`` to
``env.install_translations``. They are fully supported by the Babel
extraction tool, but might not work as expected with other extraction
tools.
With standard ``gettext`` calls, string formatting is a separate step
done with the ``|format`` filter. This requires duplicating work for
``ngettext`` calls.
.. sourcecode:: jinja
{{ gettext("Hello, World!") }}
{{ gettext("Hello, %(name)s!")|format(name=name) }}
{{ ngettext(
"%(num)d apple", "%(num)d apples", apples|count
)|format(num=apples|count) }}
{{ pgettext("greeting", "Hello, World!") }}
{{ npgettext(
"fruit", "%(num)d apple", "%(num)d apples", apples|count
)|format(num=apples|count) }}
New style ``gettext`` make formatting part of the call, and behind the
scenes enforce more consistency.
.. sourcecode:: jinja
{{ gettext("Hello, World!") }}
{{ gettext("Hello, %(name)s!", name=name) }}
{{ ngettext("%(num)d apple", "%(num)d apples", apples|count) }}
{{ pgettext("greeting", "Hello, World!") }}
{{ npgettext("fruit", "%(num)d apple", "%(num)d apples", apples|count) }}
The advantages of newstyle gettext are:
- There's no separate formatting step, you don't have to remember to
use the ``|format`` filter.
- Only named placeholders are allowed. This solves a common problem
translators face because positional placeholders can't switch
positions meaningfully. Named placeholders always carry semantic
information about what value goes where.
- String formatting is used even if no placeholders are used, which
makes all strings use a consistent format. Remember to escape any
raw percent signs as ``%%``, such as ``100%%``.
- The translated string is marked safe, formatting performs escaping
as needed. Mark a parameter as ``|safe`` if it has already been
escaped.
Expression Statement
--------------------
**Import name:** ``jinja2.ext.do``
The "do" aka expression-statement extension adds a simple ``do`` tag to the
template engine that works like a variable expression but ignores the
return value.
.. _loopcontrols-extension:
Loop Controls
-------------
**Import name:** ``jinja2.ext.loopcontrols``
This extension adds support for ``break`` and ``continue`` in loops. After
enabling, Jinja provides those two keywords which work exactly like in
Python.
.. _with-extension:
With Statement
--------------
**Import name:** ``jinja2.ext.with_``
.. versionchanged:: 2.9
This extension is now built-in and no longer does anything.
.. _autoescape-extension:
Autoescape Extension
--------------------
**Import name:** ``jinja2.ext.autoescape``
.. versionchanged:: 2.9
This extension was removed and is now built-in. Enabling the
extension no longer does anything.
.. _debug-extension:
Debug Extension
---------------
**Import name:** ``jinja2.ext.debug``
Adds a ``{% debug %}`` tag to dump the current context as well as the
available filters and tests. This is useful to see what's available to
use in the template without setting up a debugger.
.. _writing-extensions:
Writing Extensions
------------------
.. module:: jinja2.ext
By writing extensions you can add custom tags to Jinja. This is a non-trivial
task and usually not needed as the default tags and expressions cover all
common use cases. The i18n extension is a good example of why extensions are
useful. Another one would be fragment caching.
When writing extensions you have to keep in mind that you are working with the
Jinja template compiler which does not validate the node tree you are passing
to it. If the AST is malformed you will get all kinds of compiler or runtime
errors that are horrible to debug. Always make sure you are using the nodes
you create correctly. The API documentation below shows which nodes exist and
how to use them.
Example Extensions
------------------
Cache
~~~~~
The following example implements a ``cache`` tag for Jinja by using the
`cachelib`_ library:
.. literalinclude:: examples/cache_extension.py
:language: python
And here is how you use it in an environment::
from jinja2 import Environment
from cachelib import SimpleCache
env = Environment(extensions=[FragmentCacheExtension])
env.fragment_cache = SimpleCache()
Inside the template it's then possible to mark blocks as cacheable. The
following example caches a sidebar for 300 seconds:
.. sourcecode:: html+jinja
{% cache 'sidebar', 300 %}
<div class="sidebar">
...
</div>
{% endcache %}
.. _cachelib: https://github.com/pallets/cachelib
Inline ``gettext``
~~~~~~~~~~~~~~~~~~
The following example demonstrates using :meth:`Extension.filter_stream`
to parse calls to the ``_()`` gettext function inline with static data
without needing Jinja blocks.
.. code-block:: html
<h1>_(Welcome)</h1>
<p>_(This is a paragraph)</p>
It requires the i18n extension to be loaded and configured.
.. literalinclude:: examples/inline_gettext_extension.py
:language: python
Extension API
-------------
Extension
~~~~~~~~~
Extensions always have to extend the :class:`jinja2.ext.Extension` class:
.. autoclass:: Extension
:members: preprocess, filter_stream, parse, attr, call_method
.. attribute:: identifier
The identifier of the extension. This is always the true import name
of the extension class and must not be changed.
.. attribute:: tags
If the extension implements custom tags this is a set of tag names
the extension is listening for.
Parser
~~~~~~
The parser passed to :meth:`Extension.parse` provides ways to parse
expressions of different types. The following methods may be used by
extensions:
.. autoclass:: jinja2.parser.Parser
:members: parse_expression, parse_tuple, parse_assign_target,
parse_statements, free_identifier, fail
.. attribute:: filename
The filename of the template the parser processes. This is **not**
the load name of the template. For the load name see :attr:`name`.
For templates that were not loaded form the file system this is
``None``.
.. attribute:: name
The load name of the template.
.. attribute:: stream
The current :class:`~jinja2.lexer.TokenStream`
.. autoclass:: jinja2.lexer.TokenStream
:members: push, look, eos, skip, __next__, next_if, skip_if, expect
.. attribute:: current
The current :class:`~jinja2.lexer.Token`.
.. autoclass:: jinja2.lexer.Token
:members: test, test_any
.. attribute:: lineno
The line number of the token
.. attribute:: type
The type of the token. This string is interned so you may compare
it with arbitrary strings using the ``is`` operator.
.. attribute:: value
The value of the token.
There is also a utility function in the lexer module that can count newline
characters in strings:
.. autofunction:: jinja2.lexer.count_newlines
AST
~~~
The AST (Abstract Syntax Tree) is used to represent a template after parsing.
It's build of nodes that the compiler then converts into executable Python
code objects. Extensions that provide custom statements can return nodes to
execute custom Python code.
The list below describes all nodes that are currently available. The AST may
change between Jinja versions but will stay backwards compatible.
For more information have a look at the repr of :meth:`jinja2.Environment.parse`.
.. module:: jinja2.nodes
.. jinja:nodes:: jinja2.nodes.Node
.. autoexception:: Impossible
================================================
FILE: docs/faq.rst
================================================
Frequently Asked Questions
==========================
Why is it called Jinja?
-----------------------
"Jinja" is a Japanese `Shinto shrine`_, or temple, and temple and
template share a similar English pronunciation. It is not named after
the `city in Uganda`_.
.. _Shinto shrine: https://en.wikipedia.org/wiki/Shinto_shrine
.. _city in Uganda: https://en.wikipedia.org/wiki/Jinja%2C_Uganda
How fast is Jinja?
------------------
Jinja is relatively fast among template engines because it compiles and
caches template code to Python code, so that the template does not need
to be parsed and interpreted each time. Rendering a template becomes as
close to executing a Python function as possible.
Jinja also makes extensive use of caching. Templates are cached by name
after loading, so future uses of the template avoid loading. The
template loading itself uses a bytecode cache to avoid repeated
compiling. The caches can be external to persist across restarts.
Templates can also be precompiled and loaded as fast Python imports.
We dislike benchmarks because they don't reflect real use. Performance
depends on many factors. Different engines have different default
configurations and tradeoffs that make it unclear how to set up a useful
comparison. Often, database access, API calls, and data processing have
a much larger effect on performance than the template engine.
Isn't it a bad idea to put logic in templates?
----------------------------------------------
Without a doubt you should try to remove as much logic from templates as
possible. With less logic, the template is easier to understand, has
fewer potential side effects, and is faster to compile and render. But a
template without any logic means processing must be done in code before
rendering. A template engine that does that is shipped with Python,
called :class:`string.Template`, and while it's definitely fast it's not
convenient.
Jinja's features such as blocks, statements, filters, and function calls
make it much easier to write expressive templates, with very few
restrictions. Jinja doesn't allow arbitrary Python code in templates, or
every feature available in the Python language. This keeps the engine
easier to maintain, and keeps templates more readable.
Some amount of logic is required in templates to keep everyone happy.
Too much logic in the template can make it complex to reason about and
maintain. It's up to you to decide how your application will work and
balance how much logic you want to put in the template.
Why is HTML escaping not the default?
-------------------------------------
Jinja provides a feature that can be enabled to escape HTML syntax in
rendered templates. However, it is disabled by default.
Jinja is a general purpose template engine, it is not only used for HTML
documents. You can generate plain text, LaTeX, emails, CSS, JavaScript,
configuration files, etc. HTML escaping wouldn't make sense for any of
these document types.
While automatic escaping means that you are less likely have an XSS
problem, it also requires significant extra processing during compiling
and rendering, which can reduce performance. Jinja uses `MarkupSafe`_ for
escaping, which provides optimized C code for speed, but it still
introduces overhead to track escaping across methods and formatting.
.. _MarkupSafe: https://markupsafe.palletsprojects.com/
================================================
FILE: docs/index.rst
================================================
.. rst-class:: hide-header
Jinja
=====
.. image:: _static/jinja-name.svg
:align: center
:height: 200px
Jinja is a fast, expressive, extensible templating engine. Special
placeholders in the template allow writing code similar to Python
syntax. Then the template is passed data to render the final document.
.. toctree::
:maxdepth: 2
:caption: Contents:
intro
api
sandbox
nativetypes
templates
extensions
integration
switching
tricks
faq
license
changes
================================================
FILE: docs/integration.rst
================================================
Integration
===========
Flask
-----
The `Flask`_ web application framework, also maintained by Pallets, uses
Jinja templates by default. Flask sets up a Jinja environment and
template loader for you, and provides functions to easily render
templates from view functions.
.. _Flask: https://flask.palletsprojects.com
Django
------
Django supports using Jinja as its template engine, see
https://docs.djangoproject.com/en/stable/topics/templates/#support-for-template-engines.
.. _babel-integration:
Babel
-----
Jinja provides support for extracting gettext messages from templates
via a `Babel`_ extractor entry point called
``jinja2.ext.babel_extract``. The support is implemented as part of the
:ref:`i18n-extension` extension.
Gettext messages are extracted from both ``trans`` tags and code
expressions.
To extract gettext messages from templates, the project needs a Jinja
section in its Babel extraction method `mapping file`_:
.. sourcecode:: ini
[jinja2: **/templates/**.html]
encoding = utf-8
The syntax related options of the :class:`Environment` are also
available as configuration values in the mapping file. For example, to
tell the extractor that templates use ``%`` as
``line_statement_prefix`` you can use this code:
.. sourcecode:: ini
[jinja2: **/templates/**.html]
encoding = utf-8
line_statement_prefix = %
:ref:`jinja-extensions` may also be defined by passing a comma separated
list of import paths as the ``extensions`` value. The i18n extension is
added automatically.
Template syntax errors are ignored by default. The assumption is that
tests will catch syntax errors in templates. If you don't want to ignore
errors, add ``silent = false`` to the settings.
.. _Babel: https://babel.readthedocs.io/
.. _mapping file: https://babel.readthedocs.io/en/latest/messages.html#extraction-method-mapping-and-configuration
Pylons
------
It's easy to integrate Jinja into a `Pylons`_ application.
The template engine is configured in ``config/environment.py``. The
configuration for Jinja looks something like this:
.. code-block:: python
from jinja2 import Environment, PackageLoader
config['pylons.app_globals'].jinja_env = Environment(
loader=PackageLoader('yourapplication', 'templates')
)
After that you can render Jinja templates by using the ``render_jinja``
function from the ``pylons.templating`` module.
Additionally it's a good idea to set the Pylons ``c`` object to strict
mode. By default attribute access on missing attributes on the ``c``
object returns an empty string and not an undefined object. To change
this add this to ``config/environment.py``:
.. code-block:: python
config['pylons.strict_c'] = True
.. _Pylons: https://pylonsproject.org/
================================================
FILE: docs/intro.rst
================================================
Introduction
============
Jinja is a fast, expressive, extensible templating engine. Special
placeholders in the template allow writing code similar to Python
syntax. Then the template is passed data to render the final document.
It includes:
- Template inheritance and inclusion.
- Define and import macros within templates.
- HTML templates can use autoescaping to prevent XSS from untrusted
user input.
- A sandboxed environment can safely render untrusted templates.
- Async support for generating templates that automatically handle
sync and async functions without extra syntax.
- I18N support with Babel.
- Templates are compiled to optimized Python code just-in-time and
cached, or can be compiled ahead-of-time.
- Exceptions point to the correct line in templates to make debugging
easier.
- Extensible filters, tests, functions, and even syntax.
Jinja's philosophy is that while application logic belongs in Python if
possible, it shouldn't make the template designer's job difficult by
restricting functionality too much.
Installation
------------
We recommend using the latest version of Python. Jinja supports Python
3.10 and newer. We also recommend using a `virtual environment`_ in order
to isolate your project dependencies from other projects and the system.
.. _virtual environment: https://packaging.python.org/tutorials/installing-packages/#creating-virtual-environments
Install the most recent Jinja version using pip:
.. code-block:: text
$ pip install Jinja2
Dependencies
~~~~~~~~~~~~
These will be installed automatically when installing Jinja.
- `MarkupSafe`_ escapes untrusted input when rendering templates to
avoid injection attacks.
.. _MarkupSafe: https://markupsafe.palletsprojects.com/
Optional Dependencies
~~~~~~~~~~~~~~~~~~~~~
These distributions will not be installed automatically.
- `Babel`_ provides translation support in templates.
.. _Babel: https://babel.pocoo.org/
================================================
FILE: docs/license.rst
================================================
BSD-3-Clause License
====================
.. literalinclude:: ../LICENSE.txt
:language: text
================================================
FILE: docs/make.bat
================================================
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=.
set BUILDDIR=_build
if "%1" == "" goto help
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.https://www.sphinx-doc.org/
exit /b 1
)
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
:end
popd
================================================
FILE: docs/nativetypes.rst
================================================
.. module:: jinja2.nativetypes
.. _nativetypes:
Native Python Types
===================
The default :class:`~jinja2.Environment` renders templates to strings. With
:class:`NativeEnvironment`, rendering a template produces a native Python type.
This is useful if you are using Jinja outside the context of creating text
files. For example, your code may have an intermediate step where users may use
templates to define values that will then be passed to a traditional string
environment.
Examples
--------
Adding two values results in an integer, not a string with a number:
>>> env = NativeEnvironment()
>>> t = env.from_string('{{ x + y }}')
>>> result = t.render(x=4, y=2)
>>> print(result)
6
>>> print(type(result))
int
Rendering list syntax produces a list:
>>> t = env.from_string('[{% for item in data %}{{ item + 1 }},{% endfor %}]')
>>> result = t.render(data=range(5))
>>> print(result)
[1, 2, 3, 4, 5]
>>> print(type(result))
list
Rendering something that doesn't look like a Python literal produces a string:
>>> t = env.from_string('{{ x }} * {{ y }}')
>>> result = t.render(x=4, y=2)
>>> print(result)
4 * 2
>>> print(type(result))
str
Rendering a Python object produces that object as long as it is the only node:
>>> class Foo:
... def __init__(self, value):
... self.value = value
...
>>> result = env.from_string('{{ x }}').render(x=Foo(15))
>>> print(type(result).__name__)
Foo
>>> print(result.value)
15
Sandboxed Native Environment
----------------------------
You can combine :class:`.SandboxedEnvironment` and :class:`NativeEnvironment` to
get both behaviors.
.. code-block:: python
class SandboxedNativeEnvironment(SandboxedEnvironment, NativeEnvironment):
pass
API
---
.. autoclass:: NativeEnvironment([options])
.. autoclass:: NativeTemplate([options])
:members: render
================================================
FILE: docs/sandbox.rst
================================================
Sandbox
=======
The Jinja sandbox can be used to render untrusted templates. Access to
attributes, method calls, operators, mutating data structures, and
string formatting can be intercepted and prohibited.
.. code-block:: pycon
>>> from jinja2.sandbox import SandboxedEnvironment
>>> env = SandboxedEnvironment()
>>> func = lambda: "Hello, Sandbox!"
>>> env.from_string("{{ func() }}").render(func=func)
'Hello, Sandbox!'
>>> env.from_string("{{ func.__code__.co_code }}").render(func=func)
Traceback (most recent call last):
...
SecurityError: access to attribute '__code__' of 'function' object is unsafe.
A sandboxed environment can be useful, for example, to allow users of an
internal reporting system to create custom emails. You would document
what data is available in the templates, then the user would write a
template using that information. Your code would generate the report
data and pass it to the user's sandboxed template to render.
Security Considerations
-----------------------
The sandbox alone is not a solution for perfect security. Keep these
things in mind when using the sandbox.
Templates can still raise errors when compiled or rendered. Your code
should attempt to catch errors instead of crashing.
It is possible to construct a relatively small template that renders to
a very large amount of output, which could correspond to a high use of
CPU or memory. You should run your application with limits on resources
such as CPU and memory to mitigate this.
Jinja only renders text, it does not understand, for example, JavaScript
code. Depending on how the rendered template will be used, you may need
to do other postprocessing to restrict the output.
Pass only the data that is relevant to the template. Avoid passing
global data, or objects with methods that have side effects. By default
the sandbox prevents private and internal attribute access. You can
override :meth:`~SandboxedEnvironment.is_safe_attribute` to further
restrict attributes access. Decorate methods with :func:`unsafe` to
prevent calling them from templates when passing objects as data. Use
:class:`ImmutableSandboxedEnvironment` to prevent modifying lists and
dictionaries.
API
---
.. module:: jinja2.sandbox
.. autoclass:: SandboxedEnvironment([options])
:members: is_safe_attribute, is_safe_callable, default_binop_table,
default_unop_table, intercepted_binops, intercepted_unops,
call_binop, call_unop
.. autoclass:: ImmutableSandboxedEnvironment([options])
.. autoexception:: SecurityError
.. autofunction:: unsafe
.. autofunction:: is_internal_attribute
.. autofunction:: modifies_known_mutable
Operator Intercepting
---------------------
For performance, Jinja outputs operators directly when compiling. This
means it's not possible to intercept operator behavior by overriding
:meth:`SandboxEnvironment.call <Environment.call>` by default, because
operator special methods are handled by the Python interpreter, and
might not correspond with exactly one method depending on the operator's
use.
The sandbox can instruct the compiler to output a function to intercept
certain operators instead. Override
:attr:`SandboxedEnvironment.intercepted_binops` and
:attr:`SandboxedEnvironment.intercepted_unops` with the operator symbols
you want to intercept. The compiler will replace the symbols with calls
to :meth:`SandboxedEnvironment.call_binop` and
:meth:`SandboxedEnvironment.call_unop` instead. The default
implementation of those methods will use
:attr:`SandboxedEnvironment.binop_table` and
:attr:`SandboxedEnvironment.unop_table` to translate operator symbols
into :mod:`operator` functions.
For example, the power (``**``) operator can be disabled:
.. code-block:: python
from jinja2.sandbox import SandboxedEnvironment
class MyEnvironment(SandboxedEnvironment):
intercepted_binops = frozenset(["**"])
def call_binop(self, context, operator, left, right):
if operator == "**":
return self.undefined("The power (**) operator is unavailable.")
return super().call_binop(self, context, operator, left, right)
================================================
FILE: docs/switching.rst
================================================
Switching From Other Template Engines
=====================================
This is a brief guide on some of the differences between Jinja syntax
and other template languages. See :doc:`/templates` for a comprehensive
guide to Jinja syntax and features.
Django
------
If you have previously worked with Django templates, you should find
Jinja very familiar. Many of the syntax elements look and work the same.
However, Jinja provides some more syntax elements, and some work a bit
differently.
This section covers the template changes. The API, including extension
support, is fundamentally different so it won't be covered here.
Django supports using Jinja as its template engine, see
https://docs.djangoproject.com/en/stable/topics/templates/#support-for-template-engines.
Method Calls
~~~~~~~~~~~~
In Django, methods are called implicitly, without parentheses.
.. code-block:: django
{% for page in user.get_created_pages %}
...
{% endfor %}
In Jinja, using parentheses is required for calls, like in Python. This
allows you to pass variables to the method, which is not possible
in Django. This syntax is also used for calling macros.
.. code-block:: jinja
{% for page in user.get_created_pages() %}
...
{% endfor %}
Filter Arguments
~~~~~~~~~~~~~~~~
In Django, one literal value can be passed to a filter after a colon.
.. code-block:: django
{{ items|join:", " }}
In Jinja, filters can take any number of positional and keyword
arguments in parentheses, like function calls. Arguments can also be
variables instead of literal values.
.. code-block:: jinja
{{ items|join(", ") }}
Tests
~~~~~
In addition to filters, Jinja also has "tests" used with the ``is``
operator. This operator is not the same as the Python operator.
.. code-block:: jinja
{% if user.user_id is odd %}
{{ user.username|e }} is odd
{% else %}
hmm. {{ user.username|e }} looks pretty normal
{% endif %}
Loops
~~~~~
In Django, the special variable for the loop context is called
``forloop``, and the ``empty`` is used for no loop items.
.. code-block:: django
{% for item in items %}
{{ forloop.counter }}. {{ item }}
{% empty %}
No items!
{% endfor %}
In Jinja, the special variable for the loop context is called ``loop``,
and the ``else`` block is used for no loop items.
.. code-block:: jinja
{% for item in items %}
{{ loop.index }}. {{ item }}
{% else %}
No items!
{% endfor %}
Cycle
~~~~~
In Django, the ``{% cycle %}`` can be used in a for loop to alternate
between values per loop.
.. code-block:: django
{% for user in users %}
<li class="{% cycle 'odd' 'even' %}">{{ user }}</li>
{% endfor %}
In Jinja, the ``loop`` context has a ``cycle`` method.
.. code-block:: jinja
{% for user in users %}
<li class="{{ loop.cycle('odd', 'even') }}">{{ user }}</li>
{% endfor %}
A cycler can also be assigned to a variable and used outside or across
loops with the ``cycle()`` global function.
Mako
----
You can configure Jinja to look more like Mako:
.. code-block:: python
env = Environment(
block_start_string="<%",
block_end_string="%>",
variable_start_string="${",
variable_end_string="}",
comment_start_string="<%doc>",
commend_end_string="</%doc>",
line_statement_prefix="%",
line_comment_prefix="##",
)
With an environment configured like that, Jinja should be able to
interpret a small subset of Mako templates without any changes.
Jinja does not support embedded Python code, so you would have to move
that out of the template. You could either process the data with the
same code before rendering, or add a global function or filter to the
Jinja environment.
The syntax for defs (which are called macros in Jinja) and template
inheritance is different too.
The following Mako template:
.. code-block:: mako
<%inherit file="layout.html" />
<%def name="title()">Page Title</%def>
<ul>
% for item in list:
<li>${item}</li>
% endfor
</ul>
Looks like this in Jinja with the above configuration:
.. code-block:: jinja
<% extends "layout.html" %>
<% block title %>Page Title<% endblock %>
<% block body %>
<ul>
% for item in list:
<li>${item}</li>
% endfor
</ul>
<% endblock %>
================================================
FILE: docs/templates.rst
================================================
.. py:currentmodule:: jinja2
.. highlight:: html+jinja
Template Designer Documentation
===============================
This document describes the syntax and semantics of the template engine and
will be most useful as reference to those creating Jinja templates. As the
template engine is very flexible, the configuration from the application can
be slightly different from the code presented here in terms of delimiters and
behavior of undefined values.
Synopsis
--------
A Jinja template is simply a text file. Jinja can generate any text-based
format (HTML, XML, CSV, LaTeX, etc.). A Jinja template doesn't need to have a
specific extension: ``.html``, ``.xml``, or any other extension is just fine.
A template contains **variables** and/or **expressions**, which get replaced
with values when a template is *rendered*; and **tags**, which control the
logic of the template. The template syntax is heavily inspired by Django and
Python.
Below is a minimal template that illustrates a few basics using the default
Jinja configuration. We will cover the details later in this document::
<!DOCTYPE html>
<html lang="en">
<head>
<title>My Webpage</title>
</head>
<body>
<ul id="navigation">
{% for item in navigation %}
<li><a href="{{ item.href }}">{{ item.caption }}</a></li>
{% endfor %}
</ul>
<h1>My Webpage</h1>
{{ a_variable }}
{# a comment #}
</body>
</html>
The following example shows the default configuration settings. An application
developer can change the syntax configuration from ``{% foo %}`` to ``<% foo
%>``, or something similar.
There are a few kinds of delimiters. The default Jinja delimiters are
configured as follows:
* ``{% ... %}`` for :ref:`Statements <list-of-control-structures>`
* ``{{ ... }}`` for :ref:`Expressions` to print to the template output
* ``{# ... #}`` for :ref:`Comments` not included in the template output
:ref:`Line Statements and Comments <line-statements>` are also possible,
though they don't have default prefix characters. To use them, set
``line_statement_prefix`` and ``line_comment_prefix`` when creating the
:class:`~jinja2.Environment`.
Template File Extension
~~~~~~~~~~~~~~~~~~~~~~~
As stated above, any file can be loaded as a template, regardless of
file extension. Adding a ``.jinja`` extension, like ``user.html.jinja``
may make it easier for some IDEs or editor plugins, but is not required.
Autoescaping, introduced later, can be applied based on file extension,
so you'll need to take the extra suffix into account in that case.
Another good heuristic for identifying templates is that they are in a
``templates`` folder, regardless of extension. This is a common layout
for projects.
.. _variables:
Variables
---------
Template variables are defined by the context dictionary passed to the
template.
You can mess around with the variables in templates provided they are passed in
by the application. Variables may have attributes or elements on them you can
access too. What attributes a variable has depends heavily on the application
providing that variable.
You can use a dot (``.``) to access attributes of a variable in addition
to the standard Python ``__getitem__`` "subscript" syntax (``[]``).
The following lines do the same thing::
{{ foo.bar }}
{{ foo['bar'] }}
It's important to know that the outer double-curly braces are *not* part of the
variable, but the print statement. If you access variables inside tags don't
put the braces around them.
If a variable or attribute does not exist, you will get back an undefined
value. What you can do with that kind of value depends on the application
configuration: the default behavior is to evaluate to an empty string if
printed or iterated over, and to fail for every other operation.
.. _notes-on-subscriptions:
.. admonition:: Implementation
For the sake of convenience, ``foo.bar`` in Jinja does the following
things on the Python layer:
- check for an attribute called `bar` on `foo`
(``getattr(foo, 'bar')``)
- if there is not, check for an item ``'bar'`` in `foo`
(``foo.__getitem__('bar')``)
- if there is not, return an undefined object.
``foo['bar']`` works mostly the same with a small difference in sequence:
- check for an item ``'bar'`` in `foo`.
(``foo.__getitem__('bar')``)
- if there is not, check for an attribute called `bar` on `foo`.
(``getattr(foo, 'bar')``)
- if there is not, return an undefined object.
This is important if an object has an item and attribute with the same
name. Additionally, the :func:`attr` filter only looks up attributes.
.. _filters:
Filters
-------
Variables can be modified by **filters**. Filters are separated from the
variable by a pipe symbol (``|``) and may have optional arguments in
parentheses. Multiple filters can be chained. The output of one filter is
applied to the next.
For example, ``{{ name|striptags|title }}`` will remove all HTML Tags from
variable `name` and title-case the output (``title(striptags(name))``).
Filters that accept arguments have parentheses around the arguments, just like
a function call. For example: ``{{ listx|join(', ') }}`` will join a list with
commas (``str.join(', ', listx)``).
The :ref:`builtin-filters` below describes all the builtin filters.
.. _tests:
Tests
-----
Beside filters, there are also so-called "tests" available. Tests can be used
to test a variable against a common expression. To test a variable or
expression, you add `is` plus the name of the test after the variable. For
example, to find out if a variable is defined, you can do ``name is defined``,
which will then return true or false depending on whether `name` is defined
in the current template context.
Tests can accept arguments, too. If the test only takes one argument, you can
leave out the parentheses. For example, the following two
expressions do the same thing::
{% if loop.index is divisibleby 3 %}
{% if loop.index is divisibleby(3) %}
The :ref:`builtin-tests` below describes all the builtin tests.
.. _comments:
Comments
--------
To comment-out part of a line in a template, use the comment syntax which is
by default set to ``{# ... #}``. This is useful to comment out parts of the
template for debugging or to add information for other template designers or
yourself::
{# note: commented-out template because we no longer use this
{% for user in users %}
...
{% endfor %}
#}
Whitespace Control
------------------
In the default configuration:
* a single trailing newline is stripped if present
* other whitespace (spaces, tabs, newlines etc.) is returned unchanged
If an application configures Jinja to `trim_blocks`, the first newline after a
template tag is removed automatically (like in PHP). The `lstrip_blocks`
option can also be set to strip tabs and spaces from the beginning of a
line to the start of a block. (Nothing will be stripped if there are
other characters before the start of the block.)
With both ``trim_blocks`` and ``lstrip_blocks`` disabled (the default), block
tags on their own lines will be removed, but a blank line will remain and the
spaces in the content will be preserved. For example, this template:
.. code-block:: jinja
<div>
{% if True %}
yay
{% endif %}
</div>
With both ``trim_blocks`` and ``lstrip_blocks`` disabled, the template is
rendered with blank lines inside the div:
.. code-block:: text
<div>
yay
</div>
With both ``trim_blocks`` and ``lstrip_blocks`` enabled, the template block
lines are completely removed:
.. code-block:: text
<div>
yay
</div>
You can manually disable the `lstrip_blocks` behavior by putting a
plus sign (``+``) at the start of a block::
<div>
{%+ if something %}yay{% endif %}
</div>
Similarly, you can manually disable the ``trim_blocks`` behavior by
putting a plus sign (``+``) at the end of a block::
<div>
{% if something +%}
yay
{% endif %}
</div>
You can also strip whitespace in templates by hand. If you add a minus
sign (``-``) to the start or end of a block (e.g. a :ref:`for-loop` tag), a
comment, or a variable expression, the whitespaces before or after
that block will be removed::
{% for item in seq -%}
{{ item }}
{%- endfor %}
This will yield all elements without whitespace between them. If `seq` was
a list of numbers from ``1`` to ``9``, the output would be ``123456789``.
If :ref:`line-statements` are enabled, they strip leading whitespace
automatically up to the beginning of the line.
By default, Jinja also removes trailing newlines. To keep single
trailing newlines, configure Jinja to `keep_trailing_newline`.
.. admonition:: Note
You must not add whitespace between the tag and the minus sign.
**valid**::
{%- if foo -%}...{% endif %}
**invalid**::
{% - if foo - %}...{% endif %}
Escaping
--------
It is sometimes desirable -- even necessary -- to have Jinja ignore parts
it would otherwise handle as variables or blocks. For example, if, with
the default syntax, you want to use ``{{`` as a raw string in a template and
not start a variable, you have to use a trick.
The easiest way to output a literal variable delimiter (``{{``) is by using a
variable expression::
{{ '{{' }}
For bigger sections, it makes sense to mark a block `raw`. For example, to
include example Jinja syntax in a template, you can use this snippet::
{% raw %}
<ul>
{% for item in seq %}
<li>{{ item }}</li>
{% endfor %}
</ul>
{% endraw %}
.. admonition:: Note
Minus sign at the end of ``{% raw -%}`` tag cleans all the spaces and newlines
preceding the first character of your raw data.
.. _line-statements:
Line Statements
---------------
If line statements are enabled by the application, it's possible to mark a
line as a statement. For example, if the line statement prefix is configured
to ``#``, the following two examples are equivalent::
<ul>
# for item in seq
<li>{{ item }}</li>
# endfor
</ul>
<ul>
{% for item in seq %}
<li>{{ item }}</li>
{% endfor %}
</ul>
The line statement prefix can appear anywhere on the line as long as no text
precedes it. For better readability, statements that start a block (such as
`for`, `if`, `elif` etc.) may end with a colon::
# for item in seq:
...
# endfor
.. admonition:: Note
Line statements can span multiple lines if there are open parentheses,
braces or brackets::
<ul>
# for href, caption in [('index.html', 'Index'),
('about.html', 'About')]:
<li><a href="{{ href }}">{{ caption }}</a></li>
# endfor
</ul>
Since Jinja 2.2, line-based comments are available as well. For example, if
the line-comment prefix is configured to be ``##``, everything from ``##`` to
the end of the line is ignored (excluding the newline sign)::
# for item in seq:
<li>{{ item }}</li> ## this comment is ignored
# endfor
.. _template-inheritance:
Template Inheritance
--------------------
The most powerful part of Jinja is template inheritance. Template inheritance
allows you to build a base "skeleton" template that contains all the common
elements of your site and defines **blocks** that child templates can override.
Sounds complicated but is very basic. It's easiest to understand it by starting
with an example.
Base Template
~~~~~~~~~~~~~
This template, which we'll call ``base.html``, defines a simple HTML skeleton
document that you might use for a simple two-column page. It's the job of
"child" templates to fill the empty blocks with content::
<!DOCTYPE html>
<html lang="en">
<head>
{% block head %}
<link rel="stylesheet" href="style.css" />
<title>{% block title %}{% endblock %} - My Webpage</title>
{% endblock %}
</head>
<body>
<div id="content">{% block content %}{% endblock %}</div>
<div id="footer">
{% block footer %}
© Copyright 2008 by <a href="http://domain.invalid/">you</a>.
{% endblock %}
</div>
</body>
</html>
In this example, the ``{% block %}`` tags define four blocks that child templates
can fill in. All the `block` tag does is tell the template engine that a
child template may override those placeholders in the template.
``block`` tags can be inside other blocks such as ``if``, but they will
always be executed regardless of if the ``if`` block is actually
rendered.
Child Template
~~~~~~~~~~~~~~
A child template might look like this::
{% extends "base.html" %}
{% block title %}Index{% endblock %}
{% block head %}
{{ super() }}
<style type="text/css">
.important { color: #336699; }
</style>
{% endblock %}
{% block content %}
<h1>Index</h1>
<p class="important">
Welcome to my awesome homepage.
</p>
{% endblock %}
The ``{% extends %}`` tag is the key here. It tells the template engine that
this template "extends" another template. When the template system evaluates
this template, it first locates the parent. The extends tag should be the
first tag in the template. Everything before it is printed out normally and
may cause confusion. For details about this behavior and how to take
advantage of it, see :ref:`null-default-fallback`. Also a block will always be
filled in regardless of whether the surrounding condition is evaluated to be true
or false.
The filename of the template depends on the template loader. For example, the
:class:`FileSystemLoader` allows you to access other templates by giving the
filename. You can access templates in subdirectories with a slash::
{% extends "layout/default.html" %}
But this behavior can depend on the application embedding Jinja. Note that
since the child template doesn't define the ``footer`` block, the value from
the parent template is used instead.
You can't define multiple ``{% block %}`` tags with the same name in the
same template. This limitation exists because a block tag works in "both"
directions. That is, a block tag doesn't just provide a placeholder to fill
- it also defines the content that fills the placeholder in the *parent*.
If there were two similarly-named ``{% block %}`` tags in a template,
that template's parent wouldn't know which one of the blocks' content to use.
If you want to print a block multiple times, you can, however, use the special
`self` variable and call the block with that name::
<title>{% block title %}{% endblock %}</title>
<h1>{{ self.title() }}</h1>
{% block body %}{% endblock %}
Super Blocks
~~~~~~~~~~~~
It's possible to render the contents of the parent block by calling ``super()``.
This gives back the results of the parent block::
{% block sidebar %}
<h3>Table Of Contents</h3>
...
{{ super() }}
{% endblock %}
Nesting extends
~~~~~~~~~~~~~~~
In the case of multiple levels of ``{% extends %}``,
``super`` references may be chained (as in ``super.super()``)
to skip levels in the inheritance tree.
For example::
# parent.tmpl
body: {% block body %}Hi from parent.{% endblock %}
# child.tmpl
{% extends "parent.tmpl" %}
{% block body %}Hi from child. {{ super() }}{% endblock %}
# grandchild1.tmpl
{% extends "child.tmpl" %}
{% block body %}Hi from grandchild1.{% endblock %}
# grandchild2.tmpl
{% extends "child.tmpl" %}
{% block body %}Hi from grandchild2. {{ super.super() }} {% endblock %}
Rendering ``child.tmpl`` will give
``body: Hi from child. Hi from parent.``
Rendering ``grandchild1.tmpl`` will give
``body: Hi from grandchild1.``
Rendering ``grandchild2.tmpl`` will give
``body: Hi from grandchild2. Hi from parent.``
Named Block End-Tags
~~~~~~~~~~~~~~~~~~~~
Jinja allows you to put the name of the block after the end tag for better
readability::
{% block sidebar %}
{% block inner_sidebar %}
...
{% endblock inner_sidebar %}
{% endblock sidebar %}
However, the name after the `endblock` word must match the block name.
Block Nesting and Scope
~~~~~~~~~~~~~~~~~~~~~~~
Blocks can be nested for more complex layouts. By default, a block may not
access variables from outside the block (outer scopes)::
{% for item in seq %}
<li>{% block loop_item %}{{ item }}{% endblock %}</li>
{% endfor %}
This example would output empty ``<li>`` items because `item` is unavailable
inside the block. The reason for this is that if the block is replaced by
a child template, a variable would appear that was not defined in the block or
passed to the context.
Starting with Jinja 2.2, you can explicitly specify that variables are
available in a block by setting the block to "scoped" by adding the `scoped`
modifier to a block declaration::
{% for item in seq %}
<li>{% block loop_item scoped %}{{ item }}{% endblock %}</li>
{% endfor %}
When overriding a block, the `scoped` modifier does not have to be provided.
Required Blocks
~~~~~~~~~~~~~~~
Blocks can be marked as ``required``. They must be overridden at some
point, but not necessarily by the direct child template. Required blocks
may only contain space and comments, and they cannot be rendered
directly.
.. code-block:: jinja
:caption: ``page.txt``
{% block body required %}{% endblock %}
.. code-block:: jinja
:caption: ``issue.txt``
{% extends "page.txt" %}
.. code-block:: jinja
:caption: ``bug_report.txt``
{% extends "issue.txt" %}
{% block body %}Provide steps to demonstrate the bug.{% endblock %}
Rendering ``page.txt`` or ``issue.txt`` will raise
``TemplateRuntimeError`` because they don't override the ``body`` block.
Rendering ``bug_report.txt`` will succeed because it does override the
block.
When combined with ``scoped``, the ``required`` modifier must be placed
*after* the scoped modifier. Here are some valid examples:
.. code-block:: jinja
{% block body scoped %}{% endblock %}
{% block body required %}{% endblock %}
{% block body scoped required %}{% endblock %}
Template Objects
~~~~~~~~~~~~~~~~
``extends``, ``include``, and ``import`` can take a template object
instead of the name of a template to load. This could be useful in some
advanced situations, since you can use Python code to load a template
first and pass it in to ``render``.
.. code-block:: python
if debug_mode:
layout = env.get_template("debug_layout.html")
else:
layout = env.get_template("layout.html")
user_detail = env.get_template("user/detail.html")
return user_detail.render(layout=layout)
.. code-block:: jinja
{% extends layout %}
Note how ``extends`` is passed the variable with the template object
that was passed to ``render``, instead of a string.
HTML Escaping
-------------
When generating HTML from templates, there's always a risk that a variable will
include characters that affect the resulting HTML. There are two approaches:
a. manually escaping each variable; or
b. automatically escaping everything by default.
Jinja supports both. What is used depends on the application configuration.
The default configuration is no automatic escaping; for various reasons:
- Escaping everything except for safe values will also mean that Jinja is
escaping variables known to not include HTML (e.g. numbers, booleans)
which can be a huge performance hit.
- The information about the safety of a variable is very fragile. It could
happen that by coercing safe and unsafe values, the return value is
double-escaped HTML.
Working with Manual Escaping
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If manual escaping is enabled, it's **your** responsibility to escape
variables if needed. What to escape? If you have a variable that *may*
include any of the following chars (``>``, ``<``, ``&``, or ``"``) you
**SHOULD** escape it unless the variable contains well-formed and trusted
HTML. Escaping works by piping the variable through the ``|e`` filter::
{{ user.username|e }}
Working with Automatic Escaping
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When automatic escaping is enabled, everything is escaped by default except
for values explicitly marked as safe. Variables and expressions
can be marked as safe either in:
a. The context dictionary by the application with
:class:`markupsafe.Markup`
b. The template, with the ``|safe`` filter.
If a string that you marked safe is passed through other Python code
that doesn't understand that mark, it may get lost. Be aware of when
your data is marked safe and how it is processed before arriving at the
template.
If a value has been escaped but is not marked safe, auto-escaping will
still take place and result in double-escaped characters. If you know
you have data that is already safe but not marked, be sure to wrap it in
``Markup`` or use the ``|safe`` filter.
Jinja functions (macros, `super`, `self.BLOCKNAME`) always return template
data that is marked as safe.
String literals in templates with automatic escaping are considered
unsafe because native Python strings are not safe.
.. _list-of-control-structures:
List of Control Structures
--------------------------
A control structure refers to all those things that control the flow of a
program - conditionals (i.e. if/elif/else), for-loops, as well as things like
macros and blocks. With the default syntax, control structures appear inside
``{% ... %}`` blocks.
.. _for-loop:
For
~~~
Loop over each item in a sequence. For example, to display a list of users
provided in a variable called `users`::
<h1>Members</h1>
<ul>
{% for user in users %}
<li>{{ user.username|e }}</li>
{% endfor %}
</ul>
As variables in templates retain their object properties, it is possible to
iterate over containers like `dict`::
<dl>
{% for key, value in my_dict.items() %}
<dt>{{ key|e }}</dt>
<dd>{{ value|e }}</dd>
{% endfor %}
</dl>
Python dicts may not be in the order you want to display them in. If
order matters, use the ``|dictsort`` filter.
.. code-block:: jinja
<dl>
{% for key, value in my_dict | dictsort %}
<dt>{{ key|e }}</dt>
<dd>{{ value|e }}</dd>
{% endfor %}
</dl>
Inside of a for-loop block, you can access some special variables:
+-----------------------+---------------------------------------------------+
| Variable | Description |
+=======================+===================================================+
| `loop.index` | The current iteration of the loop. (1 indexed) |
+-----------------------+---------------------------------------------------+
| `loop.index0` | The current iteration of the loop. (0 indexed) |
+-----------------------+---------------------------------------------------+
| `loop.revindex` | The number of iterations from the end of the loop |
| | (1 indexed) |
+-----------------------+---------------------------------------------------+
| `loop.revindex0` | The number of iterations from the end of the loop |
| | (0 indexed) |
+-----------------------+---------------------------------------------------+
| `loop.first` | True if first iteration. |
+-----------------------+---------------------------------------------------+
| `loop.last` | True if last iteration. |
+-----------------------+---------------------------------------------------+
| `loop.length` | The number of items in the sequence. |
+-----------------------+---------------------------------------------------+
| `loop.cycle` | A helper function to cycle between a list of |
| | sequences. See the explanation below. |
+-----------------------+---------------------------------------------------+
| `loop.depth` | Indicates how deep in a recursive loop |
| | the rendering currently is. Starts at level 1 |
+-----------------------+---------------------------------------------------+
| `loop.depth0` | Indicates how deep in a recursive loop |
| | the rendering currently is. Starts at level 0 |
+-----------------------+---------------------------------------------------+
| `loop.previtem` | The item from the previous iteration of the loop. |
| | Undefined during the first iteration. |
+-----------------------+---------------------------------------------------+
| `loop.nextitem` | The item from the following iteration of the loop.|
| | Undefined during the last iteration. |
+-----------------------+---------------------------------------------------+
| `loop.changed(*val)` | True if previously called with a different value |
| | (or not called at all). |
+-----------------------+---------------------------------------------------+
Within a for-loop, it's possible to cycle among a list of strings/variables
each time through the loop by using the special `loop.cycle` helper::
{% for row in rows %}
<li class="{{ loop.cycle('odd', 'even') }}">{{ row }}</li>
{% endfor %}
Since Jinja 2.1, an extra `cycle` helper exists that allows loop-unbound
cycling. For more information, have a look at the :ref:`builtin-globals`.
.. _loop-filtering:
Unlike in Python, it's not possible to `break` or `continue` in a loop. You
can, however, filter the sequence during iteration, which allows you to skip
items. The following example skips all the users which are hidden::
{% for user in users if not user.hidden %}
<li>{{ user.username|e }}</li>
{% endfor %}
The advantage is that the special `loop` variable will count correctly; thus
not counting the users not iterated over.
If no iteration took place because the sequence was empty or the filtering
removed all the items from the sequence, you can render a default block
by using `else`::
<ul>
{% for user in users %}
<li>{{ user.username|e }}</li>
{% else %}
<li><em>no users found</em></li>
{% endfor %}
</ul>
Note that, in Python, `else` blocks are executed whenever the corresponding
loop **did not** `break`. Since Jinja loops cannot `break` anyway,
a slightly different behavior of the `else` keyword was chosen.
It is also possible to use loops recursively. This is useful if you are
dealing with recursive data such as sitemaps or RDFa.
To use loops recursively, you basically have to add the `recursive` modifier
to the loop definition and call the `loop` variable with the new iterable
where you want to recurse.
The following example implements a sitemap with recursive loops::
<ul class="sitemap">
{%- for item in sitemap recursive %}
<li><a href="{{ item.href|e }}">{{ item.title }}</a>
{%- if item.children -%}
<ul class="submenu">{{ loop(item.children) }}</ul>
{%- endif %}</li>
{%- endfor %}
</ul>
The `loop` variable always refers to the closest (innermost) loop. If we
have more than one level of loops, we can rebind the variable `loop` by
writing `{% set outer_loop = loop %}` after the loop that we want to
use recursively. Then, we can call it using `{{ outer_loop(...) }}`
Please note that assignments in loops will be cleared at the end of the
iteration and cannot outlive the loop scope. Older versions of Jinja had
a bug where in some circumstances it appeared that assignments would work.
This is not supported. See :ref:`assignments` for more information about
how to deal with this.
If all you want to do is check whether some value has changed since the
last iteration or will change in the next iteration, you can use `previtem`
and `nextitem`::
{% for value in values %}
{% if loop.previtem is defined and value > loop.previtem %}
The value just increased!
{% endif %}
{{ value }}
{% if loop.nextitem is defined and loop.nextitem > value %}
The value will increase even more!
{% endif %}
{% endfor %}
If you only care whether the value changed at all, using `changed` is even
easier::
{% for entry in entries %}
{% if loop.changed(entry.category) %}
<h2>{{ entry.category }}</h2>
{% endif %}
<p>{{ entry.message }}</p>
{% endfor %}
.. _if:
If
~~
The `if` statement in Jinja is comparable with the Python if statement.
In the simplest form, you can use it to test if a variable is defined, not
empty and not false::
{% if users %}
<ul>
{% for user in users %}
<li>{{ user.username|e }}</li>
{% endfor %}
</ul>
{% endif %}
For multiple branches, `elif` and `else` can be used like in Python. You can
use more complex :ref:`expressions` there, too::
{% if kenny.sick %}
Kenny is sick.
{% elif kenny.dead %}
You killed Kenny! You bastard!!!
{% else %}
Kenny looks okay --- so far
{% endif %}
If can also be used as an :ref:`inline expression <if-expression>` and for
:ref:`loop filtering <loop-filtering>`.
.. _macros:
Macros
~~~~~~
Macros are comparable with functions in regular programming languages. They
are useful to put often used idioms into reusable functions to not repeat
yourself ("DRY").
Here's a small example of a macro that renders a form element::
{% macro input(name, value='', type='text', size=20) -%}
<input type="{{ type }}" name="{{ name }}" value="{{
value|e }}" size="{{ size }}">
{%- endmacro %}
The macro can then be called like a function in the namespace::
<p>{{ input('username') }}</p>
<p>{{ input('password', type='password') }}</p>
If the macro was defined in a different template, you have to
:ref:`import <import>` it first.
Inside macros, you have access to three special variables:
`varargs`
If more positional arguments are passed to the macro than accepted by the
macro, they end up in the special `varargs` variable as a list of values.
`kwargs`
Like `varargs` but for keyword arguments. All unconsumed keyword
arguments are stored in this special variable.
`caller`
If the macro was called from a :ref:`call<call>` tag, the caller is stored
in this variable as a callable macro.
Macros also expose some of their internal details. The following attributes
are available on a macro object:
`name`
The name of the macro. ``{{ input.name }}`` will print ``input``.
`arguments`
A tuple of the names of arguments the macro accepts.
`catch_kwargs`
This is `true` if the macro accepts extra keyword arguments (i.e.: accesses
the special `kwargs` variable).
`catch_varargs`
This is `true` if the macro accepts extra positional arguments (i.e.:
accesses the special `varargs` variable).
`caller`
This is `true` if the macro accesses the special `caller` variable and may
be called from a :ref:`call<call>` tag.
If a macro name starts with an underscore, it's not exported and can't
be imported.
Due to how scopes work in Jinja, a macro in a child template does not
override a macro in a parent template. The following will output
"LAYOUT", not "CHILD".
.. code-block:: jinja
:caption: ``layout.txt``
{% macro foo() %}LAYOUT{% endmacro %}
{% block body %}{% endblock %}
.. code-block:: jinja
:caption: ``child.txt``
{% extends 'layout.txt' %}
{% macro foo() %}CHILD{% endmacro %}
{% block body %}{{ foo() }}{% endblock %}
.. _call:
Call
~~~~
In some cases it can be useful to pass a macro to another macro. For this
purpose, you can use the special `call` block. The following example shows
a macro that takes advantage of the call functionality and how it can be
used::
{% macro render_dialog(title, class='dialog') -%}
<div class="{{ class }}">
<h2>{{ title }}</h2>
<div class="contents">
{{ caller() }}
</div>
</div>
{%- endmacro %}
{% call render_dialog('Hello World') %}
This is a simple dialog rendered by using a macro and
a call block.
{% endcall %}
It's also possible to pass arguments back to the call block. This makes it
useful as a replacement for loops. Generally speaking, a call block works
exactly like a macro without a name.
Here's an example of how a call block can be used with arguments::
{% macro dump_users(users) -%}
<ul>
{%- for user in users %}
<li><p>{{ user.username|e }}</p>{{ caller(user) }}</li>
{%- endfor %}
</ul>
{%- endmacro %}
{% call(user) dump_users(list_of_user) %}
<dl>
<dt>Realname</dt>
<dd>{{ user.realname|e }}</dd>
<dt>Description</dt>
<dd>{{ user.description }}</dd>
</dl>
{% endcall %}
Filters
~~~~~~~
Filter sections allow you to apply regular Jinja filters on a block of
template data. Just wrap the code in the special `filter` section::
{% filter upper %}
This text becomes uppercase
{% endfilter %}
Filters that accept arguments can be called like this::
{% filter center(100) %}Center this{% endfilter %}
.. _assignments:
Assignments
~~~~~~~~~~~
Inside code blocks, you can also assign values to variables. Assignments at
top level (outside of blocks, macros or loops) are exported from the template
like top level macros and can be imported by other templates.
Assignments use the `set` tag and can have multiple targets::
{% set navigation = [('index.html', 'Index'), ('about.html', 'About')] %}
{% set key, value = call_something() %}
.. admonition:: Scoping Behavior
Please keep in mind that it is not possible to set variables inside a
block and have them show up outside of it. This also applies to
loops. The only exception to that rule are if statements which do not
introduce a scope. As a result the following template is not going
to do what you might expect::
{% set iterated = false %}
{% for item in seq %}
{{ item }}
{% set iterated = true %}
{% endfor %}
{% if not iterated %} did not iterate {% endif %}
It is not possible with Jinja syntax to do this. Instead use
alternative constructs like the loop else block or the special `loop`
variable::
{% for item in seq %}
{{ item }}
{% else %}
did not iterate
{% endfor %}
As of version 2.10 more complex use cases can be handled using namespace
objects which allow propagating of changes across scopes::
{% set ns = namespace(found=false) %}
{% for item in items %}
{% if item.check_something() %}
{% set ns.found = true %}
{% endif %}
* {{ item.title }}
{% endfor %}
Found item having something: {{ ns.found }}
Note that the ``obj.attr`` notation in the `set` tag is only allowed for
namespace objects; attempting to assign an attribute on any other object
will raise an exception.
.. versionadded:: 2.10 Added support for namespace objects
Block Assignments
~~~~~~~~~~~~~~~~~
It's possible to use `set` as a block to assign the content of the block to a
variable. This can be used to create multi-line strings, since Jinja doesn't
support Python's triple quotes (``"""``, ``'''``).
Instead of using an equals sign and a value, you only write the variable name,
and everything until ``{% endset %}`` is captured.
.. code-block:: jinja
{% set navigation %}
<li><a href="/">Index</a>
<li><a href="/downloads">Downloads</a>
{% endset %}
Filters applied to the variable name will be applied to the block's content.
.. code-block:: jinja
{% set reply | wordwrap %}
You wrote:
{{ message }}
{% endset %}
.. versionadded:: 2.8
.. versionchanged:: 2.10
Block assignment supports filters.
.. _extends:
Extends
~~~~~~~
The `extends` tag can be used to extend one template from another. You can
have multiple `extends` tags in a file, but only one of them may be executed at
a time.
See the section about :ref:`template-inheritance` above.
.. _blocks:
Blocks
~~~~~~
Blocks are used for inheritance and act as both placeholders and replacements
at the same time. They are documented in detail in the
:ref:`template-inheritance` section.
Include
~~~~~~~
The ``include`` tag renders another template and outputs the result into
the current template.
.. code-block:: jinja
{% include 'header.html' %}
Body goes here.
{% include 'footer.html' %}
The included template has access to context of the current template by
default. Use ``without context`` to use a separate context instead.
``with context`` is also valid, but is the default behavior. See
:ref:`import-visibility`.
The included template can ``extend`` another template and override
blocks in that template. However, the current template cannot override
any blocks that the included template outputs.
Use ``ignore missing`` to ignore the statement if the template does not
exist. It must be placed *before* a context visibility statement.
.. code-block:: jinja
{% include "sidebar.html" without context %}
{% include "sidebar.html" ignore missing %}
{% include "sidebar.html" ignore missing with context %}
{% include "sidebar.html" ignore missing without context %}
If a list of templates is given, each will be tried in order until one
is not missing. This can be used with ``ignore missing`` to ignore if
none of the templates exist.
.. code-block:: jinja
{% include ['page_detailed.html', 'page.html'] %}
{% include ['special_sidebar.html', 'sidebar.html'] ignore missing %}
A variable, with either a template name or template object, can also be
passed to the statement.
.. _import:
Import
~~~~~~
Jinja supports putting often used code into macros. These macros can go into
different templates and get imported from there. This works similarly to the
import statements in Python. It's important to know that imports are cached
and imported templates don't have access to the current template variables,
just the globals by default. For more details about context behavior of
imports and includes, see :ref:`import-visibility`.
There are two ways to import templates. You can import a complete template
into a variable or request specific macros / exported variables from it.
Imagine we have a helper module that renders forms (called `forms.html`)::
{% macro input(name, value='', type='text') -%}
<input type="{{ type }}" value="{{ value|e }}" name="{{ name }}">
{%- endmacro %}
{%- macro textarea(name, value='', rows=10, cols=40) -%}
<textarea name="{{ name }}" rows="{{ rows }}" cols="{{ cols
}}">{{ value|e }}</textarea>
{%- endmacro %}
The easiest and most flexible way to access a template's variables
and macros is to import the whole template module into a variable.
That way, you can access the attributes::
{% import 'forms.html' as forms %}
<dl>
<dt>Username</dt>
<dd>{{ forms.input('username') }}</dd>
<dt>Password</dt>
<dd>{{ forms.input('password', type='password') }}</dd>
</dl>
<p>{{ forms.textarea('comment') }}</p>
Alternatively, you can import specific names from a template into the current
namespace::
{% from 'forms.html' import input as input_field, textarea %}
<dl>
<dt>Username</dt>
<dd>{{ input_field('username') }}</dd>
<dt>Password</dt>
<dd>{{ input_field('password', type='password') }}</dd>
</dl>
<p>{{ textarea('comment') }}</p>
Macros and variables starting with one or more underscores are private and
cannot be imported.
.. versionchanged:: 2.4
If a template object was passed to the template context, you can
import from that object.
.. _import-visibility:
Import Context Behavior
-----------------------
By default, included templates are passed the current context and imported
templates are not. The reason for this is that imports, unlike includes,
are cached; as imports are often used just as a module that holds macros.
This behavior can be changed explicitly: by adding `with context`
or `without context` to the import/include directive, the current context
can be passed to the template and caching is disabled automatically.
Here are two examples::
{% from 'forms.html' import input with context %}
{% include 'header.html' without context %}
.. admonition:: Note
In Jinja 2.0, the context that was passed to the included template
did not include variables defined in the template. As a matter of
fact, this did not work::
{% for box in boxes %}
{% include "render_box.html" %}
{% endfor %}
The included template ``render_box.html`` is *not* able to access
`box` in Jinja 2.0. As of Jinja 2.1, ``render_box.html`` *is* able
to do so.
.. _expressions:
Expressions
-----------
Jinja allows basic expressions everywhere. These work very similarly to
regular Python; even if you're not working with Python
you should feel comfortable with it.
Literals
~~~~~~~~
The simplest form of expressions are literals. Literals are representations
for Python objects such as strings and numbers. The following literals exist:
``"Hello World"``
Everything between two double or single quotes is a string. They are
useful whenever you need a string in the template (e.g. as
arguments to function calls and filters, or just to extend or include a
template).
``42`` / ``123_456``
Integers are whole numbers without a decimal part. The '_' character
can be used to separate groups for legibility.
``42.23`` / ``42.1e2`` / ``123_456.789``
Floating point numbers can be written using a '.' as a decimal mark.
They can also be written in scientific notation with an upper or
lower case 'e' to indicate the exponent part. The '_' character can
be used to separate groups for legibility, but cannot be used in the
exponent part.
``['list', 'of', 'objects']``
Everything between two brackets is a list. Lists are useful for storing
sequential data to be iterated over. For example, you can easily
create a list of links using lists and tuples for (and with) a for loop::
<ul>
{% for href, caption in [('index.html', 'Index'), ('about.html', 'About'),
('downloads.html', 'Downloads')] %}
<li><a href="{{ href }}">{{ caption }}</a></li>
{% endfor %}
</ul>
``('tuple', 'of', 'values')``
Tuples are like lists that cannot be modified ("immutable"). If a tuple
only has one item, it must be followed by a comma (``('1-tuple',)``).
Tuples are usually used to represent items of two or more elements.
See the list example above for more details.
``{'dict': 'of', 'key': 'and', 'value': 'pairs'}``
A dict in Python is a structure that combines keys and values. Keys must
be unique and always have exactly one value. Dicts are rarely used in
templates; they are useful in some rare cases such as the :func:`xmlattr`
filter.
``true`` / ``false``
``true`` is always true and ``false`` is always false.
.. admonition:: Note
The special constants `true`, `false`, and `none` are indeed lowercase.
Because that caused confusion in the past, (`True` used to expand
to an undefined variable that was considered false),
all three can now also be written in title case
(`True`, `False`, and `None`).
However, for consistency, (all Jinja identifiers are lowercase)
you should use the lowercase versions.
Math
~~~~
Jinja allows you to calculate with values. This is rarely useful in templates
but exists for completeness' sake. The following operators are supported:
``+``
Adds two objects together. Usually the objects are numbers, but if both are
strings or lists, you can concatenate them this way. This, however, is not
the preferred way to concatenate strings! For string concatenation, have
a look-see at the ``~`` operator. ``{{ 1 + 1 }}`` is ``2``.
``-``
Subtract the second number from the first one. ``{{ 3 - 2 }}`` is ``1``.
``/``
Divide two numbers. The return value will be a floating point number.
``{{ 1 / 2 }}`` is ``{{ 0.5 }}``.
``//``
Divide two numbers and return the truncated integer result.
``{{ 20 // 7 }}`` is ``2``.
``%``
Calculate the remainder of an integer division. ``{{ 11 % 7 }}`` is ``4``.
``*``
Multiply the left operand with the right one. ``{{ 2 * 2 }}`` would
return ``4``. This can also be used to repeat a string multiple times.
``{{ '=' * 80 }}`` would print a bar of 80 equal signs.
``**``
Raise the left operand to the power of the right operand.
``{{ 2**3 }}`` would return ``8``.
Unlike Python, chained pow is evaluated left to right.
``{{ 3**3**3 }}`` is evaluated as ``(3**3)**3`` in Jinja, but would
be evaluated as ``3**(3**3)`` in Python. Use parentheses in Jinja
to be explicit about what order you want. It is usually preferable
to do extended math in Python and pass the results to ``render``
rather than doing it in the template.
This behavior may be changed in the future to match Python, if it's
possible to introduce an upgrade path.
Comparisons
~~~~~~~~~~~
``==``
Compares two objects for equality.
``!=``
Compares two objects for inequality.
``>``
``true`` if the left hand side is greater than the right hand side.
``>=``
``true`` if the left hand side is greater or equal to the right hand side.
``<``
``true`` if the left hand side is lower than the right hand side.
``<=``
``true`` if the left hand side is lower or equal to the right hand side.
Logic
~~~~~
For ``if`` statements, ``for`` filtering, and ``if`` expressions, it can be
useful to combine multiple expressions.
``and``
For ``x and y``, if ``x`` is false, then the value is ``x``, else ``y``. In
a boolean context, this will be treated as ``True`` if both operands are
truthy.
``or``
For ``x or y``, if ``x`` is true, then the value is ``x``, else ``y``. In a
boolean context, this will be treated as ``True`` if at least one operand is
truthy.
``not``
For ``not x``, if ``x`` is false, then the value is ``True``, else
``False``.
Prefer negating ``is`` and ``in`` using their infix notation:
``foo is not bar`` instead of ``not foo is bar``; ``foo not in bar`` instead
of ``not foo in bar``. All other expressions require prefix notation:
``not (foo and bar).``
``(expr)``
Parentheses group an expression. This is used to change evaluation order, or
to make a long expression easier to read or less ambiguous.
Other Operators
~~~~~~~~~~~~~~~
The following operators are very useful but don't fit into any of the other
two categories:
``in``
Perform a sequence / mapping containment test. Returns true if the left
operand is contained in the right. ``{{ 1 in [1, 2, 3] }}`` would, for
example, return true.
``is``
Performs a :ref:`test <tests>`.
``|`` (pipe, vertical bar)
Applies a :ref:`filter <filters>`.
``~`` (tilde)
Converts all operands into strings and concatenates them.
``{{ "Hello " ~ name ~ "!" }}`` would return (assuming `name` is set
to ``'John'``) ``Hello John!``.
``()``
Call a callable: ``{{ post.render() }}``. Inside of the parentheses you
can use positional arguments and keyword arguments like in Python:
``{{ post.render(user, full=true) }}``.
``.`` / ``[]``
Get an attribute of an object. (See :ref:`variables`)
.. _if-expression:
If Expression
~~~~~~~~~~~~~
It is also possible to use inline `if` expressions. These are useful in some
situations. For example, you can use this to extend from one template if a
variable is defined, otherwise from the default layout template::
{% extends layout_template if layout_template is defined else 'default.html' %}
The general syntax is ``<do something> if <something is true> else <do
something else>``.
The `else` part is optional. If not provided, the else block implicitly
evaluates into an :class:`Undefined` object (regardless of what ``undefined``
in the environment is set to):
.. code-block:: jinja
{{ "[{}]".format(page.title) if page.title }}
.. _python-methods:
Python Methods
~~~~~~~~~~~~~~
You can also use any of the methods defined on a variable's type.
The value returned from the method invocation is used as the value of the expression.
Here is an example that uses methods defined on strings (where ``page.title`` is a string):
.. code-block:: text
{{ page.title.capitalize() }}
This works for methods on user-defined types. For example, if variable
``f`` of type ``Foo`` has a method ``bar`` defined on it, you can do the
following:
.. code-block:: text
{{ f.bar(value) }}
Operator methods also work as expected. For example, ``%`` implements
printf-style for strings:
.. code-block:: text
{{ "Hello, %s!" % name }}
Although you should prefer the ``.format`` method for that case (which
is a bit contrived in the context of rendering a template):
.. code-block:: text
{{ "Hello, {}!".format(name) }}
.. _builtin-filters:
List of Builtin Filters
-----------------------
.. py:currentmodule:: jinja-filters
.. jinja:filters:: jinja2.defaults.DEFAULT_FILTERS
.. _builtin-tests:
List of Builtin Tests
---------------------
.. py:currentmodule:: jinja-tests
.. jinja:tests:: jinja2.defaults.DEFAULT_TESTS
.. _builtin-globals:
List of Global Functions
------------------------
The following functions are available in the global scope by default:
.. py:currentmodule:: jinja-globals
.. function:: range([start,] stop[, step])
Return a list containing an arithmetic progression of integers.
``range(i, j)`` returns ``[i, i+1, i+2, ..., j-1]``;
start (!) defaults to ``0``.
When step is given, it specifies the increment (or decrement).
For example, ``range(4)`` and ``range(0, 4, 1)`` return ``[0, 1, 2, 3]``.
The end point is omitted!
These are exactly the valid indices for a list of 4 elements.
This is useful to repeat a template block multiple times, e.g.
to fill a list. Imagine you have 7 users in the list but you want to
render three empty items to enforce a height with CSS::
<ul>
{% for user in users %}
<li>{{ user.username }}</li>
{% endfor %}
{% for number in range(10 - users|count) %}
<li class="empty"><span>...</span></li>
{% endfor %}
</ul>
.. function:: lipsum(n=5, html=True, min=20, max=100)
Generates some lorem ipsum for the template. By default, five paragraphs
of HTML are generated with each paragraph between 20 and 100 words.
If html is False, regular text is returned. This is useful to generate simple
contents for layout testing.
.. function:: dict(\**items)
A convenient alternative to dict literals. ``{'foo': 'bar'}`` is the same
as ``dict(foo='bar')``.
.. class:: cycler(\*items)
Cycle through values by yielding them one at a time, then restarting
once the end is reached.
Similar to ``loop.cycle``, but can be used outside loops or across
multiple loops. For example, render a list of folders and files in a
list, alternating giving them "odd" and "even" classes.
.. code-block:: html+jinja
{% set row_class = cycler("odd", "even") %}
<ul class="browser">
{% for folder in folders %}
<li class="folder {{ row_class.next() }}">{{ folder }}
{% endfor %}
{% for file in files %}
<li class="file {{ row_class.next() }}">{{ file }}
{% endfor %}
</ul>
:param items: Each positional argument will be yielded in the order
given for each cycle.
.. versionadded:: 2.1
.. property:: current
Return the current item. Equivalent to the item that will be
returned next time :meth:`next` is called.
.. method:: next()
Return the current item, then advance :attr:`current` to the
next item.
.. method:: reset()
Resets the current item to the first item.
.. class:: joiner(sep=', ')
A tiny helper that can be used to "join" multiple sections. A joiner is
passed a string and will return that string every time it's called, except
the first time (in which case it returns an empty string). You can
use this to join things::
{% set pipe = joiner("|") %}
{% if categories %} {{ pipe() }}
Categories: {{ categories|join(", ") }}
{% endif %}
{% if author %} {{ pipe() }}
Author: {{ author() }}
{% endif %}
{% if can_edit %} {{ pipe() }}
<a href="?action=edit">Edit</a>
{% endif %}
.. versionadded:: 2.1
.. class:: namespace(...)
Creates a new container that allows attribute assignment using the
``{% set %}`` tag::
{% set ns = namespace() %}
{% set ns.foo = 'bar' %}
The main purpose of this is to allow carrying a value from within a loop
body to an outer scope. Initial values can be provided as a dict, as
keyword arguments, or both (same behavior as Python's `dict` constructor)::
{% set ns = namespace(found=false) %}
{% for item in items %}
{% if item.check_something() %}
{% set ns.found = true %}
{% endif %}
* {{ item.title }}
{% endfor %}
Found item having something: {{ ns.found }}
.. versionadded:: 2.10
.. versionchanged:: 3.2
Namespace attributes can be assigned to in multiple assignment.
Extensions
----------
.. py:currentmodule:: jinja2
The following sections cover the built-in Jinja extensions that may be
enabled by an application. An application could also provide further
extensions not covered by this documentation; in which case there should
be a separate document explaining said :ref:`extensions
<jinja-extensions>`.
.. _i18n-in-templates:
i18n
~~~~
If the :ref:`i18n-extension` is enabled, it's possible to mark text in
the template as translatable. To mark a section as translatable, use a
``trans`` block:
.. code-block:: jinja
{% trans %}Hello, {{ user }}!{% endtrans %}
Inside the block, no statements are allowed, only text and simple
variable tags.
Variable tags can only be a name, not attribute access, filters, or
other expressions. To use an expression, bind it to a name in the
``trans`` tag for use in the block.
.. code-block:: jinja
{% trans user=user.username %}Hello, {{ user }}!{% endtrans %}
To bind more than one expression, separate each with a comma (``,``).
.. code-block:: jinja
{% trans book_title=book.title, author=author.name %}
This is {{ book_title }} by {{ author }}
{% endtrans %}
To pluralize, specify both the singular and plural forms separated by
the ``pluralize`` tag.
.. code-block:: jinja
{% trans count=list|length %}
There is {{ count }} {{ name }} object.
{% pluralize %}
There are {{ count }} {{ name }} objects.
{% endtrans %}
By default, the first variable in a block is used to determine whether
to use singular or plural form. If that isn't correct, specify the
variable used for pluralizing as a parameter to ``pluralize``.
.. code-block:: jinja
{% trans ..., user_count=users|length %}...
{% pluralize user_count %}...{% endtrans %}
When translating blocks of text, whitespace and linebreaks result in
hard to read and error-prone translation strings. To avoid this, a trans
block can be marked as trimmed, which will replace all linebreaks and
the whitespace surrounding them with a single space and remove leading
and trailing whitespace.
.. code-block:: jinja
{% trans trimmed book_title=book.title %}
This is {{ book_title }}.
You should read it!
{% endtrans %}
This results in ``This is %(book_title)s. You should read it!`` in the
translation file.
If trimming is enabled globally, the ``notrimmed`` modifier can be used
to disable it for a block.
.. versionadded:: 2.10
The ``trimmed`` and ``notrimmed`` modifiers have been added.
If the translation depends on the context that the message appears in,
the ``pgettext`` and ``npgettext`` functions take a ``context`` string
as the first argument, which is used to select the appropriate
translation. To specify a context with the ``{% trans %}`` tag, provide
a string as the first token after ``trans``.
.. code-block:: jinja
{% trans "fruit" %}apple{% endtrans %}
{% trans "fruit" trimmed count -%}
1 apple
{%- pluralize -%}
{{ count }} apples
{%- endtrans %}
.. versionadded:: 3.1
A context can be passed to the ``trans`` tag to use ``pgettext`` and
``npgettext``.
It's possible to translate strings in expressions with these functions:
- ``_(message)``: Alias for ``gettext``.
- ``gettext(message)``: Translate a message.
- ``ngettext(singular, plural, n)``: Translate a singular or plural
message based on a count variable.
- ``pgettext(context, message)``: Like ``gettext()``, but picks the
translation based on the context string.
- ``npgettext(context, singular, plural, n)``: Like ``npgettext()``,
but picks the translation based on the context string.
You can print a translated string like this:
.. code-block:: jinja
{{ _("Hello, World!") }}
To use placeholders, use the ``format`` filter.
.. code-block:: jinja
{{ _("Hello, %(user)s!")|format(user=user.username) }}
Always use keyword arguments to ``format``, as other languages may not
use the words in the same order.
If :ref:`newstyle-gettext` calls are activated, using placeholders is
easier. Formatting is part of the ``gettext`` call instead of using the
``format`` filter.
.. sourcecode:: jinja
{{ gettext('Hello World!') }}
{{ gettext('Hello %(name)s!', name='World') }}
{{ ngettext('%(num)d apple', '%(num)d apples', apples|count) }}
The ``ngettext`` function's format string automatically receives the
count as a ``num`` parameter in addition to the given parameters.
Expression Statement
~~~~~~~~~~~~~~~~~~~~
If the expression-statement extension is loaded, a tag called `do` is available
that works exactly like the regular variable expression (``{{ ... }}``); except
it doesn't print anything. This can be used to modify lists::
{% do navigation.append('a string') %}
Loop Controls
~~~~~~~~~~~~~
If the application enables the :ref:`loopcontrols-extension`, it's possible to
use `break` and `continue` in loops. When `break` is reached, the loop is
terminated; if `continue` is reached, the processing is stopped and continues
with the next iteration.
Here's a loop that skips every second item::
{% for user in users %}
{%- if loop.index is even %}{% continue %}{% endif %}
...
{% endfor %}
Likewise, a loop that stops processing after the 10th iteration::
{% for user in users %}
{%- if loop.index >= 10 %}{% break %}{% endif %}
{%- endfor %}
Note that ``loop.index`` starts with 1, and ``loop.index0`` starts with 0
(See: :ref:`for-loop`).
Debug Statement
~~~~~~~~~~~~~~~
If the :ref:`debug-extension` is enabled, a ``{% debug %}`` tag will be
available to dump the current context as well as the available filters
and tests. This is useful to see what's available to use in the template
without setting up a debugger.
.. code-block:: html+jinja
<pre>{% debug %}</pre>
.. code-block:: text
{'context': {'cycler': <class 'jinja2.utils.Cycler'>,
...,
'namespace': <class 'jinja2.utils.Namespace'>},
'filters': ['abs', 'attr', 'batch', 'capitalize', 'center', 'count', 'd',
..., 'urlencode', 'urlize', 'wordcount', 'wordwrap', 'xmlattr'],
'tests': ['!=', '<', '<=', '==', '>', '>=', 'callable', 'defined',
..., 'odd', 'sameas', 'sequence', 'string', 'undefined', 'upper']}
With Statement
~~~~~~~~~~~~~~
.. versionadded:: 2.3
The with statement makes it possible to create a new inner scope.
Variables set within this scope are not visible outside of the scope.
With in a nutshell::
{% with %}
{% set foo = 42 %}
{{ foo }} foo is 42 here
{% endwith %}
foo is not visible here any longer
Because it is common to set variables at the beginning of the scope,
you can do that within the `with` statement. The following two examples
are equivalent::
{% with foo = 42 %}
{{ foo }}
{% endwith %}
{% with %}
{% set foo = 42 %}
{{ foo }}
{% endwith %}
An important note on scoping here. In Jinja versions before 2.9 the
behavior of referencing one variable to another had some unintended
consequences. In particular one variable could refer to another defined
in the same with block's opening statement. This caused issues with the
cleaned up scoping behavior and has since been improved. In particular
in newer Jinja versions the following code always refers to the variable
`a` from outside the `with` block::
{% with a={}, b=a.attribute %}...{% endwith %}
In earlier Jinja versions the `b` attribute would refer to the results of
the first attribute. If you depend on this behavior you can rewrite it to
use the ``set`` tag::
{% with a={} %}
{% set b = a.attribute %}
{% endwith %}
.. admonition:: Extension
In older versions of Jinja (before 2.9) it was required to enable this
feature with an extension. It's now enabled by default.
.. _autoescape-overrides:
Autoescape Overrides
--------------------
.. versionadded:: 2.4
If you want you can activate and deactivate the autoescaping from within
the templates.
Example::
{% autoescape true %}
Autoescaping is active within this block
{% endautoescape %}
{% autoescape false %}
Autoescaping is inactive within this block
{% endautoescape %}
After an `endautoescape` the behavior is reverted to what it was before.
.. admonition:: Extension
In older versions of Jinja (before 2.9) it was required to enable this
feature with an extension. It's now enabled by default.
================================================
FILE: docs/tricks.rst
================================================
Tips and Tricks
===============
.. highlight:: html+jinja
This part of the documentation shows some tips and tricks for Jinja
templates.
.. _null-default-fallback:
Null-Default Fallback
---------------------
Jinja supports dynamic inheritance and does not distinguish between parent
and child template as long as no `extends` tag is visited. While this leads
to the surprising behavior that everything before the first `extends` tag
including whitespace is printed out instead of being ignored, it can be used
for a neat trick.
Usually child templates extend from one template that adds a basic HTML
skeleton. However it's possible to put the `extends` tag into an `if` tag to
only extend from the layout template if the `standalone` variable evaluates
to false, which it does by default if it's not defined. Additionally a very
basic skeleton is added to the file so that if it's indeed rendered with
`standalone` set to `True` a very basic HTML skeleton is added::
{% if not standalone %}{% extends 'default.html' %}{% endif -%}
<!DOCTYPE html>
<title>{% block title %}The Page Title{% endblock %}</title>
<link rel="stylesheet" href="style.css" type="text/css">
{% block body %}
<p>This is the page body.</p>
{% endblock %}
Alternating Rows
----------------
If you want to have different styles for each row of a table or
list you can use the `cycle` method on the `loop` object::
<ul>
{% for row in rows %}
<li class="{{ loop.cycle('odd', 'even') }}">{{ row }}</li>
{% endfor %}
</ul>
`cycle` can take an unlimited number of strings. Each time this
tag is encountered the next item from the list is rendered.
Highlighting Active Menu Items
------------------------------
Often you want to have a navigation bar with an active navigation
item. This is really simple to achieve. Because assignments outside
of `block`\s in child templates are global and executed before the layout
template is evaluated it's possible to define the active menu item in the
child template::
{% extends "layout.html" %}
{% set active_page = "index" %}
The layout template can then access `active_page`. Additionally it makes
sense to define a default for that variable::
{% set navigation_bar = [
('/', 'index', 'Index'),
('/downloads/', 'downloads', 'Downloads'),
('/about/', 'about', 'About')
] -%}
{% set active_page = active_page|default('index') -%}
...
<ul id="navigation">
{% for href, id, caption in navigation_bar %}
<li{% if id == active_page %} class="active"{% endif %}>
<a href="{{ href|e }}">{{ caption|e }}</a></li>
{% endfor %}
</ul>
...
.. _accessing-the-parent-loop:
Accessing the parent Loop
-------------------------
The special `loop` variable always points to the innermost loop. If it's
desired to have access to an outer loop it's possible to alias it::
<table>
{% for row in table %}
<tr>
{% set rowloop = loop %}
{% for cell in row %}
<td id="cell-{{ rowloop.index }}-{{ loop.index }}">{{ cell }}</td>
{% endfor %}
</tr>
{% endfor %}
</table>
================================================
FILE: examples/basic/cycle.py
================================================
from jinja2 import Environment
env = Environment(
line_statement_prefix="#", variable_start_string="${", variable_end_string="}"
)
print(
env.from_string(
"""\
<ul>
# for item in range(10)
<li class="${loop.cycle('odd', 'even')}">${item}</li>
# endfor
</ul>\
"""
).render()
)
================================================
FILE: examples/basic/debugger.py
================================================
from jinja2 import Environment
from jinja2.loaders import FileSystemLoader
env = Environment(loader=FileSystemLoader("templates"))
tmpl = env.get_template("broken.html")
print(tmpl.render(seq=[3, 2, 4, 5, 3, 2, 0, 2, 1]))
================================================
FILE: examples/basic/inheritance.py
================================================
from jinja2 import Environment
from jinja2.loaders import DictLoader
env = Environment(
loader=DictLoader(
{
"a": "[A[{% block body %}{% endblock %}]]",
"b": "{% extends 'a' %}{% block body %}[B]{% endblock %}",
"c": "{% extends 'b' %}{% block body %}###{{ super() }}###{% endblock %}",
}
)
)
print(env.get_template("c").render())
================================================
FILE: examples/basic/templates/broken.html
================================================
{% from 'subbroken.html' import may_break %}
<ul>
{% for item in seq %}
<li>{{ may_break(item) }}</li>
{% endfor %}
</ul>
================================================
FILE: examples/basic/templates/subbroken.html
================================================
{% macro may_break(item) -%}
[{{ item / 0 }}]
{%- endmacro %}
================================================
FILE: examples/basic/test.py
================================================
from jinja2 import Environment
from jinja2.loaders import DictLoader
env = Environment(
loader=DictLoader(
{
"child.html": """\
{% extends default_layout or 'default.html' %}
{% import 'helpers.html' as helpers %}
{% macro get_the_answer() %}42{% endmacro %}
{% set title = 'Hello World' %}
{% block body %}
{{ get_the_answer() }}
{{ helpers.conspirate() }}
{% endblock %}
""",
"default.html": """\
<!doctype html>
<title>{{ title }}</title>
{% block body %}{% endblock %}
""",
"helpers.html": """\
{% macro conspirate() %}23{% endmacro %}
""",
}
)
)
tmpl = env.get_template("child.html")
print(tmpl.render())
================================================
FILE: examples/basic/
gitextract_8ck6zqut/
├── .devcontainer/
│ ├── devcontainer.json
│ └── on-create-command.sh
├── .editorconfig
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug-report.md
│ │ ├── config.yml
│ │ └── feature-request.md
│ ├── pull_request_template.md
│ └── workflows/
│ ├── lock.yaml
│ ├── pre-commit.yaml
│ ├── publish.yaml
│ └── tests.yaml
├── .gitignore
├── .pre-commit-config.yaml
├── .readthedocs.yaml
├── CHANGES.rst
├── LICENSE.txt
├── README.md
├── docs/
│ ├── Makefile
│ ├── api.rst
│ ├── changes.rst
│ ├── conf.py
│ ├── examples/
│ │ ├── cache_extension.py
│ │ └── inline_gettext_extension.py
│ ├── extensions.rst
│ ├── faq.rst
│ ├── index.rst
│ ├── integration.rst
│ ├── intro.rst
│ ├── license.rst
│ ├── make.bat
│ ├── nativetypes.rst
│ ├── sandbox.rst
│ ├── switching.rst
│ ├── templates.rst
│ └── tricks.rst
├── examples/
│ └── basic/
│ ├── cycle.py
│ ├── debugger.py
│ ├── inheritance.py
│ ├── templates/
│ │ ├── broken.html
│ │ └── subbroken.html
│ ├── test.py
│ ├── test_filter_and_linestatements.py
│ ├── test_loop_filter.py
│ └── translate.py
├── pyproject.toml
├── scripts/
│ └── generate_identifier_pattern.py
├── src/
│ └── jinja2/
│ ├── __init__.py
│ ├── _identifier.py
│ ├── async_utils.py
│ ├── bccache.py
│ ├── compiler.py
│ ├── constants.py
│ ├── debug.py
│ ├── defaults.py
│ ├── environment.py
│ ├── exceptions.py
│ ├── ext.py
│ ├── filters.py
│ ├── idtracking.py
│ ├── lexer.py
│ ├── loaders.py
│ ├── meta.py
│ ├── nativetypes.py
│ ├── nodes.py
│ ├── optimizer.py
│ ├── parser.py
│ ├── py.typed
│ ├── runtime.py
│ ├── sandbox.py
│ ├── tests.py
│ ├── utils.py
│ └── visitor.py
└── tests/
├── conftest.py
├── res/
│ ├── __init__.py
│ ├── templates/
│ │ ├── broken.html
│ │ ├── foo/
│ │ │ └── test.html
│ │ ├── mojibake.txt
│ │ ├── syntaxerror.html
│ │ └── test.html
│ └── templates2/
│ └── foo
├── test_api.py
├── test_async.py
├── test_async_filters.py
├── test_bytecode_cache.py
├── test_compile.py
├── test_core_tags.py
├── test_debug.py
├── test_ext.py
├── test_filters.py
├── test_idtracking.py
├── test_imports.py
├── test_inheritance.py
├── test_lexnparse.py
├── test_loader.py
├── test_nativetypes.py
├── test_nodes.py
├── test_pickle.py
├── test_regression.py
├── test_runtime.py
├── test_security.py
├── test_tests.py
└── test_utils.py
SYMBOL INDEX (1684 symbols across 48 files)
FILE: docs/examples/cache_extension.py
class FragmentCacheExtension (line 5) | class FragmentCacheExtension(Extension):
method __init__ (line 9) | def __init__(self, environment):
method parse (line 15) | def parse(self, parser):
method _cache_support (line 42) | def _cache_support(self, name, timeout, caller):
FILE: docs/examples/inline_gettext_extension.py
class InlineGettext (line 12) | class InlineGettext(Extension):
method filter_stream (line 21) | def filter_stream(self, stream):
FILE: scripts/generate_identifier_pattern.py
function get_characters (line 7) | def get_characters():
function collapse_ranges (line 26) | def collapse_ranges(data):
function build_pattern (line 37) | def build_pattern(ranges):
function main (line 56) | def main():
FILE: src/jinja2/__init__.py
function __getattr__ (line 43) | def __getattr__(name: str) -> t.Any:
FILE: src/jinja2/async_utils.py
function async_variant (line 15) | def async_variant(normal_func): # type: ignore
function auto_await (line 62) | async def auto_await(value: t.Union[t.Awaitable["V"], "V"]) -> "V":
class _IteratorToAsyncIterator (line 73) | class _IteratorToAsyncIterator(t.Generic[V]):
method __init__ (line 74) | def __init__(self, iterator: "t.Iterator[V]"):
method __aiter__ (line 77) | def __aiter__(self) -> "te.Self":
method __anext__ (line 80) | async def __anext__(self) -> V:
function auto_aiter (line 87) | def auto_aiter(
function auto_to_list (line 96) | async def auto_to_list(
FILE: src/jinja2/bccache.py
class _MemcachedClient (line 27) | class _MemcachedClient(te.Protocol):
method get (line 28) | def get(self, key: str) -> bytes: ...
method set (line 30) | def set(self, key: str, value: bytes, timeout: int | None = None) -> N...
class Bucket (line 44) | class Bucket:
method __init__ (line 53) | def __init__(self, environment: "Environment", key: str, checksum: str...
method reset (line 59) | def reset(self) -> None:
method load_bytecode (line 63) | def load_bytecode(self, f: t.BinaryIO) -> None:
method write_bytecode (line 82) | def write_bytecode(self, f: t.IO[bytes]) -> None:
method bytecode_from_string (line 90) | def bytecode_from_string(self, string: bytes) -> None:
method bytecode_to_string (line 94) | def bytecode_to_string(self) -> bytes:
class BytecodeCache (line 101) | class BytecodeCache:
method load_bytecode (line 130) | def load_bytecode(self, bucket: Bucket) -> None:
method dump_bytecode (line 137) | def dump_bytecode(self, bucket: Bucket) -> None:
method clear (line 144) | def clear(self) -> None:
method get_cache_key (line 150) | def get_cache_key(self, name: str, filename: str | None = None) -> str:
method get_source_checksum (line 159) | def get_source_checksum(self, source: str) -> str:
method get_bucket (line 163) | def get_bucket(
method set_bucket (line 179) | def set_bucket(self, bucket: Bucket) -> None:
class FileSystemBytecodeCache (line 184) | class FileSystemBytecodeCache(BytecodeCache):
method __init__ (line 202) | def __init__(
method _get_default_cache_dir (line 210) | def _get_default_cache_dir(self) -> str:
method _get_cache_filename (line 257) | def _get_cache_filename(self, bucket: Bucket) -> str:
method load_bytecode (line 260) | def load_bytecode(self, bucket: Bucket) -> None:
method dump_bytecode (line 275) | def dump_bytecode(self, bucket: Bucket) -> None:
method clear (line 313) | def clear(self) -> None:
class MemcachedBytecodeCache (line 327) | class MemcachedBytecodeCache(BytecodeCache):
method __init__ (line 372) | def __init__(
method load_bytecode (line 384) | def load_bytecode(self, bucket: Bucket) -> None:
method dump_bytecode (line 393) | def dump_bytecode(self, bucket: Bucket) -> None:
FILE: src/jinja2/compiler.py
function optimizeconst (line 45) | def optimizeconst(f: F) -> F:
function _make_binop (line 61) | def _make_binop(op: str) -> t.Callable[["CodeGenerator", nodes.BinExpr, ...
function _make_unop (line 82) | def _make_unop(
function generate (line 101) | def generate(
function has_safe_repr (line 125) | def has_safe_repr(value: t.Any) -> bool:
function find_undeclared (line 142) | def find_undeclared(nodes: t.Iterable[nodes.Node], names: t.Iterable[str...
class MacroRef (line 155) | class MacroRef:
method __init__ (line 156) | def __init__(self, node: nodes.Macro | nodes.CallBlock) -> None:
class Frame (line 163) | class Frame:
method __init__ (line 166) | def __init__(
method copy (line 217) | def copy(self) -> "te.Self":
method inner (line 224) | def inner(self, isolated: bool = False) -> "Frame":
method soft (line 230) | def soft(self) -> "te.Self":
class VisitorExit (line 246) | class VisitorExit(RuntimeError):
class DependencyFinderVisitor (line 250) | class DependencyFinderVisitor(NodeVisitor):
method __init__ (line 253) | def __init__(self) -> None:
method visit_Filter (line 257) | def visit_Filter(self, node: nodes.Filter) -> None:
method visit_Test (line 261) | def visit_Test(self, node: nodes.Test) -> None:
method visit_Block (line 265) | def visit_Block(self, node: nodes.Block) -> None:
class UndeclaredNameVisitor (line 269) | class UndeclaredNameVisitor(NodeVisitor):
method __init__ (line 275) | def __init__(self, names: t.Iterable[str]) -> None:
method visit_Name (line 279) | def visit_Name(self, node: nodes.Name) -> None:
method visit_Block (line 287) | def visit_Block(self, node: nodes.Block) -> None:
class CompilerExit (line 291) | class CompilerExit(Exception):
class CodeGenerator (line 298) | class CodeGenerator(NodeVisitor):
method __init__ (line 299) | def __init__(
method optimized (line 373) | def optimized(self) -> bool:
method fail (line 378) | def fail(self, msg: str, lineno: int) -> "te.NoReturn":
method temporary_identifier (line 382) | def temporary_identifier(self) -> str:
method buffer (line 387) | def buffer(self, frame: Frame) -> None:
method return_buffer_contents (line 392) | def return_buffer_contents(
method indent (line 412) | def indent(self) -> None:
method outdent (line 416) | def outdent(self, step: int = 1) -> None:
method start_write (line 420) | def start_write(self, frame: Frame, node: nodes.Node | None = None) ->...
method end_write (line 427) | def end_write(self, frame: Frame) -> None:
method simple_write (line 432) | def simple_write(
method blockvisit (line 440) | def blockvisit(self, nodes: t.Iterable[nodes.Node], frame: Frame) -> N...
method write (line 451) | def write(self, x: str) -> None:
method writeline (line 465) | def writeline(self, x: str, node: nodes.Node | None = None, extra: int...
method newline (line 470) | def newline(self, node: nodes.Node | None = None, extra: int = 0) -> N...
method signature (line 477) | def signature(
method pull_dependencies (line 534) | def pull_dependencies(self, nodes: t.Iterable[nodes.Node]) -> None:
method enter_frame (line 580) | def enter_frame(self, frame: Frame) -> None:
method leave_frame (line 596) | def leave_frame(self, frame: Frame, with_python_scope: bool = False) -...
method choose_async (line 604) | def choose_async(self, async_value: str = "async ", sync_value: str = ...
method func (line 607) | def func(self, name: str) -> str:
method macro_body (line 610) | def macro_body(
method macro_def (line 693) | def macro_def(self, macro_ref: MacroRef, frame: Frame) -> None:
method position (line 705) | def position(self, node: nodes.Node) -> str:
method dump_local_context (line 712) | def dump_local_context(self, frame: Frame) -> str:
method write_commons (line 719) | def write_commons(self) -> None:
method push_parameter_definitions (line 732) | def push_parameter_definitions(self, frame: Frame) -> None:
method pop_parameter_definitions (line 741) | def pop_parameter_definitions(self) -> None:
method mark_parameter_stored (line 745) | def mark_parameter_stored(self, target: str) -> None:
method push_context_reference (line 752) | def push_context_reference(self, target: str) -> None:
method pop_context_reference (line 755) | def pop_context_reference(self) -> None:
method get_context_ref (line 758) | def get_context_ref(self) -> str:
method get_resolve_func (line 761) | def get_resolve_func(self) -> str:
method derive_context (line 767) | def derive_context(self, frame: Frame) -> str:
method parameter_is_undeclared (line 770) | def parameter_is_undeclared(self, target: str) -> bool:
method push_assign_tracking (line 776) | def push_assign_tracking(self) -> None:
method pop_assign_tracking (line 780) | def pop_assign_tracking(self, frame: Frame) -> None:
method visit_Template (line 825) | def visit_Template(self, node: nodes.Template, frame: Frame | None = N...
method visit_Block (line 945) | def visit_Block(self, node: nodes.Block, frame: Frame) -> None:
method visit_Extends (line 994) | def visit_Extends(self, node: nodes.Extends, frame: Frame) -> None:
method visit_Include (line 1036) | def visit_Include(self, node: nodes.Include, frame: Frame) -> None:
method _import_common (line 1094) | def _import_common(
method visit_Import (line 1109) | def visit_Import(self, node: nodes.Import, frame: Frame) -> None:
method visit_FromImport (line 1120) | def visit_FromImport(self, node: nodes.FromImport, frame: Frame) -> None:
method visit_For (line 1175) | def visit_For(self, node: nodes.For, frame: Frame) -> None:
method visit_If (line 1314) | def visit_If(self, node: nodes.If, frame: Frame) -> None:
method visit_Macro (line 1335) | def visit_Macro(self, node: nodes.Macro, frame: Frame) -> None:
method visit_CallBlock (line 1345) | def visit_CallBlock(self, node: nodes.CallBlock, frame: Frame) -> None:
method visit_FilterBlock (line 1353) | def visit_FilterBlock(self, node: nodes.FilterBlock, frame: Frame) -> ...
method visit_With (line 1364) | def visit_With(self, node: nodes.With, frame: Frame) -> None:
method visit_ExprStmt (line 1376) | def visit_ExprStmt(self, node: nodes.ExprStmt, frame: Frame) -> None:
class _FinalizeInfo (line 1380) | class _FinalizeInfo(t.NamedTuple):
method _default_finalize (line 1385) | def _default_finalize(value: t.Any) -> t.Any:
method _make_finalize (line 1394) | def _make_finalize(self) -> _FinalizeInfo:
method _output_const_repr (line 1442) | def _output_const_repr(self, group: t.Iterable[t.Any]) -> str:
method _output_child_to_const (line 1449) | def _output_child_to_const(
method _output_child_pre (line 1470) | def _output_child_pre(
method _output_child_post (line 1486) | def _output_child_post(
method visit_Output (line 1497) | def visit_Output(self, node: nodes.Output, frame: Frame) -> None:
method visit_Assign (line 1576) | def visit_Assign(self, node: nodes.Assign, frame: Frame) -> None:
method visit_AssignBlock (line 1607) | def visit_AssignBlock(self, node: nodes.AssignBlock, frame: Frame) -> ...
method visit_Name (line 1631) | def visit_Name(self, node: nodes.Name, frame: Frame) -> None:
method visit_NSRef (line 1656) | def visit_NSRef(self, node: nodes.NSRef, frame: Frame) -> None:
method visit_Const (line 1664) | def visit_Const(self, node: nodes.Const, frame: Frame) -> None:
method visit_TemplateData (line 1671) | def visit_TemplateData(self, node: nodes.TemplateData, frame: Frame) -...
method visit_Tuple (line 1679) | def visit_Tuple(self, node: nodes.Tuple, frame: Frame) -> None:
method visit_List (line 1688) | def visit_List(self, node: nodes.List, frame: Frame) -> None:
method visit_Dict (line 1696) | def visit_Dict(self, node: nodes.Dict, frame: Frame) -> None:
method visit_Concat (line 1720) | def visit_Concat(self, node: nodes.Concat, frame: Frame) -> None:
method visit_Compare (line 1734) | def visit_Compare(self, node: nodes.Compare, frame: Frame) -> None:
method visit_Operand (line 1741) | def visit_Operand(self, node: nodes.Operand, frame: Frame) -> None:
method visit_Getattr (line 1746) | def visit_Getattr(self, node: nodes.Getattr, frame: Frame) -> None:
method visit_Getitem (line 1758) | def visit_Getitem(self, node: nodes.Getitem, frame: Frame) -> None:
method visit_Slice (line 1778) | def visit_Slice(self, node: nodes.Slice, frame: Frame) -> None:
method _filter_test_common (line 1789) | def _filter_test_common(
method visit_Filter (line 1831) | def visit_Filter(self, node: nodes.Filter, frame: Frame) -> None:
method visit_Test (line 1848) | def visit_Test(self, node: nodes.Test, frame: Frame) -> None:
method visit_CondExpr (line 1853) | def visit_CondExpr(self, node: nodes.CondExpr, frame: Frame) -> None:
method visit_Call (line 1876) | def visit_Call(
method visit_Keyword (line 1898) | def visit_Keyword(self, node: nodes.Keyword, frame: Frame) -> None:
method visit_MarkSafe (line 1904) | def visit_MarkSafe(self, node: nodes.MarkSafe, frame: Frame) -> None:
method visit_MarkSafeIfAutoescape (line 1909) | def visit_MarkSafeIfAutoescape(
method visit_EnvironmentAttribute (line 1916) | def visit_EnvironmentAttribute(
method visit_ExtensionAttribute (line 1921) | def visit_ExtensionAttribute(
method visit_ImportedName (line 1926) | def visit_ImportedName(self, node: nodes.ImportedName, frame: Frame) -...
method visit_InternalName (line 1929) | def visit_InternalName(self, node: nodes.InternalName, frame: Frame) -...
method visit_ContextReference (line 1932) | def visit_ContextReference(
method visit_DerivedContextReference (line 1937) | def visit_DerivedContextReference(
method visit_Continue (line 1942) | def visit_Continue(self, node: nodes.Continue, frame: Frame) -> None:
method visit_Break (line 1945) | def visit_Break(self, node: nodes.Break, frame: Frame) -> None:
method visit_Scope (line 1948) | def visit_Scope(self, node: nodes.Scope, frame: Frame) -> None:
method visit_OverlayScope (line 1955) | def visit_OverlayScope(self, node: nodes.OverlayScope, frame: Frame) -...
method visit_EvalContextModifier (line 1969) | def visit_EvalContextModifier(
method visit_ScopedEvalContextModifier (line 1982) | def visit_ScopedEvalContextModifier(
FILE: src/jinja2/debug.py
function rewrite_traceback_stack (line 14) | def rewrite_traceback_stack(source: str | None = None) -> BaseException:
function fake_traceback (line 76) | def fake_traceback( # type: ignore
function get_template_locals (line 131) | def get_template_locals(real_locals: t.Mapping[str, t.Any]) -> dict[str,...
FILE: src/jinja2/environment.py
function get_spontaneous_environment (line 70) | def get_spontaneous_environment(cls: type[_env_bound], *args: t.Any) -> ...
function create_cache (line 83) | def create_cache(
function copy_cache (line 96) | def copy_cache(
function load_extensions (line 109) | def load_extensions(
function _environment_config_check (line 127) | def _environment_config_check(environment: _env_bound) -> _env_bound:
class Environment (line 145) | class Environment:
method __init__ (line 295) | def __init__(
method add_extension (line 371) | def add_extension(self, extension: str | type["Extension"]) -> None:
method extend (line 378) | def extend(self, **attributes: t.Any) -> None:
method overlay (line 387) | def overlay(
method lexer (line 459) | def lexer(self) -> Lexer:
method iter_extensions (line 463) | def iter_extensions(self) -> t.Iterator["Extension"]:
method getitem (line 467) | def getitem(self, obj: t.Any, argument: str | t.Any) -> t.Any | Undefi...
method getattr (line 484) | def getattr(self, obj: t.Any, attribute: str) -> t.Any:
method _filter_test_common (line 497) | def _filter_test_common(
method call_filter (line 551) | def call_filter(
method call_test (line 572) | def call_test(
method parse (line 598) | def parse(
method _parse (line 617) | def _parse(
method lex (line 623) | def lex(
method preprocess (line 644) | def preprocess(
method _tokenize (line 660) | def _tokenize(
method _generate (line 681) | def _generate(
method _compile (line 702) | def _compile(self, source: str, filename: str) -> CodeType:
method compile (line 711) | def compile(
method compile (line 721) | def compile(
method compile (line 731) | def compile(
method compile_expression (line 772) | def compile_expression(
method compile_templates (line 817) | def compile_templates(
method list_templates (line 898) | def list_templates(
method handle_exception (line 935) | def handle_exception(self, source: str | None = None) -> "te.NoReturn":
method join_path (line 943) | def join_path(self, template: str, parent: str) -> str:
method _load_template (line 956) | def _load_template(
method get_template (line 981) | def get_template(
method select_template (line 1018) | def select_template(
method get_or_select_template (line 1072) | def get_or_select_template(
method from_string (line 1089) | def from_string(
method make_globals (line 1110) | def make_globals(
class Template (line 1133) | class Template:
method __new__ (line 1164) | def __new__(
method from_code (line 1214) | def from_code(
method from_module_dict (line 1231) | def from_module_dict(
method _from_namespace (line 1245) | def _from_namespace(
method render (line 1272) | def render(self, *args: t.Any, **kwargs: t.Any) -> str:
method render_async (line 1294) | async def render_async(self, *args: t.Any, **kwargs: t.Any) -> str:
method stream (line 1317) | def stream(self, *args: t.Any, **kwargs: t.Any) -> "TemplateStream":
method generate (line 1323) | def generate(self, *args: t.Any, **kwargs: t.Any) -> t.Iterator[str]:
method generate_async (line 1347) | async def generate_async(
method new_context (line 1369) | def new_context(
method make_module (line 1386) | def make_module(
method make_module_async (line 1401) | async def make_module_async(
method _get_default_module (line 1420) | def _get_default_module(self, ctx: Context | None = None) -> "Template...
method _get_default_module_async (line 1446) | async def _get_default_module_async(
method module (line 1461) | def module(self) -> "TemplateModule":
method get_corresponding_lineno (line 1476) | def get_corresponding_lineno(self, lineno: int) -> int:
method is_up_to_date (line 1486) | def is_up_to_date(self) -> bool:
method debug_info (line 1493) | def debug_info(self) -> list[tuple[int, int]]:
method __repr__ (line 1503) | def __repr__(self) -> str:
class TemplateModule (line 1511) | class TemplateModule:
method __init__ (line 1517) | def __init__(
method __html__ (line 1537) | def __html__(self) -> Markup:
method __str__ (line 1540) | def __str__(self) -> str:
method __repr__ (line 1543) | def __repr__(self) -> str:
class TemplateExpression (line 1551) | class TemplateExpression:
method __init__ (line 1557) | def __init__(self, template: Template, undefined_to_none: bool) -> None:
method __call__ (line 1561) | def __call__(self, *args: t.Any, **kwargs: t.Any) -> t.Any | None:
class TemplateStream (line 1570) | class TemplateStream:
method __init__ (line 1581) | def __init__(self, gen: t.Iterator[str]) -> None:
method dump (line 1585) | def dump(
method disable_buffering (line 1625) | def disable_buffering(self) -> None:
method _buffered_generator (line 1630) | def _buffered_generator(self, size: int) -> t.Iterator[str]:
method enable_buffering (line 1649) | def enable_buffering(self, size: int = 5) -> None:
method __iter__ (line 1657) | def __iter__(self) -> "TemplateStream":
method __next__ (line 1660) | def __next__(self) -> str:
FILE: src/jinja2/exceptions.py
class TemplateError (line 7) | class TemplateError(Exception):
method __init__ (line 10) | def __init__(self, message: str | None = None) -> None:
method message (line 14) | def message(self) -> str | None:
class TemplateNotFound (line 18) | class TemplateNotFound(IOError, LookupError, TemplateError):
method __init__ (line 30) | def __init__(
method __str__ (line 49) | def __str__(self) -> str:
class TemplatesNotFound (line 53) | class TemplatesNotFound(TemplateNotFound):
method __init__ (line 65) | def __init__(
class TemplateSyntaxError (line 88) | class TemplateSyntaxError(TemplateError):
method __init__ (line 91) | def __init__(
method __str__ (line 108) | def __str__(self) -> str:
method __reduce__ (line 131) | def __reduce__(self): # type: ignore
class TemplateAssertionError (line 139) | class TemplateAssertionError(TemplateSyntaxError):
class TemplateRuntimeError (line 147) | class TemplateRuntimeError(TemplateError):
class UndefinedError (line 153) | class UndefinedError(TemplateRuntimeError):
class SecurityError (line 157) | class SecurityError(TemplateRuntimeError):
class FilterArgumentError (line 163) | class FilterArgumentError(TemplateRuntimeError):
FILE: src/jinja2/ext.py
class _TranslationsBasic (line 27) | class _TranslationsBasic(te.Protocol):
method gettext (line 28) | def gettext(self, message: str) -> str: ...
method ngettext (line 30) | def ngettext(self, singular: str, plural: str, n: int) -> str:
class _TranslationsContext (line 33) | class _TranslationsContext(_TranslationsBasic):
method pgettext (line 34) | def pgettext(self, context: str, message: str) -> str: ...
method npgettext (line 36) | def npgettext(
class Extension (line 55) | class Extension:
method __init_subclass__ (line 76) | def __init_subclass__(cls) -> None:
method __init__ (line 89) | def __init__(self, environment: Environment) -> None:
method bind (line 92) | def bind(self, environment: Environment) -> "te.Self":
method preprocess (line 99) | def preprocess(
method filter_stream (line 108) | def filter_stream(
method parse (line 118) | def parse(self, parser: "Parser") -> nodes.Node | list[nodes.Node]:
method attr (line 126) | def attr(self, name: str, lineno: int | None = None) -> nodes.Extensio...
method call_method (line 136) | def call_method(
function _gettext_alias (line 163) | def _gettext_alias(
function _make_new_gettext (line 169) | def _make_new_gettext(func: t.Callable[[str], str]) -> t.Callable[..., s...
function _make_new_ngettext (line 183) | def _make_new_ngettext(func: t.Callable[[str, str, int], str]) -> t.Call...
function _make_new_pgettext (line 202) | def _make_new_pgettext(func: t.Callable[[str, str], str]) -> t.Callable[...
function _make_new_npgettext (line 219) | def _make_new_npgettext(
class InternationalizationExtension (line 244) | class InternationalizationExtension(Extension):
method __init__ (line 256) | def __init__(self, environment: Environment) -> None:
method _install (line 268) | def _install(
method _install_null (line 286) | def _install_null(self, newstyle: bool | None = None) -> None:
method _install_callables (line 298) | def _install_callables(
method _uninstall (line 322) | def _uninstall(self, translations: "_SupportedTranslations") -> None:
method _extract (line 326) | def _extract(
method parse (line 335) | def parse(self, parser: "Parser") -> nodes.Node | list[nodes.Node]:
method _trim_whitespace (line 457) | def _trim_whitespace(self, string: str, _ws_re: t.Pattern[str] = _ws_r...
method _parse_block (line 460) | def _parse_block(
method _make_node (line 507) | def _make_node(
class ExprStmtExtension (line 570) | class ExprStmtExtension(Extension):
method parse (line 577) | def parse(self, parser: "Parser") -> nodes.ExprStmt:
class LoopControlExtension (line 583) | class LoopControlExtension(Extension):
method parse (line 588) | def parse(self, parser: "Parser") -> nodes.Break | nodes.Continue:
class DebugExtension (line 595) | class DebugExtension(Extension):
method parse (line 618) | def parse(self, parser: "Parser") -> nodes.Output:
method _render (line 624) | def _render(self, context: Context) -> str:
function extract_from_ast (line 635) | def extract_from_ast(
class _CommentFinder (line 712) | class _CommentFinder:
method __init__ (line 719) | def __init__(
method find_backwards (line 727) | def find_backwards(self, offset: int) -> list[str]:
method find_comments (line 743) | def find_comments(self, lineno: int) -> list[str]:
function babel_extract (line 752) | def babel_extract(
FILE: src/jinja2/filters.py
class HasHTML (line 39) | class HasHTML(te.Protocol):
method __html__ (line 40) | def __html__(self) -> str:
function ignore_case (line 49) | def ignore_case(value: V) -> V:
function make_attrgetter (line 58) | def make_attrgetter(
function make_multi_attrgetter (line 86) | def make_multi_attrgetter(
function _prepare_attribute_parts (line 127) | def _prepare_attribute_parts(
function do_forceescape (line 139) | def do_forceescape(value: "str | HasHTML") -> Markup:
function do_urlencode (line 147) | def do_urlencode(
function do_replace (line 179) | def do_replace(
function do_upper (line 214) | def do_upper(s: str) -> str:
function do_lower (line 219) | def do_lower(s: str) -> str:
function do_items (line 224) | def do_items(value: t.Mapping[K, V] | Undefined) -> t.Iterator[tuple[K, ...
function do_xmlattr (line 260) | def do_xmlattr(
function do_capitalize (line 321) | def do_capitalize(s: str) -> str:
function do_title (line 331) | def do_title(s: str) -> str:
function do_dictsort (line 344) | def do_dictsort(
function do_sort (line 386) | def do_sort(
function sync_do_unique (line 442) | def sync_do_unique(
function do_unique (line 475) | async def do_unique(
function _min_or_max (line 486) | def _min_or_max(
function do_min (line 507) | def do_min(
function do_max (line 527) | def do_max(
function do_default (line 546) | def do_default(
function sync_do_join (line 580) | def sync_do_join(
function do_join (line 638) | async def do_join(
function do_center (line 647) | def do_center(value: str, width: int = 80) -> str:
function sync_do_first (line 653) | def sync_do_first(environment: "Environment", seq: "t.Iterable[V]") -> "...
function do_first (line 662) | async def do_first(
function do_last (line 672) | def do_last(environment: "Environment", seq: "t.Reversible[V]") -> "V | ...
function do_random (line 692) | def do_random(context: "Context", seq: "t.Sequence[V]") -> "V | Undefined":
function do_filesizeformat (line 700) | def do_filesizeformat(value: str | float | int, binary: bool = False) ->...
function do_pprint (line 733) | def do_pprint(value: t.Any) -> str:
function do_urlize (line 742) | def do_urlize(
function do_indent (line 821) | def do_indent(
function do_truncate (line 870) | def do_truncate(
function do_wordwrap (line 918) | def do_wordwrap(
function do_wordcount (line 976) | def do_wordcount(s: str) -> int:
function do_int (line 981) | def do_int(value: t.Any, default: int = 0, base: int = 10) -> int:
function do_float (line 1003) | def do_float(value: t.Any, default: float = 0.0) -> float:
function do_format (line 1014) | def do_format(value: str, *args: t.Any, **kwargs: t.Any) -> str:
function do_trim (line 1042) | def do_trim(value: str, chars: str | None = None) -> str:
function do_striptags (line 1047) | def do_striptags(value: "str | HasHTML") -> str:
function sync_do_slice (line 1055) | def sync_do_slice(
function do_slice (line 1099) | async def do_slice(
function do_batch (line 1107) | def do_batch(
function do_round (line 1144) | def do_round(
class _GroupTuple (line 1184) | class _GroupTuple(t.NamedTuple):
method __repr__ (line 1190) | def __repr__(self) -> str:
method __str__ (line 1193) | def __str__(self) -> str:
function sync_do_groupby (line 1198) | def sync_do_groupby(
function do_groupby (line 1282) | async def do_groupby(
function sync_do_sum (line 1309) | def sync_do_sum(
function do_sum (line 1336) | async def do_sum(
function sync_do_list (line 1357) | def sync_do_list(value: "t.Iterable[V]") -> "list[V]":
function do_list (line 1365) | async def do_list(value: "t.AsyncIterable[V] | t.Iterable[V]") -> "list[...
function do_mark_safe (line 1369) | def do_mark_safe(value: str) -> Markup:
function do_mark_unsafe (line 1376) | def do_mark_unsafe(value: str) -> str:
function do_reverse (line 1382) | def do_reverse(value: str) -> str: ...
function do_reverse (line 1386) | def do_reverse(value: "t.Iterable[V]") -> "t.Iterable[V]": ...
function do_reverse (line 1389) | def do_reverse(value: str | t.Iterable[V]) -> str | t.Iterable[V]:
function do_attr (line 1408) | def do_attr(environment: "Environment", obj: t.Any, name: str) -> Undefi...
function sync_do_map (line 1431) | def sync_do_map(
function sync_do_map (line 1441) | def sync_do_map(
function sync_do_map (line 1451) | def sync_do_map(
function do_map (line 1501) | def do_map(
function do_map (line 1511) | def do_map(
function do_map (line 1521) | async def do_map(
function sync_do_select (line 1535) | def sync_do_select(
function do_select (line 1566) | async def do_select(
function sync_do_reject (line 1576) | def sync_do_reject(
function do_reject (line 1602) | async def do_reject(
function sync_do_selectattr (line 1612) | def sync_do_selectattr(
function do_selectattr (line 1642) | async def do_selectattr(
function sync_do_rejectattr (line 1652) | def sync_do_rejectattr(
function do_rejectattr (line 1680) | async def do_rejectattr(
function do_tojson (line 1690) | def do_tojson(
function prepare_map (line 1718) | def prepare_map(
function prepare_select_or_reject (line 1746) | def prepare_select_or_reject(
function select_or_reject (line 1780) | def select_or_reject(
function async_select_or_reject (line 1796) | async def async_select_or_reject(
FILE: src/jinja2/idtracking.py
function find_symbols (line 15) | def find_symbols(
function symbols_for_node (line 25) | def symbols_for_node(
class Symbols (line 33) | class Symbols:
method __init__ (line 34) | def __init__(
method analyze_node (line 49) | def analyze_node(self, node: nodes.Node, **kwargs: t.Any) -> None:
method _define_ref (line 53) | def _define_ref(self, name: str, load: tuple[str, str | None] | None =...
method find_load (line 60) | def find_load(self, target: str) -> t.Any | None:
method find_ref (line 69) | def find_ref(self, name: str) -> str | None:
method ref (line 78) | def ref(self, name: str) -> str:
method copy (line 87) | def copy(self) -> "te.Self":
method store (line 95) | def store(self, name: str) -> None:
method declare_parameter (line 113) | def declare_parameter(self, name: str) -> str:
method load (line 117) | def load(self, name: str) -> None:
method branch_update (line 121) | def branch_update(self, branch_symbols: t.Sequence["Symbols"]) -> None:
method dump_stores (line 145) | def dump_stores(self) -> dict[str, str]:
method dump_param_targets (line 158) | def dump_param_targets(self) -> set[str]:
class RootVisitor (line 172) | class RootVisitor(NodeVisitor):
method __init__ (line 173) | def __init__(self, symbols: "Symbols") -> None:
method _simple_visit (line 176) | def _simple_visit(self, node: nodes.Node, **kwargs: t.Any) -> None:
method visit_AssignBlock (line 188) | def visit_AssignBlock(self, node: nodes.AssignBlock, **kwargs: t.Any) ...
method visit_CallBlock (line 192) | def visit_CallBlock(self, node: nodes.CallBlock, **kwargs: t.Any) -> N...
method visit_OverlayScope (line 196) | def visit_OverlayScope(self, node: nodes.OverlayScope, **kwargs: t.Any...
method visit_For (line 200) | def visit_For(
method visit_With (line 220) | def visit_With(self, node: nodes.With, **kwargs: t.Any) -> None:
method generic_visit (line 226) | def generic_visit(self, node: nodes.Node, *args: t.Any, **kwargs: t.An...
class FrameSymbolVisitor (line 230) | class FrameSymbolVisitor(NodeVisitor):
method __init__ (line 233) | def __init__(self, symbols: "Symbols") -> None:
method visit_Name (line 236) | def visit_Name(
method visit_NSRef (line 247) | def visit_NSRef(self, node: nodes.NSRef, **kwargs: t.Any) -> None:
method visit_If (line 250) | def visit_If(self, node: nodes.If, **kwargs: t.Any) -> None:
method visit_Macro (line 268) | def visit_Macro(self, node: nodes.Macro, **kwargs: t.Any) -> None:
method visit_Import (line 271) | def visit_Import(self, node: nodes.Import, **kwargs: t.Any) -> None:
method visit_FromImport (line 275) | def visit_FromImport(self, node: nodes.FromImport, **kwargs: t.Any) ->...
method visit_Assign (line 284) | def visit_Assign(self, node: nodes.Assign, **kwargs: t.Any) -> None:
method visit_For (line 289) | def visit_For(self, node: nodes.For, **kwargs: t.Any) -> None:
method visit_CallBlock (line 295) | def visit_CallBlock(self, node: nodes.CallBlock, **kwargs: t.Any) -> N...
method visit_FilterBlock (line 298) | def visit_FilterBlock(self, node: nodes.FilterBlock, **kwargs: t.Any) ...
method visit_With (line 301) | def visit_With(self, node: nodes.With, **kwargs: t.Any) -> None:
method visit_AssignBlock (line 305) | def visit_AssignBlock(self, node: nodes.AssignBlock, **kwargs: t.Any) ...
method visit_Scope (line 309) | def visit_Scope(self, node: nodes.Scope, **kwargs: t.Any) -> None:
method visit_Block (line 312) | def visit_Block(self, node: nodes.Block, **kwargs: t.Any) -> None:
method visit_OverlayScope (line 315) | def visit_OverlayScope(self, node: nodes.OverlayScope, **kwargs: t.Any...
FILE: src/jinja2/lexer.py
function _describe_token_type (line 165) | def _describe_token_type(token_type: str) -> str:
function describe_token (line 185) | def describe_token(token: "Token") -> str:
function describe_token_expr (line 193) | def describe_token_expr(expr: str) -> str:
function count_newlines (line 206) | def count_newlines(value: str) -> int:
function compile_rules (line 213) | def compile_rules(environment: "Environment") -> list[tuple[str, str]]:
class Failure (line 254) | class Failure:
method __init__ (line 259) | def __init__(
method __call__ (line 265) | def __call__(self, lineno: int, filename: str | None) -> "te.NoReturn":
class Token (line 269) | class Token(t.NamedTuple):
method __str__ (line 274) | def __str__(self) -> str:
method test (line 277) | def test(self, expr: str) -> bool:
method test_any (line 292) | def test_any(self, *iterable: str) -> bool:
class TokenStreamIterator (line 297) | class TokenStreamIterator:
method __init__ (line 302) | def __init__(self, stream: "TokenStream") -> None:
method __iter__ (line 305) | def __iter__(self) -> "TokenStreamIterator":
method __next__ (line 308) | def __next__(self) -> Token:
class TokenStream (line 319) | class TokenStream:
method __init__ (line 325) | def __init__(
method __iter__ (line 339) | def __iter__(self) -> TokenStreamIterator:
method __bool__ (line 342) | def __bool__(self) -> bool:
method eos (line 346) | def eos(self) -> bool:
method push (line 350) | def push(self, token: Token) -> None:
method look (line 354) | def look(self) -> Token:
method skip (line 362) | def skip(self, n: int = 1) -> None:
method next_if (line 367) | def next_if(self, expr: str) -> Token | None:
method skip_if (line 376) | def skip_if(self, expr: str) -> bool:
method __next__ (line 380) | def __next__(self) -> Token:
method close (line 397) | def close(self) -> None:
method expect (line 403) | def expect(self, expr: str) -> Token:
function get_lexer (line 428) | def get_lexer(environment: "Environment") -> "Lexer":
class OptionalLStrip (line 452) | class OptionalLStrip(tuple): # type: ignore[type-arg]
method __new__ (line 461) | def __new__(cls, *members, **kwargs): # type: ignore
class _Rule (line 465) | class _Rule(t.NamedTuple):
class Lexer (line 471) | class Lexer:
method __init__ (line 479) | def __init__(self, environment: "Environment") -> None:
method _normalize_newlines (line 598) | def _normalize_newlines(self, value: str) -> str:
method tokenize (line 604) | def tokenize(
method wrap (line 615) | def wrap(
method tokeniter (line 669) | def tokeniter(
FILE: src/jinja2/loaders.py
function split_template_path (line 25) | def split_template_path(template: str) -> list[str]:
class BaseLoader (line 42) | class BaseLoader:
method get_source (line 75) | def get_source(
method list_templates (line 101) | def list_templates(self) -> list[str]:
method load (line 108) | def load(
class FileSystemLoader (line 152) | class FileSystemLoader(BaseLoader):
method __init__ (line 179) | def __init__(
method get_source (line 194) | def get_source(
method list_templates (line 228) | def list_templates(self) -> list[str]:
function _get_zipimporter_files (line 248) | def _get_zipimporter_files(z: t.Any) -> dict[str, object]:
function _get_zipimporter_files (line 259) | def _get_zipimporter_files(z: t.Any) -> dict[str, object]:
class PackageLoader (line 269) | class PackageLoader(BaseLoader):
method __init__ (line 302) | def __init__(
method get_source (line 365) | def get_source(
method list_templates (line 403) | def list_templates(self) -> list[str]:
class DictLoader (line 432) | class DictLoader(BaseLoader):
method __init__ (line 441) | def __init__(self, mapping: t.Mapping[str, str]) -> None:
method get_source (line 444) | def get_source(
method list_templates (line 452) | def list_templates(self) -> list[str]:
class FunctionLoader (line 456) | class FunctionLoader(BaseLoader):
method __init__ (line 474) | def __init__(
method get_source (line 483) | def get_source(
class PrefixLoader (line 497) | class PrefixLoader(BaseLoader):
method __init__ (line 512) | def __init__(
method get_loader (line 518) | def get_loader(self, template: str) -> tuple[BaseLoader, str]:
method get_source (line 526) | def get_source(
method load (line 538) | def load(
method list_templates (line 552) | def list_templates(self) -> list[str]:
class ChoiceLoader (line 560) | class ChoiceLoader(BaseLoader):
method __init__ (line 574) | def __init__(self, loaders: t.Sequence[BaseLoader]) -> None:
method get_source (line 577) | def get_source(
method load (line 588) | def load(
method list_templates (line 601) | def list_templates(self) -> list[str]:
class _TemplateModule (line 608) | class _TemplateModule(ModuleType):
class ModuleLoader (line 612) | class ModuleLoader(BaseLoader):
method __init__ (line 624) | def __init__(
method get_template_key (line 652) | def get_template_key(name: str) -> str:
method get_module_filename (line 656) | def get_module_filename(name: str) -> str:
method load (line 660) | def load(
FILE: src/jinja2/meta.py
class TrackingCodeGenerator (line 15) | class TrackingCodeGenerator(CodeGenerator):
method __init__ (line 18) | def __init__(self, environment: "Environment") -> None:
method write (line 22) | def write(self, x: str) -> None:
method enter_frame (line 25) | def enter_frame(self, frame: Frame) -> None:
function find_undeclared_variables (line 34) | def find_undeclared_variables(ast: nodes.Template) -> set[str]:
function find_referenced_templates (line 62) | def find_referenced_templates(ast: nodes.Template) -> t.Iterator[str | N...
FILE: src/jinja2/nativetypes.py
function native_concat (line 16) | def native_concat(values: t.Iterable[t.Any]) -> t.Any | None:
class NativeCodeGenerator (line 50) | class NativeCodeGenerator(CodeGenerator):
method _default_finalize (line 56) | def _default_finalize(value: t.Any) -> t.Any:
method _output_const_repr (line 59) | def _output_const_repr(self, group: t.Iterable[t.Any]) -> str:
method _output_child_to_const (line 62) | def _output_child_to_const(
method _output_child_pre (line 75) | def _output_child_pre(
method _output_child_post (line 81) | def _output_child_post(
class NativeEnvironment (line 88) | class NativeEnvironment(Environment):
class NativeTemplate (line 95) | class NativeTemplate(Template):
method render (line 98) | def render(self, *args: t.Any, **kwargs: t.Any) -> t.Any:
method render_async (line 114) | async def render_async(self, *args: t.Any, **kwargs: t.Any) -> t.Any:
FILE: src/jinja2/nodes.py
class Impossible (line 50) | class Impossible(Exception):
class NodeType (line 54) | class NodeType(type):
method __new__ (line 59) | def __new__(mcs, name, bases, d): # type: ignore
class EvalContext (line 71) | class EvalContext:
method __init__ (line 76) | def __init__(
method save (line 86) | def save(self) -> t.Mapping[str, t.Any]:
method revert (line 89) | def revert(self, old: t.Mapping[str, t.Any]) -> None:
function get_eval_context (line 94) | def get_eval_context(node: "Node", ctx: EvalContext | None) -> EvalContext:
class Node (line 105) | class Node(metaclass=NodeType):
method __init__ (line 129) | def __init__(self, *fields: t.Any, **attributes: t.Any) -> None:
method iter_fields (line 147) | def iter_fields(
method iter_child_nodes (line 169) | def iter_child_nodes(
method find (line 186) | def find(self, node_type: type[_NodeBound]) -> _NodeBound | None:
method find_all (line 195) | def find_all(
method set_ctx (line 206) | def set_ctx(self, ctx: str) -> "Node":
method set_lineno (line 220) | def set_lineno(self, lineno: int, override: bool = False) -> "Node":
method set_environment (line 231) | def set_environment(self, environment: "Environment") -> "Node":
method __eq__ (line 240) | def __eq__(self, other: t.Any) -> bool:
method __repr__ (line 248) | def __repr__(self) -> str:
method dump (line 252) | def dump(self) -> str:
class Stmt (line 282) | class Stmt(Node):
class Helper (line 288) | class Helper(Node):
class Template (line 294) | class Template(Node):
class Output (line 303) | class Output(Stmt):
class Extends (line 312) | class Extends(Stmt):
class For (line 319) | class For(Stmt):
class If (line 337) | class If(Stmt):
class Macro (line 347) | class Macro(Stmt):
class CallBlock (line 360) | class CallBlock(Stmt):
class FilterBlock (line 372) | class FilterBlock(Stmt):
class With (line 380) | class With(Stmt):
class Block (line 393) | class Block(Stmt):
class Include (line 407) | class Include(Stmt):
class Import (line 416) | class Import(Stmt):
class FromImport (line 425) | class FromImport(Stmt):
class ExprStmt (line 443) | class ExprStmt(Stmt):
class Assign (line 450) | class Assign(Stmt):
class AssignBlock (line 458) | class AssignBlock(Stmt):
class Expr (line 467) | class Expr(Node):
method as_const (line 472) | def as_const(self, eval_ctx: EvalContext | None = None) -> t.Any:
method can_assign (line 485) | def can_assign(self) -> bool:
class BinExpr (line 490) | class BinExpr(Expr):
method as_const (line 499) | def as_const(self, eval_ctx: EvalContext | None = None) -> t.Any:
class UnaryExpr (line 515) | class UnaryExpr(Expr):
method as_const (line 523) | def as_const(self, eval_ctx: EvalContext | None = None) -> t.Any:
class Name (line 539) | class Name(Expr):
method can_assign (line 552) | def can_assign(self) -> bool:
class NSRef (line 556) | class NSRef(Expr):
method can_assign (line 563) | def can_assign(self) -> bool:
class Literal (line 571) | class Literal(Expr):
class Const (line 577) | class Const(Literal):
method as_const (line 587) | def as_const(self, eval_ctx: EvalContext | None = None) -> t.Any:
method from_untrusted (line 591) | def from_untrusted(
class TemplateData (line 608) | class TemplateData(Literal):
method as_const (line 614) | def as_const(self, eval_ctx: EvalContext | None = None) -> str:
class Tuple (line 623) | class Tuple(Literal):
method as_const (line 633) | def as_const(self, eval_ctx: EvalContext | None = None) -> tuple[t.Any...
method can_assign (line 637) | def can_assign(self) -> bool:
class List (line 644) | class List(Literal):
method as_const (line 650) | def as_const(self, eval_ctx: EvalContext | None = None) -> list[t.Any]:
class Dict (line 655) | class Dict(Literal):
method as_const (line 663) | def as_const(self, eval_ctx: EvalContext | None = None) -> dict[t.Any,...
class Pair (line 668) | class Pair(Helper):
method as_const (line 675) | def as_const(self, eval_ctx: EvalContext | None = None) -> tuple[t.Any...
class Keyword (line 680) | class Keyword(Helper):
method as_const (line 687) | def as_const(self, eval_ctx: EvalContext | None = None) -> tuple[str, ...
class CondExpr (line 692) | class CondExpr(Expr):
method as_const (line 702) | def as_const(self, eval_ctx: EvalContext | None = None) -> t.Any:
function args_as_const (line 714) | def args_as_const(
class _FilterTestCommon (line 735) | class _FilterTestCommon(Expr):
method as_const (line 746) | def as_const(self, eval_ctx: EvalContext | None = None) -> t.Any:
class Filter (line 783) | class Filter(_FilterTestCommon):
method as_const (line 793) | def as_const(self, eval_ctx: EvalContext | None = None) -> t.Any:
class Test (line 800) | class Test(_FilterTestCommon):
class Call (line 813) | class Call(Expr):
class Getitem (line 829) | class Getitem(Expr):
method as_const (line 837) | def as_const(self, eval_ctx: EvalContext | None = None) -> t.Any:
class Getattr (line 851) | class Getattr(Expr):
method as_const (line 861) | def as_const(self, eval_ctx: EvalContext | None = None) -> t.Any:
class Slice (line 873) | class Slice(Expr):
method as_const (line 883) | def as_const(self, eval_ctx: EvalContext | None = None) -> slice:
class Concat (line 894) | class Concat(Expr):
method as_const (line 902) | def as_const(self, eval_ctx: EvalContext | None = None) -> str:
class Compare (line 907) | class Compare(Expr):
method as_const (line 916) | def as_const(self, eval_ctx: EvalContext | None = None) -> t.Any:
class Operand (line 935) | class Operand(Helper):
class Mul (line 943) | class Mul(BinExpr):
class Div (line 949) | class Div(BinExpr):
class FloorDiv (line 955) | class FloorDiv(BinExpr):
class Add (line 963) | class Add(BinExpr):
class Sub (line 969) | class Sub(BinExpr):
class Mod (line 975) | class Mod(BinExpr):
class Pow (line 981) | class Pow(BinExpr):
class And (line 987) | class And(BinExpr):
method as_const (line 992) | def as_const(self, eval_ctx: EvalContext | None = None) -> t.Any:
class Or (line 997) | class Or(BinExpr):
method as_const (line 1002) | def as_const(self, eval_ctx: EvalContext | None = None) -> t.Any:
class Not (line 1007) | class Not(UnaryExpr):
class Neg (line 1013) | class Neg(UnaryExpr):
class Pos (line 1019) | class Pos(UnaryExpr):
class EnvironmentAttribute (line 1028) | class EnvironmentAttribute(Expr):
class ExtensionAttribute (line 1037) | class ExtensionAttribute(Expr):
class ImportedName (line 1050) | class ImportedName(Expr):
class InternalName (line 1061) | class InternalName(Expr):
method __init__ (line 1072) | def __init__(self) -> None:
class MarkSafe (line 1079) | class MarkSafe(Expr):
method as_const (line 1085) | def as_const(self, eval_ctx: EvalContext | None = None) -> Markup:
class MarkSafeIfAutoescape (line 1090) | class MarkSafeIfAutoescape(Expr):
method as_const (line 1100) | def as_const(self, eval_ctx: EvalContext | None = None) -> Markup | t....
class ContextReference (line 1110) | class ContextReference(Expr):
class DerivedContextReference (line 1128) | class DerivedContextReference(Expr):
class Continue (line 1137) | class Continue(Stmt):
class Break (line 1141) | class Break(Stmt):
class Scope (line 1145) | class Scope(Stmt):
class OverlayScope (line 1152) | class OverlayScope(Stmt):
class EvalContextModifier (line 1171) | class EvalContextModifier(Stmt):
class ScopedEvalContextModifier (line 1184) | class ScopedEvalContextModifier(EvalContextModifier):
function _failing_new (line 1195) | def _failing_new(*args: t.Any, **kwargs: t.Any) -> "te.NoReturn":
FILE: src/jinja2/optimizer.py
function optimize (line 20) | def optimize(node: nodes.Node, environment: "Environment") -> nodes.Node:
class Optimizer (line 27) | class Optimizer(NodeTransformer):
method __init__ (line 28) | def __init__(self, environment: "Environment | None") -> None:
method generic_visit (line 31) | def generic_visit(
FILE: src/jinja2/parser.py
class Parser (line 48) | class Parser:
method __init__ (line 53) | def __init__(
method fail (line 76) | def fail(
method _fail_ut_eof (line 90) | def _fail_ut_eof(
method fail_unknown_tag (line 130) | def fail_unknown_tag(self, name: str, lineno: int | None = None) -> "t...
method fail_eof (line 137) | def fail_eof(
method is_tuple_end (line 148) | def is_tuple_end(self, extra_end_rules: tuple[str, ...] | None = None)...
method free_identifier (line 156) | def free_identifier(self, lineno: int | None = None) -> nodes.Internal...
method parse_statement (line 163) | def parse_statement(self) -> nodes.Node | list[nodes.Node]:
method parse_statements (line 192) | def parse_statements(
method parse_set (line 221) | def parse_set(self) -> nodes.Assign | nodes.AssignBlock:
method parse_for (line 232) | def parse_for(self) -> nodes.For:
method parse_if (line 251) | def parse_if(self) -> nodes.If:
method parse_with (line 269) | def parse_with(self) -> nodes.With:
method parse_autoescape (line 286) | def parse_autoescape(self) -> nodes.Scope:
method parse_block (line 292) | def parse_block(self) -> nodes.Block:
method parse_extends (line 324) | def parse_extends(self) -> nodes.Extends:
method parse_import_context (line 329) | def parse_import_context(
method parse_include (line 341) | def parse_include(self) -> nodes.Include:
method parse_import (line 353) | def parse_import(self) -> nodes.Import:
method parse_from (line 360) | def parse_from(self) -> nodes.FromImport:
method parse_signature (line 402) | def parse_signature(self, node: _MacroCall) -> None:
method parse_call_block (line 418) | def parse_call_block(self) -> nodes.CallBlock:
method parse_filter_block (line 433) | def parse_filter_block(self) -> nodes.FilterBlock:
method parse_macro (line 439) | def parse_macro(self) -> nodes.Macro:
method parse_print (line 446) | def parse_print(self) -> nodes.Output:
method parse_assign_target (line 456) | def parse_assign_target(
method parse_assign_target (line 461) | def parse_assign_target(
method parse_assign_target (line 469) | def parse_assign_target(
method parse_expression (line 508) | def parse_expression(self, with_condexpr: bool = True) -> nodes.Expr:
method parse_condexpr (line 517) | def parse_condexpr(self) -> nodes.Expr:
method parse_or (line 532) | def parse_or(self) -> nodes.Expr:
method parse_and (line 541) | def parse_and(self) -> nodes.Expr:
method parse_not (line 550) | def parse_not(self) -> nodes.Expr:
method parse_compare (line 556) | def parse_compare(self) -> nodes.Expr:
method parse_math1 (line 579) | def parse_math1(self) -> nodes.Expr:
method parse_concat (line 590) | def parse_concat(self) -> nodes.Expr:
method parse_math2 (line 600) | def parse_math2(self) -> nodes.Expr:
method parse_pow (line 611) | def parse_pow(self) -> nodes.Expr:
method parse_unary (line 621) | def parse_unary(self, with_filter: bool = True) -> nodes.Expr:
method parse_primary (line 639) | def parse_primary(self, with_namespace: bool = False) -> nodes.Expr:
method parse_tuple (line 681) | def parse_tuple(
method parse_list (line 750) | def parse_list(self) -> nodes.List:
method parse_dict (line 762) | def parse_dict(self) -> nodes.Dict:
method parse_postfix (line 777) | def parse_postfix(self, node: nodes.Expr) -> nodes.Expr:
method parse_filter_expr (line 790) | def parse_filter_expr(self, node: nodes.Expr) -> nodes.Expr:
method parse_subscript (line 805) | def parse_subscript(self, node: nodes.Expr) -> nodes.Getattr | nodes.G...
method parse_subscribed (line 834) | def parse_subscribed(self) -> nodes.Expr:
method parse_call_args (line 866) | def parse_call_args(
method parse_call (line 922) | def parse_call(self, node: nodes.Expr) -> nodes.Call:
method parse_filter (line 929) | def parse_filter(
method parse_test (line 952) | def parse_test(self, node: nodes.Expr) -> nodes.Expr:
method subparse (line 990) | def subparse(self, end_tokens: tuple[str, ...] | None = None) -> list[...
method parse (line 1037) | def parse(self) -> nodes.Template:
FILE: src/jinja2/runtime.py
class LoopRenderFunc (line 37) | class LoopRenderFunc(te.Protocol):
method __call__ (line 38) | def __call__(
function identity (line 70) | def identity(x: V) -> V:
function markup_join (line 77) | def markup_join(seq: t.Iterable[t.Any]) -> str:
function str_join (line 88) | def str_join(seq: t.Iterable[t.Any]) -> str:
function new_context (line 93) | def new_context(
class TemplateReference (line 122) | class TemplateReference:
method __init__ (line 125) | def __init__(self, context: "Context") -> None:
method __getitem__ (line 128) | def __getitem__(self, name: str) -> t.Any:
method __repr__ (line 132) | def __repr__(self) -> str:
function _dict_method_all (line 136) | def _dict_method_all(dict_method: F) -> F:
class Context (line 145) | class Context:
method __init__ (line 165) | def __init__(
method super (line 186) | def super(
method get (line 200) | def get(self, key: str, default: t.Any = None) -> t.Any:
method resolve (line 212) | def resolve(self, key: str) -> t.Union[t.Any, "Undefined"]:
method resolve_or_missing (line 229) | def resolve_or_missing(self, key: str) -> t.Any:
method get_exported (line 247) | def get_exported(self) -> dict[str, t.Any]:
method get_all (line 251) | def get_all(self) -> dict[str, t.Any]:
method call (line 263) | def call(
method derived (line 310) | def derived(self, locals: dict[str, t.Any] | None = None) -> "Context":
method __contains__ (line 326) | def __contains__(self, name: str) -> bool:
method __getitem__ (line 329) | def __getitem__(self, key: str) -> t.Any:
method __repr__ (line 340) | def __repr__(self) -> str:
class BlockReference (line 344) | class BlockReference:
method __init__ (line 347) | def __init__(
method super (line 360) | def super(self) -> t.Union["BlockReference", "Undefined"]:
method _async_call (line 369) | async def _async_call(self) -> str:
method __call__ (line 380) | def __call__(self) -> str:
class LoopContext (line 394) | class LoopContext:
method __init__ (line 408) | def __init__(
method _to_iterator (line 431) | def _to_iterator(iterable: t.Iterable[V]) -> t.Iterator[V]:
method length (line 435) | def length(self) -> int:
method __len__ (line 453) | def __len__(self) -> int:
method depth (line 457) | def depth(self) -> int:
method index (line 462) | def index(self) -> int:
method revindex0 (line 467) | def revindex0(self) -> int:
method revindex (line 475) | def revindex(self) -> int:
method first (line 483) | def first(self) -> bool:
method _peek_next (line 487) | def _peek_next(self) -> t.Any:
method last (line 500) | def last(self) -> bool:
method previtem (line 510) | def previtem(self) -> t.Union[t.Any, "Undefined"]:
method nextitem (line 520) | def nextitem(self) -> t.Union[t.Any, "Undefined"]:
method cycle (line 535) | def cycle(self, *args: V) -> V:
method changed (line 546) | def changed(self, *value: t.Any) -> bool:
method __iter__ (line 558) | def __iter__(self) -> "LoopContext":
method __next__ (line 561) | def __next__(self) -> tuple[t.Any, "LoopContext"]:
method __call__ (line 574) | def __call__(self, iterable: t.Iterable[V]) -> str:
method __repr__ (line 587) | def __repr__(self) -> str:
class AsyncLoopContext (line 591) | class AsyncLoopContext(LoopContext):
method _to_iterator (line 595) | def _to_iterator( # type: ignore
method length (line 601) | async def length(self) -> int: # type: ignore
method revindex0 (line 615) | async def revindex0(self) -> int: # type: ignore
method revindex (line 619) | async def revindex(self) -> int: # type: ignore
method _peek_next (line 622) | async def _peek_next(self) -> t.Any:
method last (line 634) | async def last(self) -> bool: # type: ignore
method nextitem (line 638) | async def nextitem(self) -> t.Union[t.Any, "Undefined"]:
method __aiter__ (line 646) | def __aiter__(self) -> "AsyncLoopContext":
method __anext__ (line 649) | async def __anext__(self) -> tuple[t.Any, "AsyncLoopContext"]:
class Macro (line 662) | class Macro:
method __init__ (line 665) | def __init__(
method __call__ (line 696) | def __call__(self, *args: t.Any, **kwargs: t.Any) -> str:
method _async_invoke (line 772) | async def _async_invoke(self, arguments: list[t.Any], autoescape: bool...
method _invoke (line 780) | def _invoke(self, arguments: list[t.Any], autoescape: bool) -> str:
method __repr__ (line 791) | def __repr__(self) -> str:
class Undefined (line 796) | class Undefined:
method __init__ (line 818) | def __init__(
method _undefined_message (line 831) | def _undefined_message(self) -> str:
method _fail_with_undefined_error (line 853) | def _fail_with_undefined_error(
method __getattr__ (line 862) | def __getattr__(self, name: str) -> t.Any:
method __eq__ (line 883) | def __eq__(self, other: t.Any) -> bool:
method __ne__ (line 886) | def __ne__(self, other: t.Any) -> bool:
method __hash__ (line 889) | def __hash__(self) -> int:
method __str__ (line 892) | def __str__(self) -> str:
method __len__ (line 895) | def __len__(self) -> int:
method __iter__ (line 898) | def __iter__(self) -> t.Iterator[t.Any]:
method __aiter__ (line 901) | async def __aiter__(self) -> t.AsyncIterator[t.Any]:
method __bool__ (line 905) | def __bool__(self) -> bool:
method __repr__ (line 908) | def __repr__(self) -> str:
function make_logging_undefined (line 912) | def make_logging_undefined(
class ChainableUndefined (line 970) | class ChainableUndefined(Undefined):
method __html__ (line 988) | def __html__(self) -> str:
method __getattr__ (line 991) | def __getattr__(self, name: str) -> "ChainableUndefined":
method __getitem__ (line 1003) | def __getitem__(self, _name: str) -> "ChainableUndefined": # type: ig...
class DebugUndefined (line 1007) | class DebugUndefined(Undefined):
method __str__ (line 1023) | def __str__(self) -> str:
class StrictUndefined (line 1039) | class StrictUndefined(Undefined):
FILE: src/jinja2/sandbox.py
function safe_range (line 87) | def safe_range(*args: int) -> range:
function unsafe (line 102) | def unsafe(f: F) -> F:
function is_internal_attribute (line 115) | def is_internal_attribute(obj: t.Any, attr: str) -> bool:
function modifies_known_mutable (line 152) | def modifies_known_mutable(obj: t.Any, attr: str) -> bool:
class SandboxedEnvironment (line 177) | class SandboxedEnvironment(Environment):
method __init__ (line 242) | def __init__(self, *args: t.Any, **kwargs: t.Any) -> None:
method is_safe_attribute (line 248) | def is_safe_attribute(self, obj: t.Any, attr: str, value: t.Any) -> bool:
method is_safe_callable (line 257) | def is_safe_callable(self, obj: t.Any) -> bool:
method call_binop (line 268) | def call_binop(
method call_unop (line 279) | def call_unop(self, context: Context, operator: str, arg: t.Any) -> t....
method getitem (line 288) | def getitem(self, obj: t.Any, argument: str | t.Any) -> t.Any | Undefi...
method getattr (line 312) | def getattr(self, obj: t.Any, attribute: str) -> t.Any | Undefined:
method unsafe_undefined (line 332) | def unsafe_undefined(self, obj: t.Any, attribute: str) -> Undefined:
method wrap_str_format (line 342) | def wrap_str_format(self, value: t.Any) -> t.Callable[..., str] | None:
method call (line 386) | def call(
class ImmutableSandboxedEnvironment (line 402) | class ImmutableSandboxedEnvironment(SandboxedEnvironment):
method is_safe_attribute (line 408) | def is_safe_attribute(self, obj: t.Any, attr: str, value: t.Any) -> bool:
class SandboxedFormatter (line 415) | class SandboxedFormatter(Formatter):
method __init__ (line 416) | def __init__(self, env: Environment, **kwargs: t.Any) -> None:
method get_field (line 420) | def get_field(
class SandboxedEscapeFormatter (line 433) | class SandboxedEscapeFormatter(SandboxedFormatter, EscapeFormatter):
FILE: src/jinja2/tests.py
function test_odd (line 15) | def test_odd(value: int) -> bool:
function test_even (line 20) | def test_even(value: int) -> bool:
function test_divisibleby (line 25) | def test_divisibleby(value: int, num: int) -> bool:
function test_defined (line 30) | def test_defined(value: t.Any) -> bool:
function test_undefined (line 47) | def test_undefined(value: t.Any) -> bool:
function test_filter (line 53) | def test_filter(env: "Environment", value: str) -> bool:
function test_test (line 71) | def test_test(env: "Environment", value: str) -> bool:
function test_none (line 92) | def test_none(value: t.Any) -> bool:
function test_boolean (line 97) | def test_boolean(value: t.Any) -> bool:
function test_false (line 105) | def test_false(value: t.Any) -> bool:
function test_true (line 113) | def test_true(value: t.Any) -> bool:
function test_integer (line 122) | def test_integer(value: t.Any) -> bool:
function test_float (line 131) | def test_float(value: t.Any) -> bool:
function test_lower (line 139) | def test_lower(value: str) -> bool:
function test_upper (line 144) | def test_upper(value: str) -> bool:
function test_string (line 149) | def test_string(value: t.Any) -> bool:
function test_mapping (line 154) | def test_mapping(value: t.Any) -> bool:
function test_number (line 162) | def test_number(value: t.Any) -> bool:
function test_sequence (line 167) | def test_sequence(value: t.Any) -> bool:
function test_sameas (line 180) | def test_sameas(value: t.Any, other: t.Any) -> bool:
function test_iterable (line 193) | def test_iterable(value: t.Any) -> bool:
function test_escaped (line 203) | def test_escaped(value: t.Any) -> bool:
function test_in (line 208) | def test_in(value: t.Any, seq: t.Container[t.Any]) -> bool:
FILE: src/jinja2/utils.py
class _MissingType (line 22) | class _MissingType:
method __repr__ (line 23) | def __repr__(self) -> str:
method __reduce__ (line 26) | def __reduce__(self) -> str:
function pass_context (line 38) | def pass_context(f: F) -> F:
function pass_eval_context (line 55) | def pass_eval_context(f: F) -> F:
function pass_environment (line 72) | def pass_environment(f: F) -> F:
class _PassArg (line 85) | class _PassArg(enum.Enum):
method from_obj (line 91) | def from_obj(cls, obj: F) -> t.Optional["_PassArg"]:
function internalcode (line 98) | def internalcode(f: F) -> F:
function is_undefined (line 104) | def is_undefined(obj: t.Any) -> bool:
function consume (line 121) | def consume(iterable: t.Iterable[t.Any]) -> None:
function clear_caches (line 127) | def clear_caches() -> None:
function import_string (line 140) | def import_string(import_name: str, silent: bool = False) -> t.Any:
function open_if_exists (line 164) | def open_if_exists(filename: str, mode: str = "rb") -> t.IO[t.Any] | None:
function object_type_repr (line 174) | def object_type_repr(obj: t.Any) -> str:
function pformat (line 192) | def pformat(obj: t.Any) -> str:
function urlize (line 230) | def urlize(
function generate_lorem_ipsum (line 353) | def generate_lorem_ipsum(
function url_quote (line 408) | def url_quote(obj: t.Any, charset: str = "utf-8", for_qs: bool = False) ...
class LRUCache (line 432) | class LRUCache:
method __init__ (line 439) | def __init__(self, capacity: int) -> None:
method _postinit (line 445) | def _postinit(self) -> None:
method __getstate__ (line 453) | def __getstate__(self) -> t.Mapping[str, t.Any]:
method __setstate__ (line 460) | def __setstate__(self, d: t.Mapping[str, t.Any]) -> None:
method __getnewargs__ (line 464) | def __getnewargs__(self) -> tuple[t.Any, ...]:
method copy (line 467) | def copy(self) -> "te.Self":
method get (line 474) | def get(self, key: t.Any, default: t.Any = None) -> t.Any:
method setdefault (line 481) | def setdefault(self, key: t.Any, default: t.Any = None) -> t.Any:
method clear (line 491) | def clear(self) -> None:
method __contains__ (line 497) | def __contains__(self, key: t.Any) -> bool:
method __len__ (line 501) | def __len__(self) -> int:
method __repr__ (line 505) | def __repr__(self) -> str:
method __getitem__ (line 508) | def __getitem__(self, key: t.Any) -> t.Any:
method __setitem__ (line 530) | def __setitem__(self, key: t.Any, value: t.Any) -> None:
method __delitem__ (line 543) | def __delitem__(self, key: t.Any) -> None:
method items (line 555) | def items(self) -> t.Iterable[tuple[t.Any, t.Any]]:
method values (line 561) | def values(self) -> t.Iterable[t.Any]:
method keys (line 565) | def keys(self) -> t.Iterable[t.Any]:
method __iter__ (line 569) | def __iter__(self) -> t.Iterator[t.Any]:
method __reversed__ (line 572) | def __reversed__(self) -> t.Iterator[t.Any]:
function select_autoescape (line 581) | def select_autoescape(
function htmlsafe_json_dumps (line 637) | def htmlsafe_json_dumps(
class Cycler (line 677) | class Cycler:
method __init__ (line 703) | def __init__(self, *items: t.Any) -> None:
method reset (line 709) | def reset(self) -> None:
method current (line 714) | def current(self) -> t.Any:
method next (line 720) | def next(self) -> t.Any:
class Joiner (line 731) | class Joiner:
method __init__ (line 734) | def __init__(self, sep: str = ", ") -> None:
method __call__ (line 738) | def __call__(self) -> str:
class Namespace (line 745) | class Namespace:
method __init__ (line 749) | def __init__(*args: t.Any, **kwargs: t.Any) -> None: # noqa: B902
method __getattribute__ (line 753) | def __getattribute__(self, name: str) -> t.Any:
method __setitem__ (line 762) | def __setitem__(self, name: str, value: t.Any) -> None:
method __repr__ (line 765) | def __repr__(self) -> str:
FILE: src/jinja2/visitor.py
class VisitCallable (line 12) | class VisitCallable(te.Protocol):
method __call__ (line 13) | def __call__(self, node: Node, *args: t.Any, **kwargs: t.Any) -> t.Any...
class NodeVisitor (line 16) | class NodeVisitor:
method get_visitor (line 28) | def get_visitor(self, node: Node) -> "VisitCallable | None":
method visit (line 35) | def visit(self, node: Node, *args: t.Any, **kwargs: t.Any) -> t.Any:
method generic_visit (line 44) | def generic_visit(self, node: Node, *args: t.Any, **kwargs: t.Any) -> ...
class NodeTransformer (line 50) | class NodeTransformer(NodeVisitor):
method generic_visit (line 61) | def generic_visit(self, node: Node, *args: t.Any, **kwargs: t.Any) -> ...
method visit_list (line 83) | def visit_list(self, node: Node, *args: t.Any, **kwargs: t.Any) -> lis...
FILE: tests/conftest.py
function _asyncio_run (line 11) | def _asyncio_run(async_fn, *args):
function run_async_fn (line 16) | def run_async_fn(request):
function env (line 21) | def env():
function dict_loader (line 27) | def dict_loader():
function package_loader (line 33) | def package_loader():
function filesystem_loader (line 39) | def filesystem_loader():
function function_loader (line 46) | def function_loader():
function choice_loader (line 52) | def choice_loader(dict_loader, package_loader):
function prefix_loader (line 58) | def prefix_loader(filesystem_loader, dict_loader):
FILE: tests/test_api.py
class TestExtendedAPI (line 27) | class TestExtendedAPI:
method test_item_and_attribute (line 28) | def test_item_and_attribute(self, env):
method test_finalize (line 39) | def test_finalize(self):
method test_finalize_constant_expression (line 44) | def test_finalize_constant_expression(self):
method test_no_finalize_template_data (line 49) | def test_no_finalize_template_data(self):
method test_context_finalize (line 55) | def test_context_finalize(self):
method test_eval_finalize (line 64) | def test_eval_finalize(self):
method test_env_autoescape (line 73) | def test_env_autoescape(self):
method test_cycler (line 84) | def test_cycler(self, env):
method test_expressions (line 95) | def test_expressions(self, env):
method test_template_passthrough (line 105) | def test_template_passthrough(self, env):
method test_get_template_undefined (line 112) | def test_get_template_undefined(self, env):
method test_autoescape_autoselect (line 135) | def test_autoescape_autoselect(self, env):
method test_sandbox_max_range (line 152) | def test_sandbox_max_range(self, env):
class TestMeta (line 163) | class TestMeta:
method test_find_undeclared_variables (line 164) | def test_find_undeclared_variables(self, env):
method test_find_refererenced_templates (line 182) | def test_find_refererenced_templates(self, env):
method test_find_included_templates (line 198) | def test_find_included_templates(self, env):
class TestStreaming (line 216) | class TestStreaming:
method test_basic_streaming (line 217) | def test_basic_streaming(self, env):
method test_buffered_streaming (line 226) | def test_buffered_streaming(self, env):
method test_streaming_behavior (line 236) | def test_streaming_behavior(self, env):
method test_dump_stream (line 245) | def test_dump_stream(self, env):
class TestUndefined (line 256) | class TestUndefined:
method test_stopiteration_is_undefined (line 257) | def test_stopiteration_is_undefined(self):
method test_undefined_and_special_attributes (line 266) | def test_undefined_and_special_attributes(self):
method test_undefined_attribute_error (line 270) | def test_undefined_attribute_error(self):
method test_logging_undefined (line 285) | def test_logging_undefined(self):
method test_default_undefined (line 311) | def test_default_undefined(self):
method test_chainable_undefined (line 327) | def test_chainable_undefined(self):
method test_debug_undefined (line 351) | def test_debug_undefined(self):
method test_strict_undefined (line 368) | def test_strict_undefined(self):
method test_indexing_gives_undefined (line 385) | def test_indexing_gives_undefined(self):
method test_none_gives_proper_error (line 389) | def test_none_gives_proper_error(self):
method test_object_repr (line 393) | def test_object_repr(self):
class TestLowLevel (line 400) | class TestLowLevel:
method test_custom_code_generator (line 401) | def test_custom_code_generator(self):
method test_custom_context (line 417) | def test_custom_context(self):
function test_overlay_enable_async (line 430) | def test_overlay_enable_async(env):
FILE: tests/test_async.py
function test_basic_async (line 14) | def test_basic_async(run_async_fn):
function test_await_on_calls (line 26) | def test_await_on_calls(run_async_fn):
function test_await_on_calls_normal_render (line 42) | def test_await_on_calls_normal_render():
function test_await_and_macros (line 55) | def test_await_and_macros(run_async_fn):
function test_async_blocks (line 71) | def test_async_blocks(run_async_fn):
function test_async_generate (line 85) | def test_async_generate():
function test_async_iteration_in_templates (line 91) | def test_async_iteration_in_templates():
function test_async_iteration_in_templates_extended (line 102) | def test_async_iteration_in_templates_extended():
function test_env_async (line 112) | def test_env_async():
class TestAsyncImports (line 127) | class TestAsyncImports:
method test_context_imports (line 128) | def test_context_imports(self, test_env_async):
method test_trailing_comma (line 150) | def test_trailing_comma(self, test_env_async):
method test_exports (line 157) | def test_exports(self, test_env_async, run_async_fn):
method test_import_with_globals (line 174) | def test_import_with_globals(self, test_env_async):
method test_import_with_globals_override (line 183) | def test_import_with_globals_override(self, test_env_async):
method test_from_import_with_globals (line 190) | def test_from_import_with_globals(self, test_env_async):
class TestAsyncIncludes (line 198) | class TestAsyncIncludes:
method test_context_include (line 199) | def test_context_include(self, test_env_async):
method test_choice_includes (line 207) | def test_choice_includes(self, test_env_async):
method test_include_ignoring_missing (line 241) | def test_include_ignoring_missing(self, test_env_async):
method test_context_include_with_overrides (line 250) | def test_context_include_with_overrides(self, test_env_async):
method test_unoptimized_scopes (line 261) | def test_unoptimized_scopes(self, test_env_async):
method test_unoptimized_scopes_autoescape (line 275) | def test_unoptimized_scopes_autoescape(self):
class TestAsyncForLoop (line 295) | class TestAsyncForLoop:
method test_simple (line 296) | def test_simple(self, test_env_async):
method test_else (line 300) | def test_else(self, test_env_async):
method test_empty_blocks (line 306) | def test_empty_blocks(self, test_env_async):
method test_context_vars (line 315) | def test_context_vars(self, test_env_async, transform):
method test_cycling (line 324) | def test_cycling(self, test_env_async):
method test_lookaround (line 333) | def test_lookaround(self, test_env_async):
method test_changed (line 343) | def test_changed(self, test_env_async):
method test_scope (line 352) | def test_scope(self, test_env_async):
method test_varlen (line 357) | def test_varlen(self, test_env_async):
method test_noniter (line 367) | def test_noniter(self, test_env_async):
method test_recursive (line 371) | def test_recursive(self, test_env_async):
method test_recursive_lookaround (line 388) | def test_recursive_lookaround(self, test_env_async):
method test_recursive_depth0 (line 407) | def test_recursive_depth0(self, test_env_async):
method test_recursive_depth (line 423) | def test_recursive_depth(self, test_env_async):
method test_looploop (line 439) | def test_looploop(self, test_env_async):
method test_reversed_bug (line 450) | def test_reversed_bug(self, test_env_async):
method test_loop_errors (line 456) | def test_loop_errors(self, test_env_async, run_async_fn):
method test_loop_filter (line 470) | def test_loop_filter(self, test_env_async):
method test_scoped_special_var (line 482) | def test_scoped_special_var(self, test_env_async):
method test_scoped_loop_var (line 489) | def test_scoped_loop_var(self, test_env_async):
method test_recursive_empty_loop_iter (line 501) | def test_recursive_empty_loop_iter(self, test_env_async):
method test_call_in_loop (line 509) | def test_call_in_loop(self, test_env_async):
method test_scoping_bug (line 525) | def test_scoping_bug(self, test_env_async):
method test_unpacking (line 535) | def test_unpacking(self, test_env_async):
method test_recursive_loop_filter (line 541) | def test_recursive_loop_filter(self, test_env_async):
method test_nonrecursive_loop_filter (line 566) | def test_nonrecursive_loop_filter(self, test_env_async):
method test_bare_async (line 589) | def test_bare_async(self, test_env_async):
method test_awaitable_property_slicing (line 593) | def test_awaitable_property_slicing(self, test_env_async):
function test_namespace_awaitable (line 598) | def test_namespace_awaitable(test_env_async, run_async_fn):
function test_chainable_undefined_aiter (line 609) | def test_chainable_undefined_aiter(run_async_fn):
function async_native_env (line 623) | def async_native_env():
function test_native_async (line 627) | def test_native_async(async_native_env, run_async_fn):
function test_native_list_async (line 636) | def test_native_list_async(async_native_env, run_async_fn):
function test_getitem_after_filter (line 645) | def test_getitem_after_filter():
function test_getitem_after_call (line 653) | def test_getitem_after_call():
function test_basic_generate_async (line 661) | def test_basic_generate_async(run_async_fn):
function test_include_generate_async (line 677) | def test_include_generate_async(run_async_fn, test_env_async):
function test_blocks_generate_async (line 691) | def test_blocks_generate_async(run_async_fn):
function test_async_extend (line 709) | def test_async_extend(run_async_fn, test_env_async):
FILE: tests/test_async_filters.py
function make_aiter (line 11) | async def make_aiter(iter):
function mark_dualiter (line 16) | def mark_dualiter(parameter, factory):
function env_async (line 26) | def env_async():
function closing_factory (line 31) | async def closing_factory():
function test_first (line 47) | def test_first(env_async, foo, run_async_fn):
function test_groupby (line 66) | def test_groupby(env_async, items):
function test_groupby_case (line 88) | def test_groupby_case(env_async, case_sensitive, expect):
function test_groupby_tuple_index (line 102) | def test_groupby_tuple_index(env_async, items):
function make_articles (line 112) | def make_articles():
function test_groupby_multidot (line 124) | def test_groupby_multidot(env_async, articles):
function test_join_env_int (line 139) | def test_join_env_int(env_async, int_items):
function test_join_string_list (line 146) | def test_join_string_list(string_items):
function make_users (line 152) | def make_users():
function test_join_attribute (line 158) | def test_join_attribute(env_async, users):
function test_simple_reject (line 164) | def test_simple_reject(env_async, items):
function test_bool_reject (line 170) | def test_bool_reject(env_async, items):
function test_simple_select (line 176) | def test_simple_select(env_async, items):
function test_bool_select (line 182) | def test_bool_select(env_async, items):
function make_users (line 187) | def make_users(): # type: ignore
function test_simple_select_attr (line 197) | def test_simple_select_attr(env_async, users):
function test_simple_map (line 205) | def test_simple_map(env_async, items):
function test_map_sum (line 210) | def test_map_sum(env_async): # async map + async filter
function test_attribute_map (line 216) | def test_attribute_map(env_async, users):
function test_empty_map (line 221) | def test_empty_map(env_async):
function test_sum (line 227) | def test_sum(env_async, items):
function test_sum_attributes (line 233) | def test_sum_attributes(env_async, items):
function test_sum_attributes_nested (line 238) | def test_sum_attributes_nested(env_async):
function test_sum_attributes_tuple (line 252) | def test_sum_attributes_tuple(env_async):
function test_slice (line 258) | def test_slice(env_async, items):
function test_unique_with_async_gen (line 269) | def test_unique_with_async_gen(env_async):
function test_custom_async_filter (line 276) | def test_custom_async_filter(env_async, run_async_fn):
function test_custom_async_iteratable_filter (line 292) | def test_custom_async_iteratable_filter(env_async, items, run_async_fn):
FILE: tests/test_bytecode_cache.py
function env (line 11) | def env(package_loader, tmp_path):
class TestByteCodeCache (line 16) | class TestByteCodeCache:
method test_simple (line 17) | def test_simple(self, env):
class MockMemcached (line 23) | class MockMemcached:
class Error (line 24) | class Error(Exception):
method get (line 31) | def get(self, key):
method set (line 34) | def set(self, key, value, timeout=None):
method get_side_effect (line 39) | def get_side_effect(self, key):
method set_side_effect (line 42) | def set_side_effect(self, *args):
class TestMemcachedBytecodeCache (line 46) | class TestMemcachedBytecodeCache:
method test_dump_load (line 47) | def test_dump_load(self):
method test_exception (line 60) | def test_exception(self):
FILE: tests/test_compile.py
function test_filters_deterministic (line 11) | def test_filters_deterministic(tmp_path):
function test_import_as_with_context_deterministic (line 23) | def test_import_as_with_context_deterministic(tmp_path):
function test_top_level_set_vars_unpacking_deterministic (line 34) | def test_top_level_set_vars_unpacking_deterministic(tmp_path):
function test_loop_set_vars_unpacking_deterministic (line 59) | def test_loop_set_vars_unpacking_deterministic(tmp_path):
function test_block_set_vars_unpacking_deterministic (line 77) | def test_block_set_vars_unpacking_deterministic(tmp_path):
function test_undefined_import_curly_name (line 95) | def test_undefined_import_curly_name():
FILE: tests/test_core_tags.py
function env_trim (line 11) | def env_trim():
class TestForLoop (line 15) | class TestForLoop:
method test_simple (line 16) | def test_simple(self, env):
method test_else (line 20) | def test_else(self, env):
method test_else_scoping_item (line 24) | def test_else_scoping_item(self, env):
method test_empty_blocks (line 28) | def test_empty_blocks(self, env):
method test_context_vars (line 32) | def test_context_vars(self, env):
method test_cycling (line 69) | def test_cycling(self, env):
method test_lookaround (line 78) | def test_lookaround(self, env):
method test_changed (line 88) | def test_changed(self, env):
method test_scope (line 97) | def test_scope(self, env):
method test_varlen (line 102) | def test_varlen(self, env):
method test_noniter (line 107) | def test_noniter(self, env):
method test_recursive (line 111) | def test_recursive(self, env):
method test_recursive_lookaround (line 128) | def test_recursive_lookaround(self, env):
method test_recursive_depth0 (line 147) | def test_recursive_depth0(self, env):
method test_recursive_depth (line 164) | def test_recursive_depth(self, env):
method test_looploop (line 181) | def test_looploop(self, env):
method test_reversed_bug (line 192) | def test_reversed_bug(self, env):
method test_loop_errors (line 198) | def test_loop_errors(self, env):
method test_loop_filter (line 210) | def test_loop_filter(self, env):
method test_loop_unassignable (line 222) | def test_loop_unassignable(self, env):
method test_scoped_special_var (line 227) | def test_scoped_special_var(self, env):
method test_scoped_loop_var (line 234) | def test_scoped_loop_var(self, env):
method test_recursive_empty_loop_iter (line 246) | def test_recursive_empty_loop_iter(self, env):
method test_call_in_loop (line 254) | def test_call_in_loop(self, env):
method test_scoping_bug (line 270) | def test_scoping_bug(self, env):
method test_unpacking (line 280) | def test_unpacking(self, env):
method test_intended_scoping_with_set (line 286) | def test_intended_scoping_with_set(self, env):
class TestIfCondition (line 299) | class TestIfCondition:
method test_simple (line 300) | def test_simple(self, env):
method test_elif (line 304) | def test_elif(self, env):
method test_elif_deep (line 311) | def test_elif_deep(self, env):
method test_else (line 318) | def test_else(self, env):
method test_empty (line 322) | def test_empty(self, env):
method test_complete (line 326) | def test_complete(self, env):
method test_no_scope (line 332) | def test_no_scope(self, env):
class TestMacros (line 339) | class TestMacros:
method test_simple (line 340) | def test_simple(self, env_trim):
method test_scoping (line 348) | def test_scoping(self, env_trim):
method test_arguments (line 358) | def test_arguments(self, env_trim):
method test_arguments_defaults_nonsense (line 366) | def test_arguments_defaults_nonsense(self, env_trim):
method test_caller_defaults_nonsense (line 374) | def test_caller_defaults_nonsense(self, env_trim):
method test_varargs (line 383) | def test_varargs(self, env_trim):
method test_simple_call (line 391) | def test_simple_call(self, env_trim):
method test_complex_call (line 399) | def test_complex_call(self, env_trim):
method test_caller_undefined (line 407) | def test_caller_undefined(self, env_trim):
method test_include (line 416) | def test_include(self, env_trim):
method test_macro_api (line 425) | def test_macro_api(self, env_trim):
method test_callself (line 442) | def test_callself(self, env_trim):
method test_macro_defaults_self_ref (line 450) | def test_macro_defaults_self_ref(self, env):
class TestSet (line 463) | class TestSet:
method test_normal (line 464) | def test_normal(self, env_trim):
method test_block (line 469) | def test_block(self, env_trim):
method test_block_escaping (line 474) | def test_block_escaping(self):
method test_set_invalid (line 481) | def test_set_invalid(self, env_trim):
method test_namespace_redefined (line 489) | def test_namespace_redefined(self, env_trim):
method test_namespace (line 494) | def test_namespace(self, env_trim):
method test_namespace_block (line 500) | def test_namespace_block(self, env_trim):
method test_init_namespace (line 506) | def test_init_namespace(self, env_trim):
method test_namespace_loop (line 514) | def test_namespace_loop(self, env_trim):
method test_namespace_macro (line 527) | def test_namespace_macro(self, env_trim):
method test_namespace_set_tuple (line 539) | def test_namespace_set_tuple(self, env_trim):
method test_block_escaping_filtered (line 547) | def test_block_escaping_filtered(self):
method test_block_filtered (line 554) | def test_block_filtered(self, env_trim):
method test_block_filtered_set (line 561) | def test_block_filtered_set(self, env_trim):
class TestWith (line 578) | class TestWith:
method test_with (line 579) | def test_with(self, env):
method test_with_argument_scoping (line 593) | def test_with_argument_scoping(self, env):
FILE: tests/test_debug.py
function fs_env (line 14) | def fs_env(filesystem_loader):
class TestDebug (line 19) | class TestDebug:
method assert_traceback_matches (line 20) | def assert_traceback_matches(self, callback, expected_tb):
method test_runtime_error (line 30) | def test_runtime_error(self, fs_env):
method test_syntax_error (line 48) | def test_syntax_error(self, fs_env):
method test_regular_syntax_error (line 63) | def test_regular_syntax_error(self, fs_env):
method test_pickleable_syntax_error (line 77) | def test_pickleable_syntax_error(self, fs_env):
method test_include_syntax_error_source (line 83) | def test_include_syntax_error_source(self, filesystem_loader):
method test_local_extraction (line 99) | def test_local_extraction(self):
method test_get_corresponding_lineno_traceback (line 115) | def test_get_corresponding_lineno_traceback(self, fs_env):
FILE: tests/test_ext.py
function _get_with_context (line 74) | def _get_with_context(value, ctx=None):
function gettext (line 82) | def gettext(context, string):
function ngettext (line 89) | def ngettext(context, s, p, n):
function pgettext (line 101) | def pgettext(context, c, s):
function npgettext (line 108) | def npgettext(context, c, s, p, n):
class ExampleExtension (line 152) | class ExampleExtension(Extension):
method parse (line 157) | def parse(self, parser):
method _dump (line 172) | def _dump(self, sandboxed, ext_attr, imported_object, context):
class DerivedExampleExtension (line 179) | class DerivedExampleExtension(ExampleExtension):
class PreprocessorExtension (line 183) | class PreprocessorExtension(Extension):
method preprocess (line 184) | def preprocess(self, source, name, filename=None):
class StreamFilterExtension (line 188) | class StreamFilterExtension(Extension):
method filter_stream (line 189) | def filter_stream(self, stream):
method interpolate (line 196) | def interpolate(self, token):
class TestExtensions (line 219) | class TestExtensions:
method test_extend_late (line 220) | def test_extend_late(self):
method test_loop_controls (line 225) | def test_loop_controls(self):
method test_do (line 246) | def test_do(self):
method test_extension_nodes (line 257) | def test_extension_nodes(self):
method test_contextreference_node_passes_context (line 262) | def test_contextreference_node_passes_context(self):
method test_contextreference_node_can_pass_locals (line 267) | def test_contextreference_node_can_pass_locals(self):
method test_identifier (line 274) | def test_identifier(self):
method test_rebinding (line 277) | def test_rebinding(self):
method test_preprocessor_extension (line 284) | def test_preprocessor_extension(self):
method test_streamfilter_extension (line 289) | def test_streamfilter_extension(self):
method test_extension_ordering (line 296) | def test_extension_ordering(self):
method test_debug (line 308) | def test_debug(self):
class TestInternationalization (line 317) | class TestInternationalization:
method test_trans (line 318) | def test_trans(self):
method test_trans_plural (line 322) | def test_trans_plural(self):
method test_trans_plural_with_functions (line 327) | def test_trans_plural_with_functions(self):
method test_complex_plural (line 338) | def test_complex_plural(self):
method test_trans_stringformatting (line 350) | def test_trans_stringformatting(self):
method test_trimmed (line 354) | def test_trimmed(self):
method test_trimmed_policy (line 360) | def test_trimmed_policy(self):
method test_trimmed_policy_override (line 367) | def test_trimmed_policy_override(self):
method test_trimmed_vars (line 373) | def test_trimmed_vars(self):
method test_trimmed_varname_trimmed (line 379) | def test_trimmed_varname_trimmed(self):
method test_extract (line 387) | def test_extract(self):
method test_extract_trimmed (line 403) | def test_extract_trimmed(self):
method test_extract_trimmed_option (line 420) | def test_extract_trimmed_option(self):
method test_comment_extract (line 438) | def test_comment_extract(self):
method test_extract_context (line 458) | def test_extract_context(self):
method test_nested_trans_error (line 472) | def test_nested_trans_error(self):
method test_trans_block_error (line 478) | def test_trans_block_error(self):
class TestScope (line 485) | class TestScope:
method test_basic_scope_behavior (line 486) | def test_basic_scope_behavior(self):
class TestNewstyleInternationalization (line 518) | class TestNewstyleInternationalization:
method test_trans (line 519) | def test_trans(self):
method test_trans_plural (line 523) | def test_trans_plural(self):
method test_complex_plural (line 528) | def test_complex_plural(self):
method test_trans_stringformatting (line 540) | def test_trans_stringformatting(self):
method test_newstyle_plural (line 544) | def test_newstyle_plural(self):
method test_autoescape_support (line 549) | def test_autoescape_support(self):
method test_autoescape_macros (line 562) | def test_autoescape_macros(self):
method test_num_used_twice (line 570) | def test_num_used_twice(self):
method test_num_called_num (line 574) | def test_num_called_num(self):
method test_trans_vars (line 591) | def test_trans_vars(self):
method test_novars_vars_escaping (line 599) | def test_novars_vars_escaping(self):
method test_context (line 607) | def test_context(self):
method test_context_plural (line 611) | def test_context_plural(self):
method test_context_block (line 616) | def test_context_block(self):
method test_context_plural_block (line 620) | def test_context_plural_block(self):
class TestAutoEscape (line 626) | class TestAutoEscape:
method test_scoped_setting (line 627) | def test_scoped_setting(self):
method test_nonvolatile (line 660) | def test_nonvolatile(self):
method test_volatile (line 670) | def test_volatile(self):
method test_scoping (line 679) | def test_scoping(self):
method test_volatile_scoping (line 687) | def test_volatile_scoping(self):
method test_overlay_scopes (line 712) | def test_overlay_scopes(self):
FILE: tests/test_filters.py
class Magic (line 14) | class Magic:
method __init__ (line 15) | def __init__(self, value):
method __str__ (line 18) | def __str__(self):
class Magic2 (line 22) | class Magic2:
method __init__ (line 23) | def __init__(self, value1, value2):
method __str__ (line 27) | def __str__(self):
class TestFilter (line 31) | class TestFilter:
method test_filter_calling (line 32) | def test_filter_calling(self, env):
method test_capitalize (line 36) | def test_capitalize(self, env):
method test_center (line 40) | def test_center(self, env):
method test_default (line 44) | def test_default(self, env):
method test_dictsort (line 60) | def test_dictsort(self, env, args, expect):
method test_batch (line 65) | def test_batch(self, env):
method test_slice (line 73) | def test_slice(self, env):
method test_escape (line 81) | def test_escape(self, env):
method test_trim (line 89) | def test_trim(self, env, chars, expect):
method test_striptags (line 94) | def test_striptags(self, env):
method test_filesizeformat (line 103) | def test_filesizeformat(self, env):
method test_filesizeformat_issue59 (line 122) | def test_filesizeformat_issue59(self, env):
method test_first (line 138) | def test_first(self, env):
method test_float (line 146) | def test_float(self, env, value, expect):
method test_float_default (line 150) | def test_float_default(self, env):
method test_format (line 154) | def test_format(self, env):
method _test_indent_multiline_template (line 160) | def _test_indent_multiline_template(env, markup=False):
method test_indent (line 173) | def test_indent(self, env):
method test_indent_markup_input (line 182) | def test_indent_markup_input(self, env):
method test_indent_width_string (line 188) | def test_indent_width_string(self, env):
method test_int (line 202) | def test_int(self, env, value, expect):
method test_int_base (line 210) | def test_int_base(self, env, value, base, expect):
method test_int_default (line 214) | def test_int_default(self, env):
method test_int_special_method (line 218) | def test_int_special_method(self, env):
method test_join (line 226) | def test_join(self, env):
method test_join_attribute (line 235) | def test_join_attribute(self, env):
method test_last (line 240) | def test_last(self, env):
method test_length (line 245) | def test_length(self, env):
method test_lower (line 250) | def test_lower(self, env):
method test_items (line 255) | def test_items(self, env):
method test_items_undefined (line 261) | def test_items_undefined(self, env):
method test_pprint (line 266) | def test_pprint(self, env):
method test_random (line 273) | def test_random(self, env, request):
method test_reverse (line 289) | def test_reverse(self, env):
method test_string (line 295) | def test_string(self, env):
method test_title (line 300) | def test_title(self, env):
method test_truncate (line 332) | def test_truncate(self, env):
method test_truncate_very_short (line 341) | def test_truncate_very_short(self, env):
method test_truncate_end_length (line 348) | def test_truncate_end_length(self, env):
method test_upper (line 353) | def test_upper(self, env):
method test_urlize (line 357) | def test_urlize(self, env):
method test_urlize_rel_policy (line 376) | def test_urlize_rel_policy(self):
method test_urlize_target_parameter (line 384) | def test_urlize_target_parameter(self, env):
method test_urlize_extra_schemes_parameter (line 394) | def test_urlize_extra_schemes_parameter(self, env):
method test_wordcount (line 405) | def test_wordcount(self, env):
method test_block (line 414) | def test_block(self, env):
method test_chaining (line 418) | def test_chaining(self, env):
method test_sum (line 422) | def test_sum(self, env):
method test_sum_attributes (line 426) | def test_sum_attributes(self, env):
method test_sum_attributes_nested (line 430) | def test_sum_attributes_nested(self, env):
method test_sum_attributes_tuple (line 443) | def test_sum_attributes_tuple(self, env):
method test_abs (line 447) | def test_abs(self, env):
method test_round_positive (line 451) | def test_round_positive(self, env):
method test_round_negative (line 459) | def test_round_negative(self, env):
method test_xmlattr (line 467) | def test_xmlattr(self, env):
method test_xmlattr_key_invalid (line 479) | def test_xmlattr_key_invalid(self, env: Environment, sep: str) -> None:
method test_sort1 (line 485) | def test_sort1(self, env):
method test_sort2 (line 489) | def test_sort2(self, env):
method test_sort3 (line 493) | def test_sort3(self, env):
method test_sort4 (line 497) | def test_sort4(self, env):
method test_sort5 (line 501) | def test_sort5(self, env):
method test_sort6 (line 505) | def test_sort6(self, env):
method test_sort7 (line 516) | def test_sort7(self, env):
method test_sort8 (line 527) | def test_sort8(self, env):
method test_unique (line 541) | def test_unique(self, env):
method test_unique_case_sensitive (line 545) | def test_unique_case_sensitive(self, env):
method test_unique_attribute (line 549) | def test_unique_attribute(self, env):
method test_min_max (line 564) | def test_min_max(self, env, source, expect):
method test_min_max_attribute (line 569) | def test_min_max_attribute(self, env, name, expect):
method test_groupby (line 573) | def test_groupby(self, env):
method test_groupby_tuple_index (line 585) | def test_groupby_tuple_index(self, env):
method test_groupby_multidot (line 594) | def test_groupby_multidot(self, env):
method test_groupby_default (line 615) | def test_groupby_default(self, env):
method test_groupby_case (line 637) | def test_groupby_case(self, env, case_sensitive, expect):
method test_filtertag (line 649) | def test_filtertag(self, env):
method test_replace (line 655) | def test_replace(self, env):
method test_forceescape (line 667) | def test_forceescape(self, env):
method test_safe (line 671) | def test_safe(self, env):
method test_urlencode (line 691) | def test_urlencode(self, value, expect):
method test_simple_map (line 696) | def test_simple_map(self, env):
method test_map_sum (line 701) | def test_map_sum(self, env):
method test_attribute_map (line 705) | def test_attribute_map(self, env):
method test_empty_map (line 716) | def test_empty_map(self, env):
method test_map_default (line 721) | def test_map_default(self, env):
method test_simple_select (line 744) | def test_simple_select(self, env):
method test_bool_select (line 749) | def test_bool_select(self, env):
method test_simple_reject (line 754) | def test_simple_reject(self, env):
method test_bool_reject (line 759) | def test_bool_reject(self, env):
method test_simple_select_attr (line 764) | def test_simple_select_attr(self, env):
method test_simple_reject_attr (line 777) | def test_simple_reject_attr(self, env):
method test_func_select_attr (line 790) | def test_func_select_attr(self, env):
method test_func_reject_attr (line 803) | def test_func_reject_attr(self, env):
method test_json_dump (line 816) | def test_json_dump(self):
method test_wordwrap (line 831) | def test_wordwrap(self, env):
method test_filter_undefined (line 837) | def test_filter_undefined(self, env):
method test_filter_undefined_in_if (line 841) | def test_filter_undefined_in_if(self, env):
method test_filter_undefined_in_elif (line 847) | def test_filter_undefined_in_elif(self, env):
method test_filter_undefined_in_else (line 856) | def test_filter_undefined_in_else(self, env):
method test_filter_undefined_in_nested_if (line 864) | def test_filter_undefined_in_nested_if(self, env):
method test_filter_undefined_in_condexpr (line 874) | def test_filter_undefined_in_condexpr(self, env):
FILE: tests/test_idtracking.py
function test_basics (line 5) | def test_basics():
function test_complex (line 39) | def test_complex():
function test_if_branching_stores (line 212) | def test_if_branching_stores():
function test_if_branching_stores_undefined (line 236) | def test_if_branching_stores_undefined():
function test_if_branching_multi_scope (line 261) | def test_if_branching_multi_scope():
FILE: tests/test_imports.py
function test_env (line 12) | def test_env():
class TestImports (line 26) | class TestImports:
method test_context_imports (line 27) | def test_context_imports(self, test_env):
method test_import_needs_name (line 49) | def test_import_needs_name(self, test_env):
method test_no_trailing_comma (line 56) | def test_no_trailing_comma(self, test_env):
method test_trailing_comma_with_context (line 66) | def test_trailing_comma_with_context(self, test_env):
method test_exports (line 79) | def test_exports(self, test_env):
method test_not_exported (line 95) | def test_not_exported(self, test_env):
method test_import_with_globals (line 101) | def test_import_with_globals(self, test_env):
method test_import_with_globals_override (line 110) | def test_import_with_globals_override(self, test_env):
method test_from_import_with_globals (line 117) | def test_from_import_with_globals(self, test_env):
class TestIncludes (line 125) | class TestIncludes:
method test_context_include (line 126) | def test_context_include(self, test_env):
method test_choice_includes (line 134) | def test_choice_includes(self, test_env):
method test_include_ignoring_missing (line 164) | def test_include_ignoring_missing(self, test_env):
method test_context_include_with_overrides (line 173) | def test_context_include_with_overrides(self, test_env):
method test_unoptimized_scopes (line 184) | def test_unoptimized_scopes(self, test_env):
method test_import_from_with_context (line 198) | def test_import_from_with_context(self):
FILE: tests/test_inheritance.py
function env (line 60) | def env():
class TestInheritance (line 77) | class TestInheritance:
method test_layout (line 78) | def test_layout(self, env):
method test_level1 (line 84) | def test_level1(self, env):
method test_level2 (line 90) | def test_level2(self, env):
method test_level3 (line 97) | def test_level3(self, env):
method test_level4 (line 103) | def test_level4(self, env):
method test_super (line 109) | def test_super(self, env):
method test_working (line 126) | def test_working(self, env):
method test_reuse_blocks (line 129) | def test_reuse_blocks(self, env):
method test_preserve_blocks (line 135) | def test_preserve_blocks(self, env):
method test_dynamic_inheritance (line 148) | def test_dynamic_inheritance(self, env):
method test_multi_inheritance (line 162) | def test_multi_inheritance(self, env):
method test_scoped_block (line 181) | def test_scoped_block(self, env):
method test_super_in_scoped_block (line 195) | def test_super_in_scoped_block(self, env):
method test_scoped_block_after_inheritance (line 210) | def test_scoped_block_after_inheritance(self, env):
method test_level1_required (line 237) | def test_level1_required(self, env):
method test_level2_required (line 249) | def test_level2_required(self, env):
method test_level3_required (line 265) | def test_level3_required(self, env):
method test_invalid_required (line 286) | def test_invalid_required(self, env):
method test_required_with_scope (line 319) | def test_required_with_scope(self, env):
method test_duplicate_required_or_scoped (line 343) | def test_duplicate_required_or_scoped(self, env):
class TestBugFix (line 366) | class TestBugFix:
method test_fixed_macro_scoping_bug (line 367) | def test_fixed_macro_scoping_bug(self, env):
method test_double_extends (line 405) | def test_double_extends(self, env):
FILE: tests/test_lexnparse.py
class TestTokenStream (line 15) | class TestTokenStream:
method test_simple (line 21) | def test_simple(self, env):
method test_iter (line 35) | def test_iter(self, env):
class TestLexer (line 43) | class TestLexer:
method test_raw1 (line 44) | def test_raw1(self, env):
method test_raw2 (line 50) | def test_raw2(self, env):
method test_raw3 (line 54) | def test_raw3(self, env):
method test_raw4 (line 61) | def test_raw4(self, env):
method test_balancing (line 70) | def test_balancing(self, env):
method test_comments (line 78) | def test_comments(self, env):
method test_string_escapes (line 92) | def test_string_escapes(self, env):
method test_bytefallback (line 98) | def test_bytefallback(self, env):
method test_operators (line 104) | def test_operators(self, env):
method test_normalizing (line 114) | def test_normalizing(self, env):
method test_trailing_newline (line 121) | def test_trailing_newline(self, env):
method test_name (line 156) | def test_name(self, env, name, valid):
method test_lineno_with_strip (line 165) | def test_lineno_with_strip(self, env):
class TestParser (line 184) | class TestParser:
method test_php_syntax (line 185) | def test_php_syntax(self, env):
method test_erb_syntax (line 196) | def test_erb_syntax(self, env):
method test_comment_syntax (line 207) | def test_comment_syntax(self, env):
method test_balancing (line 218) | def test_balancing(self, env):
method test_start_comment (line 222) | def test_start_comment(self, env):
method test_line_syntax (line 231) | def test_line_syntax(self, env):
method test_line_syntax_priority (line 256) | def test_line_syntax_priority(self, env):
method test_error_messages (line 280) | def test_error_messages(self, env):
class TestSyntax (line 317) | class TestSyntax:
method test_call (line 318) | def test_call(self, env):
method test_slicing (line 324) | def test_slicing(self, env):
method test_attr (line 328) | def test_attr(self, env):
method test_subscript (line 332) | def test_subscript(self, env):
method test_tuple (line 336) | def test_tuple(self, env):
method test_math (line 340) | def test_math(self, env):
method test_div (line 344) | def test_div(self, env):
method test_unary (line 348) | def test_unary(self, env):
method test_concat (line 352) | def test_concat(self, env):
method test_compare (line 367) | def test_compare(self, env, a, op, b):
method test_compare_parens (line 371) | def test_compare_parens(self, env):
method test_compare_compound (line 386) | def test_compare_compound(self, env, src, expect):
method test_inop (line 390) | def test_inop(self, env):
method test_collection_literal (line 395) | def test_collection_literal(self, env, value):
method test_numeric_literal (line 423) | def test_numeric_literal(self, env, value, expect):
method test_bool (line 427) | def test_bool(self, env):
method test_grouping (line 433) | def test_grouping(self, env):
method test_django_attr (line 439) | def test_django_attr(self, env):
method test_conditional_expression (line 443) | def test_conditional_expression(self, env):
method test_short_conditional_expression (line 447) | def test_short_conditional_expression(self, env):
method test_filter_priority (line 454) | def test_filter_priority(self, env):
method test_function_calls (line 458) | def test_function_calls(self, env):
method test_tuple_expr (line 483) | def test_tuple_expr(self, env):
method test_trailing_comma (line 496) | def test_trailing_comma(self, env):
method test_block_end_name (line 500) | def test_block_end_name(self, env):
method test_constant_casing (line 506) | def test_constant_casing(self, env):
method test_test_chaining (line 514) | def test_test_chaining(self, env):
method test_string_concatenation (line 520) | def test_string_concatenation(self, env):
method test_notin (line 524) | def test_notin(self, env):
method test_operator_precedence (line 529) | def test_operator_precedence(self, env):
method test_implicit_subscribed_tuple (line 533) | def test_implicit_subscribed_tuple(self, env):
method test_raw2 (line 541) | def test_raw2(self, env):
method test_const (line 545) | def test_const(self, env):
method test_neg_filter_priority (line 552) | def test_neg_filter_priority(self, env):
method test_const_assign (line 557) | def test_const_assign(self, env):
method test_localset (line 563) | def test_localset(self, env):
method test_parse_unary (line 571) | def test_parse_unary(self, env):
class TestLstripBlocks (line 578) | class TestLstripBlocks:
method test_lstrip (line 579) | def test_lstrip(self, env):
method test_lstrip_trim (line 584) | def test_lstrip_trim(self, env):
method test_no_lstrip (line 589) | def test_no_lstrip(self, env):
method test_lstrip_blocks_false_with_no_lstrip (line 594) | def test_lstrip_blocks_false_with_no_lstrip(self, env):
method test_lstrip_endline (line 602) | def test_lstrip_endline(self, env):
method test_lstrip_inline (line 607) | def test_lstrip_inline(self, env):
method test_lstrip_nested (line 612) | def test_lstrip_nested(self, env):
method test_lstrip_left_chars (line 619) | def test_lstrip_left_chars(self, env):
method test_lstrip_embeded_strings (line 627) | def test_lstrip_embeded_strings(self, env):
method test_lstrip_preserve_leading_newlines (line 632) | def test_lstrip_preserve_leading_newlines(self, env):
method test_lstrip_comment (line 637) | def test_lstrip_comment(self, env):
method test_lstrip_angle_bracket_simple (line 646) | def test_lstrip_angle_bracket_simple(self, env):
method test_lstrip_angle_bracket_comment (line 662) | def test_lstrip_angle_bracket_comment(self, env):
method test_lstrip_angle_bracket (line 678) | def test_lstrip_angle_bracket(self, env):
method test_lstrip_angle_bracket_compact (line 700) | def test_lstrip_angle_bracket_compact(self, env):
method test_lstrip_blocks_outside_with_new_line (line 722) | def test_lstrip_blocks_outside_with_new_line(self):
method test_lstrip_trim_blocks_outside_with_new_line (line 732) | def test_lstrip_trim_blocks_outside_with_new_line(self):
method test_lstrip_blocks_inside_with_new_line (line 742) | def test_lstrip_blocks_inside_with_new_line(self):
method test_lstrip_trim_blocks_inside_with_new_line (line 752) | def test_lstrip_trim_blocks_inside_with_new_line(self):
method test_lstrip_blocks_without_new_line (line 762) | def test_lstrip_blocks_without_new_line(self):
method test_lstrip_trim_blocks_without_new_line (line 772) | def test_lstrip_trim_blocks_without_new_line(self):
method test_lstrip_blocks_consume_after_without_new_line (line 782) | def test_lstrip_blocks_consume_after_without_new_line(self):
method test_lstrip_trim_blocks_consume_before_without_new_line (line 792) | def test_lstrip_trim_blocks_consume_before_without_new_line(self):
method test_lstrip_trim_blocks_comment (line 802) | def test_lstrip_trim_blocks_comment(self):
method test_lstrip_trim_blocks_raw (line 808) | def test_lstrip_trim_blocks_raw(self):
method test_php_syntax_with_manual (line 814) | def test_php_syntax_with_manual(self, env):
method test_php_syntax (line 827) | def test_php_syntax(self, env):
method test_php_syntax_compact (line 840) | def test_php_syntax_compact(self, env):
method test_erb_syntax (line 853) | def test_erb_syntax(self, env):
method test_erb_syntax_with_manual (line 867) | def test_erb_syntax_with_manual(self, env):
method test_erb_syntax_no_lstrip (line 880) | def test_erb_syntax_no_lstrip(self, env):
method test_comment_syntax (line 893) | def test_comment_syntax(self, env):
class TestTrimBlocks (line 914) | class TestTrimBlocks:
method test_trim (line 915) | def test_trim(self, env):
method test_no_trim (line 920) | def test_no_trim(self, env):
method test_no_trim_outer (line 925) | def test_no_trim_outer(self, env):
method test_lstrip_no_trim (line 930) | def test_lstrip_no_trim(self, env):
method test_trim_blocks_false_with_no_trim (line 935) | def test_trim_blocks_false_with_no_trim(self, env):
method test_trim_nested (line 953) | def test_trim_nested(self, env):
method test_no_trim_nested (line 960) | def test_no_trim_nested(self, env):
method test_comment_trim (line 967) | def test_comment_trim(self, env):
method test_comment_no_trim (line 972) | def test_comment_no_trim(self, env):
method test_multiple_comment_trim_lstrip (line 977) | def test_multiple_comment_trim_lstrip(self, env):
method test_multiple_comment_no_trim_lstrip (line 984) | def test_multiple_comment_no_trim_lstrip(self, env):
method test_raw_trim_lstrip (line 991) | def test_raw_trim_lstrip(self, env):
method test_raw_no_trim_lstrip (line 996) | def test_raw_no_trim_lstrip(self, env):
method test_no_trim_angle_bracket (line 1005) | def test_no_trim_angle_bracket(self, env):
method test_no_trim_php_syntax (line 1015) | def test_no_trim_php_syntax(self, env):
FILE: tests/test_loader.py
class TestLoaders (line 21) | class TestLoaders:
method test_dict_loader (line 22) | def test_dict_loader(self, dict_loader):
method test_package_loader (line 28) | def test_package_loader(self, package_loader):
method test_filesystem_loader_overlapping_names (line 34) | def test_filesystem_loader_overlapping_names(self, filesystem_loader):
method test_choice_loader (line 43) | def test_choice_loader(self, choice_loader):
method test_function_loader (line 51) | def test_function_loader(self, function_loader):
method test_prefix_loader (line 57) | def test_prefix_loader(self, prefix_loader):
method test_caching (line 65) | def test_caching(self):
method test_no_cache (line 79) | def test_no_cache(self):
method test_limited_size_cache (line 84) | def test_limited_size_cache(self):
method test_cache_loader_change (line 98) | def test_cache_loader_change(self):
method test_dict_loader_cache_invalidates (line 106) | def test_dict_loader_cache_invalidates(self):
method test_split_template_path (line 113) | def test_split_template_path(self):
class TestFileSystemLoader (line 119) | class TestFileSystemLoader:
method _test_common (line 123) | def _test_common(env):
method test_searchpath_as_str (line 130) | def test_searchpath_as_str(self):
method test_searchpath_as_pathlib (line 136) | def test_searchpath_as_pathlib(self):
method test_searchpath_as_list_including_pathlib (line 141) | def test_searchpath_as_list_including_pathlib(self):
method test_caches_template_based_on_mtime (line 148) | def test_caches_template_based_on_mtime(self):
method test_uses_specified_encoding (line 167) | def test_uses_specified_encoding(self, encoding, expect):
method test_filename_normpath (line 173) | def test_filename_normpath(self):
method test_error_includes_paths (line 182) | def test_error_includes_paths(self, env, filesystem_loader):
class TestModuleLoader (line 201) | class TestModuleLoader:
method compile_down (line 205) | def compile_down(self, prefix_loader, zip="deflated"):
method teardown_method (line 217) | def teardown_method(self):
method test_log (line 226) | def test_log(self, prefix_loader):
method _test_common (line 238) | def _test_common(self):
method test_deflated_zip_compile (line 247) | def test_deflated_zip_compile(self, prefix_loader):
method test_stored_zip_compile (line 251) | def test_stored_zip_compile(self, prefix_loader):
method test_filesystem_compile (line 255) | def test_filesystem_compile(self, prefix_loader):
method test_weak_references (line 259) | def test_weak_references(self, prefix_loader):
method test_choice_loader (line 280) | def test_choice_loader(self, prefix_loader):
method test_prefix_loader (line 290) | def test_prefix_loader(self, prefix_loader):
method test_path_as_pathlib (line 303) | def test_path_as_pathlib(self, prefix_loader):
method test_supports_pathlib_in_list_of_paths (line 312) | def test_supports_pathlib_in_list_of_paths(self, prefix_loader):
function package_dir_loader (line 323) | def package_dir_loader(monkeypatch):
function test_package_dir_source (line 331) | def test_package_dir_source(package_dir_loader, template, expect):
function test_package_dir_list (line 338) | def test_package_dir_list(package_dir_loader):
function package_file_loader (line 345) | def package_file_loader(monkeypatch):
function test_package_file_source (line 353) | def test_package_file_source(package_file_loader, template, expect):
function test_package_file_list (line 360) | def test_package_file_list(package_file_loader):
function package_zip_loader (line 367) | def package_zip_loader(monkeypatch):
function test_package_zip_source (line 376) | def test_package_zip_source(package_zip_loader, template, expect):
function test_package_zip_list (line 388) | def test_package_zip_list(package_zip_loader):
function test_package_zip_omit_curdir (line 393) | def test_package_zip_omit_curdir(package_zip_loader, package_path):
function test_pep_451_import_hook (line 403) | def test_pep_451_import_hook():
function test_package_loader_no_dir (line 434) | def test_package_loader_no_dir() -> None:
FILE: tests/test_nativetypes.py
function env (line 12) | def env():
function async_native_env (line 17) | def async_native_env():
function test_is_defined_native_return (line 21) | def test_is_defined_native_return(env):
function test_undefined_native_return (line 26) | def test_undefined_native_return(env):
function test_adding_undefined_native_return (line 31) | def test_adding_undefined_native_return(env):
function test_cast_int (line 38) | def test_cast_int(env):
function test_list_add (line 45) | def test_list_add(env):
function test_multi_expression_add (line 52) | def test_multi_expression_add(env):
function test_loops (line 59) | def test_loops(env):
function test_loops_with_ints (line 66) | def test_loops_with_ints(env):
function test_loop_look_alike (line 73) | def test_loop_look_alike(env):
function test_booleans (line 91) | def test_booleans(env, source, expect):
function test_variable_dunder (line 98) | def test_variable_dunder(env):
function test_constant_dunder (line 104) | def test_constant_dunder(env):
function test_constant_dunder_to_string (line 110) | def test_constant_dunder_to_string(env):
function test_string_literal_var (line 117) | def test_string_literal_var(env):
function test_string_top_level (line 124) | def test_string_top_level(env):
function test_string_concatenation (line 130) | def test_string_concatenation(async_native_env, run_async_fn):
function test_tuple_of_variable_strings (line 142) | def test_tuple_of_variable_strings(env):
function test_concat_strings_with_quotes (line 149) | def test_concat_strings_with_quotes(env):
function test_no_intermediate_eval (line 155) | def test_no_intermediate_eval(env):
function test_spontaneous_env (line 164) | def test_spontaneous_env():
function test_leading_spaces (line 169) | def test_leading_spaces(env):
function test_macro (line 175) | def test_macro(env):
function test_block (line 182) | def test_block(env):
FILE: tests/test_nodes.py
function test_template_hash (line 1) | def test_template_hash(env):
FILE: tests/test_pickle.py
function test_environment (line 4) | def test_environment(env):
FILE: tests/test_regression.py
class TestCorner (line 13) | class TestCorner:
method test_assigned_scoping (line 14) | def test_assigned_scoping(self, env):
method test_closure_scoping (line 47) | def test_closure_scoping(self, env):
class TestBug (line 84) | class TestBug:
method test_keyword_folding (line 85) | def test_keyword_folding(self, env):
method test_extends_output_bugs (line 93) | def test_extends_output_bugs(self, env):
method test_urlize_filter_escaping (line 106) | def test_urlize_filter_escaping(self, env):
method test_urlize_filter_closing_punctuation (line 113) | def test_urlize_filter_closing_punctuation(self, env):
method test_loop_call_loop (line 122) | def test_loop_call_loop(self, env):
method test_weird_inline_comment (line 143) | def test_weird_inline_comment(self, env):
method test_old_macro_loop_scoping_bug (line 151) | def test_old_macro_loop_scoping_bug(self, env):
method test_partial_conditional_assignments (line 158) | def test_partial_conditional_assignments(self, env):
method test_stacked_locals_scoping_bug (line 163) | def test_stacked_locals_scoping_bug(self, env):
method test_stacked_locals_scoping_bug_twoframe (line 189) | def test_stacked_locals_scoping_bug_twoframe(self, env):
method test_call_with_args (line 204) | def test_call_with_args(self, env):
method test_empty_if_condition_fails (line 245) | def test_empty_if_condition_fails(self, env):
method test_recursive_loop_compile (line 252) | def test_recursive_loop_compile(self, env):
method test_else_loop_bug (line 282) | def test_else_loop_bug(self, env):
method test_correct_prefix_loader_name (line 294) | def test_correct_prefix_loader_name(self, env):
method test_pass_context_callable_class (line 301) | def test_pass_context_callable_class(self, env):
method test_block_set_with_extends (line 313) | def test_block_set_with_extends(self):
method test_nested_for_else (line 320) | def test_nested_for_else(self, env):
method test_macro_var_bug (line 328) | def test_macro_var_bug(self, env):
method test_macro_var_bug_advanced (line 339) | def test_macro_var_bug_advanced(self, env):
method test_callable_defaults (line 352) | def test_callable_defaults(self):
method test_macro_escaping (line 365) | def test_macro_escaping(self):
method test_macro_scoping (line 371) | def test_macro_scoping(self, env):
method test_scopes_and_blocks (line 389) | def test_scopes_and_blocks(self):
method test_scopes_and_include (line 419) | def test_scopes_and_include(self):
method test_caller_scoping (line 432) | def test_caller_scoping(self, env):
method test_variable_reuse (line 459) | def test_variable_reuse(self, env):
method test_double_caller (line 469) | def test_double_caller(self, env):
method test_double_caller_no_default (line 477) | def test_double_caller_no_default(self, env):
method test_macro_blocks (line 497) | def test_macro_blocks(self, env):
method test_scoped_block (line 503) | def test_scoped_block(self, env):
method test_recursive_loop_filter (line 510) | def test_recursive_loop_filter(self, env):
method test_empty_if (line 535) | def test_empty_if(self, env):
method test_subproperty_if (line 539) | def test_subproperty_if(self, env):
method test_set_and_include (line 550) | def test_set_and_include(self):
method test_loop_include (line 561) | def test_loop_include(self):
method test_grouper_repr (line 572) | def test_grouper_repr(self):
method test_custom_context (line 581) | def test_custom_context(self, env):
method test_recursive_loop_bug (line 594) | def test_recursive_loop_bug(self, env):
method test_markup_and_chainable_undefined (line 600) | def test_markup_and_chainable_undefined(self):
method test_scoped_block_loop_vars (line 607) | def test_scoped_block_loop_vars(self, env):
method test_pass_context_loop_vars (line 620) | def test_pass_context_loop_vars(self, env):
method test_pass_context_scoped_loop_vars (line 640) | def test_pass_context_scoped_loop_vars(self, env):
method test_pass_context_in_blocks (line 660) | def test_pass_context_in_blocks(self, env):
method test_pass_context_block_and_loop (line 678) | def test_pass_context_block_and_loop(self, env):
method test_cached_extends (line 705) | def test_cached_extends(self, op):
method test_nested_loop_scoping (line 733) | def test_nested_loop_scoping(self, env):
method test_pass_context_with_select (line 740) | def test_pass_context_with_select(self, env):
function test_load_parameter_when_set_in_all_if_branches (line 753) | def test_load_parameter_when_set_in_all_if_branches(env):
function test_unicode_whitespace (line 764) | def test_unicode_whitespace(env, unicode_char):
FILE: tests/test_runtime.py
function test_loop_idx (line 26) | def test_loop_idx():
function test_loop_idx0 (line 33) | def test_loop_idx0():
function test_loopcontext0 (line 40) | def test_loopcontext0():
function test_loopcontext1 (line 46) | def test_loopcontext1():
function test_loopcontext2 (line 52) | def test_loopcontext2():
function test_iterator_not_advanced_early (line 58) | def test_iterator_not_advanced_early():
function test_mock_not_pass_arg_marker (line 68) | def test_mock_not_pass_arg_marker():
function test_undefined_copy (line 91) | def test_undefined_copy(undefined_type):
function test_undefined_deepcopy (line 103) | def test_undefined_deepcopy(undefined_type):
function test_undefined_pickle (line 115) | def test_undefined_pickle(undefined_type):
FILE: tests/test_security.py
class PrivateStuff (line 14) | class PrivateStuff:
method bar (line 15) | def bar(self):
method foo (line 19) | def foo(self):
method __repr__ (line 22) | def __repr__(self):
class PublicStuff (line 26) | class PublicStuff:
method bar (line 27) | def bar(self):
method _foo (line 30) | def _foo(self):
method __repr__ (line 33) | def __repr__(self):
class TestSandbox (line 37) | class TestSandbox:
method test_unsafe (line 38) | def test_unsafe(self, env):
method test_immutable_environment (line 58) | def test_immutable_environment(self, env):
method test_restricted (line 65) | def test_restricted(self, env):
method test_template_data (line 78) | def test_template_data(self, env):
method test_attr_filter (line 96) | def test_attr_filter(self, env):
method test_binary_operator_intercepting (line 101) | def test_binary_operator_intercepting(self, env):
method test_unary_operator_intercepting (line 115) | def test_unary_operator_intercepting(self, env):
class TestStringFormat (line 130) | class TestStringFormat:
method test_basic_format_safety (line 131) | def test_basic_format_safety(self):
method test_basic_format_all_okay (line 136) | def test_basic_format_all_okay(self):
method test_safe_format_safety (line 141) | def test_safe_format_safety(self):
method test_safe_format_all_okay (line 146) | def test_safe_format_all_okay(self):
method test_empty_braces_format (line 151) | def test_empty_braces_format(self):
class TestStringFormatMap (line 159) | class TestStringFormatMap:
method test_basic_format_safety (line 160) | def test_basic_format_safety(self):
method test_basic_format_all_okay (line 165) | def test_basic_format_all_okay(self):
method test_safe_format_all_okay (line 170) | def test_safe_format_all_okay(self):
method test_indirect_call (line 177) | def test_indirect_call(self):
method test_attr_filter (line 194) | def test_attr_filter(self) -> None:
FILE: tests/test_tests.py
class MyDict (line 9) | class MyDict(dict):
class TestTestsCase (line 13) | class TestTestsCase:
method test_defined (line 14) | def test_defined(self, env):
method test_even (line 18) | def test_even(self, env):
method test_odd (line 22) | def test_odd(self, env):
method test_lower (line 26) | def test_lower(self, env):
method test_types (line 113) | def test_types(self, env, op, expect):
method test_upper (line 117) | def test_upper(self, env):
method test_equalto (line 121) | def test_equalto(self, env):
method test_compare_aliases (line 154) | def test_compare_aliases(self, env, op, expect):
method test_sameas (line 158) | def test_sameas(self, env):
method test_no_paren_for_arg1 (line 162) | def test_no_paren_for_arg1(self, env):
method test_escaped (line 166) | def test_escaped(self, env):
method test_greaterthan (line 171) | def test_greaterthan(self, env):
method test_lessthan (line 175) | def test_lessthan(self, env):
method test_multiple_tests (line 179) | def test_multiple_tests(self):
method test_in (line 198) | def test_in(self, env):
function test_name_undefined (line 213) | def test_name_undefined(env):
function test_name_undefined_in_if (line 218) | def test_name_undefined_in_if(env):
function test_is_filter (line 226) | def test_is_filter(env):
function test_is_test (line 231) | def test_is_test(env):
FILE: tests/test_utils.py
class TestLRUCache (line 19) | class TestLRUCache:
method test_simple (line 20) | def test_simple(self):
method test_values (line 29) | def test_values(self):
method test_values_empty (line 35) | def test_values_empty(self):
method test_pickleable (line 39) | def test_pickleable(self):
method test_copy (line 52) | def test_copy(self, copy_func):
method test_clear (line 62) | def test_clear(self):
method test_repr (line 70) | def test_repr(self):
method test_items (line 78) | def test_items(self):
method test_setdefault (line 97) | def test_setdefault(self):
class TestHelpers (line 107) | class TestHelpers:
method test_object_type_repr (line 108) | def test_object_type_repr(self):
method test_autoescape_select (line 118) | def test_autoescape_select(self):
class TestEscapeUrlizeTarget (line 135) | class TestEscapeUrlizeTarget:
method test_escape_urlize_target (line 136) | def test_escape_urlize_target(self):
method test_urlize_mail_mastodon (line 145) | def test_urlize_mail_mastodon(self):
class TestLoremIpsum (line 154) | class TestLoremIpsum:
method test_lorem_ipsum_markup (line 155) | def test_lorem_ipsum_markup(self):
method test_lorem_ipsum_html (line 159) | def test_lorem_ipsum_html(self):
method test_lorem_ipsum_n (line 163) | def test_lorem_ipsum_n(self):
method test_lorem_ipsum_min (line 169) | def test_lorem_ipsum_min(self):
method test_lorem_ipsum_max (line 176) | def test_lorem_ipsum_max(self):
function test_missing (line 184) | def test_missing():
function test_consume (line 189) | def test_consume():
function test_pickle_missing (line 198) | def test_pickle_missing(protocol: int) -> None:
function test_copy_missing (line 203) | def test_copy_missing() -> None:
Condensed preview — 102 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,014K chars).
[
{
"path": ".devcontainer/devcontainer.json",
"chars": 434,
"preview": "{\n \"name\": \"pallets/jinja\",\n \"image\": \"mcr.microsoft.com/devcontainers/python:3\",\n \"customizations\": {\n \"vscode\": "
},
{
"path": ".devcontainer/on-create-command.sh",
"chars": 445,
"preview": "#!/bin/bash\nset -e\n\n# Install uv if not already installed\nif ! command -v uv &> /dev/null; then\n echo \"Installing uv."
},
{
"path": ".editorconfig",
"chars": 233,
"preview": "root = true\n\n[*]\nindent_style = space\nindent_size = 4\ninsert_final_newline = true\ntrim_trailing_whitespace = true\nend_of"
},
{
"path": ".github/ISSUE_TEMPLATE/bug-report.md",
"chars": 615,
"preview": "---\nname: Bug report\nabout: Report a bug in Jinja (not other projects which depend on Jinja)\n---\n\n<!--\nThis issue tracke"
},
{
"path": ".github/ISSUE_TEMPLATE/config.yml",
"chars": 332,
"preview": "blank_issues_enabled: false\ncontact_links:\n - name: Questions on Discussions\n url: https://github.com/pallets/jinja/"
},
{
"path": ".github/ISSUE_TEMPLATE/feature-request.md",
"chars": 416,
"preview": "---\nname: Feature request\nabout: Suggest a new feature for Jinja\n---\n\n<!--\nReplace this comment with a description of wh"
},
{
"path": ".github/pull_request_template.md",
"chars": 822,
"preview": "<!--\nBefore opening a PR, open a ticket describing the issue or feature the\nPR will address. An issue is not required fo"
},
{
"path": ".github/workflows/lock.yaml",
"chars": 682,
"preview": "name: Lock inactive closed issues\n# Lock closed issues that have not received any further activity for two weeks.\n# This"
},
{
"path": ".github/workflows/pre-commit.yaml",
"chars": 983,
"preview": "name: pre-commit\non:\n pull_request:\n push:\n branches: [main, stable]\njobs:\n main:\n runs-on: ubuntu-latest\n s"
},
{
"path": ".github/workflows/publish.yaml",
"chars": 1562,
"preview": "name: Publish\non:\n push:\n tags: ['*']\njobs:\n build:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/che"
},
{
"path": ".github/workflows/tests.yaml",
"chars": 1795,
"preview": "name: Tests\non:\n pull_request:\n paths-ignore: ['docs/**', 'README.md']\n push:\n branches: [main, stable]\n path"
},
{
"path": ".gitignore",
"chars": 74,
"preview": ".idea/\n.vscode/\n__pycache__/\ndist/\n.coverage*\nhtmlcov/\n.tox/\ndocs/_build/\n"
},
{
"path": ".pre-commit-config.yaml",
"chars": 629,
"preview": "repos:\n - repo: https://github.com/astral-sh/ruff-pre-commit\n rev: 76e47323a83cd9795e4ff9a1de1c0d2eef610f17 # froze"
},
{
"path": ".readthedocs.yaml",
"chars": 242,
"preview": "version: 2\nbuild:\n os: ubuntu-24.04\n tools:\n python: '3.13'\n commands:\n - asdf plugin add uv\n - asdf install"
},
{
"path": "CHANGES.rst",
"chars": 42982,
"preview": ".. currentmodule:: jinja2\n\nVersion 3.2.0\n-------------\n\nUnreleased\n\n- Drop support for Python 3.7, 3.8, and 3.9.\n- U"
},
{
"path": "LICENSE.txt",
"chars": 1475,
"preview": "Copyright 2007 Pallets\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted p"
},
{
"path": "README.md",
"chars": 1965,
"preview": "<div align=\"center\"><img src=\"https://raw.githubusercontent.com/pallets/jinja/refs/heads/stable/docs/_static/jinja-name."
},
{
"path": "docs/Makefile",
"chars": 581,
"preview": "# Minimal makefile for Sphinx documentation\n#\n\n# You can set these variables from the command line.\nSPHINXOPTS =\nSPHI"
},
{
"path": "docs/api.rst",
"chars": 30799,
"preview": "API\n===\n\n.. module:: jinja2\n :noindex:\n :synopsis: public Jinja API\n\nThis document describes the API to Jinja and "
},
{
"path": "docs/changes.rst",
"chars": 45,
"preview": "Changes\n=======\n\n.. include:: ../CHANGES.rst\n"
},
{
"path": "docs/conf.py",
"chars": 1957,
"preview": "from pallets_sphinx_themes import get_version\nfrom pallets_sphinx_themes import ProjectLink\n\n# Project -----------------"
},
{
"path": "docs/examples/cache_extension.py",
"chars": 2053,
"preview": "from jinja2 import nodes\nfrom jinja2.ext import Extension\n\n\nclass FragmentCacheExtension(Extension):\n # a set of name"
},
{
"path": "docs/examples/inline_gettext_extension.py",
"chars": 2397,
"preview": "import re\n\nfrom jinja2.exceptions import TemplateSyntaxError\nfrom jinja2.ext import Extension\nfrom jinja2.lexer import c"
},
{
"path": "docs/extensions.rst",
"chars": 13070,
"preview": ".. _jinja-extensions:\n\nExtensions\n==========\n\nJinja supports extensions that can add extra filters, tests, globals or ev"
},
{
"path": "docs/faq.rst",
"chars": 3376,
"preview": "Frequently Asked Questions\n==========================\n\n\nWhy is it called Jinja?\n-----------------------\n\n\"Jinja\" is a Ja"
},
{
"path": "docs/index.rst",
"chars": 522,
"preview": ".. rst-class:: hide-header\n\nJinja\n=====\n\n.. image:: _static/jinja-name.svg\n :align: center\n :height: 200px\n\nJinja "
},
{
"path": "docs/integration.rst",
"chars": 2754,
"preview": "Integration\n===========\n\n\nFlask\n-----\n\nThe `Flask`_ web application framework, also maintained by Pallets, uses\nJinja te"
},
{
"path": "docs/intro.rst",
"chars": 1977,
"preview": "Introduction\n============\n\nJinja is a fast, expressive, extensible templating engine. Special\nplaceholders in the templa"
},
{
"path": "docs/license.rst",
"chars": 98,
"preview": "BSD-3-Clause License\n====================\n\n.. literalinclude:: ../LICENSE.txt\n :language: text\n"
},
{
"path": "docs/make.bat",
"chars": 757,
"preview": "@ECHO OFF\n\npushd %~dp0\n\nREM Command file for Sphinx documentation\n\nif \"%SPHINXBUILD%\" == \"\" (\n\tset SPHINXBUILD=sphinx-bu"
},
{
"path": "docs/nativetypes.rst",
"chars": 1843,
"preview": ".. module:: jinja2.nativetypes\n\n.. _nativetypes:\n\nNative Python Types\n===================\n\nThe default :class:`~jinja2.E"
},
{
"path": "docs/sandbox.rst",
"chars": 4184,
"preview": "Sandbox\n=======\n\nThe Jinja sandbox can be used to render untrusted templates. Access to\nattributes, method calls, operat"
},
{
"path": "docs/switching.rst",
"chars": 4413,
"preview": "Switching From Other Template Engines\n=====================================\n\nThis is a brief guide on some of the differ"
},
{
"path": "docs/templates.rst",
"chars": 63165,
"preview": ".. py:currentmodule:: jinja2\n.. highlight:: html+jinja\n\nTemplate Designer Documentation\n===============================\n"
},
{
"path": "docs/tricks.rst",
"chars": 3168,
"preview": "Tips and Tricks\n===============\n\n.. highlight:: html+jinja\n\nThis part of the documentation shows some tips and tricks fo"
},
{
"path": "examples/basic/cycle.py",
"chars": 301,
"preview": "from jinja2 import Environment\n\nenv = Environment(\n line_statement_prefix=\"#\", variable_start_string=\"${\", variable_e"
},
{
"path": "examples/basic/debugger.py",
"chars": 223,
"preview": "from jinja2 import Environment\nfrom jinja2.loaders import FileSystemLoader\n\nenv = Environment(loader=FileSystemLoader(\"t"
},
{
"path": "examples/basic/inheritance.py",
"chars": 392,
"preview": "from jinja2 import Environment\nfrom jinja2.loaders import DictLoader\n\nenv = Environment(\n loader=DictLoader(\n "
},
{
"path": "examples/basic/templates/broken.html",
"chars": 124,
"preview": "{% from 'subbroken.html' import may_break %}\n<ul>\n{% for item in seq %}\n <li>{{ may_break(item) }}</li>\n{% endfor %}\n</"
},
{
"path": "examples/basic/templates/subbroken.html",
"chars": 64,
"preview": "{% macro may_break(item) -%}\n [{{ item / 0 }}]\n{%- endmacro %}\n"
},
{
"path": "examples/basic/test.py",
"chars": 679,
"preview": "from jinja2 import Environment\nfrom jinja2.loaders import DictLoader\n\nenv = Environment(\n loader=DictLoader(\n "
},
{
"path": "examples/basic/test_filter_and_linestatements.py",
"chars": 456,
"preview": "from jinja2 import Environment\n\nenv = Environment(\n line_statement_prefix=\"%\", variable_start_string=\"${\", variable_e"
},
{
"path": "examples/basic/test_loop_filter.py",
"chars": 301,
"preview": "from jinja2 import Environment\n\ntmpl = Environment().from_string(\n \"\"\"\\\n<ul>\n{%- for item in [1, 2, 3, 4, 5, 6, 7, 8,"
},
{
"path": "examples/basic/translate.py",
"chars": 544,
"preview": "from jinja2 import Environment\n\nenv = Environment(extensions=[\"jinja2.ext.i18n\"])\nenv.globals[\"gettext\"] = {\"Hello %(use"
},
{
"path": "pyproject.toml",
"chars": 4567,
"preview": "[project]\nname = \"Jinja2\"\nversion = \"3.2.0.dev\"\ndescription = \"A very fast and expressive template engine.\"\nreadme = \"RE"
},
{
"path": "scripts/generate_identifier_pattern.py",
"chars": 2149,
"preview": "import itertools\nimport os\nimport re\nimport sys\n\n\ndef get_characters():\n \"\"\"Find every Unicode character that is vali"
},
{
"path": "src/jinja2/__init__.py",
"chars": 2458,
"preview": "\"\"\"Jinja is a template engine written in pure Python. It provides a\nnon-XML syntax that supports inline expressions and "
},
{
"path": "src/jinja2/_identifier.py",
"chars": 854,
"preview": "# generated by scripts/generate_identifier_pattern.py for Python 3.10\nimport re\n\npattern = re.compile(\n r\"[\\w·̀-ͯ·҃-҇"
},
{
"path": "src/jinja2/async_utils.py",
"chars": 2816,
"preview": "import inspect\nimport typing as t\nfrom functools import WRAPPER_ASSIGNMENTS\nfrom functools import wraps\n\nfrom .utils imp"
},
{
"path": "src/jinja2/bccache.py",
"chars": 13986,
"preview": "\"\"\"The optional bytecode cache system. This is useful if you have very\ncomplex template situations and the compilation o"
},
{
"path": "src/jinja2/compiler.py",
"chars": 73918,
"preview": "\"\"\"Compiles nodes from the parser into Python code.\"\"\"\n\nimport typing as t\nfrom contextlib import contextmanager\nfrom fu"
},
{
"path": "src/jinja2/constants.py",
"chars": 1433,
"preview": "#: list of lorem ipsum words used by the lipsum() helper function\nLOREM_IPSUM_WORDS = \"\"\"\\\na ac accumsan ad adipiscing a"
},
{
"path": "src/jinja2/debug.py",
"chars": 5753,
"preview": "import sys\nimport typing as t\nfrom types import CodeType\nfrom types import TracebackType\n\nfrom .exceptions import Templa"
},
{
"path": "src/jinja2/defaults.py",
"chars": 1255,
"preview": "import typing as t\n\nfrom .filters import FILTERS as DEFAULT_FILTERS # noqa: F401\nfrom .tests import TESTS as DEFAULT_TE"
},
{
"path": "src/jinja2/environment.py",
"chars": 60847,
"preview": "\"\"\"Classes for managing templates and their runtime and compile time\noptions.\n\"\"\"\n\nimport os\nimport typing\nimport typing"
},
{
"path": "src/jinja2/exceptions.py",
"chars": 5026,
"preview": "import typing as t\n\nif t.TYPE_CHECKING:\n from .runtime import Undefined\n\n\nclass TemplateError(Exception):\n \"\"\"Base"
},
{
"path": "src/jinja2/ext.py",
"chars": 31109,
"preview": "\"\"\"Extension API for adding custom tags and behavior.\"\"\"\n\nimport pprint\nimport re\nimport typing as t\n\nfrom markupsafe im"
},
{
"path": "src/jinja2/filters.py",
"chars": 54599,
"preview": "\"\"\"Built-in template filters used with the ``|`` operator.\"\"\"\n\nimport math\nimport random\nimport re\nimport typing\nimport "
},
{
"path": "src/jinja2/idtracking.py",
"chars": 10490,
"preview": "import typing as t\n\nfrom . import nodes\nfrom .visitor import NodeVisitor\n\nif t.TYPE_CHECKING:\n import typing_extensio"
},
{
"path": "src/jinja2/lexer.py",
"chars": 29687,
"preview": "\"\"\"Implements a Jinja / Python combination lexer. The ``Lexer`` class\nis used to do some preprocessing. It filters out i"
},
{
"path": "src/jinja2/loaders.py",
"chars": 23709,
"preview": "\"\"\"API and implementations for loading templates from different data\nsources.\n\"\"\"\n\nimport importlib.util\nimport os\nimpor"
},
{
"path": "src/jinja2/meta.py",
"chars": 4382,
"preview": "\"\"\"Functions that expose information about templates that might be\ninteresting for introspection.\n\"\"\"\n\nimport typing as "
},
{
"path": "src/jinja2/nativetypes.py",
"chars": 4205,
"preview": "import typing as t\nfrom ast import literal_eval\nfrom ast import parse\nfrom itertools import chain\nfrom itertools import "
},
{
"path": "src/jinja2/nodes.py",
"chars": 34189,
"preview": "\"\"\"AST nodes generated by the parser for the compiler. Also provides\nsome node tree helper functions used by the parser "
},
{
"path": "src/jinja2/optimizer.py",
"chars": 1646,
"preview": "\"\"\"The optimizer tries to constant fold expressions and modify the AST\nin place so that it should be faster to evaluate."
},
{
"path": "src/jinja2/parser.py",
"chars": 40095,
"preview": "\"\"\"Parse tokens from the lexer into nodes for the compiler.\"\"\"\n\nimport typing\nimport typing as t\n\nfrom . import nodes\nfr"
},
{
"path": "src/jinja2/py.typed",
"chars": 0,
"preview": ""
},
{
"path": "src/jinja2/runtime.py",
"chars": 34148,
"preview": "\"\"\"The runtime functions and state used by compiled templates.\"\"\"\n\nimport functools\nimport sys\nimport typing as t\nfrom c"
},
{
"path": "src/jinja2/sandbox.py",
"chars": 14942,
"preview": "\"\"\"A sandbox layer that ensures unsafe operations cannot be performed.\nUseful when the template itself comes from an unt"
},
{
"path": "src/jinja2/tests.py",
"chars": 5926,
"preview": "\"\"\"Built-in template tests used with the ``is`` operator.\"\"\"\n\nimport operator\nimport typing as t\nfrom collections import"
},
{
"path": "src/jinja2/utils.py",
"chars": 24080,
"preview": "import enum\nimport json\nimport os\nimport re\nimport typing as t\nfrom collections import abc\nfrom collections import deque"
},
{
"path": "src/jinja2/visitor.py",
"chars": 3550,
"preview": "\"\"\"API for traversing the AST nodes. Implemented by the compiler and\nmeta introspection.\n\"\"\"\n\nimport typing as t\n\nfrom ."
},
{
"path": "tests/conftest.py",
"chars": 1416,
"preview": "import asyncio\nfrom pathlib import Path\n\nimport pytest\nimport trio\n\nfrom jinja2 import loaders\nfrom jinja2.environment i"
},
{
"path": "tests/res/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "tests/res/templates/broken.html",
"chars": 26,
"preview": "Before\n{{ fail() }}\nAfter\n"
},
{
"path": "tests/res/templates/foo/test.html",
"chars": 4,
"preview": "FOO\n"
},
{
"path": "tests/res/templates/mojibake.txt",
"chars": 5,
"preview": "文字化け\n"
},
{
"path": "tests/res/templates/syntaxerror.html",
"chars": 47,
"preview": "Foo\n{% for item in broken %}\n ...\n{% endif %}\n"
},
{
"path": "tests/res/templates/test.html",
"chars": 4,
"preview": "BAR\n"
},
{
"path": "tests/res/templates2/foo",
"chars": 99,
"preview": "Looks like the start of templates/foo/test.html\nTested by test_filesystem_loader_overlapping_names\n"
},
{
"path": "tests/test_api.py",
"chars": 16861,
"preview": "import shutil\nimport tempfile\nfrom pathlib import Path\n\nimport pytest\n\nfrom jinja2 import ChainableUndefined\nfrom jinja2"
},
{
"path": "tests/test_async.py",
"chars": 23791,
"preview": "import pytest\n\nfrom jinja2 import ChainableUndefined\nfrom jinja2 import DictLoader\nfrom jinja2 import Environment\nfrom j"
},
{
"path": "tests/test_async_filters.py",
"chars": 9130,
"preview": "import contextlib\nfrom collections import namedtuple\n\nimport pytest\nfrom markupsafe import Markup\n\nfrom jinja2 import En"
},
{
"path": "tests/test_bytecode_cache.py",
"chars": 1984,
"preview": "import pytest\n\nfrom jinja2 import Environment\nfrom jinja2.bccache import Bucket\nfrom jinja2.bccache import FileSystemByt"
},
{
"path": "tests/test_compile.py",
"chars": 3889,
"preview": "import os\nimport re\n\nimport pytest\n\nfrom jinja2 import UndefinedError\nfrom jinja2.environment import Environment\nfrom ji"
},
{
"path": "tests/test_core_tags.py",
"chars": 20567,
"preview": "import pytest\n\nfrom jinja2 import DictLoader\nfrom jinja2 import Environment\nfrom jinja2 import TemplateRuntimeError\nfrom"
},
{
"path": "tests/test_debug.py",
"chars": 3704,
"preview": "import pickle\nimport re\nfrom traceback import format_exception\n\nimport pytest\n\nfrom jinja2 import ChoiceLoader\nfrom jinj"
},
{
"path": "tests/test_ext.py",
"chars": 26297,
"preview": "import re\nfrom io import BytesIO\n\nimport pytest\n\nfrom jinja2 import DictLoader\nfrom jinja2 import Environment\nfrom jinja"
},
{
"path": "tests/test_filters.py",
"chars": 32741,
"preview": "import random\nfrom collections import namedtuple\n\nimport pytest\nfrom markupsafe import Markup\n\nfrom jinja2 import Enviro"
},
{
"path": "tests/test_idtracking.py",
"chars": 8653,
"preview": "from jinja2 import nodes\nfrom jinja2.idtracking import symbols_for_node\n\n\ndef test_basics():\n for_loop = nodes.For(\n "
},
{
"path": "tests/test_imports.py",
"chars": 7571,
"preview": "import pytest\n\nfrom jinja2.environment import Environment\nfrom jinja2.exceptions import TemplateNotFound\nfrom jinja2.exc"
},
{
"path": "tests/test_inheritance.py",
"chars": 13504,
"preview": "import pytest\n\nfrom jinja2 import DictLoader\nfrom jinja2 import Environment\nfrom jinja2 import TemplateRuntimeError\nfrom"
},
{
"path": "tests/test_lexnparse.py",
"chars": 35449,
"preview": "import pytest\n\nfrom jinja2 import Environment\nfrom jinja2 import nodes\nfrom jinja2 import Template\nfrom jinja2 import Te"
},
{
"path": "tests/test_loader.py",
"chars": 15597,
"preview": "import importlib.abc\nimport importlib.machinery\nimport importlib.util\nimport os\nimport shutil\nimport sys\nimport tempfile"
},
{
"path": "tests/test_nativetypes.py",
"chars": 4996,
"preview": "import math\n\nimport pytest\n\nfrom jinja2.exceptions import UndefinedError\nfrom jinja2.nativetypes import NativeEnvironmen"
},
{
"path": "tests/test_nodes.py",
"chars": 86,
"preview": "def test_template_hash(env):\n template = env.parse(\"hash test\")\n hash(template)\n"
},
{
"path": "tests/test_pickle.py",
"chars": 148,
"preview": "import pickle\n\n\ndef test_environment(env):\n env = pickle.loads(pickle.dumps(env))\n assert env.from_string(\"x={{ x "
},
{
"path": "tests/test_regression.py",
"chars": 22902,
"preview": "import pytest\n\nfrom jinja2 import DictLoader\nfrom jinja2 import Environment\nfrom jinja2 import PrefixLoader\nfrom jinja2 "
},
{
"path": "tests/test_runtime.py",
"chars": 4172,
"preview": "import copy\nimport itertools\nimport pickle\n\nimport pytest\n\nfrom jinja2 import ChainableUndefined\nfrom jinja2 import Debu"
},
{
"path": "tests/test_security.py",
"chars": 7078,
"preview": "import pytest\nfrom markupsafe import escape\n\nfrom jinja2 import Environment\nfrom jinja2.exceptions import SecurityError\n"
},
{
"path": "tests/test_tests.py",
"chars": 7851,
"preview": "import pytest\nfrom markupsafe import Markup\n\nfrom jinja2 import Environment\nfrom jinja2 import TemplateAssertionError\nfr"
},
{
"path": "tests/test_utils.py",
"chars": 6367,
"preview": "import copy\nimport pickle\nimport random\nfrom collections import deque\nfrom copy import copy as shallow_copy\n\nimport pyte"
}
]
About this extraction
This page contains the full source code of the pallets/jinja GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 102 files (940.1 KB), approximately 232.8k tokens, and a symbol index with 1684 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.