Updated dependency management to uv (#2093)
This commit is contained in:
commit
05f5d74849
@ -1,7 +1,17 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
python3 -m venv --upgrade-deps .venv
|
||||
. .venv/bin/activate
|
||||
pip install -r requirements/dev.txt
|
||||
pip install -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
|
||||
|
||||
1
.github/workflows/lock.yaml
vendored
1
.github/workflows/lock.yaml
vendored
@ -10,6 +10,7 @@ on:
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
discussions: write
|
||||
concurrency:
|
||||
group: lock
|
||||
jobs:
|
||||
|
||||
23
.github/workflows/pre-commit.yaml
vendored
23
.github/workflows/pre-commit.yaml
vendored
@ -7,10 +7,19 @@ jobs:
|
||||
main:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
- uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0
|
||||
with:
|
||||
python-version: 3.x
|
||||
- uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1
|
||||
- uses: pre-commit-ci/lite-action@9d882e7a565f7008d4faf128f27d1cb6503d4ebf # v1.0.2
|
||||
if: ${{ !cancelled() }}
|
||||
- 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() }}
|
||||
|
||||
32
.github/workflows/publish.yaml
vendored
32
.github/workflows/publish.yaml
vendored
@ -1,8 +1,7 @@
|
||||
name: Publish
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '*'
|
||||
tags: ['*']
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
@ -10,20 +9,19 @@ jobs:
|
||||
hash: ${{ steps.hash.outputs.hash }}
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
|
||||
- uses: astral-sh/setup-uv@f0ec1fc3b38f5e7cd731bb6ce540c5af426746bb # v6.1.0
|
||||
with:
|
||||
python-version: '3.x'
|
||||
cache: pip
|
||||
cache-dependency-path: requirements*/*.txt
|
||||
- run: pip install -r requirements/build.txt
|
||||
# Use the commit date instead of the current date during the build.
|
||||
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: python -m build
|
||||
# Generate hashes used for provenance.
|
||||
- run: uv build
|
||||
- name: generate hash
|
||||
id: hash
|
||||
run: cd dist && echo "hash=$(sha256sum * | base64 -w0)" >> $GITHUB_OUTPUT
|
||||
- uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
path: ./dist
|
||||
provenance:
|
||||
@ -33,18 +31,16 @@ jobs:
|
||||
id-token: write
|
||||
contents: write
|
||||
# Can't pin with hash due to how this workflow works.
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.0.0
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.1.0
|
||||
with:
|
||||
base64-subjects: ${{ needs.build.outputs.hash }}
|
||||
create-release:
|
||||
# Upload the sdist, wheels, and provenance to a GitHub release. They remain
|
||||
# available as build artifacts for a while as well.
|
||||
needs: [provenance]
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||
- name: create release
|
||||
run: >
|
||||
gh release create --draft --repo ${{ github.repository }}
|
||||
@ -54,8 +50,6 @@ jobs:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
publish-pypi:
|
||||
needs: [provenance]
|
||||
# Wait for approval before attempting to upload to PyPI. This allows reviewing the
|
||||
# files in the draft release.
|
||||
environment:
|
||||
name: publish
|
||||
url: https://pypi.org/project/Jinja2/${{ github.ref_name }}
|
||||
@ -63,7 +57,7 @@ jobs:
|
||||
permissions:
|
||||
id-token: write
|
||||
steps:
|
||||
- uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||
- uses: pypa/gh-action-pypi-publish@67339c736fd9354cd4f8cb0b744f2b82a74b5c70 # v1.12.3
|
||||
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||
- uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4
|
||||
with:
|
||||
packages-dir: artifact/
|
||||
|
||||
40
.github/workflows/tests.yaml
vendored
40
.github/workflows/tests.yaml
vendored
@ -1,10 +1,10 @@
|
||||
name: Tests
|
||||
on:
|
||||
pull_request:
|
||||
paths-ignore: ['docs/**', 'README.md']
|
||||
push:
|
||||
branches: [main, stable]
|
||||
paths-ignore: ['docs/**', '*.md', '*.rst']
|
||||
pull_request:
|
||||
paths-ignore: [ 'docs/**', '*.md', '*.rst' ]
|
||||
paths-ignore: ['docs/**', 'README.md']
|
||||
jobs:
|
||||
tests:
|
||||
name: ${{ matrix.name || matrix.python }}
|
||||
@ -14,37 +14,37 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- {python: '3.13'}
|
||||
- {name: Windows, python: '3.13', os: windows-latest}
|
||||
- {name: Mac, python: '3.13', os: macos-latest}
|
||||
- {python: '3.12'}
|
||||
- {name: Windows, python: '3.12', os: windows-latest}
|
||||
- {name: Mac, python: '3.12', os: macos-latest}
|
||||
- {python: '3.11'}
|
||||
- {python: '3.10'}
|
||||
- {python: '3.9'}
|
||||
- {python: '3.8'}
|
||||
- {name: PyPy, python: 'pypy-3.10', tox: pypy310}
|
||||
- {name: PyPy, python: 'pypy-3.11', tox: pypy3.11}
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
|
||||
- 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 }}
|
||||
allow-prereleases: true
|
||||
cache: pip
|
||||
cache-dependency-path: requirements*/*.txt
|
||||
- run: pip install tox
|
||||
- run: tox run -e ${{ matrix.tox || format('py{0}', 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: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
|
||||
- uses: astral-sh/setup-uv@f0ec1fc3b38f5e7cd731bb6ce540c5af426746bb # v6.1.0
|
||||
with:
|
||||
python-version: '3.x'
|
||||
cache: pip
|
||||
cache-dependency-path: requirements*/*.txt
|
||||
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@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
|
||||
with:
|
||||
path: ./.mypy_cache
|
||||
key: mypy|${{ hashFiles('pyproject.toml') }}
|
||||
- run: pip install tox
|
||||
- run: tox run -e typing
|
||||
- run: uv run --locked tox run -e typing
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,7 +1,5 @@
|
||||
.idea/
|
||||
.vscode/
|
||||
.venv*/
|
||||
venv*/
|
||||
__pycache__/
|
||||
dist/
|
||||
.coverage*
|
||||
|
||||
@ -1,11 +1,15 @@
|
||||
repos:
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.8.4
|
||||
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: v5.0.0
|
||||
rev: cef0300fd0fc4d2a87a85fa2093c6b283ea36f4b # frozen: v5.0.0
|
||||
hooks:
|
||||
- id: check-merge-conflict
|
||||
- id: debug-statements
|
||||
|
||||
@ -1,13 +1,10 @@
|
||||
version: 2
|
||||
build:
|
||||
os: ubuntu-22.04
|
||||
os: ubuntu-24.04
|
||||
tools:
|
||||
python: '3.12'
|
||||
python:
|
||||
install:
|
||||
- requirements: requirements/docs.txt
|
||||
- method: pip
|
||||
path: .
|
||||
sphinx:
|
||||
builder: dirhtml
|
||||
fail_on_warning: true
|
||||
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
|
||||
|
||||
@ -5,7 +5,7 @@ Version 3.2.0
|
||||
|
||||
Unreleased
|
||||
|
||||
- Drop support for Python 3.7.
|
||||
- Drop support for Python 3.7 and 3.8.
|
||||
- Use modern packaging metadata with ``pyproject.toml`` instead of ``setup.cfg``.
|
||||
:pr:`1793`
|
||||
- Use ``flit_core`` instead of ``setuptools`` as build backend.
|
||||
|
||||
142
pyproject.toml
142
pyproject.toml
@ -2,35 +2,66 @@
|
||||
name = "Jinja2"
|
||||
description = "A very fast and expressive template engine."
|
||||
readme = "README.md"
|
||||
license = {file = "LICENSE.txt"}
|
||||
license = "BSD-3-Clause"
|
||||
license-files = ["LICENSE.txt"]
|
||||
maintainers = [{name = "Pallets", email = "contact@palletsprojects.com"}]
|
||||
classifiers = [
|
||||
"Development Status :: 5 - Production/Stable",
|
||||
"Environment :: Web Environment",
|
||||
"Intended Audience :: Developers",
|
||||
"License :: OSI Approved :: BSD License",
|
||||
"Operating System :: OS Independent",
|
||||
"Programming Language :: Python",
|
||||
"Topic :: Internet :: WWW/HTTP :: Dynamic Content",
|
||||
"Topic :: Text Processing :: Markup :: HTML",
|
||||
"Typing :: Typed",
|
||||
]
|
||||
requires-python = ">=3.8"
|
||||
dependencies = ["MarkupSafe>=2.0"]
|
||||
requires-python = ">=3.9"
|
||||
dependencies = [
|
||||
"MarkupSafe>=2.0"
|
||||
]
|
||||
dynamic = ["version"]
|
||||
|
||||
[project.urls]
|
||||
Donate = "https://palletsprojects.com/donate"
|
||||
Documentation = "https://jinja.palletsprojects.com/"
|
||||
Changes = "https://jinja.palletsprojects.com/changes/"
|
||||
Changes = "https://jinja.palletsprojects.com/page/changes/"
|
||||
Source = "https://github.com/pallets/jinja/"
|
||||
Chat = "https://discord.gg/pallets"
|
||||
|
||||
[project.optional-dependencies]
|
||||
i18n = ["Babel>=2.7"]
|
||||
|
||||
[project.entry-points."babel.extractors"]
|
||||
jinja2 = "jinja2.ext:babel_extract[i18n]"
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"ruff",
|
||||
"tox",
|
||||
"tox-uv",
|
||||
]
|
||||
docs = [
|
||||
"pallets-sphinx-themes",
|
||||
"sphinx",
|
||||
"sphinxcontrib-log-cabinet",
|
||||
]
|
||||
docs-auto = [
|
||||
"sphinx-autobuild",
|
||||
]
|
||||
gha-update = [
|
||||
"gha-update ; python_full_version >= '3.12'",
|
||||
]
|
||||
pre-commit = [
|
||||
"pre-commit",
|
||||
"pre-commit-uv",
|
||||
]
|
||||
tests = [
|
||||
"pytest",
|
||||
"pytest-timeout",
|
||||
"trio"
|
||||
]
|
||||
typing = [
|
||||
"mypy",
|
||||
"pyright",
|
||||
"pytest",
|
||||
]
|
||||
|
||||
[build-system]
|
||||
requires = ["flit_core<4"]
|
||||
@ -43,15 +74,17 @@ name = "jinja2"
|
||||
include = [
|
||||
"docs/",
|
||||
"examples/",
|
||||
"requirements/",
|
||||
"tests/",
|
||||
"CHANGES.md",
|
||||
"tox.ini",
|
||||
"CHANGES.rst",
|
||||
"uv.lock"
|
||||
]
|
||||
exclude = [
|
||||
"docs/_build/",
|
||||
]
|
||||
|
||||
[tool.uv]
|
||||
default-groups = ["dev", "pre-commit", "tests", "typing"]
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
testpaths = ["tests"]
|
||||
filterwarnings = [
|
||||
@ -65,19 +98,24 @@ source = ["jinja2", "tests"]
|
||||
[tool.coverage.paths]
|
||||
source = ["src", "*/site-packages"]
|
||||
|
||||
[tool.coverage.report]
|
||||
exclude_also = [
|
||||
"if t.TYPE_CHECKING",
|
||||
"raise NotImplementedError",
|
||||
": \\.{3}",
|
||||
]
|
||||
|
||||
[tool.mypy]
|
||||
python_version = "3.8"
|
||||
files = ["src/jinja2"]
|
||||
python_version = "3.9"
|
||||
files = ["src"]
|
||||
show_error_codes = true
|
||||
pretty = true
|
||||
strict = true
|
||||
local_partial_types = true
|
||||
warn_unreachable = true
|
||||
|
||||
[tool.pyright]
|
||||
pythonVersion = "3.8"
|
||||
include = ["src/jinja2"]
|
||||
typeCheckingMode = "basic"
|
||||
pythonVersion = "3.9"
|
||||
include = ["src"]
|
||||
typeCheckingMode = "standard"
|
||||
|
||||
[tool.ruff]
|
||||
src = ["src"]
|
||||
@ -94,6 +132,9 @@ select = [
|
||||
"UP", # pyupgrade
|
||||
"W", # pycodestyle warning
|
||||
]
|
||||
ignore = [
|
||||
"UP038", # keep isinstance tuple
|
||||
]
|
||||
|
||||
[tool.ruff.lint.isort]
|
||||
force-single-line = true
|
||||
@ -103,3 +144,70 @@ order-by-type = false
|
||||
tag-only = [
|
||||
"slsa-framework/slsa-github-generator",
|
||||
]
|
||||
|
||||
[tool.tox]
|
||||
env_list = [
|
||||
"py3.13", "py3.12", "py3.11", "py3.10", "py3.9",
|
||||
"pypy3.11",
|
||||
"style",
|
||||
"typing",
|
||||
"docs",
|
||||
]
|
||||
|
||||
[tool.tox.env_run_base]
|
||||
description = "pytest on latest dependency versions"
|
||||
runner = "uv-venv-lock-runner"
|
||||
package = "wheel"
|
||||
wheel_build_env = ".pkg"
|
||||
constrain_package_deps = true
|
||||
use_frozen_constraints = true
|
||||
dependency_groups = ["tests"]
|
||||
commands = [[
|
||||
"pytest", "-v", "--tb=short", "--basetemp={env_tmp_dir}",
|
||||
{replace = "posargs", default = [], extend = true},
|
||||
]]
|
||||
|
||||
[tool.tox.env.style]
|
||||
description = "run all pre-commit hooks on all files"
|
||||
dependency_groups = ["pre-commit"]
|
||||
skip_install = true
|
||||
commands = [["pre-commit", "run", "--all-files"]]
|
||||
|
||||
[tool.tox.env.typing]
|
||||
description = "run static type checkers"
|
||||
dependency_groups = ["typing"]
|
||||
commands = [
|
||||
["mypy"],
|
||||
]
|
||||
|
||||
[tool.tox.env.docs]
|
||||
description = "build docs"
|
||||
dependency_groups = ["docs"]
|
||||
commands = [["sphinx-build", "-E", "-W", "-b", "dirhtml", "docs", "docs/_build/dirhtml"]]
|
||||
|
||||
[tool.tox.env.docs-auto]
|
||||
description = "continuously rebuild docs and start a local server"
|
||||
dependency_groups = ["docs", "docs-auto"]
|
||||
commands = [["sphinx-autobuild", "-W", "-b", "dirhtml", "--watch", "src", "docs", "docs/_build/dirhtml"]]
|
||||
|
||||
[tool.tox.env.update-actions]
|
||||
description = "update GitHub Actions pins"
|
||||
labels = ["update"]
|
||||
dependency_groups = ["gha-update"]
|
||||
skip_install = true
|
||||
commands = [["gha-update"]]
|
||||
|
||||
[tool.tox.env.update-pre_commit]
|
||||
description = "update pre-commit pins"
|
||||
labels = ["update"]
|
||||
dependency_groups = ["pre-commit"]
|
||||
skip_install = true
|
||||
commands = [["pre-commit", "autoupdate", "--freeze", "-j4"]]
|
||||
|
||||
[tool.tox.env.update-requirements]
|
||||
description = "update uv lock"
|
||||
labels = ["update"]
|
||||
dependency_groups = []
|
||||
no_default_groups = true
|
||||
skip_install = true
|
||||
commands = [["uv", "lock", {replace = "posargs", default = ["-U"], extend = true}]]
|
||||
|
||||
@ -1 +0,0 @@
|
||||
build
|
||||
@ -1,12 +0,0 @@
|
||||
#
|
||||
# This file is autogenerated by pip-compile with Python 3.13
|
||||
# by the following command:
|
||||
#
|
||||
# pip-compile build.in
|
||||
#
|
||||
build==1.2.2.post1
|
||||
# via -r build.in
|
||||
packaging==24.2
|
||||
# via build
|
||||
pyproject-hooks==1.2.0
|
||||
# via build
|
||||
@ -1,5 +0,0 @@
|
||||
-r docs.txt
|
||||
-r tests.txt
|
||||
-r typing.txt
|
||||
pre-commit
|
||||
tox
|
||||
@ -1,151 +0,0 @@
|
||||
#
|
||||
# This file is autogenerated by pip-compile with Python 3.13
|
||||
# by the following command:
|
||||
#
|
||||
# pip-compile dev.in
|
||||
#
|
||||
alabaster==1.0.0
|
||||
# via sphinx
|
||||
attrs==24.3.0
|
||||
# via
|
||||
# outcome
|
||||
# trio
|
||||
babel==2.16.0
|
||||
# via sphinx
|
||||
build==1.2.2.post1
|
||||
# via pip-tools
|
||||
cachetools==5.5.0
|
||||
# via tox
|
||||
certifi==2024.12.14
|
||||
# via requests
|
||||
cfgv==3.4.0
|
||||
# via pre-commit
|
||||
chardet==5.2.0
|
||||
# via tox
|
||||
charset-normalizer==3.4.0
|
||||
# via requests
|
||||
click==8.1.7
|
||||
# via
|
||||
# pip-compile-multi
|
||||
# pip-tools
|
||||
colorama==0.4.6
|
||||
# via tox
|
||||
distlib==0.3.9
|
||||
# via virtualenv
|
||||
docutils==0.21.2
|
||||
# via sphinx
|
||||
filelock==3.16.1
|
||||
# via
|
||||
# tox
|
||||
# virtualenv
|
||||
identify==2.6.3
|
||||
# via pre-commit
|
||||
idna==3.10
|
||||
# via
|
||||
# requests
|
||||
# trio
|
||||
imagesize==1.4.1
|
||||
# via sphinx
|
||||
iniconfig==2.0.0
|
||||
# via pytest
|
||||
jinja2==3.1.4
|
||||
# via sphinx
|
||||
markupsafe==3.0.2
|
||||
# via jinja2
|
||||
mypy==1.14.0
|
||||
# via -r /Users/david/Projects/jinja/requirements/typing.in
|
||||
mypy-extensions==1.0.0
|
||||
# via mypy
|
||||
nodeenv==1.9.1
|
||||
# via pre-commit
|
||||
outcome==1.3.0.post0
|
||||
# via trio
|
||||
packaging==24.2
|
||||
# via
|
||||
# build
|
||||
# pallets-sphinx-themes
|
||||
# pyproject-api
|
||||
# pytest
|
||||
# sphinx
|
||||
# tox
|
||||
pallets-sphinx-themes==2.3.0
|
||||
# via -r /Users/david/Projects/jinja/requirements/docs.in
|
||||
pip-compile-multi==2.7.1
|
||||
# via -r dev.in
|
||||
pip-tools==7.4.1
|
||||
# via pip-compile-multi
|
||||
platformdirs==4.3.6
|
||||
# via
|
||||
# tox
|
||||
# virtualenv
|
||||
pluggy==1.5.0
|
||||
# via
|
||||
# pytest
|
||||
# tox
|
||||
pre-commit==4.0.1
|
||||
# via -r dev.in
|
||||
pygments==2.18.0
|
||||
# via sphinx
|
||||
pyproject-api==1.8.0
|
||||
# via tox
|
||||
pyproject-hooks==1.2.0
|
||||
# via
|
||||
# build
|
||||
# pip-tools
|
||||
pytest==8.3.4
|
||||
# via -r /Users/david/Projects/jinja/requirements/tests.in
|
||||
pyyaml==6.0.2
|
||||
# via pre-commit
|
||||
requests==2.32.3
|
||||
# via sphinx
|
||||
sniffio==1.3.1
|
||||
# via trio
|
||||
snowballstemmer==2.2.0
|
||||
# via sphinx
|
||||
sortedcontainers==2.4.0
|
||||
# via trio
|
||||
sphinx==8.1.3
|
||||
# via
|
||||
# -r /Users/david/Projects/jinja/requirements/docs.in
|
||||
# pallets-sphinx-themes
|
||||
# sphinx-issues
|
||||
# sphinx-notfound-page
|
||||
# sphinxcontrib-log-cabinet
|
||||
sphinx-issues==5.0.0
|
||||
# via -r /Users/david/Projects/jinja/requirements/docs.in
|
||||
sphinx-notfound-page==1.0.4
|
||||
# via pallets-sphinx-themes
|
||||
sphinxcontrib-applehelp==2.0.0
|
||||
# via sphinx
|
||||
sphinxcontrib-devhelp==2.0.0
|
||||
# via sphinx
|
||||
sphinxcontrib-htmlhelp==2.1.0
|
||||
# via sphinx
|
||||
sphinxcontrib-jsmath==1.0.1
|
||||
# via sphinx
|
||||
sphinxcontrib-log-cabinet==1.0.1
|
||||
# via -r /Users/david/Projects/jinja/requirements/docs.in
|
||||
sphinxcontrib-qthelp==2.0.0
|
||||
# via sphinx
|
||||
sphinxcontrib-serializinghtml==2.0.0
|
||||
# via sphinx
|
||||
toposort==1.10
|
||||
# via pip-compile-multi
|
||||
tox==4.23.2
|
||||
# via -r dev.in
|
||||
trio==0.27.0
|
||||
# via -r /Users/david/Projects/jinja/requirements/tests.in
|
||||
typing-extensions==4.12.2
|
||||
# via mypy
|
||||
urllib3==2.2.3
|
||||
# via requests
|
||||
virtualenv==20.28.0
|
||||
# via
|
||||
# pre-commit
|
||||
# tox
|
||||
wheel==0.45.1
|
||||
# via pip-tools
|
||||
|
||||
# The following packages are considered to be unsafe in a requirements file:
|
||||
# pip
|
||||
# setuptools
|
||||
@ -1,3 +0,0 @@
|
||||
pallets-sphinx-themes
|
||||
sphinx
|
||||
sphinxcontrib-log-cabinet
|
||||
@ -1,63 +0,0 @@
|
||||
#
|
||||
# This file is autogenerated by pip-compile with Python 3.13
|
||||
# by the following command:
|
||||
#
|
||||
# pip-compile docs.in
|
||||
#
|
||||
alabaster==1.0.0
|
||||
# via sphinx
|
||||
babel==2.16.0
|
||||
# via sphinx
|
||||
certifi==2024.12.14
|
||||
# via requests
|
||||
charset-normalizer==3.4.0
|
||||
# via requests
|
||||
docutils==0.21.2
|
||||
# via sphinx
|
||||
idna==3.10
|
||||
# via requests
|
||||
imagesize==1.4.1
|
||||
# via sphinx
|
||||
jinja2==3.1.4
|
||||
# via sphinx
|
||||
markupsafe==3.0.2
|
||||
# via jinja2
|
||||
packaging==24.2
|
||||
# via
|
||||
# pallets-sphinx-themes
|
||||
# sphinx
|
||||
pallets-sphinx-themes==2.3.0
|
||||
# via -r docs.in
|
||||
pygments==2.18.0
|
||||
# via sphinx
|
||||
requests==2.32.3
|
||||
# via sphinx
|
||||
snowballstemmer==2.2.0
|
||||
# via sphinx
|
||||
sphinx==8.1.3
|
||||
# via
|
||||
# -r docs.in
|
||||
# pallets-sphinx-themes
|
||||
# sphinx-issues
|
||||
# sphinx-notfound-page
|
||||
# sphinxcontrib-log-cabinet
|
||||
sphinx-issues==5.0.0
|
||||
# via -r docs.in
|
||||
sphinx-notfound-page==1.0.4
|
||||
# via pallets-sphinx-themes
|
||||
sphinxcontrib-applehelp==2.0.0
|
||||
# via sphinx
|
||||
sphinxcontrib-devhelp==2.0.0
|
||||
# via sphinx
|
||||
sphinxcontrib-htmlhelp==2.1.0
|
||||
# via sphinx
|
||||
sphinxcontrib-jsmath==1.0.1
|
||||
# via sphinx
|
||||
sphinxcontrib-log-cabinet==1.0.1
|
||||
# via -r docs.in
|
||||
sphinxcontrib-qthelp==2.0.0
|
||||
# via sphinx
|
||||
sphinxcontrib-serializinghtml==2.0.0
|
||||
# via sphinx
|
||||
urllib3==2.2.3
|
||||
# via requests
|
||||
@ -1,2 +0,0 @@
|
||||
pytest
|
||||
trio
|
||||
@ -1,28 +0,0 @@
|
||||
#
|
||||
# This file is autogenerated by pip-compile with Python 3.13
|
||||
# by the following command:
|
||||
#
|
||||
# pip-compile tests.in
|
||||
#
|
||||
attrs==24.3.0
|
||||
# via
|
||||
# outcome
|
||||
# trio
|
||||
idna==3.10
|
||||
# via trio
|
||||
iniconfig==2.0.0
|
||||
# via pytest
|
||||
outcome==1.3.0.post0
|
||||
# via trio
|
||||
packaging==24.2
|
||||
# via pytest
|
||||
pluggy==1.5.0
|
||||
# via pytest
|
||||
pytest==8.3.4
|
||||
# via -r tests.in
|
||||
sniffio==1.3.1
|
||||
# via trio
|
||||
sortedcontainers==2.4.0
|
||||
# via trio
|
||||
trio==0.27.0
|
||||
# via -r tests.in
|
||||
@ -1,3 +0,0 @@
|
||||
mypy
|
||||
pyright
|
||||
pytest
|
||||
@ -1,12 +0,0 @@
|
||||
#
|
||||
# This file is autogenerated by pip-compile with Python 3.13
|
||||
# by the following command:
|
||||
#
|
||||
# pip-compile typing.in
|
||||
#
|
||||
mypy==1.14.0
|
||||
# via -r typing.in
|
||||
mypy-extensions==1.0.0
|
||||
# via mypy
|
||||
typing-extensions==4.12.2
|
||||
# via mypy
|
||||
@ -95,5 +95,5 @@ def auto_aiter(
|
||||
|
||||
async def auto_to_list(
|
||||
value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]",
|
||||
) -> t.List["V"]:
|
||||
) -> list["V"]:
|
||||
return [x async for x in auto_aiter(value)]
|
||||
|
||||
@ -139,9 +139,7 @@ def has_safe_repr(value: t.Any) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
def find_undeclared(
|
||||
nodes: t.Iterable[nodes.Node], names: t.Iterable[str]
|
||||
) -> t.Set[str]:
|
||||
def find_undeclared(nodes: t.Iterable[nodes.Node], names: t.Iterable[str]) -> set[str]:
|
||||
"""Check if the names passed are accessed undeclared. The return value
|
||||
is a set of all the undeclared names from the sequence of names found.
|
||||
"""
|
||||
@ -253,8 +251,8 @@ class DependencyFinderVisitor(NodeVisitor):
|
||||
"""A visitor that collects filter and test calls."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.filters: t.Set[str] = set()
|
||||
self.tests: t.Set[str] = set()
|
||||
self.filters: set[str] = set()
|
||||
self.tests: set[str] = set()
|
||||
|
||||
def visit_Filter(self, node: nodes.Filter) -> None:
|
||||
self.generic_visit(node)
|
||||
@ -276,7 +274,7 @@ class UndeclaredNameVisitor(NodeVisitor):
|
||||
|
||||
def __init__(self, names: t.Iterable[str]) -> None:
|
||||
self.names = set(names)
|
||||
self.undeclared: t.Set[str] = set()
|
||||
self.undeclared: set[str] = set()
|
||||
|
||||
def visit_Name(self, node: nodes.Name) -> None:
|
||||
if node.ctx == "load" and node.name in self.names:
|
||||
@ -321,11 +319,11 @@ class CodeGenerator(NodeVisitor):
|
||||
self.optimizer = Optimizer(environment)
|
||||
|
||||
# aliases for imports
|
||||
self.import_aliases: t.Dict[str, str] = {}
|
||||
self.import_aliases: dict[str, str] = {}
|
||||
|
||||
# a registry for all blocks. Because blocks are moved out
|
||||
# into the global python scope they are registered here
|
||||
self.blocks: t.Dict[str, nodes.Block] = {}
|
||||
self.blocks: dict[str, nodes.Block] = {}
|
||||
|
||||
# the number of extends statements so far
|
||||
self.extends_so_far = 0
|
||||
@ -339,11 +337,11 @@ class CodeGenerator(NodeVisitor):
|
||||
self.code_lineno = 1
|
||||
|
||||
# registry of all filters and tests (global, not block local)
|
||||
self.tests: t.Dict[str, str] = {}
|
||||
self.filters: t.Dict[str, str] = {}
|
||||
self.tests: dict[str, str] = {}
|
||||
self.filters: dict[str, str] = {}
|
||||
|
||||
# the debug information
|
||||
self.debug_info: t.List[t.Tuple[int, int]] = []
|
||||
self.debug_info: list[tuple[int, int]] = []
|
||||
self._write_debug_info: t.Optional[int] = None
|
||||
|
||||
# the number of new lines before the next write()
|
||||
@ -363,10 +361,10 @@ class CodeGenerator(NodeVisitor):
|
||||
self._indentation = 0
|
||||
|
||||
# Tracks toplevel assignments
|
||||
self._assign_stack: t.List[t.Set[str]] = []
|
||||
self._assign_stack: list[set[str]] = []
|
||||
|
||||
# Tracks parameter definition blocks
|
||||
self._param_def_block: t.List[t.Set[str]] = []
|
||||
self._param_def_block: list[set[str]] = []
|
||||
|
||||
# Tracks the current context.
|
||||
self._context_reference_stack = ["context"]
|
||||
@ -613,7 +611,7 @@ class CodeGenerator(NodeVisitor):
|
||||
|
||||
def macro_body(
|
||||
self, node: t.Union[nodes.Macro, nodes.CallBlock], frame: Frame
|
||||
) -> t.Tuple[Frame, MacroRef]:
|
||||
) -> tuple[Frame, MacroRef]:
|
||||
"""Dump the function def of a macro or call block."""
|
||||
frame = frame.inner()
|
||||
frame.symbols.analyze_node(node)
|
||||
@ -1511,7 +1509,7 @@ class CodeGenerator(NodeVisitor):
|
||||
self.indent()
|
||||
|
||||
finalize = self._make_finalize()
|
||||
body: t.List[t.Union[t.List[t.Any], nodes.Expr]] = []
|
||||
body: list[t.Union[list[t.Any], nodes.Expr]] = []
|
||||
|
||||
# Evaluate constants at compile time if possible. Each item in
|
||||
# body will be either a list of static data or a node to be
|
||||
@ -1586,7 +1584,7 @@ class CodeGenerator(NodeVisitor):
|
||||
# it is only valid if it references a Namespace object. Emit a check for
|
||||
# that for each ref here, before assignment code is emitted. This can't
|
||||
# be done in visit_NSRef as the ref could be in the middle of a tuple.
|
||||
seen_refs: t.Set[str] = set()
|
||||
seen_refs: set[str] = set()
|
||||
|
||||
for nsref in node.find_all(nodes.NSRef):
|
||||
if nsref.name in seen_refs:
|
||||
|
||||
@ -128,7 +128,7 @@ def fake_traceback( # type: ignore
|
||||
return sys.exc_info()[2].tb_next # type: ignore
|
||||
|
||||
|
||||
def get_template_locals(real_locals: t.Mapping[str, t.Any]) -> t.Dict[str, t.Any]:
|
||||
def get_template_locals(real_locals: t.Mapping[str, t.Any]) -> dict[str, t.Any]:
|
||||
"""Based on the runtime locals, get the context that would be
|
||||
available at that point in the template.
|
||||
"""
|
||||
@ -136,7 +136,7 @@ def get_template_locals(real_locals: t.Mapping[str, t.Any]) -> t.Dict[str, t.Any
|
||||
ctx: t.Optional[Context] = real_locals.get("context")
|
||||
|
||||
if ctx is not None:
|
||||
data: t.Dict[str, t.Any] = ctx.get_all().copy()
|
||||
data: dict[str, t.Any] = ctx.get_all().copy()
|
||||
else:
|
||||
data = {}
|
||||
|
||||
@ -144,7 +144,7 @@ def get_template_locals(real_locals: t.Mapping[str, t.Any]) -> t.Dict[str, t.Any
|
||||
# rather than pushing a context. Local variables follow the scheme
|
||||
# l_depth_name. Find the highest-depth local that has a value for
|
||||
# each name.
|
||||
local_overrides: t.Dict[str, t.Tuple[int, t.Any]] = {}
|
||||
local_overrides: dict[str, tuple[int, t.Any]] = {}
|
||||
|
||||
for name, value in real_locals.items():
|
||||
if not name.startswith("l_") or value is missing:
|
||||
|
||||
@ -36,7 +36,7 @@ DEFAULT_NAMESPACE = {
|
||||
}
|
||||
|
||||
# default policies
|
||||
DEFAULT_POLICIES: t.Dict[str, t.Any] = {
|
||||
DEFAULT_POLICIES: dict[str, t.Any] = {
|
||||
"compiler.ascii_str": True,
|
||||
"urlize.rel": "noopener",
|
||||
"urlize.target": None,
|
||||
|
||||
@ -66,7 +66,7 @@ _env_bound = t.TypeVar("_env_bound", bound="Environment")
|
||||
|
||||
# for direct template usage we have up to ten living environments
|
||||
@lru_cache(maxsize=10)
|
||||
def get_spontaneous_environment(cls: t.Type[_env_bound], *args: t.Any) -> _env_bound:
|
||||
def get_spontaneous_environment(cls: type[_env_bound], *args: t.Any) -> _env_bound:
|
||||
"""Return a new spontaneous environment. A spontaneous environment
|
||||
is used for templates created directly rather than through an
|
||||
existing environment.
|
||||
@ -81,7 +81,7 @@ def get_spontaneous_environment(cls: t.Type[_env_bound], *args: t.Any) -> _env_b
|
||||
|
||||
def create_cache(
|
||||
size: int,
|
||||
) -> t.Optional[t.MutableMapping[t.Tuple["weakref.ref[BaseLoader]", str], "Template"]]:
|
||||
) -> t.Optional[t.MutableMapping[tuple["weakref.ref[BaseLoader]", str], "Template"]]:
|
||||
"""Return the cache class for the given size."""
|
||||
if size == 0:
|
||||
return None
|
||||
@ -94,9 +94,9 @@ def create_cache(
|
||||
|
||||
def copy_cache(
|
||||
cache: t.Optional[
|
||||
t.MutableMapping[t.Tuple["weakref.ref[BaseLoader]", str], "Template"]
|
||||
t.MutableMapping[tuple["weakref.ref[BaseLoader]", str], "Template"]
|
||||
],
|
||||
) -> t.Optional[t.MutableMapping[t.Tuple["weakref.ref[BaseLoader]", str], "Template"]]:
|
||||
) -> t.Optional[t.MutableMapping[tuple["weakref.ref[BaseLoader]", str], "Template"]]:
|
||||
"""Create an empty copy of the given cache."""
|
||||
if cache is None:
|
||||
return None
|
||||
@ -109,8 +109,8 @@ def copy_cache(
|
||||
|
||||
def load_extensions(
|
||||
environment: "Environment",
|
||||
extensions: t.Sequence[t.Union[str, t.Type["Extension"]]],
|
||||
) -> t.Dict[str, "Extension"]:
|
||||
extensions: t.Sequence[t.Union[str, type["Extension"]]],
|
||||
) -> dict[str, "Extension"]:
|
||||
"""Load the extensions from the list and bind it to the environment.
|
||||
Returns a dict of instantiated extensions.
|
||||
"""
|
||||
@ -118,7 +118,7 @@ def load_extensions(
|
||||
|
||||
for extension in extensions:
|
||||
if isinstance(extension, str):
|
||||
extension = t.cast(t.Type["Extension"], import_string(extension))
|
||||
extension = t.cast(type["Extension"], import_string(extension))
|
||||
|
||||
result[extension.identifier] = extension(environment)
|
||||
|
||||
@ -127,9 +127,9 @@ def load_extensions(
|
||||
|
||||
def _environment_config_check(environment: _env_bound) -> _env_bound:
|
||||
"""Perform a sanity check on the environment."""
|
||||
assert issubclass(
|
||||
environment.undefined, Undefined
|
||||
), "'undefined' must be a subclass of 'jinja2.Undefined'."
|
||||
assert issubclass(environment.undefined, Undefined), (
|
||||
"'undefined' must be a subclass of 'jinja2.Undefined'."
|
||||
)
|
||||
assert (
|
||||
environment.block_start_string
|
||||
!= environment.variable_start_string
|
||||
@ -283,15 +283,15 @@ class Environment:
|
||||
|
||||
#: the class that is used for code generation. See
|
||||
#: :class:`~jinja2.compiler.CodeGenerator` for more information.
|
||||
code_generator_class: t.Type["CodeGenerator"] = CodeGenerator
|
||||
code_generator_class: type["CodeGenerator"] = CodeGenerator
|
||||
|
||||
concat = "".join
|
||||
|
||||
#: the context class that is used for templates. See
|
||||
#: :class:`~jinja2.runtime.Context` for more information.
|
||||
context_class: t.Type[Context] = Context
|
||||
context_class: type[Context] = Context
|
||||
|
||||
template_class: t.Type["Template"]
|
||||
template_class: type["Template"]
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@ -307,9 +307,9 @@ class Environment:
|
||||
lstrip_blocks: bool = LSTRIP_BLOCKS,
|
||||
newline_sequence: "te.Literal['\\n', '\\r\\n', '\\r']" = NEWLINE_SEQUENCE,
|
||||
keep_trailing_newline: bool = KEEP_TRAILING_NEWLINE,
|
||||
extensions: t.Sequence[t.Union[str, t.Type["Extension"]]] = (),
|
||||
extensions: t.Sequence[t.Union[str, type["Extension"]]] = (),
|
||||
optimized: bool = True,
|
||||
undefined: t.Type[Undefined] = Undefined,
|
||||
undefined: type[Undefined] = Undefined,
|
||||
finalize: t.Optional[t.Callable[..., t.Any]] = None,
|
||||
autoescape: t.Union[bool, t.Callable[[t.Optional[str]], bool]] = False,
|
||||
loader: t.Optional["BaseLoader"] = None,
|
||||
@ -344,7 +344,7 @@ class Environment:
|
||||
self.keep_trailing_newline = keep_trailing_newline
|
||||
|
||||
# runtime information
|
||||
self.undefined: t.Type[Undefined] = undefined
|
||||
self.undefined: type[Undefined] = undefined
|
||||
self.optimized = optimized
|
||||
self.finalize = finalize
|
||||
self.autoescape = autoescape
|
||||
@ -369,7 +369,7 @@ class Environment:
|
||||
self.is_async = enable_async
|
||||
_environment_config_check(self)
|
||||
|
||||
def add_extension(self, extension: t.Union[str, t.Type["Extension"]]) -> None:
|
||||
def add_extension(self, extension: t.Union[str, type["Extension"]]) -> None:
|
||||
"""Adds an extension after the environment was created.
|
||||
|
||||
.. versionadded:: 2.5
|
||||
@ -399,9 +399,9 @@ class Environment:
|
||||
lstrip_blocks: bool = missing,
|
||||
newline_sequence: "te.Literal['\\n', '\\r\\n', '\\r']" = missing,
|
||||
keep_trailing_newline: bool = missing,
|
||||
extensions: t.Sequence[t.Union[str, t.Type["Extension"]]] = missing,
|
||||
extensions: t.Sequence[t.Union[str, type["Extension"]]] = missing,
|
||||
optimized: bool = missing,
|
||||
undefined: t.Type[Undefined] = missing,
|
||||
undefined: type[Undefined] = missing,
|
||||
finalize: t.Optional[t.Callable[..., t.Any]] = missing,
|
||||
autoescape: t.Union[bool, t.Callable[[t.Optional[str]], bool]] = missing,
|
||||
loader: t.Optional["BaseLoader"] = missing,
|
||||
@ -628,7 +628,7 @@ class Environment:
|
||||
source: str,
|
||||
name: t.Optional[str] = None,
|
||||
filename: t.Optional[str] = None,
|
||||
) -> t.Iterator[t.Tuple[int, str, str]]:
|
||||
) -> t.Iterator[tuple[int, str, str]]:
|
||||
"""Lex the given sourcecode and return a generator that yields
|
||||
tokens as tuples in the form ``(lineno, token_type, value)``.
|
||||
This can be useful for :ref:`extension development <writing-extensions>`
|
||||
@ -677,7 +677,7 @@ class Environment:
|
||||
stream = ext.filter_stream(stream) # type: ignore
|
||||
|
||||
if not isinstance(stream, TokenStream):
|
||||
stream = TokenStream(stream, name, filename) # type: ignore[unreachable]
|
||||
stream = TokenStream(stream, name, filename)
|
||||
|
||||
return stream
|
||||
|
||||
@ -902,7 +902,7 @@ class Environment:
|
||||
self,
|
||||
extensions: t.Optional[t.Collection[str]] = None,
|
||||
filter_func: t.Optional[t.Callable[[str], bool]] = None,
|
||||
) -> t.List[str]:
|
||||
) -> list[str]:
|
||||
"""Returns a list of templates for this environment. This requires
|
||||
that the loader supports the loader's
|
||||
:meth:`~BaseLoader.list_templates` method.
|
||||
@ -1074,9 +1074,7 @@ class Environment:
|
||||
@internalcode
|
||||
def get_or_select_template(
|
||||
self,
|
||||
template_name_or_list: t.Union[
|
||||
str, "Template", t.List[t.Union[str, "Template"]]
|
||||
],
|
||||
template_name_or_list: t.Union[str, "Template", list[t.Union[str, "Template"]]],
|
||||
parent: t.Optional[str] = None,
|
||||
globals: t.Optional[t.MutableMapping[str, t.Any]] = None,
|
||||
) -> "Template":
|
||||
@ -1095,7 +1093,7 @@ class Environment:
|
||||
self,
|
||||
source: t.Union[str, nodes.Template],
|
||||
globals: t.Optional[t.MutableMapping[str, t.Any]] = None,
|
||||
template_class: t.Optional[t.Type["Template"]] = None,
|
||||
template_class: t.Optional[type["Template"]] = None,
|
||||
) -> "Template":
|
||||
"""Load a template from a source string without using
|
||||
:attr:`loader`.
|
||||
@ -1154,13 +1152,13 @@ class Template:
|
||||
|
||||
#: Type of environment to create when creating a template directly
|
||||
#: rather than through an existing environment.
|
||||
environment_class: t.Type[Environment] = Environment
|
||||
environment_class: type[Environment] = Environment
|
||||
|
||||
environment: Environment
|
||||
globals: t.MutableMapping[str, t.Any]
|
||||
name: t.Optional[str]
|
||||
filename: t.Optional[str]
|
||||
blocks: t.Dict[str, t.Callable[[Context], t.Iterator[str]]]
|
||||
blocks: dict[str, t.Callable[[Context], t.Iterator[str]]]
|
||||
root_render_func: t.Callable[[Context], t.Iterator[str]]
|
||||
_module: t.Optional["TemplateModule"]
|
||||
_debug_info: str
|
||||
@ -1181,9 +1179,9 @@ class Template:
|
||||
lstrip_blocks: bool = LSTRIP_BLOCKS,
|
||||
newline_sequence: "te.Literal['\\n', '\\r\\n', '\\r']" = NEWLINE_SEQUENCE,
|
||||
keep_trailing_newline: bool = KEEP_TRAILING_NEWLINE,
|
||||
extensions: t.Sequence[t.Union[str, t.Type["Extension"]]] = (),
|
||||
extensions: t.Sequence[t.Union[str, type["Extension"]]] = (),
|
||||
optimized: bool = True,
|
||||
undefined: t.Type[Undefined] = Undefined,
|
||||
undefined: type[Undefined] = Undefined,
|
||||
finalize: t.Optional[t.Callable[..., t.Any]] = None,
|
||||
autoescape: t.Union[bool, t.Callable[[t.Optional[str]], bool]] = False,
|
||||
enable_async: bool = False,
|
||||
@ -1336,7 +1334,7 @@ class Template:
|
||||
if self.environment.is_async:
|
||||
import asyncio
|
||||
|
||||
async def to_list() -> t.List[str]:
|
||||
async def to_list() -> list[str]:
|
||||
return [x async for x in self.generate_async(*args, **kwargs)]
|
||||
|
||||
yield from asyncio.run(to_list())
|
||||
@ -1376,7 +1374,7 @@ class Template:
|
||||
|
||||
def new_context(
|
||||
self,
|
||||
vars: t.Optional[t.Dict[str, t.Any]] = None,
|
||||
vars: t.Optional[dict[str, t.Any]] = None,
|
||||
shared: bool = False,
|
||||
locals: t.Optional[t.Mapping[str, t.Any]] = None,
|
||||
) -> Context:
|
||||
@ -1393,7 +1391,7 @@ class Template:
|
||||
|
||||
def make_module(
|
||||
self,
|
||||
vars: t.Optional[t.Dict[str, t.Any]] = None,
|
||||
vars: t.Optional[dict[str, t.Any]] = None,
|
||||
shared: bool = False,
|
||||
locals: t.Optional[t.Mapping[str, t.Any]] = None,
|
||||
) -> "TemplateModule":
|
||||
@ -1408,7 +1406,7 @@ class Template:
|
||||
|
||||
async def make_module_async(
|
||||
self,
|
||||
vars: t.Optional[t.Dict[str, t.Any]] = None,
|
||||
vars: t.Optional[dict[str, t.Any]] = None,
|
||||
shared: bool = False,
|
||||
locals: t.Optional[t.Mapping[str, t.Any]] = None,
|
||||
) -> "TemplateModule":
|
||||
@ -1498,7 +1496,7 @@ class Template:
|
||||
return self._uptodate()
|
||||
|
||||
@property
|
||||
def debug_info(self) -> t.List[t.Tuple[int, int]]:
|
||||
def debug_info(self) -> list[tuple[int, int]]:
|
||||
"""The debug info mapping."""
|
||||
if self._debug_info:
|
||||
return [
|
||||
@ -1636,7 +1634,7 @@ class TemplateStream:
|
||||
self.buffered = False
|
||||
|
||||
def _buffered_generator(self, size: int) -> t.Iterator[str]:
|
||||
buf: t.List[str] = []
|
||||
buf: list[str] = []
|
||||
c_size = 0
|
||||
push = buf.append
|
||||
|
||||
|
||||
@ -42,7 +42,7 @@ if t.TYPE_CHECKING:
|
||||
|
||||
# I18N functions available in Jinja templates. If the I18N library
|
||||
# provides ugettext, it will be assigned to gettext.
|
||||
GETTEXT_FUNCTIONS: t.Tuple[str, ...] = (
|
||||
GETTEXT_FUNCTIONS: tuple[str, ...] = (
|
||||
"_",
|
||||
"gettext",
|
||||
"ngettext",
|
||||
@ -77,7 +77,7 @@ class Extension:
|
||||
cls.identifier = f"{cls.__module__}.{cls.__name__}"
|
||||
|
||||
#: if this extension parses this is the list of tags it's listening to.
|
||||
tags: t.Set[str] = set()
|
||||
tags: set[str] = set()
|
||||
|
||||
#: the priority of that extension. This is especially useful for
|
||||
#: extensions that preprocess values. A lower value means higher
|
||||
@ -115,7 +115,7 @@ class Extension:
|
||||
"""
|
||||
return stream
|
||||
|
||||
def parse(self, parser: "Parser") -> t.Union[nodes.Node, t.List[nodes.Node]]:
|
||||
def parse(self, parser: "Parser") -> t.Union[nodes.Node, list[nodes.Node]]:
|
||||
"""If any of the :attr:`tags` matched this method is called with the
|
||||
parser as first argument. The token the parser stream is pointing at
|
||||
is the name token that matched. This method has to return one or a
|
||||
@ -138,8 +138,8 @@ class Extension:
|
||||
def call_method(
|
||||
self,
|
||||
name: str,
|
||||
args: t.Optional[t.List[nodes.Expr]] = None,
|
||||
kwargs: t.Optional[t.List[nodes.Keyword]] = None,
|
||||
args: t.Optional[list[nodes.Expr]] = None,
|
||||
kwargs: t.Optional[list[nodes.Keyword]] = None,
|
||||
dyn_args: t.Optional[nodes.Expr] = None,
|
||||
dyn_kwargs: t.Optional[nodes.Expr] = None,
|
||||
lineno: t.Optional[int] = None,
|
||||
@ -330,13 +330,13 @@ class InternationalizationExtension(Extension):
|
||||
source: t.Union[str, nodes.Template],
|
||||
gettext_functions: t.Sequence[str] = GETTEXT_FUNCTIONS,
|
||||
) -> t.Iterator[
|
||||
t.Tuple[int, str, t.Union[t.Optional[str], t.Tuple[t.Optional[str], ...]]]
|
||||
tuple[int, str, t.Union[t.Optional[str], tuple[t.Optional[str], ...]]]
|
||||
]:
|
||||
if isinstance(source, str):
|
||||
source = self.environment.parse(source)
|
||||
return extract_from_ast(source, gettext_functions)
|
||||
|
||||
def parse(self, parser: "Parser") -> t.Union[nodes.Node, t.List[nodes.Node]]:
|
||||
def parse(self, parser: "Parser") -> t.Union[nodes.Node, list[nodes.Node]]:
|
||||
"""Parse a translatable tag."""
|
||||
lineno = next(parser.stream).lineno
|
||||
|
||||
@ -352,7 +352,7 @@ class InternationalizationExtension(Extension):
|
||||
plural_expr: t.Optional[nodes.Expr] = None
|
||||
plural_expr_assignment: t.Optional[nodes.Assign] = None
|
||||
num_called_num = False
|
||||
variables: t.Dict[str, nodes.Expr] = {}
|
||||
variables: dict[str, nodes.Expr] = {}
|
||||
trimmed = None
|
||||
while parser.stream.current.type != "block_end":
|
||||
if variables:
|
||||
@ -463,7 +463,7 @@ class InternationalizationExtension(Extension):
|
||||
|
||||
def _parse_block(
|
||||
self, parser: "Parser", allow_pluralize: bool
|
||||
) -> t.Tuple[t.List[str], str]:
|
||||
) -> tuple[list[str], str]:
|
||||
"""Parse until the next block tag with a given name."""
|
||||
referenced = []
|
||||
buf = []
|
||||
@ -513,7 +513,7 @@ class InternationalizationExtension(Extension):
|
||||
singular: str,
|
||||
plural: t.Optional[str],
|
||||
context: t.Optional[str],
|
||||
variables: t.Dict[str, nodes.Expr],
|
||||
variables: dict[str, nodes.Expr],
|
||||
plural_expr: t.Optional[nodes.Expr],
|
||||
vars_referenced: bool,
|
||||
num_called_num: bool,
|
||||
@ -530,7 +530,7 @@ class InternationalizationExtension(Extension):
|
||||
plural = plural.replace("%%", "%")
|
||||
|
||||
func_name = "gettext"
|
||||
func_args: t.List[nodes.Expr] = [nodes.Const(singular)]
|
||||
func_args: list[nodes.Expr] = [nodes.Const(singular)]
|
||||
|
||||
if context is not None:
|
||||
func_args.insert(0, nodes.Const(context))
|
||||
@ -640,9 +640,7 @@ def extract_from_ast(
|
||||
ast: nodes.Template,
|
||||
gettext_functions: t.Sequence[str] = GETTEXT_FUNCTIONS,
|
||||
babel_style: bool = True,
|
||||
) -> t.Iterator[
|
||||
t.Tuple[int, str, t.Union[t.Optional[str], t.Tuple[t.Optional[str], ...]]]
|
||||
]:
|
||||
) -> t.Iterator[tuple[int, str, t.Union[t.Optional[str], tuple[t.Optional[str], ...]]]]:
|
||||
"""Extract localizable strings from the given template node. Per
|
||||
default this function returns matches in babel style that means non string
|
||||
parameters as well as keyword arguments are returned as `None`. This
|
||||
@ -677,7 +675,7 @@ def extract_from_ast(
|
||||
to extract any comments. For comment support you have to use the babel
|
||||
extraction interface or extract comments yourself.
|
||||
"""
|
||||
out: t.Union[t.Optional[str], t.Tuple[t.Optional[str], ...]]
|
||||
out: t.Union[t.Optional[str], tuple[t.Optional[str], ...]]
|
||||
|
||||
for node in ast.find_all(nodes.Call):
|
||||
if (
|
||||
@ -686,7 +684,7 @@ def extract_from_ast(
|
||||
):
|
||||
continue
|
||||
|
||||
strings: t.List[t.Optional[str]] = []
|
||||
strings: list[t.Optional[str]] = []
|
||||
|
||||
for arg in node.args:
|
||||
if isinstance(arg, nodes.Const) and isinstance(arg.value, str):
|
||||
@ -723,14 +721,14 @@ class _CommentFinder:
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, tokens: t.Sequence[t.Tuple[int, str, str]], comment_tags: t.Sequence[str]
|
||||
self, tokens: t.Sequence[tuple[int, str, str]], comment_tags: t.Sequence[str]
|
||||
) -> None:
|
||||
self.tokens = tokens
|
||||
self.comment_tags = comment_tags
|
||||
self.offset = 0
|
||||
self.last_lineno = 0
|
||||
|
||||
def find_backwards(self, offset: int) -> t.List[str]:
|
||||
def find_backwards(self, offset: int) -> list[str]:
|
||||
try:
|
||||
for _, token_type, token_value in reversed(
|
||||
self.tokens[self.offset : offset]
|
||||
@ -746,7 +744,7 @@ class _CommentFinder:
|
||||
finally:
|
||||
self.offset = offset
|
||||
|
||||
def find_comments(self, lineno: int) -> t.List[str]:
|
||||
def find_comments(self, lineno: int) -> list[str]:
|
||||
if not self.comment_tags or self.last_lineno > lineno:
|
||||
return []
|
||||
for idx, (token_lineno, _, _) in enumerate(self.tokens[self.offset :]):
|
||||
@ -759,11 +757,9 @@ def babel_extract(
|
||||
fileobj: t.BinaryIO,
|
||||
keywords: t.Sequence[str],
|
||||
comment_tags: t.Sequence[str],
|
||||
options: t.Dict[str, t.Any],
|
||||
options: dict[str, t.Any],
|
||||
) -> t.Iterator[
|
||||
t.Tuple[
|
||||
int, str, t.Union[t.Optional[str], t.Tuple[t.Optional[str], ...]], t.List[str]
|
||||
]
|
||||
tuple[int, str, t.Union[t.Optional[str], tuple[t.Optional[str], ...]], list[str]]
|
||||
]:
|
||||
"""Babel extraction method for Jinja templates.
|
||||
|
||||
@ -792,7 +788,7 @@ def babel_extract(
|
||||
:return: an iterator over ``(lineno, funcname, message, comments)`` tuples.
|
||||
(comments will be empty currently)
|
||||
"""
|
||||
extensions: t.Dict[t.Type[Extension], None] = {}
|
||||
extensions: dict[type[Extension], None] = {}
|
||||
|
||||
for extension_name in options.get("extensions", "").split(","):
|
||||
extension_name = extension_name.strip()
|
||||
|
||||
@ -87,7 +87,7 @@ def make_multi_attrgetter(
|
||||
environment: "Environment",
|
||||
attribute: t.Optional[t.Union[str, int]],
|
||||
postprocess: t.Optional[t.Callable[[t.Any], t.Any]] = None,
|
||||
) -> t.Callable[[t.Any], t.List[t.Any]]:
|
||||
) -> t.Callable[[t.Any], list[t.Any]]:
|
||||
"""Returns a callable that looks up the given comma separated
|
||||
attributes from a passed object with the rules of the environment.
|
||||
Dots are allowed to access attributes of each attribute. Integer
|
||||
@ -105,7 +105,7 @@ def make_multi_attrgetter(
|
||||
|
||||
parts = [_prepare_attribute_parts(item) for item in split]
|
||||
|
||||
def attrgetter(item: t.Any) -> t.List[t.Any]:
|
||||
def attrgetter(item: t.Any) -> list[t.Any]:
|
||||
items = [None] * len(parts)
|
||||
|
||||
for i, attribute_part in enumerate(parts):
|
||||
@ -126,7 +126,7 @@ def make_multi_attrgetter(
|
||||
|
||||
def _prepare_attribute_parts(
|
||||
attr: t.Optional[t.Union[str, int]],
|
||||
) -> t.List[t.Union[str, int]]:
|
||||
) -> list[t.Union[str, int]]:
|
||||
if attr is None:
|
||||
return []
|
||||
|
||||
@ -145,7 +145,7 @@ def do_forceescape(value: "t.Union[str, HasHTML]") -> Markup:
|
||||
|
||||
|
||||
def do_urlencode(
|
||||
value: t.Union[str, t.Mapping[str, t.Any], t.Iterable[t.Tuple[str, t.Any]]],
|
||||
value: t.Union[str, t.Mapping[str, t.Any], t.Iterable[tuple[str, t.Any]]],
|
||||
) -> str:
|
||||
"""Quote data for use in a URL path or query using UTF-8.
|
||||
|
||||
@ -166,7 +166,7 @@ def do_urlencode(
|
||||
return url_quote(value)
|
||||
|
||||
if isinstance(value, dict):
|
||||
items: t.Iterable[t.Tuple[str, t.Any]] = value.items()
|
||||
items: t.Iterable[tuple[str, t.Any]] = value.items()
|
||||
else:
|
||||
items = value # type: ignore
|
||||
|
||||
@ -221,7 +221,7 @@ def do_lower(s: str) -> str:
|
||||
return soft_str(s).lower()
|
||||
|
||||
|
||||
def do_items(value: t.Union[t.Mapping[K, V], Undefined]) -> t.Iterator[t.Tuple[K, V]]:
|
||||
def do_items(value: t.Union[t.Mapping[K, V], Undefined]) -> t.Iterator[tuple[K, V]]:
|
||||
"""Return an iterator over the ``(key, value)`` items of a mapping.
|
||||
|
||||
``x|items`` is the same as ``x.items()``, except if ``x`` is
|
||||
@ -346,7 +346,7 @@ def do_dictsort(
|
||||
case_sensitive: bool = False,
|
||||
by: 'te.Literal["key", "value"]' = "key",
|
||||
reverse: bool = False,
|
||||
) -> t.List[t.Tuple[K, V]]:
|
||||
) -> list[tuple[K, V]]:
|
||||
"""Sort a dict and yield (key, value) pairs. Python dicts may not
|
||||
be in the order you want to display them in, so sort them first.
|
||||
|
||||
@ -371,7 +371,7 @@ def do_dictsort(
|
||||
else:
|
||||
raise FilterArgumentError('You can only sort by either "key" or "value"')
|
||||
|
||||
def sort_func(item: t.Tuple[t.Any, t.Any]) -> t.Any:
|
||||
def sort_func(item: tuple[t.Any, t.Any]) -> t.Any:
|
||||
value = item[pos]
|
||||
|
||||
if not case_sensitive:
|
||||
@ -389,7 +389,7 @@ def do_sort(
|
||||
reverse: bool = False,
|
||||
case_sensitive: bool = False,
|
||||
attribute: t.Optional[t.Union[str, int]] = None,
|
||||
) -> "t.List[V]":
|
||||
) -> "list[V]":
|
||||
"""Sort an iterable using Python's :func:`sorted`.
|
||||
|
||||
.. sourcecode:: jinja
|
||||
@ -1058,7 +1058,7 @@ def do_striptags(value: "t.Union[str, HasHTML]") -> str:
|
||||
|
||||
def sync_do_slice(
|
||||
value: "t.Collection[V]", slices: int, fill_with: "t.Optional[V]" = None
|
||||
) -> "t.Iterator[t.List[V]]":
|
||||
) -> "t.Iterator[list[V]]":
|
||||
"""Slice an iterator and return a list of lists containing
|
||||
those items. Useful if you want to create a div containing
|
||||
three ul tags that represent columns:
|
||||
@ -1104,13 +1104,13 @@ async def do_slice(
|
||||
value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]",
|
||||
slices: int,
|
||||
fill_with: t.Optional[t.Any] = None,
|
||||
) -> "t.Iterator[t.List[V]]":
|
||||
) -> "t.Iterator[list[V]]":
|
||||
return sync_do_slice(await auto_to_list(value), slices, fill_with)
|
||||
|
||||
|
||||
def do_batch(
|
||||
value: "t.Iterable[V]", linecount: int, fill_with: "t.Optional[V]" = None
|
||||
) -> "t.Iterator[t.List[V]]":
|
||||
) -> "t.Iterator[list[V]]":
|
||||
"""
|
||||
A filter that batches items. It works pretty much like `slice`
|
||||
just the other way round. It returns a list of lists with the
|
||||
@ -1129,7 +1129,7 @@ def do_batch(
|
||||
{%- endfor %}
|
||||
</table>
|
||||
"""
|
||||
tmp: t.List[V] = []
|
||||
tmp: list[V] = []
|
||||
|
||||
for item in value:
|
||||
if len(tmp) == linecount:
|
||||
@ -1187,7 +1187,7 @@ def do_round(
|
||||
|
||||
class _GroupTuple(t.NamedTuple):
|
||||
grouper: t.Any
|
||||
list: t.List[t.Any]
|
||||
list: list[t.Any]
|
||||
|
||||
# Use the regular tuple repr to hide this subclass if users print
|
||||
# out the value during debugging.
|
||||
@ -1205,7 +1205,7 @@ def sync_do_groupby(
|
||||
attribute: t.Union[str, int],
|
||||
default: t.Optional[t.Any] = None,
|
||||
case_sensitive: bool = False,
|
||||
) -> "t.List[_GroupTuple]":
|
||||
) -> "list[_GroupTuple]":
|
||||
"""Group a sequence of objects by an attribute using Python's
|
||||
:func:`itertools.groupby`. The attribute can use dot notation for
|
||||
nested access, like ``"address.city"``. Unlike Python's ``groupby``,
|
||||
@ -1289,7 +1289,7 @@ async def do_groupby(
|
||||
attribute: t.Union[str, int],
|
||||
default: t.Optional[t.Any] = None,
|
||||
case_sensitive: bool = False,
|
||||
) -> "t.List[_GroupTuple]":
|
||||
) -> "list[_GroupTuple]":
|
||||
expr = make_attrgetter(
|
||||
environment,
|
||||
attribute,
|
||||
@ -1358,7 +1358,7 @@ async def do_sum(
|
||||
return rv
|
||||
|
||||
|
||||
def sync_do_list(value: "t.Iterable[V]") -> "t.List[V]":
|
||||
def sync_do_list(value: "t.Iterable[V]") -> "list[V]":
|
||||
"""Convert the value into a list. If it was a string the returned list
|
||||
will be a list of characters.
|
||||
"""
|
||||
@ -1366,7 +1366,7 @@ def sync_do_list(value: "t.Iterable[V]") -> "t.List[V]":
|
||||
|
||||
|
||||
@async_variant(sync_do_list) # type: ignore
|
||||
async def do_list(value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]") -> "t.List[V]":
|
||||
async def do_list(value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]") -> "list[V]":
|
||||
return await auto_to_list(value)
|
||||
|
||||
|
||||
@ -1722,7 +1722,7 @@ def do_tojson(
|
||||
|
||||
|
||||
def prepare_map(
|
||||
context: "Context", args: t.Tuple[t.Any, ...], kwargs: t.Dict[str, t.Any]
|
||||
context: "Context", args: tuple[t.Any, ...], kwargs: dict[str, t.Any]
|
||||
) -> t.Callable[[t.Any], t.Any]:
|
||||
if not args and "attribute" in kwargs:
|
||||
attribute = kwargs.pop("attribute")
|
||||
@ -1751,8 +1751,8 @@ def prepare_map(
|
||||
|
||||
def prepare_select_or_reject(
|
||||
context: "Context",
|
||||
args: t.Tuple[t.Any, ...],
|
||||
kwargs: t.Dict[str, t.Any],
|
||||
args: tuple[t.Any, ...],
|
||||
kwargs: dict[str, t.Any],
|
||||
modfunc: t.Callable[[t.Any], t.Any],
|
||||
lookup_attr: bool,
|
||||
) -> t.Callable[[t.Any], t.Any]:
|
||||
@ -1786,8 +1786,8 @@ def prepare_select_or_reject(
|
||||
def select_or_reject(
|
||||
context: "Context",
|
||||
value: "t.Iterable[V]",
|
||||
args: t.Tuple[t.Any, ...],
|
||||
kwargs: t.Dict[str, t.Any],
|
||||
args: tuple[t.Any, ...],
|
||||
kwargs: dict[str, t.Any],
|
||||
modfunc: t.Callable[[t.Any], t.Any],
|
||||
lookup_attr: bool,
|
||||
) -> "t.Iterator[V]":
|
||||
@ -1802,8 +1802,8 @@ def select_or_reject(
|
||||
async def async_select_or_reject(
|
||||
context: "Context",
|
||||
value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]",
|
||||
args: t.Tuple[t.Any, ...],
|
||||
kwargs: t.Dict[str, t.Any],
|
||||
args: tuple[t.Any, ...],
|
||||
kwargs: dict[str, t.Any],
|
||||
modfunc: t.Callable[[t.Any], t.Any],
|
||||
lookup_attr: bool,
|
||||
) -> "t.AsyncIterator[V]":
|
||||
|
||||
@ -42,16 +42,16 @@ class Symbols:
|
||||
|
||||
self.level: int = level
|
||||
self.parent = parent
|
||||
self.refs: t.Dict[str, str] = {}
|
||||
self.loads: t.Dict[str, t.Any] = {}
|
||||
self.stores: t.Set[str] = set()
|
||||
self.refs: dict[str, str] = {}
|
||||
self.loads: dict[str, t.Any] = {}
|
||||
self.stores: set[str] = set()
|
||||
|
||||
def analyze_node(self, node: nodes.Node, **kwargs: t.Any) -> None:
|
||||
visitor = RootVisitor(self)
|
||||
visitor.visit(node, **kwargs)
|
||||
|
||||
def _define_ref(
|
||||
self, name: str, load: t.Optional[t.Tuple[str, t.Optional[str]]] = None
|
||||
self, name: str, load: t.Optional[tuple[str, t.Optional[str]]] = None
|
||||
) -> str:
|
||||
ident = f"l_{self.level}_{name}"
|
||||
self.refs[name] = ident
|
||||
@ -121,7 +121,7 @@ class Symbols:
|
||||
self._define_ref(name, load=(VAR_LOAD_RESOLVE, name))
|
||||
|
||||
def branch_update(self, branch_symbols: t.Sequence["Symbols"]) -> None:
|
||||
stores: t.Set[str] = set()
|
||||
stores: set[str] = set()
|
||||
|
||||
for branch in branch_symbols:
|
||||
stores.update(branch.stores)
|
||||
@ -144,8 +144,8 @@ class Symbols:
|
||||
continue
|
||||
self.loads[target] = (VAR_LOAD_RESOLVE, name)
|
||||
|
||||
def dump_stores(self) -> t.Dict[str, str]:
|
||||
rv: t.Dict[str, str] = {}
|
||||
def dump_stores(self) -> dict[str, str]:
|
||||
rv: dict[str, str] = {}
|
||||
node: t.Optional[Symbols] = self
|
||||
|
||||
while node is not None:
|
||||
@ -157,7 +157,7 @@ class Symbols:
|
||||
|
||||
return rv
|
||||
|
||||
def dump_param_targets(self) -> t.Set[str]:
|
||||
def dump_param_targets(self) -> set[str]:
|
||||
rv = set()
|
||||
node: t.Optional[Symbols] = self
|
||||
|
||||
|
||||
@ -21,7 +21,7 @@ if t.TYPE_CHECKING:
|
||||
|
||||
# cache for the lexers. Exists in order to be able to have multiple
|
||||
# environments with the same lexer
|
||||
_lexer_cache: t.MutableMapping[t.Tuple, "Lexer"] = LRUCache(50) # type: ignore
|
||||
_lexer_cache: t.MutableMapping[tuple, "Lexer"] = LRUCache(50) # type: ignore
|
||||
|
||||
# static regular expressions
|
||||
whitespace_re = re.compile(r"\s+")
|
||||
@ -210,7 +210,7 @@ def count_newlines(value: str) -> int:
|
||||
return len(newline_re.findall(value))
|
||||
|
||||
|
||||
def compile_rules(environment: "Environment") -> t.List[t.Tuple[str, str]]:
|
||||
def compile_rules(environment: "Environment") -> list[tuple[str, str]]:
|
||||
"""Compiles all the rules from the environment into a list of rules."""
|
||||
e = re.escape
|
||||
rules = [
|
||||
@ -257,7 +257,7 @@ class Failure:
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, message: str, cls: t.Type[TemplateSyntaxError] = TemplateSyntaxError
|
||||
self, message: str, cls: type[TemplateSyntaxError] = TemplateSyntaxError
|
||||
) -> None:
|
||||
self.message = message
|
||||
self.error_class = cls
|
||||
@ -329,7 +329,7 @@ class TokenStream:
|
||||
filename: t.Optional[str],
|
||||
):
|
||||
self._iter = iter(generator)
|
||||
self._pushed: te.Deque[Token] = deque()
|
||||
self._pushed: deque[Token] = deque()
|
||||
self.name = name
|
||||
self.filename = filename
|
||||
self.closed = False
|
||||
@ -464,7 +464,7 @@ class OptionalLStrip(tuple): # type: ignore[type-arg]
|
||||
|
||||
class _Rule(t.NamedTuple):
|
||||
pattern: t.Pattern[str]
|
||||
tokens: t.Union[str, t.Tuple[str, ...], t.Tuple[Failure]]
|
||||
tokens: t.Union[str, tuple[str, ...], tuple[Failure]]
|
||||
command: t.Optional[str]
|
||||
|
||||
|
||||
@ -484,7 +484,7 @@ class Lexer:
|
||||
return re.compile(x, re.M | re.S)
|
||||
|
||||
# lexing rules for tags
|
||||
tag_rules: t.List[_Rule] = [
|
||||
tag_rules: list[_Rule] = [
|
||||
_Rule(whitespace_re, TOKEN_WHITESPACE, None),
|
||||
_Rule(float_re, TOKEN_FLOAT, None),
|
||||
_Rule(integer_re, TOKEN_INTEGER, None),
|
||||
@ -523,7 +523,7 @@ class Lexer:
|
||||
)
|
||||
|
||||
# global lexing rules
|
||||
self.rules: t.Dict[str, t.List[_Rule]] = {
|
||||
self.rules: dict[str, list[_Rule]] = {
|
||||
"root": [
|
||||
# directives
|
||||
_Rule(
|
||||
@ -614,7 +614,7 @@ class Lexer:
|
||||
|
||||
def wrap(
|
||||
self,
|
||||
stream: t.Iterable[t.Tuple[int, str, str]],
|
||||
stream: t.Iterable[tuple[int, str, str]],
|
||||
name: t.Optional[str] = None,
|
||||
filename: t.Optional[str] = None,
|
||||
) -> t.Iterator[Token]:
|
||||
@ -672,7 +672,7 @@ class Lexer:
|
||||
name: t.Optional[str],
|
||||
filename: t.Optional[str] = None,
|
||||
state: t.Optional[str] = None,
|
||||
) -> t.Iterator[t.Tuple[int, str, str]]:
|
||||
) -> t.Iterator[tuple[int, str, str]]:
|
||||
"""This method tokenizes the text and returns the tokens in a
|
||||
generator. Use this method if you just want to tokenize a template.
|
||||
|
||||
@ -696,7 +696,7 @@ class Lexer:
|
||||
|
||||
statetokens = self.rules[stack[-1]]
|
||||
source_length = len(source)
|
||||
balancing_stack: t.List[str] = []
|
||||
balancing_stack: list[str] = []
|
||||
newlines_stripped = 0
|
||||
line_starting = True
|
||||
|
||||
|
||||
@ -22,7 +22,7 @@ if t.TYPE_CHECKING:
|
||||
from .environment import Template
|
||||
|
||||
|
||||
def split_template_path(template: str) -> t.List[str]:
|
||||
def split_template_path(template: str) -> list[str]:
|
||||
"""Split a path into segments and perform a sanity check. If it detects
|
||||
'..' in the path it will raise a `TemplateNotFound` error.
|
||||
"""
|
||||
@ -74,7 +74,7 @@ class BaseLoader:
|
||||
|
||||
def get_source(
|
||||
self, environment: "Environment", template: str
|
||||
) -> t.Tuple[str, t.Optional[str], t.Optional[t.Callable[[], bool]]]:
|
||||
) -> tuple[str, t.Optional[str], t.Optional[t.Callable[[], bool]]]:
|
||||
"""Get the template source, filename and reload helper for a template.
|
||||
It's passed the environment and template name and has to return a
|
||||
tuple in the form ``(source, filename, uptodate)`` or raise a
|
||||
@ -98,7 +98,7 @@ class BaseLoader:
|
||||
)
|
||||
raise TemplateNotFound(template)
|
||||
|
||||
def list_templates(self) -> t.List[str]:
|
||||
def list_templates(self) -> list[str]:
|
||||
"""Iterates over all templates. If the loader does not support that
|
||||
it should raise a :exc:`TypeError` which is the default behavior.
|
||||
"""
|
||||
@ -193,7 +193,7 @@ class FileSystemLoader(BaseLoader):
|
||||
|
||||
def get_source(
|
||||
self, environment: "Environment", template: str
|
||||
) -> t.Tuple[str, str, t.Callable[[], bool]]:
|
||||
) -> tuple[str, str, t.Callable[[], bool]]:
|
||||
pieces = split_template_path(template)
|
||||
|
||||
for searchpath in self.searchpath:
|
||||
@ -225,7 +225,7 @@ class FileSystemLoader(BaseLoader):
|
||||
# Use normpath to convert Windows altsep to sep.
|
||||
return contents, os.path.normpath(filename), uptodate
|
||||
|
||||
def list_templates(self) -> t.List[str]:
|
||||
def list_templates(self) -> list[str]:
|
||||
found = set()
|
||||
for searchpath in self.searchpath:
|
||||
walk_dir = os.walk(searchpath, followlinks=self.followlinks)
|
||||
@ -245,24 +245,23 @@ class FileSystemLoader(BaseLoader):
|
||||
|
||||
if sys.version_info >= (3, 13):
|
||||
|
||||
def _get_zipimporter_files(z: t.Any) -> t.Dict[str, object]:
|
||||
def _get_zipimporter_files(z: t.Any) -> dict[str, object]:
|
||||
try:
|
||||
get_files = z._get_files
|
||||
except AttributeError as e:
|
||||
raise TypeError(
|
||||
"This zip import does not have the required"
|
||||
" metadata to list templates."
|
||||
"This zip import does not have the required metadata to list templates."
|
||||
) from e
|
||||
return get_files()
|
||||
|
||||
else:
|
||||
|
||||
def _get_zipimporter_files(z: t.Any) -> t.Dict[str, object]:
|
||||
def _get_zipimporter_files(z: t.Any) -> dict[str, object]:
|
||||
try:
|
||||
files = z._files
|
||||
except AttributeError as e:
|
||||
raise TypeError(
|
||||
"This zip import does not have the required"
|
||||
" metadata to list templates."
|
||||
"This zip import does not have the required metadata to list templates."
|
||||
) from e
|
||||
return files # type: ignore[no-any-return]
|
||||
|
||||
@ -333,7 +332,7 @@ class PackageLoader(BaseLoader):
|
||||
pkgdir = next(iter(spec.submodule_search_locations)) # type: ignore
|
||||
template_root = os.path.join(pkgdir, package_path).rstrip(os.sep)
|
||||
else:
|
||||
roots: t.List[str] = []
|
||||
roots: list[str] = []
|
||||
|
||||
# One element for regular packages, multiple for namespace
|
||||
# packages, or None for single module file.
|
||||
@ -365,7 +364,7 @@ class PackageLoader(BaseLoader):
|
||||
|
||||
def get_source(
|
||||
self, environment: "Environment", template: str
|
||||
) -> t.Tuple[str, str, t.Optional[t.Callable[[], bool]]]:
|
||||
) -> tuple[str, str, t.Optional[t.Callable[[], bool]]]:
|
||||
# Use posixpath even on Windows to avoid "drive:" or UNC
|
||||
# segments breaking out of the search directory. Use normpath to
|
||||
# convert Windows altsep to sep.
|
||||
@ -401,8 +400,8 @@ class PackageLoader(BaseLoader):
|
||||
|
||||
return source.decode(self.encoding), p, up_to_date
|
||||
|
||||
def list_templates(self) -> t.List[str]:
|
||||
results: t.List[str] = []
|
||||
def list_templates(self) -> list[str]:
|
||||
results: list[str] = []
|
||||
|
||||
if self._archive is None:
|
||||
# Package is a directory.
|
||||
@ -444,13 +443,13 @@ class DictLoader(BaseLoader):
|
||||
|
||||
def get_source(
|
||||
self, environment: "Environment", template: str
|
||||
) -> t.Tuple[str, None, t.Callable[[], bool]]:
|
||||
) -> tuple[str, None, t.Callable[[], bool]]:
|
||||
if template in self.mapping:
|
||||
source = self.mapping[template]
|
||||
return source, None, lambda: source == self.mapping.get(template)
|
||||
raise TemplateNotFound(template)
|
||||
|
||||
def list_templates(self) -> t.List[str]:
|
||||
def list_templates(self) -> list[str]:
|
||||
return sorted(self.mapping)
|
||||
|
||||
|
||||
@ -478,7 +477,7 @@ class FunctionLoader(BaseLoader):
|
||||
[str],
|
||||
t.Optional[
|
||||
t.Union[
|
||||
str, t.Tuple[str, t.Optional[str], t.Optional[t.Callable[[], bool]]]
|
||||
str, tuple[str, t.Optional[str], t.Optional[t.Callable[[], bool]]]
|
||||
]
|
||||
],
|
||||
],
|
||||
@ -487,7 +486,7 @@ class FunctionLoader(BaseLoader):
|
||||
|
||||
def get_source(
|
||||
self, environment: "Environment", template: str
|
||||
) -> t.Tuple[str, t.Optional[str], t.Optional[t.Callable[[], bool]]]:
|
||||
) -> tuple[str, t.Optional[str], t.Optional[t.Callable[[], bool]]]:
|
||||
rv = self.load_func(template)
|
||||
|
||||
if rv is None:
|
||||
@ -520,7 +519,7 @@ class PrefixLoader(BaseLoader):
|
||||
self.mapping = mapping
|
||||
self.delimiter = delimiter
|
||||
|
||||
def get_loader(self, template: str) -> t.Tuple[BaseLoader, str]:
|
||||
def get_loader(self, template: str) -> tuple[BaseLoader, str]:
|
||||
try:
|
||||
prefix, name = template.split(self.delimiter, 1)
|
||||
loader = self.mapping[prefix]
|
||||
@ -530,7 +529,7 @@ class PrefixLoader(BaseLoader):
|
||||
|
||||
def get_source(
|
||||
self, environment: "Environment", template: str
|
||||
) -> t.Tuple[str, t.Optional[str], t.Optional[t.Callable[[], bool]]]:
|
||||
) -> tuple[str, t.Optional[str], t.Optional[t.Callable[[], bool]]]:
|
||||
loader, name = self.get_loader(template)
|
||||
try:
|
||||
return loader.get_source(environment, name)
|
||||
@ -554,7 +553,7 @@ class PrefixLoader(BaseLoader):
|
||||
# (the one that includes the prefix)
|
||||
raise TemplateNotFound(name) from e
|
||||
|
||||
def list_templates(self) -> t.List[str]:
|
||||
def list_templates(self) -> list[str]:
|
||||
result = []
|
||||
for prefix, loader in self.mapping.items():
|
||||
for template in loader.list_templates():
|
||||
@ -581,7 +580,7 @@ class ChoiceLoader(BaseLoader):
|
||||
|
||||
def get_source(
|
||||
self, environment: "Environment", template: str
|
||||
) -> t.Tuple[str, t.Optional[str], t.Optional[t.Callable[[], bool]]]:
|
||||
) -> tuple[str, t.Optional[str], t.Optional[t.Callable[[], bool]]]:
|
||||
for loader in self.loaders:
|
||||
try:
|
||||
return loader.get_source(environment, template)
|
||||
@ -603,7 +602,7 @@ class ChoiceLoader(BaseLoader):
|
||||
pass
|
||||
raise TemplateNotFound(name)
|
||||
|
||||
def list_templates(self) -> t.List[str]:
|
||||
def list_templates(self) -> list[str]:
|
||||
found = set()
|
||||
for loader in self.loaders:
|
||||
found.update(loader.list_templates())
|
||||
|
||||
@ -17,7 +17,7 @@ class TrackingCodeGenerator(CodeGenerator):
|
||||
|
||||
def __init__(self, environment: "Environment") -> None:
|
||||
super().__init__(environment, "<introspection>", "<introspection>")
|
||||
self.undeclared_identifiers: t.Set[str] = set()
|
||||
self.undeclared_identifiers: set[str] = set()
|
||||
|
||||
def write(self, x: str) -> None:
|
||||
"""Don't write."""
|
||||
@ -31,7 +31,7 @@ class TrackingCodeGenerator(CodeGenerator):
|
||||
self.undeclared_identifiers.add(param)
|
||||
|
||||
|
||||
def find_undeclared_variables(ast: nodes.Template) -> t.Set[str]:
|
||||
def find_undeclared_variables(ast: nodes.Template) -> set[str]:
|
||||
"""Returns a set of all variables in the AST that will be looked up from
|
||||
the context at runtime. Because at compile time it's not known which
|
||||
variables will be used depending on the path the execution takes at
|
||||
|
||||
@ -19,7 +19,7 @@ if t.TYPE_CHECKING:
|
||||
|
||||
_NodeBound = t.TypeVar("_NodeBound", bound="Node")
|
||||
|
||||
_binop_to_func: t.Dict[str, t.Callable[[t.Any, t.Any], t.Any]] = {
|
||||
_binop_to_func: dict[str, t.Callable[[t.Any, t.Any], t.Any]] = {
|
||||
"*": operator.mul,
|
||||
"/": operator.truediv,
|
||||
"//": operator.floordiv,
|
||||
@ -29,13 +29,13 @@ _binop_to_func: t.Dict[str, t.Callable[[t.Any, t.Any], t.Any]] = {
|
||||
"-": operator.sub,
|
||||
}
|
||||
|
||||
_uaop_to_func: t.Dict[str, t.Callable[[t.Any], t.Any]] = {
|
||||
_uaop_to_func: dict[str, t.Callable[[t.Any], t.Any]] = {
|
||||
"not": operator.not_,
|
||||
"+": operator.pos,
|
||||
"-": operator.neg,
|
||||
}
|
||||
|
||||
_cmpop_to_func: t.Dict[str, t.Callable[[t.Any, t.Any], t.Any]] = {
|
||||
_cmpop_to_func: dict[str, t.Callable[[t.Any, t.Any], t.Any]] = {
|
||||
"eq": operator.eq,
|
||||
"ne": operator.ne,
|
||||
"gt": operator.gt,
|
||||
@ -58,7 +58,7 @@ class NodeType(type):
|
||||
|
||||
def __new__(mcs, name, bases, d): # type: ignore
|
||||
for attr in "fields", "attributes":
|
||||
storage: t.List[t.Tuple[str, ...]] = []
|
||||
storage: list[tuple[str, ...]] = []
|
||||
storage.extend(getattr(bases[0] if bases else object, attr, ()))
|
||||
storage.extend(d.get(attr, ()))
|
||||
assert len(bases) <= 1, "multiple inheritance not allowed"
|
||||
@ -119,8 +119,8 @@ class Node(metaclass=NodeType):
|
||||
all nodes automatically.
|
||||
"""
|
||||
|
||||
fields: t.Tuple[str, ...] = ()
|
||||
attributes: t.Tuple[str, ...] = ("lineno", "environment")
|
||||
fields: tuple[str, ...] = ()
|
||||
attributes: tuple[str, ...] = ("lineno", "environment")
|
||||
abstract = True
|
||||
|
||||
lineno: int
|
||||
@ -148,7 +148,7 @@ class Node(metaclass=NodeType):
|
||||
self,
|
||||
exclude: t.Optional[t.Container[str]] = None,
|
||||
only: t.Optional[t.Container[str]] = None,
|
||||
) -> t.Iterator[t.Tuple[str, t.Any]]:
|
||||
) -> t.Iterator[tuple[str, t.Any]]:
|
||||
"""This method iterates over all fields that are defined and yields
|
||||
``(key, value)`` tuples. Per default all fields are returned, but
|
||||
it's possible to limit that to some fields by providing the `only`
|
||||
@ -183,7 +183,7 @@ class Node(metaclass=NodeType):
|
||||
elif isinstance(item, Node):
|
||||
yield item
|
||||
|
||||
def find(self, node_type: t.Type[_NodeBound]) -> t.Optional[_NodeBound]:
|
||||
def find(self, node_type: type[_NodeBound]) -> t.Optional[_NodeBound]:
|
||||
"""Find the first node of a given type. If no such node exists the
|
||||
return value is `None`.
|
||||
"""
|
||||
@ -193,7 +193,7 @@ class Node(metaclass=NodeType):
|
||||
return None
|
||||
|
||||
def find_all(
|
||||
self, node_type: t.Union[t.Type[_NodeBound], t.Tuple[t.Type[_NodeBound], ...]]
|
||||
self, node_type: t.Union[type[_NodeBound], tuple[type[_NodeBound], ...]]
|
||||
) -> t.Iterator[_NodeBound]:
|
||||
"""Find all the nodes of a given type. If the type is a tuple,
|
||||
the check is performed for any of the tuple items.
|
||||
@ -274,7 +274,7 @@ class Node(metaclass=NodeType):
|
||||
_dump(value)
|
||||
buf.append(")")
|
||||
|
||||
buf: t.List[str] = []
|
||||
buf: list[str] = []
|
||||
_dump(self)
|
||||
return "".join(buf)
|
||||
|
||||
@ -297,7 +297,7 @@ class Template(Node):
|
||||
"""
|
||||
|
||||
fields = ("body",)
|
||||
body: t.List[Node]
|
||||
body: list[Node]
|
||||
|
||||
|
||||
class Output(Stmt):
|
||||
@ -306,7 +306,7 @@ class Output(Stmt):
|
||||
"""
|
||||
|
||||
fields = ("nodes",)
|
||||
nodes: t.List["Expr"]
|
||||
nodes: list["Expr"]
|
||||
|
||||
|
||||
class Extends(Stmt):
|
||||
@ -328,8 +328,8 @@ class For(Stmt):
|
||||
fields = ("target", "iter", "body", "else_", "test", "recursive")
|
||||
target: Node
|
||||
iter: Node
|
||||
body: t.List[Node]
|
||||
else_: t.List[Node]
|
||||
body: list[Node]
|
||||
else_: list[Node]
|
||||
test: t.Optional[Node]
|
||||
recursive: bool
|
||||
|
||||
@ -339,9 +339,9 @@ class If(Stmt):
|
||||
|
||||
fields = ("test", "body", "elif_", "else_")
|
||||
test: Node
|
||||
body: t.List[Node]
|
||||
elif_: t.List["If"]
|
||||
else_: t.List[Node]
|
||||
body: list[Node]
|
||||
elif_: list["If"]
|
||||
else_: list[Node]
|
||||
|
||||
|
||||
class Macro(Stmt):
|
||||
@ -352,9 +352,9 @@ class Macro(Stmt):
|
||||
|
||||
fields = ("name", "args", "defaults", "body")
|
||||
name: str
|
||||
args: t.List["Name"]
|
||||
defaults: t.List["Expr"]
|
||||
body: t.List[Node]
|
||||
args: list["Name"]
|
||||
defaults: list["Expr"]
|
||||
body: list[Node]
|
||||
|
||||
|
||||
class CallBlock(Stmt):
|
||||
@ -364,16 +364,16 @@ class CallBlock(Stmt):
|
||||
|
||||
fields = ("call", "args", "defaults", "body")
|
||||
call: "Call"
|
||||
args: t.List["Name"]
|
||||
defaults: t.List["Expr"]
|
||||
body: t.List[Node]
|
||||
args: list["Name"]
|
||||
defaults: list["Expr"]
|
||||
body: list[Node]
|
||||
|
||||
|
||||
class FilterBlock(Stmt):
|
||||
"""Node for filter sections."""
|
||||
|
||||
fields = ("body", "filter")
|
||||
body: t.List[Node]
|
||||
body: list[Node]
|
||||
filter: "Filter"
|
||||
|
||||
|
||||
@ -385,9 +385,9 @@ class With(Stmt):
|
||||
"""
|
||||
|
||||
fields = ("targets", "values", "body")
|
||||
targets: t.List["Expr"]
|
||||
values: t.List["Expr"]
|
||||
body: t.List[Node]
|
||||
targets: list["Expr"]
|
||||
values: list["Expr"]
|
||||
body: list[Node]
|
||||
|
||||
|
||||
class Block(Stmt):
|
||||
@ -399,7 +399,7 @@ class Block(Stmt):
|
||||
|
||||
fields = ("name", "body", "scoped", "required")
|
||||
name: str
|
||||
body: t.List[Node]
|
||||
body: list[Node]
|
||||
scoped: bool
|
||||
required: bool
|
||||
|
||||
@ -436,7 +436,7 @@ class FromImport(Stmt):
|
||||
|
||||
fields = ("template", "names", "with_context")
|
||||
template: "Expr"
|
||||
names: t.List[t.Union[str, t.Tuple[str, str]]]
|
||||
names: list[t.Union[str, tuple[str, str]]]
|
||||
with_context: bool
|
||||
|
||||
|
||||
@ -461,7 +461,7 @@ class AssignBlock(Stmt):
|
||||
fields = ("target", "filter", "body")
|
||||
target: "Expr"
|
||||
filter: t.Optional["Filter"]
|
||||
body: t.List[Node]
|
||||
body: list[Node]
|
||||
|
||||
|
||||
class Expr(Node):
|
||||
@ -627,10 +627,10 @@ class Tuple(Literal):
|
||||
"""
|
||||
|
||||
fields = ("items", "ctx")
|
||||
items: t.List[Expr]
|
||||
items: list[Expr]
|
||||
ctx: str
|
||||
|
||||
def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> t.Tuple[t.Any, ...]:
|
||||
def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> tuple[t.Any, ...]:
|
||||
eval_ctx = get_eval_context(self, eval_ctx)
|
||||
return tuple(x.as_const(eval_ctx) for x in self.items)
|
||||
|
||||
@ -645,9 +645,9 @@ class List(Literal):
|
||||
"""Any list literal such as ``[1, 2, 3]``"""
|
||||
|
||||
fields = ("items",)
|
||||
items: t.List[Expr]
|
||||
items: list[Expr]
|
||||
|
||||
def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> t.List[t.Any]:
|
||||
def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> list[t.Any]:
|
||||
eval_ctx = get_eval_context(self, eval_ctx)
|
||||
return [x.as_const(eval_ctx) for x in self.items]
|
||||
|
||||
@ -658,11 +658,9 @@ class Dict(Literal):
|
||||
"""
|
||||
|
||||
fields = ("items",)
|
||||
items: t.List["Pair"]
|
||||
items: list["Pair"]
|
||||
|
||||
def as_const(
|
||||
self, eval_ctx: t.Optional[EvalContext] = None
|
||||
) -> t.Dict[t.Any, t.Any]:
|
||||
def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> dict[t.Any, t.Any]:
|
||||
eval_ctx = get_eval_context(self, eval_ctx)
|
||||
return dict(x.as_const(eval_ctx) for x in self.items)
|
||||
|
||||
@ -674,9 +672,7 @@ class Pair(Helper):
|
||||
key: Expr
|
||||
value: Expr
|
||||
|
||||
def as_const(
|
||||
self, eval_ctx: t.Optional[EvalContext] = None
|
||||
) -> t.Tuple[t.Any, t.Any]:
|
||||
def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> tuple[t.Any, t.Any]:
|
||||
eval_ctx = get_eval_context(self, eval_ctx)
|
||||
return self.key.as_const(eval_ctx), self.value.as_const(eval_ctx)
|
||||
|
||||
@ -688,7 +684,7 @@ class Keyword(Helper):
|
||||
key: str
|
||||
value: Expr
|
||||
|
||||
def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> t.Tuple[str, t.Any]:
|
||||
def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> tuple[str, t.Any]:
|
||||
eval_ctx = get_eval_context(self, eval_ctx)
|
||||
return self.key, self.value.as_const(eval_ctx)
|
||||
|
||||
@ -717,7 +713,7 @@ class CondExpr(Expr):
|
||||
|
||||
def args_as_const(
|
||||
node: t.Union["_FilterTestCommon", "Call"], eval_ctx: t.Optional[EvalContext]
|
||||
) -> t.Tuple[t.List[t.Any], t.Dict[t.Any, t.Any]]:
|
||||
) -> tuple[list[t.Any], dict[t.Any, t.Any]]:
|
||||
args = [x.as_const(eval_ctx) for x in node.args]
|
||||
kwargs = dict(x.as_const(eval_ctx) for x in node.kwargs)
|
||||
|
||||
@ -740,8 +736,8 @@ class _FilterTestCommon(Expr):
|
||||
fields = ("node", "name", "args", "kwargs", "dyn_args", "dyn_kwargs")
|
||||
node: Expr
|
||||
name: str
|
||||
args: t.List[Expr]
|
||||
kwargs: t.List[Pair]
|
||||
args: list[Expr]
|
||||
kwargs: list[Pair]
|
||||
dyn_args: t.Optional[Expr]
|
||||
dyn_kwargs: t.Optional[Expr]
|
||||
abstract = True
|
||||
@ -824,8 +820,8 @@ class Call(Expr):
|
||||
|
||||
fields = ("node", "args", "kwargs", "dyn_args", "dyn_kwargs")
|
||||
node: Expr
|
||||
args: t.List[Expr]
|
||||
kwargs: t.List[Keyword]
|
||||
args: list[Expr]
|
||||
kwargs: list[Keyword]
|
||||
dyn_args: t.Optional[Expr]
|
||||
dyn_kwargs: t.Optional[Expr]
|
||||
|
||||
@ -901,7 +897,7 @@ class Concat(Expr):
|
||||
"""
|
||||
|
||||
fields = ("nodes",)
|
||||
nodes: t.List[Expr]
|
||||
nodes: list[Expr]
|
||||
|
||||
def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> str:
|
||||
eval_ctx = get_eval_context(self, eval_ctx)
|
||||
@ -915,7 +911,7 @@ class Compare(Expr):
|
||||
|
||||
fields = ("expr", "ops")
|
||||
expr: Expr
|
||||
ops: t.List["Operand"]
|
||||
ops: list["Operand"]
|
||||
|
||||
def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> t.Any:
|
||||
eval_ctx = get_eval_context(self, eval_ctx)
|
||||
@ -1152,7 +1148,7 @@ class Scope(Stmt):
|
||||
"""An artificial scope."""
|
||||
|
||||
fields = ("body",)
|
||||
body: t.List[Node]
|
||||
body: list[Node]
|
||||
|
||||
|
||||
class OverlayScope(Stmt):
|
||||
@ -1171,7 +1167,7 @@ class OverlayScope(Stmt):
|
||||
|
||||
fields = ("context", "body")
|
||||
context: Expr
|
||||
body: t.List[Node]
|
||||
body: list[Node]
|
||||
|
||||
|
||||
class EvalContextModifier(Stmt):
|
||||
@ -1184,7 +1180,7 @@ class EvalContextModifier(Stmt):
|
||||
"""
|
||||
|
||||
fields = ("options",)
|
||||
options: t.List[Keyword]
|
||||
options: list[Keyword]
|
||||
|
||||
|
||||
class ScopedEvalContextModifier(EvalContextModifier):
|
||||
@ -1194,7 +1190,7 @@ class ScopedEvalContextModifier(EvalContextModifier):
|
||||
"""
|
||||
|
||||
fields = ("body",)
|
||||
body: t.List[Node]
|
||||
body: list[Node]
|
||||
|
||||
|
||||
# make sure nobody creates custom nodes
|
||||
|
||||
@ -35,7 +35,7 @@ _statement_keywords = frozenset(
|
||||
)
|
||||
_compare_operators = frozenset(["eq", "ne", "lt", "lteq", "gt", "gteq"])
|
||||
|
||||
_math_nodes: t.Dict[str, t.Type[nodes.Expr]] = {
|
||||
_math_nodes: dict[str, type[nodes.Expr]] = {
|
||||
"add": nodes.Add,
|
||||
"sub": nodes.Sub,
|
||||
"mul": nodes.Mul,
|
||||
@ -63,21 +63,21 @@ class Parser:
|
||||
self.name = name
|
||||
self.filename = filename
|
||||
self.closed = False
|
||||
self.extensions: t.Dict[
|
||||
str, t.Callable[[Parser], t.Union[nodes.Node, t.List[nodes.Node]]]
|
||||
self.extensions: dict[
|
||||
str, t.Callable[[Parser], t.Union[nodes.Node, list[nodes.Node]]]
|
||||
] = {}
|
||||
for extension in environment.iter_extensions():
|
||||
for tag in extension.tags:
|
||||
self.extensions[tag] = extension.parse
|
||||
self._last_identifier = 0
|
||||
self._tag_stack: t.List[str] = []
|
||||
self._end_token_stack: t.List[t.Tuple[str, ...]] = []
|
||||
self._tag_stack: list[str] = []
|
||||
self._end_token_stack: list[tuple[str, ...]] = []
|
||||
|
||||
def fail(
|
||||
self,
|
||||
msg: str,
|
||||
lineno: t.Optional[int] = None,
|
||||
exc: t.Type[TemplateSyntaxError] = TemplateSyntaxError,
|
||||
exc: type[TemplateSyntaxError] = TemplateSyntaxError,
|
||||
) -> "te.NoReturn":
|
||||
"""Convenience method that raises `exc` with the message, passed
|
||||
line number or last line number as well as the current name and
|
||||
@ -90,10 +90,10 @@ class Parser:
|
||||
def _fail_ut_eof(
|
||||
self,
|
||||
name: t.Optional[str],
|
||||
end_token_stack: t.List[t.Tuple[str, ...]],
|
||||
end_token_stack: list[tuple[str, ...]],
|
||||
lineno: t.Optional[int],
|
||||
) -> "te.NoReturn":
|
||||
expected: t.Set[str] = set()
|
||||
expected: set[str] = set()
|
||||
for exprs in end_token_stack:
|
||||
expected.update(map(describe_token_expr, exprs))
|
||||
if end_token_stack:
|
||||
@ -138,7 +138,7 @@ class Parser:
|
||||
|
||||
def fail_eof(
|
||||
self,
|
||||
end_tokens: t.Optional[t.Tuple[str, ...]] = None,
|
||||
end_tokens: t.Optional[tuple[str, ...]] = None,
|
||||
lineno: t.Optional[int] = None,
|
||||
) -> "te.NoReturn":
|
||||
"""Like fail_unknown_tag but for end of template situations."""
|
||||
@ -147,9 +147,7 @@ class Parser:
|
||||
stack.append(end_tokens)
|
||||
self._fail_ut_eof(None, stack, lineno)
|
||||
|
||||
def is_tuple_end(
|
||||
self, extra_end_rules: t.Optional[t.Tuple[str, ...]] = None
|
||||
) -> bool:
|
||||
def is_tuple_end(self, extra_end_rules: t.Optional[tuple[str, ...]] = None) -> bool:
|
||||
"""Are we at the end of a tuple?"""
|
||||
if self.stream.current.type in ("variable_end", "block_end", "rparen"):
|
||||
return True
|
||||
@ -164,7 +162,7 @@ class Parser:
|
||||
nodes.Node.__init__(rv, f"fi{self._last_identifier}", lineno=lineno)
|
||||
return rv
|
||||
|
||||
def parse_statement(self) -> t.Union[nodes.Node, t.List[nodes.Node]]:
|
||||
def parse_statement(self) -> t.Union[nodes.Node, list[nodes.Node]]:
|
||||
"""Parse a single statement."""
|
||||
token = self.stream.current
|
||||
if token.type != "name":
|
||||
@ -194,8 +192,8 @@ class Parser:
|
||||
self._tag_stack.pop()
|
||||
|
||||
def parse_statements(
|
||||
self, end_tokens: t.Tuple[str, ...], drop_needle: bool = False
|
||||
) -> t.List[nodes.Node]:
|
||||
self, end_tokens: tuple[str, ...], drop_needle: bool = False
|
||||
) -> list[nodes.Node]:
|
||||
"""Parse multiple statements into a list until one of the end tokens
|
||||
is reached. This is used to parse the body of statements as it also
|
||||
parses template data if appropriate. The parser checks first if the
|
||||
@ -272,8 +270,8 @@ class Parser:
|
||||
|
||||
def parse_with(self) -> nodes.With:
|
||||
node = nodes.With(lineno=next(self.stream).lineno)
|
||||
targets: t.List[nodes.Expr] = []
|
||||
values: t.List[nodes.Expr] = []
|
||||
targets: list[nodes.Expr] = []
|
||||
values: list[nodes.Expr] = []
|
||||
while self.stream.current.type != "block_end":
|
||||
if targets:
|
||||
self.stream.expect("comma")
|
||||
@ -466,7 +464,7 @@ class Parser:
|
||||
self,
|
||||
with_tuple: bool = True,
|
||||
name_only: bool = False,
|
||||
extra_end_rules: t.Optional[t.Tuple[str, ...]] = None,
|
||||
extra_end_rules: t.Optional[tuple[str, ...]] = None,
|
||||
with_namespace: bool = False,
|
||||
) -> t.Union[nodes.NSRef, nodes.Name, nodes.Tuple]: ...
|
||||
|
||||
@ -474,7 +472,7 @@ class Parser:
|
||||
self,
|
||||
with_tuple: bool = True,
|
||||
name_only: bool = False,
|
||||
extra_end_rules: t.Optional[t.Tuple[str, ...]] = None,
|
||||
extra_end_rules: t.Optional[tuple[str, ...]] = None,
|
||||
with_namespace: bool = False,
|
||||
) -> t.Union[nodes.NSRef, nodes.Name, nodes.Tuple]:
|
||||
"""Parse an assignment target. As Jinja allows assignments to
|
||||
@ -686,7 +684,7 @@ class Parser:
|
||||
self,
|
||||
simplified: bool = False,
|
||||
with_condexpr: bool = True,
|
||||
extra_end_rules: t.Optional[t.Tuple[str, ...]] = None,
|
||||
extra_end_rules: t.Optional[tuple[str, ...]] = None,
|
||||
explicit_parentheses: bool = False,
|
||||
with_namespace: bool = False,
|
||||
) -> t.Union[nodes.Tuple, nodes.Expr]:
|
||||
@ -720,7 +718,7 @@ class Parser:
|
||||
def parse() -> nodes.Expr:
|
||||
return self.parse_expression(with_condexpr=with_condexpr)
|
||||
|
||||
args: t.List[nodes.Expr] = []
|
||||
args: list[nodes.Expr] = []
|
||||
is_tuple = False
|
||||
|
||||
while True:
|
||||
@ -753,7 +751,7 @@ class Parser:
|
||||
|
||||
def parse_list(self) -> nodes.List:
|
||||
token = self.stream.expect("lbracket")
|
||||
items: t.List[nodes.Expr] = []
|
||||
items: list[nodes.Expr] = []
|
||||
while self.stream.current.type != "rbracket":
|
||||
if items:
|
||||
self.stream.expect("comma")
|
||||
@ -765,7 +763,7 @@ class Parser:
|
||||
|
||||
def parse_dict(self) -> nodes.Dict:
|
||||
token = self.stream.expect("lbrace")
|
||||
items: t.List[nodes.Pair] = []
|
||||
items: list[nodes.Pair] = []
|
||||
while self.stream.current.type != "rbrace":
|
||||
if items:
|
||||
self.stream.expect("comma")
|
||||
@ -824,7 +822,7 @@ class Parser:
|
||||
arg = nodes.Const(attr_token.value, lineno=attr_token.lineno)
|
||||
return nodes.Getitem(node, arg, "load", lineno=token.lineno)
|
||||
if token.type == "lbracket":
|
||||
args: t.List[nodes.Expr] = []
|
||||
args: list[nodes.Expr] = []
|
||||
while self.stream.current.type != "rbracket":
|
||||
if args:
|
||||
self.stream.expect("comma")
|
||||
@ -839,7 +837,7 @@ class Parser:
|
||||
|
||||
def parse_subscribed(self) -> nodes.Expr:
|
||||
lineno = self.stream.current.lineno
|
||||
args: t.List[t.Optional[nodes.Expr]]
|
||||
args: list[t.Optional[nodes.Expr]]
|
||||
|
||||
if self.stream.current.type == "colon":
|
||||
next(self.stream)
|
||||
@ -871,9 +869,9 @@ class Parser:
|
||||
|
||||
def parse_call_args(
|
||||
self,
|
||||
) -> t.Tuple[
|
||||
t.List[nodes.Expr],
|
||||
t.List[nodes.Keyword],
|
||||
) -> tuple[
|
||||
list[nodes.Expr],
|
||||
list[nodes.Keyword],
|
||||
t.Union[nodes.Expr, None],
|
||||
t.Union[nodes.Expr, None],
|
||||
]:
|
||||
@ -967,7 +965,7 @@ class Parser:
|
||||
next(self.stream)
|
||||
name += "." + self.stream.expect("name").value
|
||||
dyn_args = dyn_kwargs = None
|
||||
kwargs: t.List[nodes.Keyword] = []
|
||||
kwargs: list[nodes.Keyword] = []
|
||||
if self.stream.current.type == "lparen":
|
||||
args, kwargs, dyn_args, dyn_kwargs = self.parse_call_args()
|
||||
elif self.stream.current.type in {
|
||||
@ -994,10 +992,10 @@ class Parser:
|
||||
return node
|
||||
|
||||
def subparse(
|
||||
self, end_tokens: t.Optional[t.Tuple[str, ...]] = None
|
||||
) -> t.List[nodes.Node]:
|
||||
body: t.List[nodes.Node] = []
|
||||
data_buffer: t.List[nodes.Node] = []
|
||||
self, end_tokens: t.Optional[tuple[str, ...]] = None
|
||||
) -> list[nodes.Node]:
|
||||
body: list[nodes.Node] = []
|
||||
data_buffer: list[nodes.Node] = []
|
||||
add_data = data_buffer.append
|
||||
|
||||
if end_tokens is not None:
|
||||
|
||||
@ -93,8 +93,8 @@ def str_join(seq: t.Iterable[t.Any]) -> str:
|
||||
def new_context(
|
||||
environment: "Environment",
|
||||
template_name: t.Optional[str],
|
||||
blocks: t.Dict[str, t.Callable[["Context"], t.Iterator[str]]],
|
||||
vars: t.Optional[t.Dict[str, t.Any]] = None,
|
||||
blocks: dict[str, t.Callable[["Context"], t.Iterator[str]]],
|
||||
vars: t.Optional[dict[str, t.Any]] = None,
|
||||
shared: bool = False,
|
||||
globals: t.Optional[t.MutableMapping[str, t.Any]] = None,
|
||||
locals: t.Optional[t.Mapping[str, t.Any]] = None,
|
||||
@ -165,16 +165,16 @@ class Context:
|
||||
def __init__(
|
||||
self,
|
||||
environment: "Environment",
|
||||
parent: t.Dict[str, t.Any],
|
||||
parent: dict[str, t.Any],
|
||||
name: t.Optional[str],
|
||||
blocks: t.Dict[str, t.Callable[["Context"], t.Iterator[str]]],
|
||||
blocks: dict[str, t.Callable[["Context"], t.Iterator[str]]],
|
||||
globals: t.Optional[t.MutableMapping[str, t.Any]] = None,
|
||||
):
|
||||
self.parent = parent
|
||||
self.vars: t.Dict[str, t.Any] = {}
|
||||
self.vars: dict[str, t.Any] = {}
|
||||
self.environment: Environment = environment
|
||||
self.eval_ctx = EvalContext(self.environment, name)
|
||||
self.exported_vars: t.Set[str] = set()
|
||||
self.exported_vars: set[str] = set()
|
||||
self.name = name
|
||||
self.globals_keys = set() if globals is None else set(globals)
|
||||
|
||||
@ -244,11 +244,11 @@ class Context:
|
||||
|
||||
return missing
|
||||
|
||||
def get_exported(self) -> t.Dict[str, t.Any]:
|
||||
def get_exported(self) -> dict[str, t.Any]:
|
||||
"""Get a new dict with the exported variables."""
|
||||
return {k: self.vars[k] for k in self.exported_vars}
|
||||
|
||||
def get_all(self) -> t.Dict[str, t.Any]:
|
||||
def get_all(self) -> dict[str, t.Any]:
|
||||
"""Return the complete context as dict including the exported
|
||||
variables. For optimizations reasons this might not return an
|
||||
actual copy so be careful with using it.
|
||||
@ -307,7 +307,7 @@ class Context:
|
||||
" StopIteration exception"
|
||||
)
|
||||
|
||||
def derived(self, locals: t.Optional[t.Dict[str, t.Any]] = None) -> "Context":
|
||||
def derived(self, locals: t.Optional[dict[str, t.Any]] = None) -> "Context":
|
||||
"""Internal helper function to create a derived context. This is
|
||||
used in situations where the system needs a new context in the same
|
||||
template that is independent.
|
||||
@ -348,7 +348,7 @@ class BlockReference:
|
||||
self,
|
||||
name: str,
|
||||
context: "Context",
|
||||
stack: t.List[t.Callable[["Context"], t.Iterator[str]]],
|
||||
stack: list[t.Callable[["Context"], t.Iterator[str]]],
|
||||
depth: int,
|
||||
) -> None:
|
||||
self.name = name
|
||||
@ -408,7 +408,7 @@ class LoopContext:
|
||||
def __init__(
|
||||
self,
|
||||
iterable: t.Iterable[V],
|
||||
undefined: t.Type["Undefined"],
|
||||
undefined: type["Undefined"],
|
||||
recurse: t.Optional["LoopRenderFunc"] = None,
|
||||
depth0: int = 0,
|
||||
) -> None:
|
||||
@ -558,7 +558,7 @@ class LoopContext:
|
||||
def __iter__(self) -> "LoopContext":
|
||||
return self
|
||||
|
||||
def __next__(self) -> t.Tuple[t.Any, "LoopContext"]:
|
||||
def __next__(self) -> tuple[t.Any, "LoopContext"]:
|
||||
if self._after is not missing:
|
||||
rv = self._after
|
||||
self._after = missing
|
||||
@ -646,7 +646,7 @@ class AsyncLoopContext(LoopContext):
|
||||
def __aiter__(self) -> "AsyncLoopContext":
|
||||
return self
|
||||
|
||||
async def __anext__(self) -> t.Tuple[t.Any, "AsyncLoopContext"]:
|
||||
async def __anext__(self) -> tuple[t.Any, "AsyncLoopContext"]:
|
||||
if self._after is not missing:
|
||||
rv = self._after
|
||||
self._after = missing
|
||||
@ -667,7 +667,7 @@ class Macro:
|
||||
environment: "Environment",
|
||||
func: t.Callable[..., str],
|
||||
name: str,
|
||||
arguments: t.List[str],
|
||||
arguments: list[str],
|
||||
catch_kwargs: bool,
|
||||
catch_varargs: bool,
|
||||
caller: bool,
|
||||
@ -769,7 +769,7 @@ class Macro:
|
||||
|
||||
return self._invoke(arguments, autoescape)
|
||||
|
||||
async def _async_invoke(self, arguments: t.List[t.Any], autoescape: bool) -> str:
|
||||
async def _async_invoke(self, arguments: list[t.Any], autoescape: bool) -> str:
|
||||
rv = await self._func(*arguments) # type: ignore
|
||||
|
||||
if autoescape:
|
||||
@ -777,7 +777,7 @@ class Macro:
|
||||
|
||||
return rv # type: ignore
|
||||
|
||||
def _invoke(self, arguments: t.List[t.Any], autoescape: bool) -> str:
|
||||
def _invoke(self, arguments: list[t.Any], autoescape: bool) -> str:
|
||||
if self._environment.is_async:
|
||||
return self._async_invoke(arguments, autoescape) # type: ignore
|
||||
|
||||
@ -820,7 +820,7 @@ class Undefined:
|
||||
hint: t.Optional[str] = None,
|
||||
obj: t.Any = missing,
|
||||
name: t.Optional[str] = None,
|
||||
exc: t.Type[TemplateRuntimeError] = UndefinedError,
|
||||
exc: type[TemplateRuntimeError] = UndefinedError,
|
||||
) -> None:
|
||||
self._undefined_hint = hint
|
||||
self._undefined_obj = obj
|
||||
@ -910,8 +910,8 @@ class Undefined:
|
||||
|
||||
|
||||
def make_logging_undefined(
|
||||
logger: t.Optional["logging.Logger"] = None, base: t.Type[Undefined] = Undefined
|
||||
) -> t.Type[Undefined]:
|
||||
logger: t.Optional["logging.Logger"] = None, base: type[Undefined] = Undefined
|
||||
) -> type[Undefined]:
|
||||
"""Given a logger object this returns a new undefined class that will
|
||||
log certain failures. It will log iterations and printing. If no
|
||||
logger is given a default logger is created.
|
||||
|
||||
@ -25,10 +25,10 @@ F = t.TypeVar("F", bound=t.Callable[..., t.Any])
|
||||
MAX_RANGE = 100000
|
||||
|
||||
#: Unsafe function attributes.
|
||||
UNSAFE_FUNCTION_ATTRIBUTES: t.Set[str] = set()
|
||||
UNSAFE_FUNCTION_ATTRIBUTES: set[str] = set()
|
||||
|
||||
#: Unsafe method attributes. Function attributes are unsafe for methods too.
|
||||
UNSAFE_METHOD_ATTRIBUTES: t.Set[str] = set()
|
||||
UNSAFE_METHOD_ATTRIBUTES: set[str] = set()
|
||||
|
||||
#: unsafe generator attributes.
|
||||
UNSAFE_GENERATOR_ATTRIBUTES = {"gi_frame", "gi_code"}
|
||||
@ -39,7 +39,7 @@ UNSAFE_COROUTINE_ATTRIBUTES = {"cr_frame", "cr_code"}
|
||||
#: unsafe attributes on async generators
|
||||
UNSAFE_ASYNC_GENERATOR_ATTRIBUTES = {"ag_code", "ag_frame"}
|
||||
|
||||
_mutable_spec: t.Tuple[t.Tuple[t.Type[t.Any], t.FrozenSet[str]], ...] = (
|
||||
_mutable_spec: tuple[tuple[type[t.Any], frozenset[str]], ...] = (
|
||||
(
|
||||
abc.MutableSet,
|
||||
frozenset(
|
||||
@ -190,7 +190,7 @@ class SandboxedEnvironment(Environment):
|
||||
#: default callback table for the binary operators. A copy of this is
|
||||
#: available on each instance of a sandboxed environment as
|
||||
#: :attr:`binop_table`
|
||||
default_binop_table: t.Dict[str, t.Callable[[t.Any, t.Any], t.Any]] = {
|
||||
default_binop_table: dict[str, t.Callable[[t.Any, t.Any], t.Any]] = {
|
||||
"+": operator.add,
|
||||
"-": operator.sub,
|
||||
"*": operator.mul,
|
||||
@ -203,7 +203,7 @@ class SandboxedEnvironment(Environment):
|
||||
#: default callback table for the unary operators. A copy of this is
|
||||
#: available on each instance of a sandboxed environment as
|
||||
#: :attr:`unop_table`
|
||||
default_unop_table: t.Dict[str, t.Callable[[t.Any], t.Any]] = {
|
||||
default_unop_table: dict[str, t.Callable[[t.Any], t.Any]] = {
|
||||
"+": operator.pos,
|
||||
"-": operator.neg,
|
||||
}
|
||||
@ -222,7 +222,7 @@ class SandboxedEnvironment(Environment):
|
||||
#: interested in.
|
||||
#:
|
||||
#: .. versionadded:: 2.6
|
||||
intercepted_binops: t.FrozenSet[str] = frozenset()
|
||||
intercepted_binops: frozenset[str] = frozenset()
|
||||
|
||||
#: a set of unary operators that should be intercepted. Each operator
|
||||
#: that is added to this set (empty by default) is delegated to the
|
||||
@ -237,7 +237,7 @@ class SandboxedEnvironment(Environment):
|
||||
#: interested in.
|
||||
#:
|
||||
#: .. versionadded:: 2.6
|
||||
intercepted_unops: t.FrozenSet[str] = frozenset()
|
||||
intercepted_unops: frozenset[str] = frozenset()
|
||||
|
||||
def __init__(self, *args: t.Any, **kwargs: t.Any) -> None:
|
||||
super().__init__(*args, **kwargs)
|
||||
@ -357,7 +357,7 @@ class SandboxedEnvironment(Environment):
|
||||
if not isinstance(f_self, str):
|
||||
return None
|
||||
|
||||
str_type: t.Type[str] = type(f_self)
|
||||
str_type: type[str] = type(f_self)
|
||||
is_format_map = value.__name__ == "format_map"
|
||||
formatter: SandboxedFormatter
|
||||
|
||||
@ -421,7 +421,7 @@ class SandboxedFormatter(Formatter):
|
||||
|
||||
def get_field(
|
||||
self, field_name: str, args: t.Sequence[t.Any], kwargs: t.Mapping[str, t.Any]
|
||||
) -> t.Tuple[t.Any, str]:
|
||||
) -> tuple[t.Any, str]:
|
||||
first, rest = formatter_field_name_split(field_name)
|
||||
obj = self.get_value(first, args, kwargs)
|
||||
for is_attr, i in rest:
|
||||
|
||||
@ -438,8 +438,8 @@ class LRUCache:
|
||||
|
||||
def __init__(self, capacity: int) -> None:
|
||||
self.capacity = capacity
|
||||
self._mapping: t.Dict[t.Any, t.Any] = {}
|
||||
self._queue: te.Deque[t.Any] = deque()
|
||||
self._mapping: dict[t.Any, t.Any] = {}
|
||||
self._queue: deque[t.Any] = deque()
|
||||
self._postinit()
|
||||
|
||||
def _postinit(self) -> None:
|
||||
@ -461,7 +461,7 @@ class LRUCache:
|
||||
self.__dict__.update(d)
|
||||
self._postinit()
|
||||
|
||||
def __getnewargs__(self) -> t.Tuple[t.Any, ...]:
|
||||
def __getnewargs__(self) -> tuple[t.Any, ...]:
|
||||
return (self.capacity,)
|
||||
|
||||
def copy(self) -> "te.Self":
|
||||
@ -552,7 +552,7 @@ class LRUCache:
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
def items(self) -> t.Iterable[t.Tuple[t.Any, t.Any]]:
|
||||
def items(self) -> t.Iterable[tuple[t.Any, t.Any]]:
|
||||
"""Return a list of items."""
|
||||
result = [(key, self._mapping[key]) for key in list(self._queue)]
|
||||
result.reverse()
|
||||
|
||||
@ -80,7 +80,7 @@ class NodeTransformer(NodeVisitor):
|
||||
setattr(node, field, new_node)
|
||||
return node
|
||||
|
||||
def visit_list(self, node: Node, *args: t.Any, **kwargs: t.Any) -> t.List[Node]:
|
||||
def visit_list(self, node: Node, *args: t.Any, **kwargs: t.Any) -> list[Node]:
|
||||
"""As transformers may return lists in some places this method
|
||||
can be used to enforce a list as return value.
|
||||
"""
|
||||
|
||||
@ -449,9 +449,7 @@ class TestAsyncForLoop:
|
||||
|
||||
def test_reversed_bug(self, test_env_async):
|
||||
tmpl = test_env_async.from_string(
|
||||
"{% for i in items %}{{ i }}"
|
||||
"{% if not loop.last %}"
|
||||
",{% endif %}{% endfor %}"
|
||||
"{% for i in items %}{{ i }}{% if not loop.last %},{% endif %}{% endfor %}"
|
||||
)
|
||||
assert tmpl.render(items=reversed([3, 2, 1])) == "1,2,3"
|
||||
|
||||
|
||||
@ -191,9 +191,7 @@ class TestForLoop:
|
||||
|
||||
def test_reversed_bug(self, env):
|
||||
tmpl = env.from_string(
|
||||
"{% for i in items %}{{ i }}"
|
||||
"{% if not loop.last %}"
|
||||
",{% endif %}{% endfor %}"
|
||||
"{% for i in items %}{{ i }}{% if not loop.last %},{% endif %}{% endfor %}"
|
||||
)
|
||||
assert tmpl.render(items=reversed([3, 2, 1])) == "1,2,3"
|
||||
|
||||
|
||||
@ -23,9 +23,9 @@ class TestDebug:
|
||||
|
||||
tb = format_exception(exc_info.type, exc_info.value, exc_info.tb)
|
||||
m = re.search(expected_tb.strip(), "".join(tb))
|
||||
assert (
|
||||
m is not None
|
||||
), f"Traceback did not match:\n\n{''.join(tb)}\nexpected:\n{expected_tb}"
|
||||
assert m is not None, (
|
||||
f"Traceback did not match:\n\n{''.join(tb)}\nexpected:\n{expected_tb}"
|
||||
)
|
||||
|
||||
def test_runtime_error(self, fs_env):
|
||||
def test():
|
||||
|
||||
@ -554,8 +554,7 @@ class TestNewstyleInternationalization:
|
||||
newstyle=True,
|
||||
)
|
||||
t = env.from_string(
|
||||
'{% autoescape ae %}{{ gettext("foo", name='
|
||||
'"<test>") }}{% endautoescape %}'
|
||||
'{% autoescape ae %}{{ gettext("foo", name="<test>") }}{% endautoescape %}'
|
||||
)
|
||||
assert t.render(ae=True) == "<strong>Wert: <test></strong>"
|
||||
assert t.render(ae=False) == "<strong>Wert: <test></strong>"
|
||||
|
||||
@ -357,7 +357,7 @@ class TestFilter:
|
||||
def test_urlize(self, env):
|
||||
tmpl = env.from_string('{{ "foo example.org bar"|urlize }}')
|
||||
assert tmpl.render() == (
|
||||
'foo <a href="https://example.org" rel="noopener">' "example.org</a> bar"
|
||||
'foo <a href="https://example.org" rel="noopener">example.org</a> bar'
|
||||
)
|
||||
tmpl = env.from_string('{{ "foo http://www.example.com/ bar"|urlize }}')
|
||||
assert tmpl.render() == (
|
||||
|
||||
@ -43,8 +43,7 @@ class TestTokenStream:
|
||||
class TestLexer:
|
||||
def test_raw1(self, env):
|
||||
tmpl = env.from_string(
|
||||
"{% raw %}foo{% endraw %}|"
|
||||
"{%raw%}{{ bar }}|{% baz %}{% endraw %}"
|
||||
"{% raw %}foo{% endraw %}|{%raw%}{{ bar }}|{% baz %}{% endraw %}"
|
||||
)
|
||||
assert tmpl.render() == "foo|{{ bar }}|{% baz %}"
|
||||
|
||||
|
||||
52
tox.ini
52
tox.ini
@ -1,52 +0,0 @@
|
||||
[tox]
|
||||
envlist =
|
||||
py3{13,12,11,10,9,8}
|
||||
pypy310
|
||||
style
|
||||
typing
|
||||
docs
|
||||
skip_missing_interpreters = true
|
||||
|
||||
[testenv]
|
||||
package = wheel
|
||||
wheel_build_env = .pkg
|
||||
constrain_package_deps = true
|
||||
use_frozen_constraints = true
|
||||
deps = -r requirements/tests.txt
|
||||
commands = pytest -v --tb=short --basetemp={envtmpdir} {posargs}
|
||||
|
||||
[testenv:style]
|
||||
deps = pre-commit
|
||||
skip_install = true
|
||||
commands = pre-commit run --all-files
|
||||
|
||||
[testenv:typing]
|
||||
deps = -r requirements/typing.txt
|
||||
commands = mypy
|
||||
|
||||
[testenv:docs]
|
||||
deps = -r requirements/docs.txt
|
||||
commands = sphinx-build -E -W -b dirhtml docs docs/_build/dirhtml
|
||||
|
||||
[testenv:update-actions]
|
||||
labels = update
|
||||
deps = gha-update
|
||||
commands = gha-update
|
||||
|
||||
[testenv:update-pre_commit]
|
||||
labels = update
|
||||
deps = pre-commit
|
||||
skip_install = true
|
||||
commands = pre-commit autoupdate -j4
|
||||
|
||||
[testenv:update-requirements]
|
||||
labels = update
|
||||
deps = pip-tools
|
||||
skip_install = true
|
||||
change_dir = requirements
|
||||
commands =
|
||||
pip-compile build.in -q {posargs:-U}
|
||||
pip-compile docs.in -q {posargs:-U}
|
||||
pip-compile tests.in -q {posargs:-U}
|
||||
pip-compile typing.in -q {posargs:-U}
|
||||
pip-compile dev.in -q {posargs:-U}
|
||||
Loading…
Reference in New Issue
Block a user