reach-vb HF staff commited on
Commit
8caf5eb
1 Parent(s): 803812e

2ebf362ced2ed2d371063d42fcd330b8d8f4dfd54da93eab551b86860bb15b4e

Browse files
Files changed (50) hide show
  1. lib/python3.11/site-packages/jinja2/__pycache__/idtracking.cpython-311.pyc +0 -0
  2. lib/python3.11/site-packages/jinja2/__pycache__/lexer.cpython-311.pyc +0 -0
  3. lib/python3.11/site-packages/jinja2/__pycache__/loaders.cpython-311.pyc +0 -0
  4. lib/python3.11/site-packages/jinja2/__pycache__/meta.cpython-311.pyc +0 -0
  5. lib/python3.11/site-packages/jinja2/__pycache__/nativetypes.cpython-311.pyc +0 -0
  6. lib/python3.11/site-packages/jinja2/__pycache__/nodes.cpython-311.pyc +0 -0
  7. lib/python3.11/site-packages/jinja2/__pycache__/optimizer.cpython-311.pyc +0 -0
  8. lib/python3.11/site-packages/jinja2/__pycache__/parser.cpython-311.pyc +0 -0
  9. lib/python3.11/site-packages/jinja2/__pycache__/runtime.cpython-311.pyc +0 -0
  10. lib/python3.11/site-packages/jinja2/__pycache__/sandbox.cpython-311.pyc +0 -0
  11. lib/python3.11/site-packages/jinja2/__pycache__/tests.cpython-311.pyc +0 -0
  12. lib/python3.11/site-packages/jinja2/__pycache__/utils.cpython-311.pyc +0 -0
  13. lib/python3.11/site-packages/jinja2/__pycache__/visitor.cpython-311.pyc +0 -0
  14. lib/python3.11/site-packages/jinja2/_identifier.py +6 -0
  15. lib/python3.11/site-packages/jinja2/async_utils.py +84 -0
  16. lib/python3.11/site-packages/jinja2/bccache.py +406 -0
  17. lib/python3.11/site-packages/jinja2/compiler.py +1957 -0
  18. lib/python3.11/site-packages/jinja2/constants.py +20 -0
  19. lib/python3.11/site-packages/jinja2/debug.py +191 -0
  20. lib/python3.11/site-packages/jinja2/defaults.py +48 -0
  21. lib/python3.11/site-packages/jinja2/environment.py +1667 -0
  22. lib/python3.11/site-packages/jinja2/exceptions.py +166 -0
  23. lib/python3.11/site-packages/jinja2/ext.py +859 -0
  24. lib/python3.11/site-packages/jinja2/filters.py +1840 -0
  25. lib/python3.11/site-packages/jinja2/idtracking.py +318 -0
  26. lib/python3.11/site-packages/jinja2/lexer.py +866 -0
  27. lib/python3.11/site-packages/jinja2/loaders.py +661 -0
  28. lib/python3.11/site-packages/jinja2/meta.py +111 -0
  29. lib/python3.11/site-packages/jinja2/nativetypes.py +130 -0
  30. lib/python3.11/site-packages/jinja2/nodes.py +1204 -0
  31. lib/python3.11/site-packages/jinja2/optimizer.py +47 -0
  32. lib/python3.11/site-packages/jinja2/parser.py +1032 -0
  33. lib/python3.11/site-packages/jinja2/py.typed +0 -0
  34. lib/python3.11/site-packages/jinja2/runtime.py +1053 -0
  35. lib/python3.11/site-packages/jinja2/sandbox.py +428 -0
  36. lib/python3.11/site-packages/jinja2/tests.py +255 -0
  37. lib/python3.11/site-packages/jinja2/utils.py +755 -0
  38. lib/python3.11/site-packages/jinja2/visitor.py +92 -0
  39. lib/python3.11/site-packages/llvmlite/__init__.py +3 -0
  40. lib/python3.11/site-packages/llvmlite/__pycache__/__init__.cpython-311.pyc +0 -0
  41. lib/python3.11/site-packages/llvmlite/__pycache__/_version.cpython-311.pyc +0 -0
  42. lib/python3.11/site-packages/llvmlite/__pycache__/utils.cpython-311.pyc +0 -0
  43. lib/python3.11/site-packages/llvmlite/_version.py +11 -0
  44. lib/python3.11/site-packages/llvmlite/binding/__init__.py +17 -0
  45. lib/python3.11/site-packages/llvmlite/binding/__pycache__/__init__.cpython-311.pyc +0 -0
  46. lib/python3.11/site-packages/llvmlite/binding/__pycache__/analysis.cpython-311.pyc +0 -0
  47. lib/python3.11/site-packages/llvmlite/binding/__pycache__/common.cpython-311.pyc +0 -0
  48. lib/python3.11/site-packages/llvmlite/binding/__pycache__/context.cpython-311.pyc +0 -0
  49. lib/python3.11/site-packages/llvmlite/binding/__pycache__/dylib.cpython-311.pyc +0 -0
  50. lib/python3.11/site-packages/llvmlite/binding/__pycache__/executionengine.cpython-311.pyc +0 -0
lib/python3.11/site-packages/jinja2/__pycache__/idtracking.cpython-311.pyc ADDED
Binary file (19.6 kB). View file
 
lib/python3.11/site-packages/jinja2/__pycache__/lexer.cpython-311.pyc ADDED
Binary file (35.7 kB). View file
 
lib/python3.11/site-packages/jinja2/__pycache__/loaders.cpython-311.pyc ADDED
Binary file (33.1 kB). View file
 
lib/python3.11/site-packages/jinja2/__pycache__/meta.cpython-311.pyc ADDED
Binary file (5.73 kB). View file
 
lib/python3.11/site-packages/jinja2/__pycache__/nativetypes.cpython-311.pyc ADDED
Binary file (8 kB). View file
 
lib/python3.11/site-packages/jinja2/__pycache__/nodes.cpython-311.pyc ADDED
Binary file (64.5 kB). View file
 
lib/python3.11/site-packages/jinja2/__pycache__/optimizer.cpython-311.pyc ADDED
Binary file (2.89 kB). View file
 
lib/python3.11/site-packages/jinja2/__pycache__/parser.cpython-311.pyc ADDED
Binary file (59.3 kB). View file
 
lib/python3.11/site-packages/jinja2/__pycache__/runtime.cpython-311.pyc ADDED
Binary file (50.7 kB). View file
 
lib/python3.11/site-packages/jinja2/__pycache__/sandbox.cpython-311.pyc ADDED
Binary file (18.9 kB). View file
 
lib/python3.11/site-packages/jinja2/__pycache__/tests.cpython-311.pyc ADDED
Binary file (9.28 kB). View file
 
lib/python3.11/site-packages/jinja2/__pycache__/utils.cpython-311.pyc ADDED
Binary file (37.1 kB). View file
 
lib/python3.11/site-packages/jinja2/__pycache__/visitor.cpython-311.pyc ADDED
Binary file (5.75 kB). View file
 
lib/python3.11/site-packages/jinja2/_identifier.py ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ import re
2
+
3
+ # generated by scripts/generate_identifier_pattern.py
4
+ pattern = re.compile(
5
+ r"[\w·̀-ͯ·҃-֑҇-ׇֽֿׁׂׅׄؐ-ًؚ-ٰٟۖ-ۜ۟-۪ۤۧۨ-ܑۭܰ-݊ަ-ް߫-߽߳ࠖ-࠙ࠛ-ࠣࠥ-ࠧࠩ-࡙࠭-࡛࣓-ࣣ࣡-ःऺ-़ा-ॏ॑-ॗॢॣঁ-ঃ়া-ৄেৈো-্ৗৢৣ৾ਁ-ਃ਼ਾ-ੂੇੈੋ-੍ੑੰੱੵઁ-ઃ઼ા-ૅે-ૉો-્ૢૣૺ-૿ଁ-ଃ଼ା-ୄେୈୋ-୍ୖୗୢୣஂா-ூெ-ைொ-்ௗఀ-ఄా-ౄె-ైొ-్ౕౖౢౣಁ-ಃ಼ಾ-ೄೆ-ೈೊ-್ೕೖೢೣഀ-ഃ഻഼ാ-ൄെ-ൈൊ-്ൗൢൣංඃ්ා-ුූෘ-ෟෲෳัิ-ฺ็-๎ັິ-ູົຼ່-ໍ༹༘༙༵༷༾༿ཱ-྄྆྇ྍ-ྗྙ-ྼ࿆ါ-ှၖ-ၙၞ-ၠၢ-ၤၧ-ၭၱ-ၴႂ-ႍႏႚ-ႝ፝-፟ᜒ-᜔ᜲ-᜴ᝒᝓᝲᝳ឴-៓៝᠋-᠍ᢅᢆᢩᤠ-ᤫᤰ-᤻ᨗ-ᨛᩕ-ᩞ᩠-᩿᩼᪰-᪽ᬀ-ᬄ᬴-᭄᭫-᭳ᮀ-ᮂᮡ-ᮭ᯦-᯳ᰤ-᰷᳐-᳔᳒-᳨᳭ᳲ-᳴᳷-᳹᷀-᷹᷻-᷿‿⁀⁔⃐-⃥⃜⃡-⃰℘℮⳯-⵿⳱ⷠ-〪ⷿ-゙゚〯꙯ꙴ-꙽ꚞꚟ꛰꛱ꠂ꠆ꠋꠣ-ꠧꢀꢁꢴ-ꣅ꣠-꣱ꣿꤦ-꤭ꥇ-꥓ꦀ-ꦃ꦳-꧀ꧥꨩ-ꨶꩃꩌꩍꩻ-ꩽꪰꪲ-ꪴꪷꪸꪾ꪿꫁ꫫ-ꫯꫵ꫶ꯣ-ꯪ꯬꯭ﬞ︀-️︠-︯︳︴﹍-﹏_𐇽𐋠𐍶-𐍺𐨁-𐨃𐨅𐨆𐨌-𐨏𐨸-𐨿𐨺𐫦𐫥𐴤-𐽆𐴧-𐽐𑀀-𑀂𑀸-𑁆𑁿-𑂂𑂰-𑂺𑄀-𑄂𑄧-𑄴𑅅𑅆𑅳𑆀-𑆂𑆳-𑇀𑇉-𑇌𑈬-𑈷𑈾𑋟-𑋪𑌀-𑌃𑌻𑌼𑌾-𑍄𑍇𑍈𑍋-𑍍𑍗𑍢𑍣𑍦-𑍬𑍰-𑍴𑐵-𑑆𑑞𑒰-𑓃𑖯-𑖵𑖸-𑗀𑗜𑗝𑘰-𑙀𑚫-𑚷𑜝-𑜫𑠬-𑠺𑨁-𑨊𑨳-𑨹𑨻-𑨾𑩇𑩑-𑩛𑪊-𑪙𑰯-𑰶𑰸-𑰿𑲒-𑲧𑲩-𑲶𑴱-𑴶𑴺𑴼𑴽𑴿-𑵅𑵇𑶊-𑶎𑶐𑶑𑶓-𑶗𑻳-𑻶𖫰-𖫴𖬰-𖬶𖽑-𖽾𖾏-𖾒𛲝𛲞𝅥-𝅩𝅭-𝅲𝅻-𝆂𝆅-𝆋𝆪-𝆭𝉂-𝉄𝨀-𝨶𝨻-𝩬𝩵𝪄𝪛-𝪟𝪡-𝪯𞀀-𞀆𞀈-𞀘𞀛-𞀡𞀣𞀤𞀦-𞣐𞀪-𞣖𞥄-𞥊󠄀-󠇯]+" # noqa: B950
6
+ )
lib/python3.11/site-packages/jinja2/async_utils.py ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import inspect
2
+ import typing as t
3
+ from functools import WRAPPER_ASSIGNMENTS
4
+ from functools import wraps
5
+
6
+ from .utils import _PassArg
7
+ from .utils import pass_eval_context
8
+
9
+ V = t.TypeVar("V")
10
+
11
+
12
+ def async_variant(normal_func): # type: ignore
13
+ def decorator(async_func): # type: ignore
14
+ pass_arg = _PassArg.from_obj(normal_func)
15
+ need_eval_context = pass_arg is None
16
+
17
+ if pass_arg is _PassArg.environment:
18
+
19
+ def is_async(args: t.Any) -> bool:
20
+ return t.cast(bool, args[0].is_async)
21
+
22
+ else:
23
+
24
+ def is_async(args: t.Any) -> bool:
25
+ return t.cast(bool, args[0].environment.is_async)
26
+
27
+ # Take the doc and annotations from the sync function, but the
28
+ # name from the async function. Pallets-Sphinx-Themes
29
+ # build_function_directive expects __wrapped__ to point to the
30
+ # sync function.
31
+ async_func_attrs = ("__module__", "__name__", "__qualname__")
32
+ normal_func_attrs = tuple(set(WRAPPER_ASSIGNMENTS).difference(async_func_attrs))
33
+
34
+ @wraps(normal_func, assigned=normal_func_attrs)
35
+ @wraps(async_func, assigned=async_func_attrs, updated=())
36
+ def wrapper(*args, **kwargs): # type: ignore
37
+ b = is_async(args)
38
+
39
+ if need_eval_context:
40
+ args = args[1:]
41
+
42
+ if b:
43
+ return async_func(*args, **kwargs)
44
+
45
+ return normal_func(*args, **kwargs)
46
+
47
+ if need_eval_context:
48
+ wrapper = pass_eval_context(wrapper)
49
+
50
+ wrapper.jinja_async_variant = True
51
+ return wrapper
52
+
53
+ return decorator
54
+
55
+
56
+ _common_primitives = {int, float, bool, str, list, dict, tuple, type(None)}
57
+
58
+
59
+ async def auto_await(value: t.Union[t.Awaitable["V"], "V"]) -> "V":
60
+ # Avoid a costly call to isawaitable
61
+ if type(value) in _common_primitives:
62
+ return t.cast("V", value)
63
+
64
+ if inspect.isawaitable(value):
65
+ return await t.cast("t.Awaitable[V]", value)
66
+
67
+ return t.cast("V", value)
68
+
69
+
70
+ async def auto_aiter(
71
+ iterable: "t.Union[t.AsyncIterable[V], t.Iterable[V]]",
72
+ ) -> "t.AsyncIterator[V]":
73
+ if hasattr(iterable, "__aiter__"):
74
+ async for item in t.cast("t.AsyncIterable[V]", iterable):
75
+ yield item
76
+ else:
77
+ for item in t.cast("t.Iterable[V]", iterable):
78
+ yield item
79
+
80
+
81
+ async def auto_to_list(
82
+ value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]",
83
+ ) -> t.List["V"]:
84
+ return [x async for x in auto_aiter(value)]
lib/python3.11/site-packages/jinja2/bccache.py ADDED
@@ -0,0 +1,406 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """The optional bytecode cache system. This is useful if you have very
2
+ complex template situations and the compilation of all those templates
3
+ slows down your application too much.
4
+
5
+ Situations where this is useful are often forking web applications that
6
+ are initialized on the first request.
7
+ """
8
+ import errno
9
+ import fnmatch
10
+ import marshal
11
+ import os
12
+ import pickle
13
+ import stat
14
+ import sys
15
+ import tempfile
16
+ import typing as t
17
+ from hashlib import sha1
18
+ from io import BytesIO
19
+ from types import CodeType
20
+
21
+ if t.TYPE_CHECKING:
22
+ import typing_extensions as te
23
+ from .environment import Environment
24
+
25
+ class _MemcachedClient(te.Protocol):
26
+ def get(self, key: str) -> bytes:
27
+ ...
28
+
29
+ def set(self, key: str, value: bytes, timeout: t.Optional[int] = None) -> None:
30
+ ...
31
+
32
+
33
+ bc_version = 5
34
+ # Magic bytes to identify Jinja bytecode cache files. Contains the
35
+ # Python major and minor version to avoid loading incompatible bytecode
36
+ # if a project upgrades its Python version.
37
+ bc_magic = (
38
+ b"j2"
39
+ + pickle.dumps(bc_version, 2)
40
+ + pickle.dumps((sys.version_info[0] << 24) | sys.version_info[1], 2)
41
+ )
42
+
43
+
44
+ class Bucket:
45
+ """Buckets are used to store the bytecode for one template. It's created
46
+ and initialized by the bytecode cache and passed to the loading functions.
47
+
48
+ The buckets get an internal checksum from the cache assigned and use this
49
+ to automatically reject outdated cache material. Individual bytecode
50
+ cache subclasses don't have to care about cache invalidation.
51
+ """
52
+
53
+ def __init__(self, environment: "Environment", key: str, checksum: str) -> None:
54
+ self.environment = environment
55
+ self.key = key
56
+ self.checksum = checksum
57
+ self.reset()
58
+
59
+ def reset(self) -> None:
60
+ """Resets the bucket (unloads the bytecode)."""
61
+ self.code: t.Optional[CodeType] = None
62
+
63
+ def load_bytecode(self, f: t.BinaryIO) -> None:
64
+ """Loads bytecode from a file or file like object."""
65
+ # make sure the magic header is correct
66
+ magic = f.read(len(bc_magic))
67
+ if magic != bc_magic:
68
+ self.reset()
69
+ return
70
+ # the source code of the file changed, we need to reload
71
+ checksum = pickle.load(f)
72
+ if self.checksum != checksum:
73
+ self.reset()
74
+ return
75
+ # if marshal_load fails then we need to reload
76
+ try:
77
+ self.code = marshal.load(f)
78
+ except (EOFError, ValueError, TypeError):
79
+ self.reset()
80
+ return
81
+
82
+ def write_bytecode(self, f: t.IO[bytes]) -> None:
83
+ """Dump the bytecode into the file or file like object passed."""
84
+ if self.code is None:
85
+ raise TypeError("can't write empty bucket")
86
+ f.write(bc_magic)
87
+ pickle.dump(self.checksum, f, 2)
88
+ marshal.dump(self.code, f)
89
+
90
+ def bytecode_from_string(self, string: bytes) -> None:
91
+ """Load bytecode from bytes."""
92
+ self.load_bytecode(BytesIO(string))
93
+
94
+ def bytecode_to_string(self) -> bytes:
95
+ """Return the bytecode as bytes."""
96
+ out = BytesIO()
97
+ self.write_bytecode(out)
98
+ return out.getvalue()
99
+
100
+
101
+ class BytecodeCache:
102
+ """To implement your own bytecode cache you have to subclass this class
103
+ and override :meth:`load_bytecode` and :meth:`dump_bytecode`. Both of
104
+ these methods are passed a :class:`~jinja2.bccache.Bucket`.
105
+
106
+ A very basic bytecode cache that saves the bytecode on the file system::
107
+
108
+ from os import path
109
+
110
+ class MyCache(BytecodeCache):
111
+
112
+ def __init__(self, directory):
113
+ self.directory = directory
114
+
115
+ def load_bytecode(self, bucket):
116
+ filename = path.join(self.directory, bucket.key)
117
+ if path.exists(filename):
118
+ with open(filename, 'rb') as f:
119
+ bucket.load_bytecode(f)
120
+
121
+ def dump_bytecode(self, bucket):
122
+ filename = path.join(self.directory, bucket.key)
123
+ with open(filename, 'wb') as f:
124
+ bucket.write_bytecode(f)
125
+
126
+ A more advanced version of a filesystem based bytecode cache is part of
127
+ Jinja.
128
+ """
129
+
130
+ def load_bytecode(self, bucket: Bucket) -> None:
131
+ """Subclasses have to override this method to load bytecode into a
132
+ bucket. If they are not able to find code in the cache for the
133
+ bucket, it must not do anything.
134
+ """
135
+ raise NotImplementedError()
136
+
137
+ def dump_bytecode(self, bucket: Bucket) -> None:
138
+ """Subclasses have to override this method to write the bytecode
139
+ from a bucket back to the cache. If it unable to do so it must not
140
+ fail silently but raise an exception.
141
+ """
142
+ raise NotImplementedError()
143
+
144
+ def clear(self) -> None:
145
+ """Clears the cache. This method is not used by Jinja but should be
146
+ implemented to allow applications to clear the bytecode cache used
147
+ by a particular environment.
148
+ """
149
+
150
+ def get_cache_key(
151
+ self, name: str, filename: t.Optional[t.Union[str]] = None
152
+ ) -> str:
153
+ """Returns the unique hash key for this template name."""
154
+ hash = sha1(name.encode("utf-8"))
155
+
156
+ if filename is not None:
157
+ hash.update(f"|{filename}".encode())
158
+
159
+ return hash.hexdigest()
160
+
161
+ def get_source_checksum(self, source: str) -> str:
162
+ """Returns a checksum for the source."""
163
+ return sha1(source.encode("utf-8")).hexdigest()
164
+
165
+ def get_bucket(
166
+ self,
167
+ environment: "Environment",
168
+ name: str,
169
+ filename: t.Optional[str],
170
+ source: str,
171
+ ) -> Bucket:
172
+ """Return a cache bucket for the given template. All arguments are
173
+ mandatory but filename may be `None`.
174
+ """
175
+ key = self.get_cache_key(name, filename)
176
+ checksum = self.get_source_checksum(source)
177
+ bucket = Bucket(environment, key, checksum)
178
+ self.load_bytecode(bucket)
179
+ return bucket
180
+
181
+ def set_bucket(self, bucket: Bucket) -> None:
182
+ """Put the bucket into the cache."""
183
+ self.dump_bytecode(bucket)
184
+
185
+
186
+ class FileSystemBytecodeCache(BytecodeCache):
187
+ """A bytecode cache that stores bytecode on the filesystem. It accepts
188
+ two arguments: The directory where the cache items are stored and a
189
+ pattern string that is used to build the filename.
190
+
191
+ If no directory is specified a default cache directory is selected. On
192
+ Windows the user's temp directory is used, on UNIX systems a directory
193
+ is created for the user in the system temp directory.
194
+
195
+ The pattern can be used to have multiple separate caches operate on the
196
+ same directory. The default pattern is ``'__jinja2_%s.cache'``. ``%s``
197
+ is replaced with the cache key.
198
+
199
+ >>> bcc = FileSystemBytecodeCache('/tmp/jinja_cache', '%s.cache')
200
+
201
+ This bytecode cache supports clearing of the cache using the clear method.
202
+ """
203
+
204
+ def __init__(
205
+ self, directory: t.Optional[str] = None, pattern: str = "__jinja2_%s.cache"
206
+ ) -> None:
207
+ if directory is None:
208
+ directory = self._get_default_cache_dir()
209
+ self.directory = directory
210
+ self.pattern = pattern
211
+
212
+ def _get_default_cache_dir(self) -> str:
213
+ def _unsafe_dir() -> "te.NoReturn":
214
+ raise RuntimeError(
215
+ "Cannot determine safe temp directory. You "
216
+ "need to explicitly provide one."
217
+ )
218
+
219
+ tmpdir = tempfile.gettempdir()
220
+
221
+ # On windows the temporary directory is used specific unless
222
+ # explicitly forced otherwise. We can just use that.
223
+ if os.name == "nt":
224
+ return tmpdir
225
+ if not hasattr(os, "getuid"):
226
+ _unsafe_dir()
227
+
228
+ dirname = f"_jinja2-cache-{os.getuid()}"
229
+ actual_dir = os.path.join(tmpdir, dirname)
230
+
231
+ try:
232
+ os.mkdir(actual_dir, stat.S_IRWXU)
233
+ except OSError as e:
234
+ if e.errno != errno.EEXIST:
235
+ raise
236
+ try:
237
+ os.chmod(actual_dir, stat.S_IRWXU)
238
+ actual_dir_stat = os.lstat(actual_dir)
239
+ if (
240
+ actual_dir_stat.st_uid != os.getuid()
241
+ or not stat.S_ISDIR(actual_dir_stat.st_mode)
242
+ or stat.S_IMODE(actual_dir_stat.st_mode) != stat.S_IRWXU
243
+ ):
244
+ _unsafe_dir()
245
+ except OSError as e:
246
+ if e.errno != errno.EEXIST:
247
+ raise
248
+
249
+ actual_dir_stat = os.lstat(actual_dir)
250
+ if (
251
+ actual_dir_stat.st_uid != os.getuid()
252
+ or not stat.S_ISDIR(actual_dir_stat.st_mode)
253
+ or stat.S_IMODE(actual_dir_stat.st_mode) != stat.S_IRWXU
254
+ ):
255
+ _unsafe_dir()
256
+
257
+ return actual_dir
258
+
259
+ def _get_cache_filename(self, bucket: Bucket) -> str:
260
+ return os.path.join(self.directory, self.pattern % (bucket.key,))
261
+
262
+ def load_bytecode(self, bucket: Bucket) -> None:
263
+ filename = self._get_cache_filename(bucket)
264
+
265
+ # Don't test for existence before opening the file, since the
266
+ # file could disappear after the test before the open.
267
+ try:
268
+ f = open(filename, "rb")
269
+ except (FileNotFoundError, IsADirectoryError, PermissionError):
270
+ # PermissionError can occur on Windows when an operation is
271
+ # in progress, such as calling clear().
272
+ return
273
+
274
+ with f:
275
+ bucket.load_bytecode(f)
276
+
277
+ def dump_bytecode(self, bucket: Bucket) -> None:
278
+ # Write to a temporary file, then rename to the real name after
279
+ # writing. This avoids another process reading the file before
280
+ # it is fully written.
281
+ name = self._get_cache_filename(bucket)
282
+ f = tempfile.NamedTemporaryFile(
283
+ mode="wb",
284
+ dir=os.path.dirname(name),
285
+ prefix=os.path.basename(name),
286
+ suffix=".tmp",
287
+ delete=False,
288
+ )
289
+
290
+ def remove_silent() -> None:
291
+ try:
292
+ os.remove(f.name)
293
+ except OSError:
294
+ # Another process may have called clear(). On Windows,
295
+ # another program may be holding the file open.
296
+ pass
297
+
298
+ try:
299
+ with f:
300
+ bucket.write_bytecode(f)
301
+ except BaseException:
302
+ remove_silent()
303
+ raise
304
+
305
+ try:
306
+ os.replace(f.name, name)
307
+ except OSError:
308
+ # Another process may have called clear(). On Windows,
309
+ # another program may be holding the file open.
310
+ remove_silent()
311
+ except BaseException:
312
+ remove_silent()
313
+ raise
314
+
315
+ def clear(self) -> None:
316
+ # imported lazily here because google app-engine doesn't support
317
+ # write access on the file system and the function does not exist
318
+ # normally.
319
+ from os import remove
320
+
321
+ files = fnmatch.filter(os.listdir(self.directory), self.pattern % ("*",))
322
+ for filename in files:
323
+ try:
324
+ remove(os.path.join(self.directory, filename))
325
+ except OSError:
326
+ pass
327
+
328
+
329
+ class MemcachedBytecodeCache(BytecodeCache):
330
+ """This class implements a bytecode cache that uses a memcache cache for
331
+ storing the information. It does not enforce a specific memcache library
332
+ (tummy's memcache or cmemcache) but will accept any class that provides
333
+ the minimal interface required.
334
+
335
+ Libraries compatible with this class:
336
+
337
+ - `cachelib <https://github.com/pallets/cachelib>`_
338
+ - `python-memcached <https://pypi.org/project/python-memcached/>`_
339
+
340
+ (Unfortunately the django cache interface is not compatible because it
341
+ does not support storing binary data, only text. You can however pass
342
+ the underlying cache client to the bytecode cache which is available
343
+ as `django.core.cache.cache._client`.)
344
+
345
+ The minimal interface for the client passed to the constructor is this:
346
+
347
+ .. class:: MinimalClientInterface
348
+
349
+ .. method:: set(key, value[, timeout])
350
+
351
+ Stores the bytecode in the cache. `value` is a string and
352
+ `timeout` the timeout of the key. If timeout is not provided
353
+ a default timeout or no timeout should be assumed, if it's
354
+ provided it's an integer with the number of seconds the cache
355
+ item should exist.
356
+
357
+ .. method:: get(key)
358
+
359
+ Returns the value for the cache key. If the item does not
360
+ exist in the cache the return value must be `None`.
361
+
362
+ The other arguments to the constructor are the prefix for all keys that
363
+ is added before the actual cache key and the timeout for the bytecode in
364
+ the cache system. We recommend a high (or no) timeout.
365
+
366
+ This bytecode cache does not support clearing of used items in the cache.
367
+ The clear method is a no-operation function.
368
+
369
+ .. versionadded:: 2.7
370
+ Added support for ignoring memcache errors through the
371
+ `ignore_memcache_errors` parameter.
372
+ """
373
+
374
+ def __init__(
375
+ self,
376
+ client: "_MemcachedClient",
377
+ prefix: str = "jinja2/bytecode/",
378
+ timeout: t.Optional[int] = None,
379
+ ignore_memcache_errors: bool = True,
380
+ ):
381
+ self.client = client
382
+ self.prefix = prefix
383
+ self.timeout = timeout
384
+ self.ignore_memcache_errors = ignore_memcache_errors
385
+
386
+ def load_bytecode(self, bucket: Bucket) -> None:
387
+ try:
388
+ code = self.client.get(self.prefix + bucket.key)
389
+ except Exception:
390
+ if not self.ignore_memcache_errors:
391
+ raise
392
+ else:
393
+ bucket.bytecode_from_string(code)
394
+
395
+ def dump_bytecode(self, bucket: Bucket) -> None:
396
+ key = self.prefix + bucket.key
397
+ value = bucket.bytecode_to_string()
398
+
399
+ try:
400
+ if self.timeout is not None:
401
+ self.client.set(key, value, self.timeout)
402
+ else:
403
+ self.client.set(key, value)
404
+ except Exception:
405
+ if not self.ignore_memcache_errors:
406
+ raise
lib/python3.11/site-packages/jinja2/compiler.py ADDED
@@ -0,0 +1,1957 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Compiles nodes from the parser into Python code."""
2
+ import typing as t
3
+ from contextlib import contextmanager
4
+ from functools import update_wrapper
5
+ from io import StringIO
6
+ from itertools import chain
7
+ from keyword import iskeyword as is_python_keyword
8
+
9
+ from markupsafe import escape
10
+ from markupsafe import Markup
11
+
12
+ from . import nodes
13
+ from .exceptions import TemplateAssertionError
14
+ from .idtracking import Symbols
15
+ from .idtracking import VAR_LOAD_ALIAS
16
+ from .idtracking import VAR_LOAD_PARAMETER
17
+ from .idtracking import VAR_LOAD_RESOLVE
18
+ from .idtracking import VAR_LOAD_UNDEFINED
19
+ from .nodes import EvalContext
20
+ from .optimizer import Optimizer
21
+ from .utils import _PassArg
22
+ from .utils import concat
23
+ from .visitor import NodeVisitor
24
+
25
+ if t.TYPE_CHECKING:
26
+ import typing_extensions as te
27
+ from .environment import Environment
28
+
29
+ F = t.TypeVar("F", bound=t.Callable[..., t.Any])
30
+
31
+ operators = {
32
+ "eq": "==",
33
+ "ne": "!=",
34
+ "gt": ">",
35
+ "gteq": ">=",
36
+ "lt": "<",
37
+ "lteq": "<=",
38
+ "in": "in",
39
+ "notin": "not in",
40
+ }
41
+
42
+
43
+ def optimizeconst(f: F) -> F:
44
+ def new_func(
45
+ self: "CodeGenerator", node: nodes.Expr, frame: "Frame", **kwargs: t.Any
46
+ ) -> t.Any:
47
+ # Only optimize if the frame is not volatile
48
+ if self.optimizer is not None and not frame.eval_ctx.volatile:
49
+ new_node = self.optimizer.visit(node, frame.eval_ctx)
50
+
51
+ if new_node != node:
52
+ return self.visit(new_node, frame)
53
+
54
+ return f(self, node, frame, **kwargs)
55
+
56
+ return update_wrapper(t.cast(F, new_func), f)
57
+
58
+
59
+ def _make_binop(op: str) -> t.Callable[["CodeGenerator", nodes.BinExpr, "Frame"], None]:
60
+ @optimizeconst
61
+ def visitor(self: "CodeGenerator", node: nodes.BinExpr, frame: Frame) -> None:
62
+ if (
63
+ self.environment.sandboxed
64
+ and op in self.environment.intercepted_binops # type: ignore
65
+ ):
66
+ self.write(f"environment.call_binop(context, {op!r}, ")
67
+ self.visit(node.left, frame)
68
+ self.write(", ")
69
+ self.visit(node.right, frame)
70
+ else:
71
+ self.write("(")
72
+ self.visit(node.left, frame)
73
+ self.write(f" {op} ")
74
+ self.visit(node.right, frame)
75
+
76
+ self.write(")")
77
+
78
+ return visitor
79
+
80
+
81
+ def _make_unop(
82
+ op: str,
83
+ ) -> t.Callable[["CodeGenerator", nodes.UnaryExpr, "Frame"], None]:
84
+ @optimizeconst
85
+ def visitor(self: "CodeGenerator", node: nodes.UnaryExpr, frame: Frame) -> None:
86
+ if (
87
+ self.environment.sandboxed
88
+ and op in self.environment.intercepted_unops # type: ignore
89
+ ):
90
+ self.write(f"environment.call_unop(context, {op!r}, ")
91
+ self.visit(node.node, frame)
92
+ else:
93
+ self.write("(" + op)
94
+ self.visit(node.node, frame)
95
+
96
+ self.write(")")
97
+
98
+ return visitor
99
+
100
+
101
+ def generate(
102
+ node: nodes.Template,
103
+ environment: "Environment",
104
+ name: t.Optional[str],
105
+ filename: t.Optional[str],
106
+ stream: t.Optional[t.TextIO] = None,
107
+ defer_init: bool = False,
108
+ optimized: bool = True,
109
+ ) -> t.Optional[str]:
110
+ """Generate the python source for a node tree."""
111
+ if not isinstance(node, nodes.Template):
112
+ raise TypeError("Can't compile non template nodes")
113
+
114
+ generator = environment.code_generator_class(
115
+ environment, name, filename, stream, defer_init, optimized
116
+ )
117
+ generator.visit(node)
118
+
119
+ if stream is None:
120
+ return generator.stream.getvalue() # type: ignore
121
+
122
+ return None
123
+
124
+
125
+ def has_safe_repr(value: t.Any) -> bool:
126
+ """Does the node have a safe representation?"""
127
+ if value is None or value is NotImplemented or value is Ellipsis:
128
+ return True
129
+
130
+ if type(value) in {bool, int, float, complex, range, str, Markup}:
131
+ return True
132
+
133
+ if type(value) in {tuple, list, set, frozenset}:
134
+ return all(has_safe_repr(v) for v in value)
135
+
136
+ if type(value) is dict:
137
+ return all(has_safe_repr(k) and has_safe_repr(v) for k, v in value.items())
138
+
139
+ return False
140
+
141
+
142
+ def find_undeclared(
143
+ nodes: t.Iterable[nodes.Node], names: t.Iterable[str]
144
+ ) -> t.Set[str]:
145
+ """Check if the names passed are accessed undeclared. The return value
146
+ is a set of all the undeclared names from the sequence of names found.
147
+ """
148
+ visitor = UndeclaredNameVisitor(names)
149
+ try:
150
+ for node in nodes:
151
+ visitor.visit(node)
152
+ except VisitorExit:
153
+ pass
154
+ return visitor.undeclared
155
+
156
+
157
+ class MacroRef:
158
+ def __init__(self, node: t.Union[nodes.Macro, nodes.CallBlock]) -> None:
159
+ self.node = node
160
+ self.accesses_caller = False
161
+ self.accesses_kwargs = False
162
+ self.accesses_varargs = False
163
+
164
+
165
+ class Frame:
166
+ """Holds compile time information for us."""
167
+
168
+ def __init__(
169
+ self,
170
+ eval_ctx: EvalContext,
171
+ parent: t.Optional["Frame"] = None,
172
+ level: t.Optional[int] = None,
173
+ ) -> None:
174
+ self.eval_ctx = eval_ctx
175
+
176
+ # the parent of this frame
177
+ self.parent = parent
178
+
179
+ if parent is None:
180
+ self.symbols = Symbols(level=level)
181
+
182
+ # in some dynamic inheritance situations the compiler needs to add
183
+ # write tests around output statements.
184
+ self.require_output_check = False
185
+
186
+ # inside some tags we are using a buffer rather than yield statements.
187
+ # this for example affects {% filter %} or {% macro %}. If a frame
188
+ # is buffered this variable points to the name of the list used as
189
+ # buffer.
190
+ self.buffer: t.Optional[str] = None
191
+
192
+ # the name of the block we're in, otherwise None.
193
+ self.block: t.Optional[str] = None
194
+
195
+ else:
196
+ self.symbols = Symbols(parent.symbols, level=level)
197
+ self.require_output_check = parent.require_output_check
198
+ self.buffer = parent.buffer
199
+ self.block = parent.block
200
+
201
+ # a toplevel frame is the root + soft frames such as if conditions.
202
+ self.toplevel = False
203
+
204
+ # the root frame is basically just the outermost frame, so no if
205
+ # conditions. This information is used to optimize inheritance
206
+ # situations.
207
+ self.rootlevel = False
208
+
209
+ # variables set inside of loops and blocks should not affect outer frames,
210
+ # but they still needs to be kept track of as part of the active context.
211
+ self.loop_frame = False
212
+ self.block_frame = False
213
+
214
+ # track whether the frame is being used in an if-statement or conditional
215
+ # expression as it determines which errors should be raised during runtime
216
+ # or compile time.
217
+ self.soft_frame = False
218
+
219
+ def copy(self) -> "Frame":
220
+ """Create a copy of the current one."""
221
+ rv = object.__new__(self.__class__)
222
+ rv.__dict__.update(self.__dict__)
223
+ rv.symbols = self.symbols.copy()
224
+ return rv
225
+
226
+ def inner(self, isolated: bool = False) -> "Frame":
227
+ """Return an inner frame."""
228
+ if isolated:
229
+ return Frame(self.eval_ctx, level=self.symbols.level + 1)
230
+ return Frame(self.eval_ctx, self)
231
+
232
+ def soft(self) -> "Frame":
233
+ """Return a soft frame. A soft frame may not be modified as
234
+ standalone thing as it shares the resources with the frame it
235
+ was created of, but it's not a rootlevel frame any longer.
236
+
237
+ This is only used to implement if-statements and conditional
238
+ expressions.
239
+ """
240
+ rv = self.copy()
241
+ rv.rootlevel = False
242
+ rv.soft_frame = True
243
+ return rv
244
+
245
+ __copy__ = copy
246
+
247
+
248
+ class VisitorExit(RuntimeError):
249
+ """Exception used by the `UndeclaredNameVisitor` to signal a stop."""
250
+
251
+
252
+ class DependencyFinderVisitor(NodeVisitor):
253
+ """A visitor that collects filter and test calls."""
254
+
255
+ def __init__(self) -> None:
256
+ self.filters: t.Set[str] = set()
257
+ self.tests: t.Set[str] = set()
258
+
259
+ def visit_Filter(self, node: nodes.Filter) -> None:
260
+ self.generic_visit(node)
261
+ self.filters.add(node.name)
262
+
263
+ def visit_Test(self, node: nodes.Test) -> None:
264
+ self.generic_visit(node)
265
+ self.tests.add(node.name)
266
+
267
+ def visit_Block(self, node: nodes.Block) -> None:
268
+ """Stop visiting at blocks."""
269
+
270
+
271
+ class UndeclaredNameVisitor(NodeVisitor):
272
+ """A visitor that checks if a name is accessed without being
273
+ declared. This is different from the frame visitor as it will
274
+ not stop at closure frames.
275
+ """
276
+
277
+ def __init__(self, names: t.Iterable[str]) -> None:
278
+ self.names = set(names)
279
+ self.undeclared: t.Set[str] = set()
280
+
281
+ def visit_Name(self, node: nodes.Name) -> None:
282
+ if node.ctx == "load" and node.name in self.names:
283
+ self.undeclared.add(node.name)
284
+ if self.undeclared == self.names:
285
+ raise VisitorExit()
286
+ else:
287
+ self.names.discard(node.name)
288
+
289
+ def visit_Block(self, node: nodes.Block) -> None:
290
+ """Stop visiting a blocks."""
291
+
292
+
293
+ class CompilerExit(Exception):
294
+ """Raised if the compiler encountered a situation where it just
295
+ doesn't make sense to further process the code. Any block that
296
+ raises such an exception is not further processed.
297
+ """
298
+
299
+
300
+ class CodeGenerator(NodeVisitor):
301
+ def __init__(
302
+ self,
303
+ environment: "Environment",
304
+ name: t.Optional[str],
305
+ filename: t.Optional[str],
306
+ stream: t.Optional[t.TextIO] = None,
307
+ defer_init: bool = False,
308
+ optimized: bool = True,
309
+ ) -> None:
310
+ if stream is None:
311
+ stream = StringIO()
312
+ self.environment = environment
313
+ self.name = name
314
+ self.filename = filename
315
+ self.stream = stream
316
+ self.created_block_context = False
317
+ self.defer_init = defer_init
318
+ self.optimizer: t.Optional[Optimizer] = None
319
+
320
+ if optimized:
321
+ self.optimizer = Optimizer(environment)
322
+
323
+ # aliases for imports
324
+ self.import_aliases: t.Dict[str, str] = {}
325
+
326
+ # a registry for all blocks. Because blocks are moved out
327
+ # into the global python scope they are registered here
328
+ self.blocks: t.Dict[str, nodes.Block] = {}
329
+
330
+ # the number of extends statements so far
331
+ self.extends_so_far = 0
332
+
333
+ # some templates have a rootlevel extends. In this case we
334
+ # can safely assume that we're a child template and do some
335
+ # more optimizations.
336
+ self.has_known_extends = False
337
+
338
+ # the current line number
339
+ self.code_lineno = 1
340
+
341
+ # registry of all filters and tests (global, not block local)
342
+ self.tests: t.Dict[str, str] = {}
343
+ self.filters: t.Dict[str, str] = {}
344
+
345
+ # the debug information
346
+ self.debug_info: t.List[t.Tuple[int, int]] = []
347
+ self._write_debug_info: t.Optional[int] = None
348
+
349
+ # the number of new lines before the next write()
350
+ self._new_lines = 0
351
+
352
+ # the line number of the last written statement
353
+ self._last_line = 0
354
+
355
+ # true if nothing was written so far.
356
+ self._first_write = True
357
+
358
+ # used by the `temporary_identifier` method to get new
359
+ # unique, temporary identifier
360
+ self._last_identifier = 0
361
+
362
+ # the current indentation
363
+ self._indentation = 0
364
+
365
+ # Tracks toplevel assignments
366
+ self._assign_stack: t.List[t.Set[str]] = []
367
+
368
+ # Tracks parameter definition blocks
369
+ self._param_def_block: t.List[t.Set[str]] = []
370
+
371
+ # Tracks the current context.
372
+ self._context_reference_stack = ["context"]
373
+
374
+ @property
375
+ def optimized(self) -> bool:
376
+ return self.optimizer is not None
377
+
378
+ # -- Various compilation helpers
379
+
380
+ def fail(self, msg: str, lineno: int) -> "te.NoReturn":
381
+ """Fail with a :exc:`TemplateAssertionError`."""
382
+ raise TemplateAssertionError(msg, lineno, self.name, self.filename)
383
+
384
+ def temporary_identifier(self) -> str:
385
+ """Get a new unique identifier."""
386
+ self._last_identifier += 1
387
+ return f"t_{self._last_identifier}"
388
+
389
+ def buffer(self, frame: Frame) -> None:
390
+ """Enable buffering for the frame from that point onwards."""
391
+ frame.buffer = self.temporary_identifier()
392
+ self.writeline(f"{frame.buffer} = []")
393
+
394
+ def return_buffer_contents(
395
+ self, frame: Frame, force_unescaped: bool = False
396
+ ) -> None:
397
+ """Return the buffer contents of the frame."""
398
+ if not force_unescaped:
399
+ if frame.eval_ctx.volatile:
400
+ self.writeline("if context.eval_ctx.autoescape:")
401
+ self.indent()
402
+ self.writeline(f"return Markup(concat({frame.buffer}))")
403
+ self.outdent()
404
+ self.writeline("else:")
405
+ self.indent()
406
+ self.writeline(f"return concat({frame.buffer})")
407
+ self.outdent()
408
+ return
409
+ elif frame.eval_ctx.autoescape:
410
+ self.writeline(f"return Markup(concat({frame.buffer}))")
411
+ return
412
+ self.writeline(f"return concat({frame.buffer})")
413
+
414
+ def indent(self) -> None:
415
+ """Indent by one."""
416
+ self._indentation += 1
417
+
418
+ def outdent(self, step: int = 1) -> None:
419
+ """Outdent by step."""
420
+ self._indentation -= step
421
+
422
+ def start_write(self, frame: Frame, node: t.Optional[nodes.Node] = None) -> None:
423
+ """Yield or write into the frame buffer."""
424
+ if frame.buffer is None:
425
+ self.writeline("yield ", node)
426
+ else:
427
+ self.writeline(f"{frame.buffer}.append(", node)
428
+
429
+ def end_write(self, frame: Frame) -> None:
430
+ """End the writing process started by `start_write`."""
431
+ if frame.buffer is not None:
432
+ self.write(")")
433
+
434
+ def simple_write(
435
+ self, s: str, frame: Frame, node: t.Optional[nodes.Node] = None
436
+ ) -> None:
437
+ """Simple shortcut for start_write + write + end_write."""
438
+ self.start_write(frame, node)
439
+ self.write(s)
440
+ self.end_write(frame)
441
+
442
+ def blockvisit(self, nodes: t.Iterable[nodes.Node], frame: Frame) -> None:
443
+ """Visit a list of nodes as block in a frame. If the current frame
444
+ is no buffer a dummy ``if 0: yield None`` is written automatically.
445
+ """
446
+ try:
447
+ self.writeline("pass")
448
+ for node in nodes:
449
+ self.visit(node, frame)
450
+ except CompilerExit:
451
+ pass
452
+
453
+ def write(self, x: str) -> None:
454
+ """Write a string into the output stream."""
455
+ if self._new_lines:
456
+ if not self._first_write:
457
+ self.stream.write("\n" * self._new_lines)
458
+ self.code_lineno += self._new_lines
459
+ if self._write_debug_info is not None:
460
+ self.debug_info.append((self._write_debug_info, self.code_lineno))
461
+ self._write_debug_info = None
462
+ self._first_write = False
463
+ self.stream.write(" " * self._indentation)
464
+ self._new_lines = 0
465
+ self.stream.write(x)
466
+
467
+ def writeline(
468
+ self, x: str, node: t.Optional[nodes.Node] = None, extra: int = 0
469
+ ) -> None:
470
+ """Combination of newline and write."""
471
+ self.newline(node, extra)
472
+ self.write(x)
473
+
474
+ def newline(self, node: t.Optional[nodes.Node] = None, extra: int = 0) -> None:
475
+ """Add one or more newlines before the next write."""
476
+ self._new_lines = max(self._new_lines, 1 + extra)
477
+ if node is not None and node.lineno != self._last_line:
478
+ self._write_debug_info = node.lineno
479
+ self._last_line = node.lineno
480
+
481
+ def signature(
482
+ self,
483
+ node: t.Union[nodes.Call, nodes.Filter, nodes.Test],
484
+ frame: Frame,
485
+ extra_kwargs: t.Optional[t.Mapping[str, t.Any]] = None,
486
+ ) -> None:
487
+ """Writes a function call to the stream for the current node.
488
+ A leading comma is added automatically. The extra keyword
489
+ arguments may not include python keywords otherwise a syntax
490
+ error could occur. The extra keyword arguments should be given
491
+ as python dict.
492
+ """
493
+ # if any of the given keyword arguments is a python keyword
494
+ # we have to make sure that no invalid call is created.
495
+ kwarg_workaround = any(
496
+ is_python_keyword(t.cast(str, k))
497
+ for k in chain((x.key for x in node.kwargs), extra_kwargs or ())
498
+ )
499
+
500
+ for arg in node.args:
501
+ self.write(", ")
502
+ self.visit(arg, frame)
503
+
504
+ if not kwarg_workaround:
505
+ for kwarg in node.kwargs:
506
+ self.write(", ")
507
+ self.visit(kwarg, frame)
508
+ if extra_kwargs is not None:
509
+ for key, value in extra_kwargs.items():
510
+ self.write(f", {key}={value}")
511
+ if node.dyn_args:
512
+ self.write(", *")
513
+ self.visit(node.dyn_args, frame)
514
+
515
+ if kwarg_workaround:
516
+ if node.dyn_kwargs is not None:
517
+ self.write(", **dict({")
518
+ else:
519
+ self.write(", **{")
520
+ for kwarg in node.kwargs:
521
+ self.write(f"{kwarg.key!r}: ")
522
+ self.visit(kwarg.value, frame)
523
+ self.write(", ")
524
+ if extra_kwargs is not None:
525
+ for key, value in extra_kwargs.items():
526
+ self.write(f"{key!r}: {value}, ")
527
+ if node.dyn_kwargs is not None:
528
+ self.write("}, **")
529
+ self.visit(node.dyn_kwargs, frame)
530
+ self.write(")")
531
+ else:
532
+ self.write("}")
533
+
534
+ elif node.dyn_kwargs is not None:
535
+ self.write(", **")
536
+ self.visit(node.dyn_kwargs, frame)
537
+
538
+ def pull_dependencies(self, nodes: t.Iterable[nodes.Node]) -> None:
539
+ """Find all filter and test names used in the template and
540
+ assign them to variables in the compiled namespace. Checking
541
+ that the names are registered with the environment is done when
542
+ compiling the Filter and Test nodes. If the node is in an If or
543
+ CondExpr node, the check is done at runtime instead.
544
+
545
+ .. versionchanged:: 3.0
546
+ Filters and tests in If and CondExpr nodes are checked at
547
+ runtime instead of compile time.
548
+ """
549
+ visitor = DependencyFinderVisitor()
550
+
551
+ for node in nodes:
552
+ visitor.visit(node)
553
+
554
+ for id_map, names, dependency in (self.filters, visitor.filters, "filters"), (
555
+ self.tests,
556
+ visitor.tests,
557
+ "tests",
558
+ ):
559
+ for name in sorted(names):
560
+ if name not in id_map:
561
+ id_map[name] = self.temporary_identifier()
562
+
563
+ # add check during runtime that dependencies used inside of executed
564
+ # blocks are defined, as this step may be skipped during compile time
565
+ self.writeline("try:")
566
+ self.indent()
567
+ self.writeline(f"{id_map[name]} = environment.{dependency}[{name!r}]")
568
+ self.outdent()
569
+ self.writeline("except KeyError:")
570
+ self.indent()
571
+ self.writeline("@internalcode")
572
+ self.writeline(f"def {id_map[name]}(*unused):")
573
+ self.indent()
574
+ self.writeline(
575
+ f'raise TemplateRuntimeError("No {dependency[:-1]}'
576
+ f' named {name!r} found.")'
577
+ )
578
+ self.outdent()
579
+ self.outdent()
580
+
581
+ def enter_frame(self, frame: Frame) -> None:
582
+ undefs = []
583
+ for target, (action, param) in frame.symbols.loads.items():
584
+ if action == VAR_LOAD_PARAMETER:
585
+ pass
586
+ elif action == VAR_LOAD_RESOLVE:
587
+ self.writeline(f"{target} = {self.get_resolve_func()}({param!r})")
588
+ elif action == VAR_LOAD_ALIAS:
589
+ self.writeline(f"{target} = {param}")
590
+ elif action == VAR_LOAD_UNDEFINED:
591
+ undefs.append(target)
592
+ else:
593
+ raise NotImplementedError("unknown load instruction")
594
+ if undefs:
595
+ self.writeline(f"{' = '.join(undefs)} = missing")
596
+
597
+ def leave_frame(self, frame: Frame, with_python_scope: bool = False) -> None:
598
+ if not with_python_scope:
599
+ undefs = []
600
+ for target in frame.symbols.loads:
601
+ undefs.append(target)
602
+ if undefs:
603
+ self.writeline(f"{' = '.join(undefs)} = missing")
604
+
605
+ def choose_async(self, async_value: str = "async ", sync_value: str = "") -> str:
606
+ return async_value if self.environment.is_async else sync_value
607
+
608
+ def func(self, name: str) -> str:
609
+ return f"{self.choose_async()}def {name}"
610
+
611
+ def macro_body(
612
+ self, node: t.Union[nodes.Macro, nodes.CallBlock], frame: Frame
613
+ ) -> t.Tuple[Frame, MacroRef]:
614
+ """Dump the function def of a macro or call block."""
615
+ frame = frame.inner()
616
+ frame.symbols.analyze_node(node)
617
+ macro_ref = MacroRef(node)
618
+
619
+ explicit_caller = None
620
+ skip_special_params = set()
621
+ args = []
622
+
623
+ for idx, arg in enumerate(node.args):
624
+ if arg.name == "caller":
625
+ explicit_caller = idx
626
+ if arg.name in ("kwargs", "varargs"):
627
+ skip_special_params.add(arg.name)
628
+ args.append(frame.symbols.ref(arg.name))
629
+
630
+ undeclared = find_undeclared(node.body, ("caller", "kwargs", "varargs"))
631
+
632
+ if "caller" in undeclared:
633
+ # In older Jinja versions there was a bug that allowed caller
634
+ # to retain the special behavior even if it was mentioned in
635
+ # the argument list. However thankfully this was only really
636
+ # working if it was the last argument. So we are explicitly
637
+ # checking this now and error out if it is anywhere else in
638
+ # the argument list.
639
+ if explicit_caller is not None:
640
+ try:
641
+ node.defaults[explicit_caller - len(node.args)]
642
+ except IndexError:
643
+ self.fail(
644
+ "When defining macros or call blocks the "
645
+ 'special "caller" argument must be omitted '
646
+ "or be given a default.",
647
+ node.lineno,
648
+ )
649
+ else:
650
+ args.append(frame.symbols.declare_parameter("caller"))
651
+ macro_ref.accesses_caller = True
652
+ if "kwargs" in undeclared and "kwargs" not in skip_special_params:
653
+ args.append(frame.symbols.declare_parameter("kwargs"))
654
+ macro_ref.accesses_kwargs = True
655
+ if "varargs" in undeclared and "varargs" not in skip_special_params:
656
+ args.append(frame.symbols.declare_parameter("varargs"))
657
+ macro_ref.accesses_varargs = True
658
+
659
+ # macros are delayed, they never require output checks
660
+ frame.require_output_check = False
661
+ frame.symbols.analyze_node(node)
662
+ self.writeline(f"{self.func('macro')}({', '.join(args)}):", node)
663
+ self.indent()
664
+
665
+ self.buffer(frame)
666
+ self.enter_frame(frame)
667
+
668
+ self.push_parameter_definitions(frame)
669
+ for idx, arg in enumerate(node.args):
670
+ ref = frame.symbols.ref(arg.name)
671
+ self.writeline(f"if {ref} is missing:")
672
+ self.indent()
673
+ try:
674
+ default = node.defaults[idx - len(node.args)]
675
+ except IndexError:
676
+ self.writeline(
677
+ f'{ref} = undefined("parameter {arg.name!r} was not provided",'
678
+ f" name={arg.name!r})"
679
+ )
680
+ else:
681
+ self.writeline(f"{ref} = ")
682
+ self.visit(default, frame)
683
+ self.mark_parameter_stored(ref)
684
+ self.outdent()
685
+ self.pop_parameter_definitions()
686
+
687
+ self.blockvisit(node.body, frame)
688
+ self.return_buffer_contents(frame, force_unescaped=True)
689
+ self.leave_frame(frame, with_python_scope=True)
690
+ self.outdent()
691
+
692
+ return frame, macro_ref
693
+
694
+ def macro_def(self, macro_ref: MacroRef, frame: Frame) -> None:
695
+ """Dump the macro definition for the def created by macro_body."""
696
+ arg_tuple = ", ".join(repr(x.name) for x in macro_ref.node.args)
697
+ name = getattr(macro_ref.node, "name", None)
698
+ if len(macro_ref.node.args) == 1:
699
+ arg_tuple += ","
700
+ self.write(
701
+ f"Macro(environment, macro, {name!r}, ({arg_tuple}),"
702
+ f" {macro_ref.accesses_kwargs!r}, {macro_ref.accesses_varargs!r},"
703
+ f" {macro_ref.accesses_caller!r}, context.eval_ctx.autoescape)"
704
+ )
705
+
706
+ def position(self, node: nodes.Node) -> str:
707
+ """Return a human readable position for the node."""
708
+ rv = f"line {node.lineno}"
709
+ if self.name is not None:
710
+ rv = f"{rv} in {self.name!r}"
711
+ return rv
712
+
713
+ def dump_local_context(self, frame: Frame) -> str:
714
+ items_kv = ", ".join(
715
+ f"{name!r}: {target}"
716
+ for name, target in frame.symbols.dump_stores().items()
717
+ )
718
+ return f"{{{items_kv}}}"
719
+
720
+ def write_commons(self) -> None:
721
+ """Writes a common preamble that is used by root and block functions.
722
+ Primarily this sets up common local helpers and enforces a generator
723
+ through a dead branch.
724
+ """
725
+ self.writeline("resolve = context.resolve_or_missing")
726
+ self.writeline("undefined = environment.undefined")
727
+ self.writeline("concat = environment.concat")
728
+ # always use the standard Undefined class for the implicit else of
729
+ # conditional expressions
730
+ self.writeline("cond_expr_undefined = Undefined")
731
+ self.writeline("if 0: yield None")
732
+
733
+ def push_parameter_definitions(self, frame: Frame) -> None:
734
+ """Pushes all parameter targets from the given frame into a local
735
+ stack that permits tracking of yet to be assigned parameters. In
736
+ particular this enables the optimization from `visit_Name` to skip
737
+ undefined expressions for parameters in macros as macros can reference
738
+ otherwise unbound parameters.
739
+ """
740
+ self._param_def_block.append(frame.symbols.dump_param_targets())
741
+
742
+ def pop_parameter_definitions(self) -> None:
743
+ """Pops the current parameter definitions set."""
744
+ self._param_def_block.pop()
745
+
746
+ def mark_parameter_stored(self, target: str) -> None:
747
+ """Marks a parameter in the current parameter definitions as stored.
748
+ This will skip the enforced undefined checks.
749
+ """
750
+ if self._param_def_block:
751
+ self._param_def_block[-1].discard(target)
752
+
753
+ def push_context_reference(self, target: str) -> None:
754
+ self._context_reference_stack.append(target)
755
+
756
+ def pop_context_reference(self) -> None:
757
+ self._context_reference_stack.pop()
758
+
759
+ def get_context_ref(self) -> str:
760
+ return self._context_reference_stack[-1]
761
+
762
+ def get_resolve_func(self) -> str:
763
+ target = self._context_reference_stack[-1]
764
+ if target == "context":
765
+ return "resolve"
766
+ return f"{target}.resolve"
767
+
768
+ def derive_context(self, frame: Frame) -> str:
769
+ return f"{self.get_context_ref()}.derived({self.dump_local_context(frame)})"
770
+
771
+ def parameter_is_undeclared(self, target: str) -> bool:
772
+ """Checks if a given target is an undeclared parameter."""
773
+ if not self._param_def_block:
774
+ return False
775
+ return target in self._param_def_block[-1]
776
+
777
+ def push_assign_tracking(self) -> None:
778
+ """Pushes a new layer for assignment tracking."""
779
+ self._assign_stack.append(set())
780
+
781
+ def pop_assign_tracking(self, frame: Frame) -> None:
782
+ """Pops the topmost level for assignment tracking and updates the
783
+ context variables if necessary.
784
+ """
785
+ vars = self._assign_stack.pop()
786
+ if (
787
+ not frame.block_frame
788
+ and not frame.loop_frame
789
+ and not frame.toplevel
790
+ or not vars
791
+ ):
792
+ return
793
+ public_names = [x for x in vars if x[:1] != "_"]
794
+ if len(vars) == 1:
795
+ name = next(iter(vars))
796
+ ref = frame.symbols.ref(name)
797
+ if frame.loop_frame:
798
+ self.writeline(f"_loop_vars[{name!r}] = {ref}")
799
+ return
800
+ if frame.block_frame:
801
+ self.writeline(f"_block_vars[{name!r}] = {ref}")
802
+ return
803
+ self.writeline(f"context.vars[{name!r}] = {ref}")
804
+ else:
805
+ if frame.loop_frame:
806
+ self.writeline("_loop_vars.update({")
807
+ elif frame.block_frame:
808
+ self.writeline("_block_vars.update({")
809
+ else:
810
+ self.writeline("context.vars.update({")
811
+ for idx, name in enumerate(vars):
812
+ if idx:
813
+ self.write(", ")
814
+ ref = frame.symbols.ref(name)
815
+ self.write(f"{name!r}: {ref}")
816
+ self.write("})")
817
+ if not frame.block_frame and not frame.loop_frame and public_names:
818
+ if len(public_names) == 1:
819
+ self.writeline(f"context.exported_vars.add({public_names[0]!r})")
820
+ else:
821
+ names_str = ", ".join(map(repr, public_names))
822
+ self.writeline(f"context.exported_vars.update(({names_str}))")
823
+
824
+ # -- Statement Visitors
825
+
826
+ def visit_Template(
827
+ self, node: nodes.Template, frame: t.Optional[Frame] = None
828
+ ) -> None:
829
+ assert frame is None, "no root frame allowed"
830
+ eval_ctx = EvalContext(self.environment, self.name)
831
+
832
+ from .runtime import exported, async_exported
833
+
834
+ if self.environment.is_async:
835
+ exported_names = sorted(exported + async_exported)
836
+ else:
837
+ exported_names = sorted(exported)
838
+
839
+ self.writeline("from jinja2.runtime import " + ", ".join(exported_names))
840
+
841
+ # if we want a deferred initialization we cannot move the
842
+ # environment into a local name
843
+ envenv = "" if self.defer_init else ", environment=environment"
844
+
845
+ # do we have an extends tag at all? If not, we can save some
846
+ # overhead by just not processing any inheritance code.
847
+ have_extends = node.find(nodes.Extends) is not None
848
+
849
+ # find all blocks
850
+ for block in node.find_all(nodes.Block):
851
+ if block.name in self.blocks:
852
+ self.fail(f"block {block.name!r} defined twice", block.lineno)
853
+ self.blocks[block.name] = block
854
+
855
+ # find all imports and import them
856
+ for import_ in node.find_all(nodes.ImportedName):
857
+ if import_.importname not in self.import_aliases:
858
+ imp = import_.importname
859
+ self.import_aliases[imp] = alias = self.temporary_identifier()
860
+ if "." in imp:
861
+ module, obj = imp.rsplit(".", 1)
862
+ self.writeline(f"from {module} import {obj} as {alias}")
863
+ else:
864
+ self.writeline(f"import {imp} as {alias}")
865
+
866
+ # add the load name
867
+ self.writeline(f"name = {self.name!r}")
868
+
869
+ # generate the root render function.
870
+ self.writeline(
871
+ f"{self.func('root')}(context, missing=missing{envenv}):", extra=1
872
+ )
873
+ self.indent()
874
+ self.write_commons()
875
+
876
+ # process the root
877
+ frame = Frame(eval_ctx)
878
+ if "self" in find_undeclared(node.body, ("self",)):
879
+ ref = frame.symbols.declare_parameter("self")
880
+ self.writeline(f"{ref} = TemplateReference(context)")
881
+ frame.symbols.analyze_node(node)
882
+ frame.toplevel = frame.rootlevel = True
883
+ frame.require_output_check = have_extends and not self.has_known_extends
884
+ if have_extends:
885
+ self.writeline("parent_template = None")
886
+ self.enter_frame(frame)
887
+ self.pull_dependencies(node.body)
888
+ self.blockvisit(node.body, frame)
889
+ self.leave_frame(frame, with_python_scope=True)
890
+ self.outdent()
891
+
892
+ # make sure that the parent root is called.
893
+ if have_extends:
894
+ if not self.has_known_extends:
895
+ self.indent()
896
+ self.writeline("if parent_template is not None:")
897
+ self.indent()
898
+ if not self.environment.is_async:
899
+ self.writeline("yield from parent_template.root_render_func(context)")
900
+ else:
901
+ self.writeline(
902
+ "async for event in parent_template.root_render_func(context):"
903
+ )
904
+ self.indent()
905
+ self.writeline("yield event")
906
+ self.outdent()
907
+ self.outdent(1 + (not self.has_known_extends))
908
+
909
+ # at this point we now have the blocks collected and can visit them too.
910
+ for name, block in self.blocks.items():
911
+ self.writeline(
912
+ f"{self.func('block_' + name)}(context, missing=missing{envenv}):",
913
+ block,
914
+ 1,
915
+ )
916
+ self.indent()
917
+ self.write_commons()
918
+ # It's important that we do not make this frame a child of the
919
+ # toplevel template. This would cause a variety of
920
+ # interesting issues with identifier tracking.
921
+ block_frame = Frame(eval_ctx)
922
+ block_frame.block_frame = True
923
+ undeclared = find_undeclared(block.body, ("self", "super"))
924
+ if "self" in undeclared:
925
+ ref = block_frame.symbols.declare_parameter("self")
926
+ self.writeline(f"{ref} = TemplateReference(context)")
927
+ if "super" in undeclared:
928
+ ref = block_frame.symbols.declare_parameter("super")
929
+ self.writeline(f"{ref} = context.super({name!r}, block_{name})")
930
+ block_frame.symbols.analyze_node(block)
931
+ block_frame.block = name
932
+ self.writeline("_block_vars = {}")
933
+ self.enter_frame(block_frame)
934
+ self.pull_dependencies(block.body)
935
+ self.blockvisit(block.body, block_frame)
936
+ self.leave_frame(block_frame, with_python_scope=True)
937
+ self.outdent()
938
+
939
+ blocks_kv_str = ", ".join(f"{x!r}: block_{x}" for x in self.blocks)
940
+ self.writeline(f"blocks = {{{blocks_kv_str}}}", extra=1)
941
+ debug_kv_str = "&".join(f"{k}={v}" for k, v in self.debug_info)
942
+ self.writeline(f"debug_info = {debug_kv_str!r}")
943
+
944
+ def visit_Block(self, node: nodes.Block, frame: Frame) -> None:
945
+ """Call a block and register it for the template."""
946
+ level = 0
947
+ if frame.toplevel:
948
+ # if we know that we are a child template, there is no need to
949
+ # check if we are one
950
+ if self.has_known_extends:
951
+ return
952
+ if self.extends_so_far > 0:
953
+ self.writeline("if parent_template is None:")
954
+ self.indent()
955
+ level += 1
956
+
957
+ if node.scoped:
958
+ context = self.derive_context(frame)
959
+ else:
960
+ context = self.get_context_ref()
961
+
962
+ if node.required:
963
+ self.writeline(f"if len(context.blocks[{node.name!r}]) <= 1:", node)
964
+ self.indent()
965
+ self.writeline(
966
+ f'raise TemplateRuntimeError("Required block {node.name!r} not found")',
967
+ node,
968
+ )
969
+ self.outdent()
970
+
971
+ if not self.environment.is_async and frame.buffer is None:
972
+ self.writeline(
973
+ f"yield from context.blocks[{node.name!r}][0]({context})", node
974
+ )
975
+ else:
976
+ self.writeline(
977
+ f"{self.choose_async()}for event in"
978
+ f" context.blocks[{node.name!r}][0]({context}):",
979
+ node,
980
+ )
981
+ self.indent()
982
+ self.simple_write("event", frame)
983
+ self.outdent()
984
+
985
+ self.outdent(level)
986
+
987
+ def visit_Extends(self, node: nodes.Extends, frame: Frame) -> None:
988
+ """Calls the extender."""
989
+ if not frame.toplevel:
990
+ self.fail("cannot use extend from a non top-level scope", node.lineno)
991
+
992
+ # if the number of extends statements in general is zero so
993
+ # far, we don't have to add a check if something extended
994
+ # the template before this one.
995
+ if self.extends_so_far > 0:
996
+
997
+ # if we have a known extends we just add a template runtime
998
+ # error into the generated code. We could catch that at compile
999
+ # time too, but i welcome it not to confuse users by throwing the
1000
+ # same error at different times just "because we can".
1001
+ if not self.has_known_extends:
1002
+ self.writeline("if parent_template is not None:")
1003
+ self.indent()
1004
+ self.writeline('raise TemplateRuntimeError("extended multiple times")')
1005
+
1006
+ # if we have a known extends already we don't need that code here
1007
+ # as we know that the template execution will end here.
1008
+ if self.has_known_extends:
1009
+ raise CompilerExit()
1010
+ else:
1011
+ self.outdent()
1012
+
1013
+ self.writeline("parent_template = environment.get_template(", node)
1014
+ self.visit(node.template, frame)
1015
+ self.write(f", {self.name!r})")
1016
+ self.writeline("for name, parent_block in parent_template.blocks.items():")
1017
+ self.indent()
1018
+ self.writeline("context.blocks.setdefault(name, []).append(parent_block)")
1019
+ self.outdent()
1020
+
1021
+ # if this extends statement was in the root level we can take
1022
+ # advantage of that information and simplify the generated code
1023
+ # in the top level from this point onwards
1024
+ if frame.rootlevel:
1025
+ self.has_known_extends = True
1026
+
1027
+ # and now we have one more
1028
+ self.extends_so_far += 1
1029
+
1030
+ def visit_Include(self, node: nodes.Include, frame: Frame) -> None:
1031
+ """Handles includes."""
1032
+ if node.ignore_missing:
1033
+ self.writeline("try:")
1034
+ self.indent()
1035
+
1036
+ func_name = "get_or_select_template"
1037
+ if isinstance(node.template, nodes.Const):
1038
+ if isinstance(node.template.value, str):
1039
+ func_name = "get_template"
1040
+ elif isinstance(node.template.value, (tuple, list)):
1041
+ func_name = "select_template"
1042
+ elif isinstance(node.template, (nodes.Tuple, nodes.List)):
1043
+ func_name = "select_template"
1044
+
1045
+ self.writeline(f"template = environment.{func_name}(", node)
1046
+ self.visit(node.template, frame)
1047
+ self.write(f", {self.name!r})")
1048
+ if node.ignore_missing:
1049
+ self.outdent()
1050
+ self.writeline("except TemplateNotFound:")
1051
+ self.indent()
1052
+ self.writeline("pass")
1053
+ self.outdent()
1054
+ self.writeline("else:")
1055
+ self.indent()
1056
+
1057
+ skip_event_yield = False
1058
+ if node.with_context:
1059
+ self.writeline(
1060
+ f"{self.choose_async()}for event in template.root_render_func("
1061
+ "template.new_context(context.get_all(), True,"
1062
+ f" {self.dump_local_context(frame)})):"
1063
+ )
1064
+ elif self.environment.is_async:
1065
+ self.writeline(
1066
+ "for event in (await template._get_default_module_async())"
1067
+ "._body_stream:"
1068
+ )
1069
+ else:
1070
+ self.writeline("yield from template._get_default_module()._body_stream")
1071
+ skip_event_yield = True
1072
+
1073
+ if not skip_event_yield:
1074
+ self.indent()
1075
+ self.simple_write("event", frame)
1076
+ self.outdent()
1077
+
1078
+ if node.ignore_missing:
1079
+ self.outdent()
1080
+
1081
+ def _import_common(
1082
+ self, node: t.Union[nodes.Import, nodes.FromImport], frame: Frame
1083
+ ) -> None:
1084
+ self.write(f"{self.choose_async('await ')}environment.get_template(")
1085
+ self.visit(node.template, frame)
1086
+ self.write(f", {self.name!r}).")
1087
+
1088
+ if node.with_context:
1089
+ f_name = f"make_module{self.choose_async('_async')}"
1090
+ self.write(
1091
+ f"{f_name}(context.get_all(), True, {self.dump_local_context(frame)})"
1092
+ )
1093
+ else:
1094
+ self.write(f"_get_default_module{self.choose_async('_async')}(context)")
1095
+
1096
+ def visit_Import(self, node: nodes.Import, frame: Frame) -> None:
1097
+ """Visit regular imports."""
1098
+ self.writeline(f"{frame.symbols.ref(node.target)} = ", node)
1099
+ if frame.toplevel:
1100
+ self.write(f"context.vars[{node.target!r}] = ")
1101
+
1102
+ self._import_common(node, frame)
1103
+
1104
+ if frame.toplevel and not node.target.startswith("_"):
1105
+ self.writeline(f"context.exported_vars.discard({node.target!r})")
1106
+
1107
+ def visit_FromImport(self, node: nodes.FromImport, frame: Frame) -> None:
1108
+ """Visit named imports."""
1109
+ self.newline(node)
1110
+ self.write("included_template = ")
1111
+ self._import_common(node, frame)
1112
+ var_names = []
1113
+ discarded_names = []
1114
+ for name in node.names:
1115
+ if isinstance(name, tuple):
1116
+ name, alias = name
1117
+ else:
1118
+ alias = name
1119
+ self.writeline(
1120
+ f"{frame.symbols.ref(alias)} ="
1121
+ f" getattr(included_template, {name!r}, missing)"
1122
+ )
1123
+ self.writeline(f"if {frame.symbols.ref(alias)} is missing:")
1124
+ self.indent()
1125
+ message = (
1126
+ "the template {included_template.__name__!r}"
1127
+ f" (imported on {self.position(node)})"
1128
+ f" does not export the requested name {name!r}"
1129
+ )
1130
+ self.writeline(
1131
+ f"{frame.symbols.ref(alias)} = undefined(f{message!r}, name={name!r})"
1132
+ )
1133
+ self.outdent()
1134
+ if frame.toplevel:
1135
+ var_names.append(alias)
1136
+ if not alias.startswith("_"):
1137
+ discarded_names.append(alias)
1138
+
1139
+ if var_names:
1140
+ if len(var_names) == 1:
1141
+ name = var_names[0]
1142
+ self.writeline(f"context.vars[{name!r}] = {frame.symbols.ref(name)}")
1143
+ else:
1144
+ names_kv = ", ".join(
1145
+ f"{name!r}: {frame.symbols.ref(name)}" for name in var_names
1146
+ )
1147
+ self.writeline(f"context.vars.update({{{names_kv}}})")
1148
+ if discarded_names:
1149
+ if len(discarded_names) == 1:
1150
+ self.writeline(f"context.exported_vars.discard({discarded_names[0]!r})")
1151
+ else:
1152
+ names_str = ", ".join(map(repr, discarded_names))
1153
+ self.writeline(
1154
+ f"context.exported_vars.difference_update(({names_str}))"
1155
+ )
1156
+
1157
+ def visit_For(self, node: nodes.For, frame: Frame) -> None:
1158
+ loop_frame = frame.inner()
1159
+ loop_frame.loop_frame = True
1160
+ test_frame = frame.inner()
1161
+ else_frame = frame.inner()
1162
+
1163
+ # try to figure out if we have an extended loop. An extended loop
1164
+ # is necessary if the loop is in recursive mode if the special loop
1165
+ # variable is accessed in the body if the body is a scoped block.
1166
+ extended_loop = (
1167
+ node.recursive
1168
+ or "loop"
1169
+ in find_undeclared(node.iter_child_nodes(only=("body",)), ("loop",))
1170
+ or any(block.scoped for block in node.find_all(nodes.Block))
1171
+ )
1172
+
1173
+ loop_ref = None
1174
+ if extended_loop:
1175
+ loop_ref = loop_frame.symbols.declare_parameter("loop")
1176
+
1177
+ loop_frame.symbols.analyze_node(node, for_branch="body")
1178
+ if node.else_:
1179
+ else_frame.symbols.analyze_node(node, for_branch="else")
1180
+
1181
+ if node.test:
1182
+ loop_filter_func = self.temporary_identifier()
1183
+ test_frame.symbols.analyze_node(node, for_branch="test")
1184
+ self.writeline(f"{self.func(loop_filter_func)}(fiter):", node.test)
1185
+ self.indent()
1186
+ self.enter_frame(test_frame)
1187
+ self.writeline(self.choose_async("async for ", "for "))
1188
+ self.visit(node.target, loop_frame)
1189
+ self.write(" in ")
1190
+ self.write(self.choose_async("auto_aiter(fiter)", "fiter"))
1191
+ self.write(":")
1192
+ self.indent()
1193
+ self.writeline("if ", node.test)
1194
+ self.visit(node.test, test_frame)
1195
+ self.write(":")
1196
+ self.indent()
1197
+ self.writeline("yield ")
1198
+ self.visit(node.target, loop_frame)
1199
+ self.outdent(3)
1200
+ self.leave_frame(test_frame, with_python_scope=True)
1201
+
1202
+ # if we don't have an recursive loop we have to find the shadowed
1203
+ # variables at that point. Because loops can be nested but the loop
1204
+ # variable is a special one we have to enforce aliasing for it.
1205
+ if node.recursive:
1206
+ self.writeline(
1207
+ f"{self.func('loop')}(reciter, loop_render_func, depth=0):", node
1208
+ )
1209
+ self.indent()
1210
+ self.buffer(loop_frame)
1211
+
1212
+ # Use the same buffer for the else frame
1213
+ else_frame.buffer = loop_frame.buffer
1214
+
1215
+ # make sure the loop variable is a special one and raise a template
1216
+ # assertion error if a loop tries to write to loop
1217
+ if extended_loop:
1218
+ self.writeline(f"{loop_ref} = missing")
1219
+
1220
+ for name in node.find_all(nodes.Name):
1221
+ if name.ctx == "store" and name.name == "loop":
1222
+ self.fail(
1223
+ "Can't assign to special loop variable in for-loop target",
1224
+ name.lineno,
1225
+ )
1226
+
1227
+ if node.else_:
1228
+ iteration_indicator = self.temporary_identifier()
1229
+ self.writeline(f"{iteration_indicator} = 1")
1230
+
1231
+ self.writeline(self.choose_async("async for ", "for "), node)
1232
+ self.visit(node.target, loop_frame)
1233
+ if extended_loop:
1234
+ self.write(f", {loop_ref} in {self.choose_async('Async')}LoopContext(")
1235
+ else:
1236
+ self.write(" in ")
1237
+
1238
+ if node.test:
1239
+ self.write(f"{loop_filter_func}(")
1240
+ if node.recursive:
1241
+ self.write("reciter")
1242
+ else:
1243
+ if self.environment.is_async and not extended_loop:
1244
+ self.write("auto_aiter(")
1245
+ self.visit(node.iter, frame)
1246
+ if self.environment.is_async and not extended_loop:
1247
+ self.write(")")
1248
+ if node.test:
1249
+ self.write(")")
1250
+
1251
+ if node.recursive:
1252
+ self.write(", undefined, loop_render_func, depth):")
1253
+ else:
1254
+ self.write(", undefined):" if extended_loop else ":")
1255
+
1256
+ self.indent()
1257
+ self.enter_frame(loop_frame)
1258
+
1259
+ self.writeline("_loop_vars = {}")
1260
+ self.blockvisit(node.body, loop_frame)
1261
+ if node.else_:
1262
+ self.writeline(f"{iteration_indicator} = 0")
1263
+ self.outdent()
1264
+ self.leave_frame(
1265
+ loop_frame, with_python_scope=node.recursive and not node.else_
1266
+ )
1267
+
1268
+ if node.else_:
1269
+ self.writeline(f"if {iteration_indicator}:")
1270
+ self.indent()
1271
+ self.enter_frame(else_frame)
1272
+ self.blockvisit(node.else_, else_frame)
1273
+ self.leave_frame(else_frame)
1274
+ self.outdent()
1275
+
1276
+ # if the node was recursive we have to return the buffer contents
1277
+ # and start the iteration code
1278
+ if node.recursive:
1279
+ self.return_buffer_contents(loop_frame)
1280
+ self.outdent()
1281
+ self.start_write(frame, node)
1282
+ self.write(f"{self.choose_async('await ')}loop(")
1283
+ if self.environment.is_async:
1284
+ self.write("auto_aiter(")
1285
+ self.visit(node.iter, frame)
1286
+ if self.environment.is_async:
1287
+ self.write(")")
1288
+ self.write(", loop)")
1289
+ self.end_write(frame)
1290
+
1291
+ # at the end of the iteration, clear any assignments made in the
1292
+ # loop from the top level
1293
+ if self._assign_stack:
1294
+ self._assign_stack[-1].difference_update(loop_frame.symbols.stores)
1295
+
1296
+ def visit_If(self, node: nodes.If, frame: Frame) -> None:
1297
+ if_frame = frame.soft()
1298
+ self.writeline("if ", node)
1299
+ self.visit(node.test, if_frame)
1300
+ self.write(":")
1301
+ self.indent()
1302
+ self.blockvisit(node.body, if_frame)
1303
+ self.outdent()
1304
+ for elif_ in node.elif_:
1305
+ self.writeline("elif ", elif_)
1306
+ self.visit(elif_.test, if_frame)
1307
+ self.write(":")
1308
+ self.indent()
1309
+ self.blockvisit(elif_.body, if_frame)
1310
+ self.outdent()
1311
+ if node.else_:
1312
+ self.writeline("else:")
1313
+ self.indent()
1314
+ self.blockvisit(node.else_, if_frame)
1315
+ self.outdent()
1316
+
1317
+ def visit_Macro(self, node: nodes.Macro, frame: Frame) -> None:
1318
+ macro_frame, macro_ref = self.macro_body(node, frame)
1319
+ self.newline()
1320
+ if frame.toplevel:
1321
+ if not node.name.startswith("_"):
1322
+ self.write(f"context.exported_vars.add({node.name!r})")
1323
+ self.writeline(f"context.vars[{node.name!r}] = ")
1324
+ self.write(f"{frame.symbols.ref(node.name)} = ")
1325
+ self.macro_def(macro_ref, macro_frame)
1326
+
1327
+ def visit_CallBlock(self, node: nodes.CallBlock, frame: Frame) -> None:
1328
+ call_frame, macro_ref = self.macro_body(node, frame)
1329
+ self.writeline("caller = ")
1330
+ self.macro_def(macro_ref, call_frame)
1331
+ self.start_write(frame, node)
1332
+ self.visit_Call(node.call, frame, forward_caller=True)
1333
+ self.end_write(frame)
1334
+
1335
+ def visit_FilterBlock(self, node: nodes.FilterBlock, frame: Frame) -> None:
1336
+ filter_frame = frame.inner()
1337
+ filter_frame.symbols.analyze_node(node)
1338
+ self.enter_frame(filter_frame)
1339
+ self.buffer(filter_frame)
1340
+ self.blockvisit(node.body, filter_frame)
1341
+ self.start_write(frame, node)
1342
+ self.visit_Filter(node.filter, filter_frame)
1343
+ self.end_write(frame)
1344
+ self.leave_frame(filter_frame)
1345
+
1346
+ def visit_With(self, node: nodes.With, frame: Frame) -> None:
1347
+ with_frame = frame.inner()
1348
+ with_frame.symbols.analyze_node(node)
1349
+ self.enter_frame(with_frame)
1350
+ for target, expr in zip(node.targets, node.values):
1351
+ self.newline()
1352
+ self.visit(target, with_frame)
1353
+ self.write(" = ")
1354
+ self.visit(expr, frame)
1355
+ self.blockvisit(node.body, with_frame)
1356
+ self.leave_frame(with_frame)
1357
+
1358
+ def visit_ExprStmt(self, node: nodes.ExprStmt, frame: Frame) -> None:
1359
+ self.newline(node)
1360
+ self.visit(node.node, frame)
1361
+
1362
+ class _FinalizeInfo(t.NamedTuple):
1363
+ const: t.Optional[t.Callable[..., str]]
1364
+ src: t.Optional[str]
1365
+
1366
+ @staticmethod
1367
+ def _default_finalize(value: t.Any) -> t.Any:
1368
+ """The default finalize function if the environment isn't
1369
+ configured with one. Or, if the environment has one, this is
1370
+ called on that function's output for constants.
1371
+ """
1372
+ return str(value)
1373
+
1374
+ _finalize: t.Optional[_FinalizeInfo] = None
1375
+
1376
+ def _make_finalize(self) -> _FinalizeInfo:
1377
+ """Build the finalize function to be used on constants and at
1378
+ runtime. Cached so it's only created once for all output nodes.
1379
+
1380
+ Returns a ``namedtuple`` with the following attributes:
1381
+
1382
+ ``const``
1383
+ A function to finalize constant data at compile time.
1384
+
1385
+ ``src``
1386
+ Source code to output around nodes to be evaluated at
1387
+ runtime.
1388
+ """
1389
+ if self._finalize is not None:
1390
+ return self._finalize
1391
+
1392
+ finalize: t.Optional[t.Callable[..., t.Any]]
1393
+ finalize = default = self._default_finalize
1394
+ src = None
1395
+
1396
+ if self.environment.finalize:
1397
+ src = "environment.finalize("
1398
+ env_finalize = self.environment.finalize
1399
+ pass_arg = {
1400
+ _PassArg.context: "context",
1401
+ _PassArg.eval_context: "context.eval_ctx",
1402
+ _PassArg.environment: "environment",
1403
+ }.get(
1404
+ _PassArg.from_obj(env_finalize) # type: ignore
1405
+ )
1406
+ finalize = None
1407
+
1408
+ if pass_arg is None:
1409
+
1410
+ def finalize(value: t.Any) -> t.Any:
1411
+ return default(env_finalize(value))
1412
+
1413
+ else:
1414
+ src = f"{src}{pass_arg}, "
1415
+
1416
+ if pass_arg == "environment":
1417
+
1418
+ def finalize(value: t.Any) -> t.Any:
1419
+ return default(env_finalize(self.environment, value))
1420
+
1421
+ self._finalize = self._FinalizeInfo(finalize, src)
1422
+ return self._finalize
1423
+
1424
+ def _output_const_repr(self, group: t.Iterable[t.Any]) -> str:
1425
+ """Given a group of constant values converted from ``Output``
1426
+ child nodes, produce a string to write to the template module
1427
+ source.
1428
+ """
1429
+ return repr(concat(group))
1430
+
1431
+ def _output_child_to_const(
1432
+ self, node: nodes.Expr, frame: Frame, finalize: _FinalizeInfo
1433
+ ) -> str:
1434
+ """Try to optimize a child of an ``Output`` node by trying to
1435
+ convert it to constant, finalized data at compile time.
1436
+
1437
+ If :exc:`Impossible` is raised, the node is not constant and
1438
+ will be evaluated at runtime. Any other exception will also be
1439
+ evaluated at runtime for easier debugging.
1440
+ """
1441
+ const = node.as_const(frame.eval_ctx)
1442
+
1443
+ if frame.eval_ctx.autoescape:
1444
+ const = escape(const)
1445
+
1446
+ # Template data doesn't go through finalize.
1447
+ if isinstance(node, nodes.TemplateData):
1448
+ return str(const)
1449
+
1450
+ return finalize.const(const) # type: ignore
1451
+
1452
+ def _output_child_pre(
1453
+ self, node: nodes.Expr, frame: Frame, finalize: _FinalizeInfo
1454
+ ) -> None:
1455
+ """Output extra source code before visiting a child of an
1456
+ ``Output`` node.
1457
+ """
1458
+ if frame.eval_ctx.volatile:
1459
+ self.write("(escape if context.eval_ctx.autoescape else str)(")
1460
+ elif frame.eval_ctx.autoescape:
1461
+ self.write("escape(")
1462
+ else:
1463
+ self.write("str(")
1464
+
1465
+ if finalize.src is not None:
1466
+ self.write(finalize.src)
1467
+
1468
+ def _output_child_post(
1469
+ self, node: nodes.Expr, frame: Frame, finalize: _FinalizeInfo
1470
+ ) -> None:
1471
+ """Output extra source code after visiting a child of an
1472
+ ``Output`` node.
1473
+ """
1474
+ self.write(")")
1475
+
1476
+ if finalize.src is not None:
1477
+ self.write(")")
1478
+
1479
+ def visit_Output(self, node: nodes.Output, frame: Frame) -> None:
1480
+ # If an extends is active, don't render outside a block.
1481
+ if frame.require_output_check:
1482
+ # A top-level extends is known to exist at compile time.
1483
+ if self.has_known_extends:
1484
+ return
1485
+
1486
+ self.writeline("if parent_template is None:")
1487
+ self.indent()
1488
+
1489
+ finalize = self._make_finalize()
1490
+ body: t.List[t.Union[t.List[t.Any], nodes.Expr]] = []
1491
+
1492
+ # Evaluate constants at compile time if possible. Each item in
1493
+ # body will be either a list of static data or a node to be
1494
+ # evaluated at runtime.
1495
+ for child in node.nodes:
1496
+ try:
1497
+ if not (
1498
+ # If the finalize function requires runtime context,
1499
+ # constants can't be evaluated at compile time.
1500
+ finalize.const
1501
+ # Unless it's basic template data that won't be
1502
+ # finalized anyway.
1503
+ or isinstance(child, nodes.TemplateData)
1504
+ ):
1505
+ raise nodes.Impossible()
1506
+
1507
+ const = self._output_child_to_const(child, frame, finalize)
1508
+ except (nodes.Impossible, Exception):
1509
+ # The node was not constant and needs to be evaluated at
1510
+ # runtime. Or another error was raised, which is easier
1511
+ # to debug at runtime.
1512
+ body.append(child)
1513
+ continue
1514
+
1515
+ if body and isinstance(body[-1], list):
1516
+ body[-1].append(const)
1517
+ else:
1518
+ body.append([const])
1519
+
1520
+ if frame.buffer is not None:
1521
+ if len(body) == 1:
1522
+ self.writeline(f"{frame.buffer}.append(")
1523
+ else:
1524
+ self.writeline(f"{frame.buffer}.extend((")
1525
+
1526
+ self.indent()
1527
+
1528
+ for item in body:
1529
+ if isinstance(item, list):
1530
+ # A group of constant data to join and output.
1531
+ val = self._output_const_repr(item)
1532
+
1533
+ if frame.buffer is None:
1534
+ self.writeline("yield " + val)
1535
+ else:
1536
+ self.writeline(val + ",")
1537
+ else:
1538
+ if frame.buffer is None:
1539
+ self.writeline("yield ", item)
1540
+ else:
1541
+ self.newline(item)
1542
+
1543
+ # A node to be evaluated at runtime.
1544
+ self._output_child_pre(item, frame, finalize)
1545
+ self.visit(item, frame)
1546
+ self._output_child_post(item, frame, finalize)
1547
+
1548
+ if frame.buffer is not None:
1549
+ self.write(",")
1550
+
1551
+ if frame.buffer is not None:
1552
+ self.outdent()
1553
+ self.writeline(")" if len(body) == 1 else "))")
1554
+
1555
+ if frame.require_output_check:
1556
+ self.outdent()
1557
+
1558
+ def visit_Assign(self, node: nodes.Assign, frame: Frame) -> None:
1559
+ self.push_assign_tracking()
1560
+ self.newline(node)
1561
+ self.visit(node.target, frame)
1562
+ self.write(" = ")
1563
+ self.visit(node.node, frame)
1564
+ self.pop_assign_tracking(frame)
1565
+
1566
+ def visit_AssignBlock(self, node: nodes.AssignBlock, frame: Frame) -> None:
1567
+ self.push_assign_tracking()
1568
+ block_frame = frame.inner()
1569
+ # This is a special case. Since a set block always captures we
1570
+ # will disable output checks. This way one can use set blocks
1571
+ # toplevel even in extended templates.
1572
+ block_frame.require_output_check = False
1573
+ block_frame.symbols.analyze_node(node)
1574
+ self.enter_frame(block_frame)
1575
+ self.buffer(block_frame)
1576
+ self.blockvisit(node.body, block_frame)
1577
+ self.newline(node)
1578
+ self.visit(node.target, frame)
1579
+ self.write(" = (Markup if context.eval_ctx.autoescape else identity)(")
1580
+ if node.filter is not None:
1581
+ self.visit_Filter(node.filter, block_frame)
1582
+ else:
1583
+ self.write(f"concat({block_frame.buffer})")
1584
+ self.write(")")
1585
+ self.pop_assign_tracking(frame)
1586
+ self.leave_frame(block_frame)
1587
+
1588
+ # -- Expression Visitors
1589
+
1590
+ def visit_Name(self, node: nodes.Name, frame: Frame) -> None:
1591
+ if node.ctx == "store" and (
1592
+ frame.toplevel or frame.loop_frame or frame.block_frame
1593
+ ):
1594
+ if self._assign_stack:
1595
+ self._assign_stack[-1].add(node.name)
1596
+ ref = frame.symbols.ref(node.name)
1597
+
1598
+ # If we are looking up a variable we might have to deal with the
1599
+ # case where it's undefined. We can skip that case if the load
1600
+ # instruction indicates a parameter which are always defined.
1601
+ if node.ctx == "load":
1602
+ load = frame.symbols.find_load(ref)
1603
+ if not (
1604
+ load is not None
1605
+ and load[0] == VAR_LOAD_PARAMETER
1606
+ and not self.parameter_is_undeclared(ref)
1607
+ ):
1608
+ self.write(
1609
+ f"(undefined(name={node.name!r}) if {ref} is missing else {ref})"
1610
+ )
1611
+ return
1612
+
1613
+ self.write(ref)
1614
+
1615
+ def visit_NSRef(self, node: nodes.NSRef, frame: Frame) -> None:
1616
+ # NSRefs can only be used to store values; since they use the normal
1617
+ # `foo.bar` notation they will be parsed as a normal attribute access
1618
+ # when used anywhere but in a `set` context
1619
+ ref = frame.symbols.ref(node.name)
1620
+ self.writeline(f"if not isinstance({ref}, Namespace):")
1621
+ self.indent()
1622
+ self.writeline(
1623
+ "raise TemplateRuntimeError"
1624
+ '("cannot assign attribute on non-namespace object")'
1625
+ )
1626
+ self.outdent()
1627
+ self.writeline(f"{ref}[{node.attr!r}]")
1628
+
1629
+ def visit_Const(self, node: nodes.Const, frame: Frame) -> None:
1630
+ val = node.as_const(frame.eval_ctx)
1631
+ if isinstance(val, float):
1632
+ self.write(str(val))
1633
+ else:
1634
+ self.write(repr(val))
1635
+
1636
+ def visit_TemplateData(self, node: nodes.TemplateData, frame: Frame) -> None:
1637
+ try:
1638
+ self.write(repr(node.as_const(frame.eval_ctx)))
1639
+ except nodes.Impossible:
1640
+ self.write(
1641
+ f"(Markup if context.eval_ctx.autoescape else identity)({node.data!r})"
1642
+ )
1643
+
1644
+ def visit_Tuple(self, node: nodes.Tuple, frame: Frame) -> None:
1645
+ self.write("(")
1646
+ idx = -1
1647
+ for idx, item in enumerate(node.items):
1648
+ if idx:
1649
+ self.write(", ")
1650
+ self.visit(item, frame)
1651
+ self.write(",)" if idx == 0 else ")")
1652
+
1653
+ def visit_List(self, node: nodes.List, frame: Frame) -> None:
1654
+ self.write("[")
1655
+ for idx, item in enumerate(node.items):
1656
+ if idx:
1657
+ self.write(", ")
1658
+ self.visit(item, frame)
1659
+ self.write("]")
1660
+
1661
+ def visit_Dict(self, node: nodes.Dict, frame: Frame) -> None:
1662
+ self.write("{")
1663
+ for idx, item in enumerate(node.items):
1664
+ if idx:
1665
+ self.write(", ")
1666
+ self.visit(item.key, frame)
1667
+ self.write(": ")
1668
+ self.visit(item.value, frame)
1669
+ self.write("}")
1670
+
1671
+ visit_Add = _make_binop("+")
1672
+ visit_Sub = _make_binop("-")
1673
+ visit_Mul = _make_binop("*")
1674
+ visit_Div = _make_binop("/")
1675
+ visit_FloorDiv = _make_binop("//")
1676
+ visit_Pow = _make_binop("**")
1677
+ visit_Mod = _make_binop("%")
1678
+ visit_And = _make_binop("and")
1679
+ visit_Or = _make_binop("or")
1680
+ visit_Pos = _make_unop("+")
1681
+ visit_Neg = _make_unop("-")
1682
+ visit_Not = _make_unop("not ")
1683
+
1684
+ @optimizeconst
1685
+ def visit_Concat(self, node: nodes.Concat, frame: Frame) -> None:
1686
+ if frame.eval_ctx.volatile:
1687
+ func_name = "(markup_join if context.eval_ctx.volatile else str_join)"
1688
+ elif frame.eval_ctx.autoescape:
1689
+ func_name = "markup_join"
1690
+ else:
1691
+ func_name = "str_join"
1692
+ self.write(f"{func_name}((")
1693
+ for arg in node.nodes:
1694
+ self.visit(arg, frame)
1695
+ self.write(", ")
1696
+ self.write("))")
1697
+
1698
+ @optimizeconst
1699
+ def visit_Compare(self, node: nodes.Compare, frame: Frame) -> None:
1700
+ self.write("(")
1701
+ self.visit(node.expr, frame)
1702
+ for op in node.ops:
1703
+ self.visit(op, frame)
1704
+ self.write(")")
1705
+
1706
+ def visit_Operand(self, node: nodes.Operand, frame: Frame) -> None:
1707
+ self.write(f" {operators[node.op]} ")
1708
+ self.visit(node.expr, frame)
1709
+
1710
+ @optimizeconst
1711
+ def visit_Getattr(self, node: nodes.Getattr, frame: Frame) -> None:
1712
+ if self.environment.is_async:
1713
+ self.write("(await auto_await(")
1714
+
1715
+ self.write("environment.getattr(")
1716
+ self.visit(node.node, frame)
1717
+ self.write(f", {node.attr!r})")
1718
+
1719
+ if self.environment.is_async:
1720
+ self.write("))")
1721
+
1722
+ @optimizeconst
1723
+ def visit_Getitem(self, node: nodes.Getitem, frame: Frame) -> None:
1724
+ # slices bypass the environment getitem method.
1725
+ if isinstance(node.arg, nodes.Slice):
1726
+ self.visit(node.node, frame)
1727
+ self.write("[")
1728
+ self.visit(node.arg, frame)
1729
+ self.write("]")
1730
+ else:
1731
+ if self.environment.is_async:
1732
+ self.write("(await auto_await(")
1733
+
1734
+ self.write("environment.getitem(")
1735
+ self.visit(node.node, frame)
1736
+ self.write(", ")
1737
+ self.visit(node.arg, frame)
1738
+ self.write(")")
1739
+
1740
+ if self.environment.is_async:
1741
+ self.write("))")
1742
+
1743
+ def visit_Slice(self, node: nodes.Slice, frame: Frame) -> None:
1744
+ if node.start is not None:
1745
+ self.visit(node.start, frame)
1746
+ self.write(":")
1747
+ if node.stop is not None:
1748
+ self.visit(node.stop, frame)
1749
+ if node.step is not None:
1750
+ self.write(":")
1751
+ self.visit(node.step, frame)
1752
+
1753
+ @contextmanager
1754
+ def _filter_test_common(
1755
+ self, node: t.Union[nodes.Filter, nodes.Test], frame: Frame, is_filter: bool
1756
+ ) -> t.Iterator[None]:
1757
+ if self.environment.is_async:
1758
+ self.write("(await auto_await(")
1759
+
1760
+ if is_filter:
1761
+ self.write(f"{self.filters[node.name]}(")
1762
+ func = self.environment.filters.get(node.name)
1763
+ else:
1764
+ self.write(f"{self.tests[node.name]}(")
1765
+ func = self.environment.tests.get(node.name)
1766
+
1767
+ # When inside an If or CondExpr frame, allow the filter to be
1768
+ # undefined at compile time and only raise an error if it's
1769
+ # actually called at runtime. See pull_dependencies.
1770
+ if func is None and not frame.soft_frame:
1771
+ type_name = "filter" if is_filter else "test"
1772
+ self.fail(f"No {type_name} named {node.name!r}.", node.lineno)
1773
+
1774
+ pass_arg = {
1775
+ _PassArg.context: "context",
1776
+ _PassArg.eval_context: "context.eval_ctx",
1777
+ _PassArg.environment: "environment",
1778
+ }.get(
1779
+ _PassArg.from_obj(func) # type: ignore
1780
+ )
1781
+
1782
+ if pass_arg is not None:
1783
+ self.write(f"{pass_arg}, ")
1784
+
1785
+ # Back to the visitor function to handle visiting the target of
1786
+ # the filter or test.
1787
+ yield
1788
+
1789
+ self.signature(node, frame)
1790
+ self.write(")")
1791
+
1792
+ if self.environment.is_async:
1793
+ self.write("))")
1794
+
1795
+ @optimizeconst
1796
+ def visit_Filter(self, node: nodes.Filter, frame: Frame) -> None:
1797
+ with self._filter_test_common(node, frame, True):
1798
+ # if the filter node is None we are inside a filter block
1799
+ # and want to write to the current buffer
1800
+ if node.node is not None:
1801
+ self.visit(node.node, frame)
1802
+ elif frame.eval_ctx.volatile:
1803
+ self.write(
1804
+ f"(Markup(concat({frame.buffer}))"
1805
+ f" if context.eval_ctx.autoescape else concat({frame.buffer}))"
1806
+ )
1807
+ elif frame.eval_ctx.autoescape:
1808
+ self.write(f"Markup(concat({frame.buffer}))")
1809
+ else:
1810
+ self.write(f"concat({frame.buffer})")
1811
+
1812
+ @optimizeconst
1813
+ def visit_Test(self, node: nodes.Test, frame: Frame) -> None:
1814
+ with self._filter_test_common(node, frame, False):
1815
+ self.visit(node.node, frame)
1816
+
1817
+ @optimizeconst
1818
+ def visit_CondExpr(self, node: nodes.CondExpr, frame: Frame) -> None:
1819
+ frame = frame.soft()
1820
+
1821
+ def write_expr2() -> None:
1822
+ if node.expr2 is not None:
1823
+ self.visit(node.expr2, frame)
1824
+ return
1825
+
1826
+ self.write(
1827
+ f'cond_expr_undefined("the inline if-expression on'
1828
+ f" {self.position(node)} evaluated to false and no else"
1829
+ f' section was defined.")'
1830
+ )
1831
+
1832
+ self.write("(")
1833
+ self.visit(node.expr1, frame)
1834
+ self.write(" if ")
1835
+ self.visit(node.test, frame)
1836
+ self.write(" else ")
1837
+ write_expr2()
1838
+ self.write(")")
1839
+
1840
+ @optimizeconst
1841
+ def visit_Call(
1842
+ self, node: nodes.Call, frame: Frame, forward_caller: bool = False
1843
+ ) -> None:
1844
+ if self.environment.is_async:
1845
+ self.write("(await auto_await(")
1846
+ if self.environment.sandboxed:
1847
+ self.write("environment.call(context, ")
1848
+ else:
1849
+ self.write("context.call(")
1850
+ self.visit(node.node, frame)
1851
+ extra_kwargs = {"caller": "caller"} if forward_caller else None
1852
+ loop_kwargs = {"_loop_vars": "_loop_vars"} if frame.loop_frame else {}
1853
+ block_kwargs = {"_block_vars": "_block_vars"} if frame.block_frame else {}
1854
+ if extra_kwargs:
1855
+ extra_kwargs.update(loop_kwargs, **block_kwargs)
1856
+ elif loop_kwargs or block_kwargs:
1857
+ extra_kwargs = dict(loop_kwargs, **block_kwargs)
1858
+ self.signature(node, frame, extra_kwargs)
1859
+ self.write(")")
1860
+ if self.environment.is_async:
1861
+ self.write("))")
1862
+
1863
+ def visit_Keyword(self, node: nodes.Keyword, frame: Frame) -> None:
1864
+ self.write(node.key + "=")
1865
+ self.visit(node.value, frame)
1866
+
1867
+ # -- Unused nodes for extensions
1868
+
1869
+ def visit_MarkSafe(self, node: nodes.MarkSafe, frame: Frame) -> None:
1870
+ self.write("Markup(")
1871
+ self.visit(node.expr, frame)
1872
+ self.write(")")
1873
+
1874
+ def visit_MarkSafeIfAutoescape(
1875
+ self, node: nodes.MarkSafeIfAutoescape, frame: Frame
1876
+ ) -> None:
1877
+ self.write("(Markup if context.eval_ctx.autoescape else identity)(")
1878
+ self.visit(node.expr, frame)
1879
+ self.write(")")
1880
+
1881
+ def visit_EnvironmentAttribute(
1882
+ self, node: nodes.EnvironmentAttribute, frame: Frame
1883
+ ) -> None:
1884
+ self.write("environment." + node.name)
1885
+
1886
+ def visit_ExtensionAttribute(
1887
+ self, node: nodes.ExtensionAttribute, frame: Frame
1888
+ ) -> None:
1889
+ self.write(f"environment.extensions[{node.identifier!r}].{node.name}")
1890
+
1891
+ def visit_ImportedName(self, node: nodes.ImportedName, frame: Frame) -> None:
1892
+ self.write(self.import_aliases[node.importname])
1893
+
1894
+ def visit_InternalName(self, node: nodes.InternalName, frame: Frame) -> None:
1895
+ self.write(node.name)
1896
+
1897
+ def visit_ContextReference(
1898
+ self, node: nodes.ContextReference, frame: Frame
1899
+ ) -> None:
1900
+ self.write("context")
1901
+
1902
+ def visit_DerivedContextReference(
1903
+ self, node: nodes.DerivedContextReference, frame: Frame
1904
+ ) -> None:
1905
+ self.write(self.derive_context(frame))
1906
+
1907
+ def visit_Continue(self, node: nodes.Continue, frame: Frame) -> None:
1908
+ self.writeline("continue", node)
1909
+
1910
+ def visit_Break(self, node: nodes.Break, frame: Frame) -> None:
1911
+ self.writeline("break", node)
1912
+
1913
+ def visit_Scope(self, node: nodes.Scope, frame: Frame) -> None:
1914
+ scope_frame = frame.inner()
1915
+ scope_frame.symbols.analyze_node(node)
1916
+ self.enter_frame(scope_frame)
1917
+ self.blockvisit(node.body, scope_frame)
1918
+ self.leave_frame(scope_frame)
1919
+
1920
+ def visit_OverlayScope(self, node: nodes.OverlayScope, frame: Frame) -> None:
1921
+ ctx = self.temporary_identifier()
1922
+ self.writeline(f"{ctx} = {self.derive_context(frame)}")
1923
+ self.writeline(f"{ctx}.vars = ")
1924
+ self.visit(node.context, frame)
1925
+ self.push_context_reference(ctx)
1926
+
1927
+ scope_frame = frame.inner(isolated=True)
1928
+ scope_frame.symbols.analyze_node(node)
1929
+ self.enter_frame(scope_frame)
1930
+ self.blockvisit(node.body, scope_frame)
1931
+ self.leave_frame(scope_frame)
1932
+ self.pop_context_reference()
1933
+
1934
+ def visit_EvalContextModifier(
1935
+ self, node: nodes.EvalContextModifier, frame: Frame
1936
+ ) -> None:
1937
+ for keyword in node.options:
1938
+ self.writeline(f"context.eval_ctx.{keyword.key} = ")
1939
+ self.visit(keyword.value, frame)
1940
+ try:
1941
+ val = keyword.value.as_const(frame.eval_ctx)
1942
+ except nodes.Impossible:
1943
+ frame.eval_ctx.volatile = True
1944
+ else:
1945
+ setattr(frame.eval_ctx, keyword.key, val)
1946
+
1947
+ def visit_ScopedEvalContextModifier(
1948
+ self, node: nodes.ScopedEvalContextModifier, frame: Frame
1949
+ ) -> None:
1950
+ old_ctx_name = self.temporary_identifier()
1951
+ saved_ctx = frame.eval_ctx.save()
1952
+ self.writeline(f"{old_ctx_name} = context.eval_ctx.save()")
1953
+ self.visit_EvalContextModifier(node, frame)
1954
+ for child in node.body:
1955
+ self.visit(child, frame)
1956
+ frame.eval_ctx.revert(saved_ctx)
1957
+ self.writeline(f"context.eval_ctx.revert({old_ctx_name})")
lib/python3.11/site-packages/jinja2/constants.py ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #: list of lorem ipsum words used by the lipsum() helper function
2
+ LOREM_IPSUM_WORDS = """\
3
+ a ac accumsan ad adipiscing aenean aliquam aliquet amet ante aptent arcu at
4
+ auctor augue bibendum blandit class commodo condimentum congue consectetuer
5
+ consequat conubia convallis cras cubilia cum curabitur curae cursus dapibus
6
+ diam dictum dictumst dignissim dis dolor donec dui duis egestas eget eleifend
7
+ elementum elit enim erat eros est et etiam eu euismod facilisi facilisis fames
8
+ faucibus felis fermentum feugiat fringilla fusce gravida habitant habitasse hac
9
+ hendrerit hymenaeos iaculis id imperdiet in inceptos integer interdum ipsum
10
+ justo lacinia lacus laoreet lectus leo libero ligula litora lobortis lorem
11
+ luctus maecenas magna magnis malesuada massa mattis mauris metus mi molestie
12
+ mollis montes morbi mus nam nascetur natoque nec neque netus nibh nisi nisl non
13
+ nonummy nostra nulla nullam nunc odio orci ornare parturient pede pellentesque
14
+ penatibus per pharetra phasellus placerat platea porta porttitor posuere
15
+ potenti praesent pretium primis proin pulvinar purus quam quis quisque rhoncus
16
+ ridiculus risus rutrum sagittis sapien scelerisque sed sem semper senectus sit
17
+ sociis sociosqu sodales sollicitudin suscipit suspendisse taciti tellus tempor
18
+ tempus tincidunt torquent tortor tristique turpis ullamcorper ultrices
19
+ ultricies urna ut varius vehicula vel velit venenatis vestibulum vitae vivamus
20
+ viverra volutpat vulputate"""
lib/python3.11/site-packages/jinja2/debug.py ADDED
@@ -0,0 +1,191 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sys
2
+ import typing as t
3
+ from types import CodeType
4
+ from types import TracebackType
5
+
6
+ from .exceptions import TemplateSyntaxError
7
+ from .utils import internal_code
8
+ from .utils import missing
9
+
10
+ if t.TYPE_CHECKING:
11
+ from .runtime import Context
12
+
13
+
14
+ def rewrite_traceback_stack(source: t.Optional[str] = None) -> BaseException:
15
+ """Rewrite the current exception to replace any tracebacks from
16
+ within compiled template code with tracebacks that look like they
17
+ came from the template source.
18
+
19
+ This must be called within an ``except`` block.
20
+
21
+ :param source: For ``TemplateSyntaxError``, the original source if
22
+ known.
23
+ :return: The original exception with the rewritten traceback.
24
+ """
25
+ _, exc_value, tb = sys.exc_info()
26
+ exc_value = t.cast(BaseException, exc_value)
27
+ tb = t.cast(TracebackType, tb)
28
+
29
+ if isinstance(exc_value, TemplateSyntaxError) and not exc_value.translated:
30
+ exc_value.translated = True
31
+ exc_value.source = source
32
+ # Remove the old traceback, otherwise the frames from the
33
+ # compiler still show up.
34
+ exc_value.with_traceback(None)
35
+ # Outside of runtime, so the frame isn't executing template
36
+ # code, but it still needs to point at the template.
37
+ tb = fake_traceback(
38
+ exc_value, None, exc_value.filename or "<unknown>", exc_value.lineno
39
+ )
40
+ else:
41
+ # Skip the frame for the render function.
42
+ tb = tb.tb_next
43
+
44
+ stack = []
45
+
46
+ # Build the stack of traceback object, replacing any in template
47
+ # code with the source file and line information.
48
+ while tb is not None:
49
+ # Skip frames decorated with @internalcode. These are internal
50
+ # calls that aren't useful in template debugging output.
51
+ if tb.tb_frame.f_code in internal_code:
52
+ tb = tb.tb_next
53
+ continue
54
+
55
+ template = tb.tb_frame.f_globals.get("__jinja_template__")
56
+
57
+ if template is not None:
58
+ lineno = template.get_corresponding_lineno(tb.tb_lineno)
59
+ fake_tb = fake_traceback(exc_value, tb, template.filename, lineno)
60
+ stack.append(fake_tb)
61
+ else:
62
+ stack.append(tb)
63
+
64
+ tb = tb.tb_next
65
+
66
+ tb_next = None
67
+
68
+ # Assign tb_next in reverse to avoid circular references.
69
+ for tb in reversed(stack):
70
+ tb.tb_next = tb_next
71
+ tb_next = tb
72
+
73
+ return exc_value.with_traceback(tb_next)
74
+
75
+
76
+ def fake_traceback( # type: ignore
77
+ exc_value: BaseException, tb: t.Optional[TracebackType], filename: str, lineno: int
78
+ ) -> TracebackType:
79
+ """Produce a new traceback object that looks like it came from the
80
+ template source instead of the compiled code. The filename, line
81
+ number, and location name will point to the template, and the local
82
+ variables will be the current template context.
83
+
84
+ :param exc_value: The original exception to be re-raised to create
85
+ the new traceback.
86
+ :param tb: The original traceback to get the local variables and
87
+ code info from.
88
+ :param filename: The template filename.
89
+ :param lineno: The line number in the template source.
90
+ """
91
+ if tb is not None:
92
+ # Replace the real locals with the context that would be
93
+ # available at that point in the template.
94
+ locals = get_template_locals(tb.tb_frame.f_locals)
95
+ locals.pop("__jinja_exception__", None)
96
+ else:
97
+ locals = {}
98
+
99
+ globals = {
100
+ "__name__": filename,
101
+ "__file__": filename,
102
+ "__jinja_exception__": exc_value,
103
+ }
104
+ # Raise an exception at the correct line number.
105
+ code: CodeType = compile(
106
+ "\n" * (lineno - 1) + "raise __jinja_exception__", filename, "exec"
107
+ )
108
+
109
+ # Build a new code object that points to the template file and
110
+ # replaces the location with a block name.
111
+ location = "template"
112
+
113
+ if tb is not None:
114
+ function = tb.tb_frame.f_code.co_name
115
+
116
+ if function == "root":
117
+ location = "top-level template code"
118
+ elif function.startswith("block_"):
119
+ location = f"block {function[6:]!r}"
120
+
121
+ if sys.version_info >= (3, 8):
122
+ code = code.replace(co_name=location)
123
+ else:
124
+ code = CodeType(
125
+ code.co_argcount,
126
+ code.co_kwonlyargcount,
127
+ code.co_nlocals,
128
+ code.co_stacksize,
129
+ code.co_flags,
130
+ code.co_code,
131
+ code.co_consts,
132
+ code.co_names,
133
+ code.co_varnames,
134
+ code.co_filename,
135
+ location,
136
+ code.co_firstlineno,
137
+ code.co_lnotab,
138
+ code.co_freevars,
139
+ code.co_cellvars,
140
+ )
141
+
142
+ # Execute the new code, which is guaranteed to raise, and return
143
+ # the new traceback without this frame.
144
+ try:
145
+ exec(code, globals, locals)
146
+ except BaseException:
147
+ return sys.exc_info()[2].tb_next # type: ignore
148
+
149
+
150
+ def get_template_locals(real_locals: t.Mapping[str, t.Any]) -> t.Dict[str, t.Any]:
151
+ """Based on the runtime locals, get the context that would be
152
+ available at that point in the template.
153
+ """
154
+ # Start with the current template context.
155
+ ctx: "t.Optional[Context]" = real_locals.get("context")
156
+
157
+ if ctx is not None:
158
+ data: t.Dict[str, t.Any] = ctx.get_all().copy()
159
+ else:
160
+ data = {}
161
+
162
+ # Might be in a derived context that only sets local variables
163
+ # rather than pushing a context. Local variables follow the scheme
164
+ # l_depth_name. Find the highest-depth local that has a value for
165
+ # each name.
166
+ local_overrides: t.Dict[str, t.Tuple[int, t.Any]] = {}
167
+
168
+ for name, value in real_locals.items():
169
+ if not name.startswith("l_") or value is missing:
170
+ # Not a template variable, or no longer relevant.
171
+ continue
172
+
173
+ try:
174
+ _, depth_str, name = name.split("_", 2)
175
+ depth = int(depth_str)
176
+ except ValueError:
177
+ continue
178
+
179
+ cur_depth = local_overrides.get(name, (-1,))[0]
180
+
181
+ if cur_depth < depth:
182
+ local_overrides[name] = (depth, value)
183
+
184
+ # Modify the context with any derived context.
185
+ for name, (_, value) in local_overrides.items():
186
+ if value is missing:
187
+ data.pop(name, None)
188
+ else:
189
+ data[name] = value
190
+
191
+ return data
lib/python3.11/site-packages/jinja2/defaults.py ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import typing as t
2
+
3
+ from .filters import FILTERS as DEFAULT_FILTERS # noqa: F401
4
+ from .tests import TESTS as DEFAULT_TESTS # noqa: F401
5
+ from .utils import Cycler
6
+ from .utils import generate_lorem_ipsum
7
+ from .utils import Joiner
8
+ from .utils import Namespace
9
+
10
+ if t.TYPE_CHECKING:
11
+ import typing_extensions as te
12
+
13
+ # defaults for the parser / lexer
14
+ BLOCK_START_STRING = "{%"
15
+ BLOCK_END_STRING = "%}"
16
+ VARIABLE_START_STRING = "{{"
17
+ VARIABLE_END_STRING = "}}"
18
+ COMMENT_START_STRING = "{#"
19
+ COMMENT_END_STRING = "#}"
20
+ LINE_STATEMENT_PREFIX: t.Optional[str] = None
21
+ LINE_COMMENT_PREFIX: t.Optional[str] = None
22
+ TRIM_BLOCKS = False
23
+ LSTRIP_BLOCKS = False
24
+ NEWLINE_SEQUENCE: "te.Literal['\\n', '\\r\\n', '\\r']" = "\n"
25
+ KEEP_TRAILING_NEWLINE = False
26
+
27
+ # default filters, tests and namespace
28
+
29
+ DEFAULT_NAMESPACE = {
30
+ "range": range,
31
+ "dict": dict,
32
+ "lipsum": generate_lorem_ipsum,
33
+ "cycler": Cycler,
34
+ "joiner": Joiner,
35
+ "namespace": Namespace,
36
+ }
37
+
38
+ # default policies
39
+ DEFAULT_POLICIES: t.Dict[str, t.Any] = {
40
+ "compiler.ascii_str": True,
41
+ "urlize.rel": "noopener",
42
+ "urlize.target": None,
43
+ "urlize.extra_schemes": None,
44
+ "truncate.leeway": 5,
45
+ "json.dumps_function": None,
46
+ "json.dumps_kwargs": {"sort_keys": True},
47
+ "ext.i18n.trimmed": False,
48
+ }
lib/python3.11/site-packages/jinja2/environment.py ADDED
@@ -0,0 +1,1667 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Classes for managing templates and their runtime and compile time
2
+ options.
3
+ """
4
+ import os
5
+ import typing
6
+ import typing as t
7
+ import weakref
8
+ from collections import ChainMap
9
+ from functools import lru_cache
10
+ from functools import partial
11
+ from functools import reduce
12
+ from types import CodeType
13
+
14
+ from markupsafe import Markup
15
+
16
+ from . import nodes
17
+ from .compiler import CodeGenerator
18
+ from .compiler import generate
19
+ from .defaults import BLOCK_END_STRING
20
+ from .defaults import BLOCK_START_STRING
21
+ from .defaults import COMMENT_END_STRING
22
+ from .defaults import COMMENT_START_STRING
23
+ from .defaults import DEFAULT_FILTERS
24
+ from .defaults import DEFAULT_NAMESPACE
25
+ from .defaults import DEFAULT_POLICIES
26
+ from .defaults import DEFAULT_TESTS
27
+ from .defaults import KEEP_TRAILING_NEWLINE
28
+ from .defaults import LINE_COMMENT_PREFIX
29
+ from .defaults import LINE_STATEMENT_PREFIX
30
+ from .defaults import LSTRIP_BLOCKS
31
+ from .defaults import NEWLINE_SEQUENCE
32
+ from .defaults import TRIM_BLOCKS
33
+ from .defaults import VARIABLE_END_STRING
34
+ from .defaults import VARIABLE_START_STRING
35
+ from .exceptions import TemplateNotFound
36
+ from .exceptions import TemplateRuntimeError
37
+ from .exceptions import TemplatesNotFound
38
+ from .exceptions import TemplateSyntaxError
39
+ from .exceptions import UndefinedError
40
+ from .lexer import get_lexer
41
+ from .lexer import Lexer
42
+ from .lexer import TokenStream
43
+ from .nodes import EvalContext
44
+ from .parser import Parser
45
+ from .runtime import Context
46
+ from .runtime import new_context
47
+ from .runtime import Undefined
48
+ from .utils import _PassArg
49
+ from .utils import concat
50
+ from .utils import consume
51
+ from .utils import import_string
52
+ from .utils import internalcode
53
+ from .utils import LRUCache
54
+ from .utils import missing
55
+
56
+ if t.TYPE_CHECKING:
57
+ import typing_extensions as te
58
+ from .bccache import BytecodeCache
59
+ from .ext import Extension
60
+ from .loaders import BaseLoader
61
+
62
+ _env_bound = t.TypeVar("_env_bound", bound="Environment")
63
+
64
+
65
+ # for direct template usage we have up to ten living environments
66
+ @lru_cache(maxsize=10)
67
+ def get_spontaneous_environment(cls: t.Type[_env_bound], *args: t.Any) -> _env_bound:
68
+ """Return a new spontaneous environment. A spontaneous environment
69
+ is used for templates created directly rather than through an
70
+ existing environment.
71
+
72
+ :param cls: Environment class to create.
73
+ :param args: Positional arguments passed to environment.
74
+ """
75
+ env = cls(*args)
76
+ env.shared = True
77
+ return env
78
+
79
+
80
+ def create_cache(
81
+ size: int,
82
+ ) -> t.Optional[t.MutableMapping[t.Tuple[weakref.ref, str], "Template"]]:
83
+ """Return the cache class for the given size."""
84
+ if size == 0:
85
+ return None
86
+
87
+ if size < 0:
88
+ return {}
89
+
90
+ return LRUCache(size) # type: ignore
91
+
92
+
93
+ def copy_cache(
94
+ cache: t.Optional[t.MutableMapping],
95
+ ) -> t.Optional[t.MutableMapping[t.Tuple[weakref.ref, str], "Template"]]:
96
+ """Create an empty copy of the given cache."""
97
+ if cache is None:
98
+ return None
99
+
100
+ if type(cache) is dict:
101
+ return {}
102
+
103
+ return LRUCache(cache.capacity) # type: ignore
104
+
105
+
106
+ def load_extensions(
107
+ environment: "Environment",
108
+ extensions: t.Sequence[t.Union[str, t.Type["Extension"]]],
109
+ ) -> t.Dict[str, "Extension"]:
110
+ """Load the extensions from the list and bind it to the environment.
111
+ Returns a dict of instantiated extensions.
112
+ """
113
+ result = {}
114
+
115
+ for extension in extensions:
116
+ if isinstance(extension, str):
117
+ extension = t.cast(t.Type["Extension"], import_string(extension))
118
+
119
+ result[extension.identifier] = extension(environment)
120
+
121
+ return result
122
+
123
+
124
+ def _environment_config_check(environment: "Environment") -> "Environment":
125
+ """Perform a sanity check on the environment."""
126
+ assert issubclass(
127
+ environment.undefined, Undefined
128
+ ), "'undefined' must be a subclass of 'jinja2.Undefined'."
129
+ assert (
130
+ environment.block_start_string
131
+ != environment.variable_start_string
132
+ != environment.comment_start_string
133
+ ), "block, variable and comment start strings must be different."
134
+ assert environment.newline_sequence in {
135
+ "\r",
136
+ "\r\n",
137
+ "\n",
138
+ }, "'newline_sequence' must be one of '\\n', '\\r\\n', or '\\r'."
139
+ return environment
140
+
141
+
142
+ class Environment:
143
+ r"""The core component of Jinja is the `Environment`. It contains
144
+ important shared variables like configuration, filters, tests,
145
+ globals and others. Instances of this class may be modified if
146
+ they are not shared and if no template was loaded so far.
147
+ Modifications on environments after the first template was loaded
148
+ will lead to surprising effects and undefined behavior.
149
+
150
+ Here are the possible initialization parameters:
151
+
152
+ `block_start_string`
153
+ The string marking the beginning of a block. Defaults to ``'{%'``.
154
+
155
+ `block_end_string`
156
+ The string marking the end of a block. Defaults to ``'%}'``.
157
+
158
+ `variable_start_string`
159
+ The string marking the beginning of a print statement.
160
+ Defaults to ``'{{'``.
161
+
162
+ `variable_end_string`
163
+ The string marking the end of a print statement. Defaults to
164
+ ``'}}'``.
165
+
166
+ `comment_start_string`
167
+ The string marking the beginning of a comment. Defaults to ``'{#'``.
168
+
169
+ `comment_end_string`
170
+ The string marking the end of a comment. Defaults to ``'#}'``.
171
+
172
+ `line_statement_prefix`
173
+ If given and a string, this will be used as prefix for line based
174
+ statements. See also :ref:`line-statements`.
175
+
176
+ `line_comment_prefix`
177
+ If given and a string, this will be used as prefix for line based
178
+ comments. See also :ref:`line-statements`.
179
+
180
+ .. versionadded:: 2.2
181
+
182
+ `trim_blocks`
183
+ If this is set to ``True`` the first newline after a block is
184
+ removed (block, not variable tag!). Defaults to `False`.
185
+
186
+ `lstrip_blocks`
187
+ If this is set to ``True`` leading spaces and tabs are stripped
188
+ from the start of a line to a block. Defaults to `False`.
189
+
190
+ `newline_sequence`
191
+ The sequence that starts a newline. Must be one of ``'\r'``,
192
+ ``'\n'`` or ``'\r\n'``. The default is ``'\n'`` which is a
193
+ useful default for Linux and OS X systems as well as web
194
+ applications.
195
+
196
+ `keep_trailing_newline`
197
+ Preserve the trailing newline when rendering templates.
198
+ The default is ``False``, which causes a single newline,
199
+ if present, to be stripped from the end of the template.
200
+
201
+ .. versionadded:: 2.7
202
+
203
+ `extensions`
204
+ List of Jinja extensions to use. This can either be import paths
205
+ as strings or extension classes. For more information have a
206
+ look at :ref:`the extensions documentation <jinja-extensions>`.
207
+
208
+ `optimized`
209
+ should the optimizer be enabled? Default is ``True``.
210
+
211
+ `undefined`
212
+ :class:`Undefined` or a subclass of it that is used to represent
213
+ undefined values in the template.
214
+
215
+ `finalize`
216
+ A callable that can be used to process the result of a variable
217
+ expression before it is output. For example one can convert
218
+ ``None`` implicitly into an empty string here.
219
+
220
+ `autoescape`
221
+ If set to ``True`` the XML/HTML autoescaping feature is enabled by
222
+ default. For more details about autoescaping see
223
+ :class:`~markupsafe.Markup`. As of Jinja 2.4 this can also
224
+ be a callable that is passed the template name and has to
225
+ return ``True`` or ``False`` depending on autoescape should be
226
+ enabled by default.
227
+
228
+ .. versionchanged:: 2.4
229
+ `autoescape` can now be a function
230
+
231
+ `loader`
232
+ The template loader for this environment.
233
+
234
+ `cache_size`
235
+ The size of the cache. Per default this is ``400`` which means
236
+ that if more than 400 templates are loaded the loader will clean
237
+ out the least recently used template. If the cache size is set to
238
+ ``0`` templates are recompiled all the time, if the cache size is
239
+ ``-1`` the cache will not be cleaned.
240
+
241
+ .. versionchanged:: 2.8
242
+ The cache size was increased to 400 from a low 50.
243
+
244
+ `auto_reload`
245
+ Some loaders load templates from locations where the template
246
+ sources may change (ie: file system or database). If
247
+ ``auto_reload`` is set to ``True`` (default) every time a template is
248
+ requested the loader checks if the source changed and if yes, it
249
+ will reload the template. For higher performance it's possible to
250
+ disable that.
251
+
252
+ `bytecode_cache`
253
+ If set to a bytecode cache object, this object will provide a
254
+ cache for the internal Jinja bytecode so that templates don't
255
+ have to be parsed if they were not changed.
256
+
257
+ See :ref:`bytecode-cache` for more information.
258
+
259
+ `enable_async`
260
+ If set to true this enables async template execution which
261
+ allows using async functions and generators.
262
+ """
263
+
264
+ #: if this environment is sandboxed. Modifying this variable won't make
265
+ #: the environment sandboxed though. For a real sandboxed environment
266
+ #: have a look at jinja2.sandbox. This flag alone controls the code
267
+ #: generation by the compiler.
268
+ sandboxed = False
269
+
270
+ #: True if the environment is just an overlay
271
+ overlayed = False
272
+
273
+ #: the environment this environment is linked to if it is an overlay
274
+ linked_to: t.Optional["Environment"] = None
275
+
276
+ #: shared environments have this set to `True`. A shared environment
277
+ #: must not be modified
278
+ shared = False
279
+
280
+ #: the class that is used for code generation. See
281
+ #: :class:`~jinja2.compiler.CodeGenerator` for more information.
282
+ code_generator_class: t.Type["CodeGenerator"] = CodeGenerator
283
+
284
+ concat = "".join
285
+
286
+ #: the context class that is used for templates. See
287
+ #: :class:`~jinja2.runtime.Context` for more information.
288
+ context_class: t.Type[Context] = Context
289
+
290
+ template_class: t.Type["Template"]
291
+
292
+ def __init__(
293
+ self,
294
+ block_start_string: str = BLOCK_START_STRING,
295
+ block_end_string: str = BLOCK_END_STRING,
296
+ variable_start_string: str = VARIABLE_START_STRING,
297
+ variable_end_string: str = VARIABLE_END_STRING,
298
+ comment_start_string: str = COMMENT_START_STRING,
299
+ comment_end_string: str = COMMENT_END_STRING,
300
+ line_statement_prefix: t.Optional[str] = LINE_STATEMENT_PREFIX,
301
+ line_comment_prefix: t.Optional[str] = LINE_COMMENT_PREFIX,
302
+ trim_blocks: bool = TRIM_BLOCKS,
303
+ lstrip_blocks: bool = LSTRIP_BLOCKS,
304
+ newline_sequence: "te.Literal['\\n', '\\r\\n', '\\r']" = NEWLINE_SEQUENCE,
305
+ keep_trailing_newline: bool = KEEP_TRAILING_NEWLINE,
306
+ extensions: t.Sequence[t.Union[str, t.Type["Extension"]]] = (),
307
+ optimized: bool = True,
308
+ undefined: t.Type[Undefined] = Undefined,
309
+ finalize: t.Optional[t.Callable[..., t.Any]] = None,
310
+ autoescape: t.Union[bool, t.Callable[[t.Optional[str]], bool]] = False,
311
+ loader: t.Optional["BaseLoader"] = None,
312
+ cache_size: int = 400,
313
+ auto_reload: bool = True,
314
+ bytecode_cache: t.Optional["BytecodeCache"] = None,
315
+ enable_async: bool = False,
316
+ ):
317
+ # !!Important notice!!
318
+ # The constructor accepts quite a few arguments that should be
319
+ # passed by keyword rather than position. However it's important to
320
+ # not change the order of arguments because it's used at least
321
+ # internally in those cases:
322
+ # - spontaneous environments (i18n extension and Template)
323
+ # - unittests
324
+ # If parameter changes are required only add parameters at the end
325
+ # and don't change the arguments (or the defaults!) of the arguments
326
+ # existing already.
327
+
328
+ # lexer / parser information
329
+ self.block_start_string = block_start_string
330
+ self.block_end_string = block_end_string
331
+ self.variable_start_string = variable_start_string
332
+ self.variable_end_string = variable_end_string
333
+ self.comment_start_string = comment_start_string
334
+ self.comment_end_string = comment_end_string
335
+ self.line_statement_prefix = line_statement_prefix
336
+ self.line_comment_prefix = line_comment_prefix
337
+ self.trim_blocks = trim_blocks
338
+ self.lstrip_blocks = lstrip_blocks
339
+ self.newline_sequence = newline_sequence
340
+ self.keep_trailing_newline = keep_trailing_newline
341
+
342
+ # runtime information
343
+ self.undefined: t.Type[Undefined] = undefined
344
+ self.optimized = optimized
345
+ self.finalize = finalize
346
+ self.autoescape = autoescape
347
+
348
+ # defaults
349
+ self.filters = DEFAULT_FILTERS.copy()
350
+ self.tests = DEFAULT_TESTS.copy()
351
+ self.globals = DEFAULT_NAMESPACE.copy()
352
+
353
+ # set the loader provided
354
+ self.loader = loader
355
+ self.cache = create_cache(cache_size)
356
+ self.bytecode_cache = bytecode_cache
357
+ self.auto_reload = auto_reload
358
+
359
+ # configurable policies
360
+ self.policies = DEFAULT_POLICIES.copy()
361
+
362
+ # load extensions
363
+ self.extensions = load_extensions(self, extensions)
364
+
365
+ self.is_async = enable_async
366
+ _environment_config_check(self)
367
+
368
+ def add_extension(self, extension: t.Union[str, t.Type["Extension"]]) -> None:
369
+ """Adds an extension after the environment was created.
370
+
371
+ .. versionadded:: 2.5
372
+ """
373
+ self.extensions.update(load_extensions(self, [extension]))
374
+
375
+ def extend(self, **attributes: t.Any) -> None:
376
+ """Add the items to the instance of the environment if they do not exist
377
+ yet. This is used by :ref:`extensions <writing-extensions>` to register
378
+ callbacks and configuration values without breaking inheritance.
379
+ """
380
+ for key, value in attributes.items():
381
+ if not hasattr(self, key):
382
+ setattr(self, key, value)
383
+
384
+ def overlay(
385
+ self,
386
+ block_start_string: str = missing,
387
+ block_end_string: str = missing,
388
+ variable_start_string: str = missing,
389
+ variable_end_string: str = missing,
390
+ comment_start_string: str = missing,
391
+ comment_end_string: str = missing,
392
+ line_statement_prefix: t.Optional[str] = missing,
393
+ line_comment_prefix: t.Optional[str] = missing,
394
+ trim_blocks: bool = missing,
395
+ lstrip_blocks: bool = missing,
396
+ newline_sequence: "te.Literal['\\n', '\\r\\n', '\\r']" = missing,
397
+ keep_trailing_newline: bool = missing,
398
+ extensions: t.Sequence[t.Union[str, t.Type["Extension"]]] = missing,
399
+ optimized: bool = missing,
400
+ undefined: t.Type[Undefined] = missing,
401
+ finalize: t.Optional[t.Callable[..., t.Any]] = missing,
402
+ autoescape: t.Union[bool, t.Callable[[t.Optional[str]], bool]] = missing,
403
+ loader: t.Optional["BaseLoader"] = missing,
404
+ cache_size: int = missing,
405
+ auto_reload: bool = missing,
406
+ bytecode_cache: t.Optional["BytecodeCache"] = missing,
407
+ enable_async: bool = False,
408
+ ) -> "Environment":
409
+ """Create a new overlay environment that shares all the data with the
410
+ current environment except for cache and the overridden attributes.
411
+ Extensions cannot be removed for an overlayed environment. An overlayed
412
+ environment automatically gets all the extensions of the environment it
413
+ is linked to plus optional extra extensions.
414
+
415
+ Creating overlays should happen after the initial environment was set
416
+ up completely. Not all attributes are truly linked, some are just
417
+ copied over so modifications on the original environment may not shine
418
+ through.
419
+
420
+ .. versionchanged:: 3.1.2
421
+ Added the ``newline_sequence``,, ``keep_trailing_newline``,
422
+ and ``enable_async`` parameters to match ``__init__``.
423
+ """
424
+ args = dict(locals())
425
+ del args["self"], args["cache_size"], args["extensions"], args["enable_async"]
426
+
427
+ rv = object.__new__(self.__class__)
428
+ rv.__dict__.update(self.__dict__)
429
+ rv.overlayed = True
430
+ rv.linked_to = self
431
+
432
+ for key, value in args.items():
433
+ if value is not missing:
434
+ setattr(rv, key, value)
435
+
436
+ if cache_size is not missing:
437
+ rv.cache = create_cache(cache_size)
438
+ else:
439
+ rv.cache = copy_cache(self.cache)
440
+
441
+ rv.extensions = {}
442
+ for key, value in self.extensions.items():
443
+ rv.extensions[key] = value.bind(rv)
444
+ if extensions is not missing:
445
+ rv.extensions.update(load_extensions(rv, extensions))
446
+
447
+ if enable_async is not missing:
448
+ rv.is_async = enable_async
449
+
450
+ return _environment_config_check(rv)
451
+
452
+ @property
453
+ def lexer(self) -> Lexer:
454
+ """The lexer for this environment."""
455
+ return get_lexer(self)
456
+
457
+ def iter_extensions(self) -> t.Iterator["Extension"]:
458
+ """Iterates over the extensions by priority."""
459
+ return iter(sorted(self.extensions.values(), key=lambda x: x.priority))
460
+
461
+ def getitem(
462
+ self, obj: t.Any, argument: t.Union[str, t.Any]
463
+ ) -> t.Union[t.Any, Undefined]:
464
+ """Get an item or attribute of an object but prefer the item."""
465
+ try:
466
+ return obj[argument]
467
+ except (AttributeError, TypeError, LookupError):
468
+ if isinstance(argument, str):
469
+ try:
470
+ attr = str(argument)
471
+ except Exception:
472
+ pass
473
+ else:
474
+ try:
475
+ return getattr(obj, attr)
476
+ except AttributeError:
477
+ pass
478
+ return self.undefined(obj=obj, name=argument)
479
+
480
+ def getattr(self, obj: t.Any, attribute: str) -> t.Any:
481
+ """Get an item or attribute of an object but prefer the attribute.
482
+ Unlike :meth:`getitem` the attribute *must* be a string.
483
+ """
484
+ try:
485
+ return getattr(obj, attribute)
486
+ except AttributeError:
487
+ pass
488
+ try:
489
+ return obj[attribute]
490
+ except (TypeError, LookupError, AttributeError):
491
+ return self.undefined(obj=obj, name=attribute)
492
+
493
+ def _filter_test_common(
494
+ self,
495
+ name: t.Union[str, Undefined],
496
+ value: t.Any,
497
+ args: t.Optional[t.Sequence[t.Any]],
498
+ kwargs: t.Optional[t.Mapping[str, t.Any]],
499
+ context: t.Optional[Context],
500
+ eval_ctx: t.Optional[EvalContext],
501
+ is_filter: bool,
502
+ ) -> t.Any:
503
+ if is_filter:
504
+ env_map = self.filters
505
+ type_name = "filter"
506
+ else:
507
+ env_map = self.tests
508
+ type_name = "test"
509
+
510
+ func = env_map.get(name) # type: ignore
511
+
512
+ if func is None:
513
+ msg = f"No {type_name} named {name!r}."
514
+
515
+ if isinstance(name, Undefined):
516
+ try:
517
+ name._fail_with_undefined_error()
518
+ except Exception as e:
519
+ msg = f"{msg} ({e}; did you forget to quote the callable name?)"
520
+
521
+ raise TemplateRuntimeError(msg)
522
+
523
+ args = [value, *(args if args is not None else ())]
524
+ kwargs = kwargs if kwargs is not None else {}
525
+ pass_arg = _PassArg.from_obj(func)
526
+
527
+ if pass_arg is _PassArg.context:
528
+ if context is None:
529
+ raise TemplateRuntimeError(
530
+ f"Attempted to invoke a context {type_name} without context."
531
+ )
532
+
533
+ args.insert(0, context)
534
+ elif pass_arg is _PassArg.eval_context:
535
+ if eval_ctx is None:
536
+ if context is not None:
537
+ eval_ctx = context.eval_ctx
538
+ else:
539
+ eval_ctx = EvalContext(self)
540
+
541
+ args.insert(0, eval_ctx)
542
+ elif pass_arg is _PassArg.environment:
543
+ args.insert(0, self)
544
+
545
+ return func(*args, **kwargs)
546
+
547
+ def call_filter(
548
+ self,
549
+ name: str,
550
+ value: t.Any,
551
+ args: t.Optional[t.Sequence[t.Any]] = None,
552
+ kwargs: t.Optional[t.Mapping[str, t.Any]] = None,
553
+ context: t.Optional[Context] = None,
554
+ eval_ctx: t.Optional[EvalContext] = None,
555
+ ) -> t.Any:
556
+ """Invoke a filter on a value the same way the compiler does.
557
+
558
+ This might return a coroutine if the filter is running from an
559
+ environment in async mode and the filter supports async
560
+ execution. It's your responsibility to await this if needed.
561
+
562
+ .. versionadded:: 2.7
563
+ """
564
+ return self._filter_test_common(
565
+ name, value, args, kwargs, context, eval_ctx, True
566
+ )
567
+
568
+ def call_test(
569
+ self,
570
+ name: str,
571
+ value: t.Any,
572
+ args: t.Optional[t.Sequence[t.Any]] = None,
573
+ kwargs: t.Optional[t.Mapping[str, t.Any]] = None,
574
+ context: t.Optional[Context] = None,
575
+ eval_ctx: t.Optional[EvalContext] = None,
576
+ ) -> t.Any:
577
+ """Invoke a test on a value the same way the compiler does.
578
+
579
+ This might return a coroutine if the test is running from an
580
+ environment in async mode and the test supports async execution.
581
+ It's your responsibility to await this if needed.
582
+
583
+ .. versionchanged:: 3.0
584
+ Tests support ``@pass_context``, etc. decorators. Added
585
+ the ``context`` and ``eval_ctx`` parameters.
586
+
587
+ .. versionadded:: 2.7
588
+ """
589
+ return self._filter_test_common(
590
+ name, value, args, kwargs, context, eval_ctx, False
591
+ )
592
+
593
+ @internalcode
594
+ def parse(
595
+ self,
596
+ source: str,
597
+ name: t.Optional[str] = None,
598
+ filename: t.Optional[str] = None,
599
+ ) -> nodes.Template:
600
+ """Parse the sourcecode and return the abstract syntax tree. This
601
+ tree of nodes is used by the compiler to convert the template into
602
+ executable source- or bytecode. This is useful for debugging or to
603
+ extract information from templates.
604
+
605
+ If you are :ref:`developing Jinja extensions <writing-extensions>`
606
+ this gives you a good overview of the node tree generated.
607
+ """
608
+ try:
609
+ return self._parse(source, name, filename)
610
+ except TemplateSyntaxError:
611
+ self.handle_exception(source=source)
612
+
613
+ def _parse(
614
+ self, source: str, name: t.Optional[str], filename: t.Optional[str]
615
+ ) -> nodes.Template:
616
+ """Internal parsing function used by `parse` and `compile`."""
617
+ return Parser(self, source, name, filename).parse()
618
+
619
+ def lex(
620
+ self,
621
+ source: str,
622
+ name: t.Optional[str] = None,
623
+ filename: t.Optional[str] = None,
624
+ ) -> t.Iterator[t.Tuple[int, str, str]]:
625
+ """Lex the given sourcecode and return a generator that yields
626
+ tokens as tuples in the form ``(lineno, token_type, value)``.
627
+ This can be useful for :ref:`extension development <writing-extensions>`
628
+ and debugging templates.
629
+
630
+ This does not perform preprocessing. If you want the preprocessing
631
+ of the extensions to be applied you have to filter source through
632
+ the :meth:`preprocess` method.
633
+ """
634
+ source = str(source)
635
+ try:
636
+ return self.lexer.tokeniter(source, name, filename)
637
+ except TemplateSyntaxError:
638
+ self.handle_exception(source=source)
639
+
640
+ def preprocess(
641
+ self,
642
+ source: str,
643
+ name: t.Optional[str] = None,
644
+ filename: t.Optional[str] = None,
645
+ ) -> str:
646
+ """Preprocesses the source with all extensions. This is automatically
647
+ called for all parsing and compiling methods but *not* for :meth:`lex`
648
+ because there you usually only want the actual source tokenized.
649
+ """
650
+ return reduce(
651
+ lambda s, e: e.preprocess(s, name, filename),
652
+ self.iter_extensions(),
653
+ str(source),
654
+ )
655
+
656
+ def _tokenize(
657
+ self,
658
+ source: str,
659
+ name: t.Optional[str],
660
+ filename: t.Optional[str] = None,
661
+ state: t.Optional[str] = None,
662
+ ) -> TokenStream:
663
+ """Called by the parser to do the preprocessing and filtering
664
+ for all the extensions. Returns a :class:`~jinja2.lexer.TokenStream`.
665
+ """
666
+ source = self.preprocess(source, name, filename)
667
+ stream = self.lexer.tokenize(source, name, filename, state)
668
+
669
+ for ext in self.iter_extensions():
670
+ stream = ext.filter_stream(stream) # type: ignore
671
+
672
+ if not isinstance(stream, TokenStream):
673
+ stream = TokenStream(stream, name, filename) # type: ignore
674
+
675
+ return stream
676
+
677
+ def _generate(
678
+ self,
679
+ source: nodes.Template,
680
+ name: t.Optional[str],
681
+ filename: t.Optional[str],
682
+ defer_init: bool = False,
683
+ ) -> str:
684
+ """Internal hook that can be overridden to hook a different generate
685
+ method in.
686
+
687
+ .. versionadded:: 2.5
688
+ """
689
+ return generate( # type: ignore
690
+ source,
691
+ self,
692
+ name,
693
+ filename,
694
+ defer_init=defer_init,
695
+ optimized=self.optimized,
696
+ )
697
+
698
+ def _compile(self, source: str, filename: str) -> CodeType:
699
+ """Internal hook that can be overridden to hook a different compile
700
+ method in.
701
+
702
+ .. versionadded:: 2.5
703
+ """
704
+ return compile(source, filename, "exec") # type: ignore
705
+
706
+ @typing.overload
707
+ def compile( # type: ignore
708
+ self,
709
+ source: t.Union[str, nodes.Template],
710
+ name: t.Optional[str] = None,
711
+ filename: t.Optional[str] = None,
712
+ raw: "te.Literal[False]" = False,
713
+ defer_init: bool = False,
714
+ ) -> CodeType:
715
+ ...
716
+
717
+ @typing.overload
718
+ def compile(
719
+ self,
720
+ source: t.Union[str, nodes.Template],
721
+ name: t.Optional[str] = None,
722
+ filename: t.Optional[str] = None,
723
+ raw: "te.Literal[True]" = ...,
724
+ defer_init: bool = False,
725
+ ) -> str:
726
+ ...
727
+
728
+ @internalcode
729
+ def compile(
730
+ self,
731
+ source: t.Union[str, nodes.Template],
732
+ name: t.Optional[str] = None,
733
+ filename: t.Optional[str] = None,
734
+ raw: bool = False,
735
+ defer_init: bool = False,
736
+ ) -> t.Union[str, CodeType]:
737
+ """Compile a node or template source code. The `name` parameter is
738
+ the load name of the template after it was joined using
739
+ :meth:`join_path` if necessary, not the filename on the file system.
740
+ the `filename` parameter is the estimated filename of the template on
741
+ the file system. If the template came from a database or memory this
742
+ can be omitted.
743
+
744
+ The return value of this method is a python code object. If the `raw`
745
+ parameter is `True` the return value will be a string with python
746
+ code equivalent to the bytecode returned otherwise. This method is
747
+ mainly used internally.
748
+
749
+ `defer_init` is use internally to aid the module code generator. This
750
+ causes the generated code to be able to import without the global
751
+ environment variable to be set.
752
+
753
+ .. versionadded:: 2.4
754
+ `defer_init` parameter added.
755
+ """
756
+ source_hint = None
757
+ try:
758
+ if isinstance(source, str):
759
+ source_hint = source
760
+ source = self._parse(source, name, filename)
761
+ source = self._generate(source, name, filename, defer_init=defer_init)
762
+ if raw:
763
+ return source
764
+ if filename is None:
765
+ filename = "<template>"
766
+ return self._compile(source, filename)
767
+ except TemplateSyntaxError:
768
+ self.handle_exception(source=source_hint)
769
+
770
+ def compile_expression(
771
+ self, source: str, undefined_to_none: bool = True
772
+ ) -> "TemplateExpression":
773
+ """A handy helper method that returns a callable that accepts keyword
774
+ arguments that appear as variables in the expression. If called it
775
+ returns the result of the expression.
776
+
777
+ This is useful if applications want to use the same rules as Jinja
778
+ in template "configuration files" or similar situations.
779
+
780
+ Example usage:
781
+
782
+ >>> env = Environment()
783
+ >>> expr = env.compile_expression('foo == 42')
784
+ >>> expr(foo=23)
785
+ False
786
+ >>> expr(foo=42)
787
+ True
788
+
789
+ Per default the return value is converted to `None` if the
790
+ expression returns an undefined value. This can be changed
791
+ by setting `undefined_to_none` to `False`.
792
+
793
+ >>> env.compile_expression('var')() is None
794
+ True
795
+ >>> env.compile_expression('var', undefined_to_none=False)()
796
+ Undefined
797
+
798
+ .. versionadded:: 2.1
799
+ """
800
+ parser = Parser(self, source, state="variable")
801
+ try:
802
+ expr = parser.parse_expression()
803
+ if not parser.stream.eos:
804
+ raise TemplateSyntaxError(
805
+ "chunk after expression", parser.stream.current.lineno, None, None
806
+ )
807
+ expr.set_environment(self)
808
+ except TemplateSyntaxError:
809
+ self.handle_exception(source=source)
810
+
811
+ body = [nodes.Assign(nodes.Name("result", "store"), expr, lineno=1)]
812
+ template = self.from_string(nodes.Template(body, lineno=1))
813
+ return TemplateExpression(template, undefined_to_none)
814
+
815
+ def compile_templates(
816
+ self,
817
+ target: t.Union[str, os.PathLike],
818
+ extensions: t.Optional[t.Collection[str]] = None,
819
+ filter_func: t.Optional[t.Callable[[str], bool]] = None,
820
+ zip: t.Optional[str] = "deflated",
821
+ log_function: t.Optional[t.Callable[[str], None]] = None,
822
+ ignore_errors: bool = True,
823
+ ) -> None:
824
+ """Finds all the templates the loader can find, compiles them
825
+ and stores them in `target`. If `zip` is `None`, instead of in a
826
+ zipfile, the templates will be stored in a directory.
827
+ By default a deflate zip algorithm is used. To switch to
828
+ the stored algorithm, `zip` can be set to ``'stored'``.
829
+
830
+ `extensions` and `filter_func` are passed to :meth:`list_templates`.
831
+ Each template returned will be compiled to the target folder or
832
+ zipfile.
833
+
834
+ By default template compilation errors are ignored. In case a
835
+ log function is provided, errors are logged. If you want template
836
+ syntax errors to abort the compilation you can set `ignore_errors`
837
+ to `False` and you will get an exception on syntax errors.
838
+
839
+ .. versionadded:: 2.4
840
+ """
841
+ from .loaders import ModuleLoader
842
+
843
+ if log_function is None:
844
+
845
+ def log_function(x: str) -> None:
846
+ pass
847
+
848
+ assert log_function is not None
849
+ assert self.loader is not None, "No loader configured."
850
+
851
+ def write_file(filename: str, data: str) -> None:
852
+ if zip:
853
+ info = ZipInfo(filename)
854
+ info.external_attr = 0o755 << 16
855
+ zip_file.writestr(info, data)
856
+ else:
857
+ with open(os.path.join(target, filename), "wb") as f:
858
+ f.write(data.encode("utf8"))
859
+
860
+ if zip is not None:
861
+ from zipfile import ZipFile, ZipInfo, ZIP_DEFLATED, ZIP_STORED
862
+
863
+ zip_file = ZipFile(
864
+ target, "w", dict(deflated=ZIP_DEFLATED, stored=ZIP_STORED)[zip]
865
+ )
866
+ log_function(f"Compiling into Zip archive {target!r}")
867
+ else:
868
+ if not os.path.isdir(target):
869
+ os.makedirs(target)
870
+ log_function(f"Compiling into folder {target!r}")
871
+
872
+ try:
873
+ for name in self.list_templates(extensions, filter_func):
874
+ source, filename, _ = self.loader.get_source(self, name)
875
+ try:
876
+ code = self.compile(source, name, filename, True, True)
877
+ except TemplateSyntaxError as e:
878
+ if not ignore_errors:
879
+ raise
880
+ log_function(f'Could not compile "{name}": {e}')
881
+ continue
882
+
883
+ filename = ModuleLoader.get_module_filename(name)
884
+
885
+ write_file(filename, code)
886
+ log_function(f'Compiled "{name}" as {filename}')
887
+ finally:
888
+ if zip:
889
+ zip_file.close()
890
+
891
+ log_function("Finished compiling templates")
892
+
893
+ def list_templates(
894
+ self,
895
+ extensions: t.Optional[t.Collection[str]] = None,
896
+ filter_func: t.Optional[t.Callable[[str], bool]] = None,
897
+ ) -> t.List[str]:
898
+ """Returns a list of templates for this environment. This requires
899
+ that the loader supports the loader's
900
+ :meth:`~BaseLoader.list_templates` method.
901
+
902
+ If there are other files in the template folder besides the
903
+ actual templates, the returned list can be filtered. There are two
904
+ ways: either `extensions` is set to a list of file extensions for
905
+ templates, or a `filter_func` can be provided which is a callable that
906
+ is passed a template name and should return `True` if it should end up
907
+ in the result list.
908
+
909
+ If the loader does not support that, a :exc:`TypeError` is raised.
910
+
911
+ .. versionadded:: 2.4
912
+ """
913
+ assert self.loader is not None, "No loader configured."
914
+ names = self.loader.list_templates()
915
+
916
+ if extensions is not None:
917
+ if filter_func is not None:
918
+ raise TypeError(
919
+ "either extensions or filter_func can be passed, but not both"
920
+ )
921
+
922
+ def filter_func(x: str) -> bool:
923
+ return "." in x and x.rsplit(".", 1)[1] in extensions # type: ignore
924
+
925
+ if filter_func is not None:
926
+ names = [name for name in names if filter_func(name)]
927
+
928
+ return names
929
+
930
+ def handle_exception(self, source: t.Optional[str] = None) -> "te.NoReturn":
931
+ """Exception handling helper. This is used internally to either raise
932
+ rewritten exceptions or return a rendered traceback for the template.
933
+ """
934
+ from .debug import rewrite_traceback_stack
935
+
936
+ raise rewrite_traceback_stack(source=source)
937
+
938
+ def join_path(self, template: str, parent: str) -> str:
939
+ """Join a template with the parent. By default all the lookups are
940
+ relative to the loader root so this method returns the `template`
941
+ parameter unchanged, but if the paths should be relative to the
942
+ parent template, this function can be used to calculate the real
943
+ template name.
944
+
945
+ Subclasses may override this method and implement template path
946
+ joining here.
947
+ """
948
+ return template
949
+
950
+ @internalcode
951
+ def _load_template(
952
+ self, name: str, globals: t.Optional[t.MutableMapping[str, t.Any]]
953
+ ) -> "Template":
954
+ if self.loader is None:
955
+ raise TypeError("no loader for this environment specified")
956
+ cache_key = (weakref.ref(self.loader), name)
957
+ if self.cache is not None:
958
+ template = self.cache.get(cache_key)
959
+ if template is not None and (
960
+ not self.auto_reload or template.is_up_to_date
961
+ ):
962
+ # template.globals is a ChainMap, modifying it will only
963
+ # affect the template, not the environment globals.
964
+ if globals:
965
+ template.globals.update(globals)
966
+
967
+ return template
968
+
969
+ template = self.loader.load(self, name, self.make_globals(globals))
970
+
971
+ if self.cache is not None:
972
+ self.cache[cache_key] = template
973
+ return template
974
+
975
+ @internalcode
976
+ def get_template(
977
+ self,
978
+ name: t.Union[str, "Template"],
979
+ parent: t.Optional[str] = None,
980
+ globals: t.Optional[t.MutableMapping[str, t.Any]] = None,
981
+ ) -> "Template":
982
+ """Load a template by name with :attr:`loader` and return a
983
+ :class:`Template`. If the template does not exist a
984
+ :exc:`TemplateNotFound` exception is raised.
985
+
986
+ :param name: Name of the template to load. When loading
987
+ templates from the filesystem, "/" is used as the path
988
+ separator, even on Windows.
989
+ :param parent: The name of the parent template importing this
990
+ template. :meth:`join_path` can be used to implement name
991
+ transformations with this.
992
+ :param globals: Extend the environment :attr:`globals` with
993
+ these extra variables available for all renders of this
994
+ template. If the template has already been loaded and
995
+ cached, its globals are updated with any new items.
996
+
997
+ .. versionchanged:: 3.0
998
+ If a template is loaded from cache, ``globals`` will update
999
+ the template's globals instead of ignoring the new values.
1000
+
1001
+ .. versionchanged:: 2.4
1002
+ If ``name`` is a :class:`Template` object it is returned
1003
+ unchanged.
1004
+ """
1005
+ if isinstance(name, Template):
1006
+ return name
1007
+ if parent is not None:
1008
+ name = self.join_path(name, parent)
1009
+
1010
+ return self._load_template(name, globals)
1011
+
1012
+ @internalcode
1013
+ def select_template(
1014
+ self,
1015
+ names: t.Iterable[t.Union[str, "Template"]],
1016
+ parent: t.Optional[str] = None,
1017
+ globals: t.Optional[t.MutableMapping[str, t.Any]] = None,
1018
+ ) -> "Template":
1019
+ """Like :meth:`get_template`, but tries loading multiple names.
1020
+ If none of the names can be loaded a :exc:`TemplatesNotFound`
1021
+ exception is raised.
1022
+
1023
+ :param names: List of template names to try loading in order.
1024
+ :param parent: The name of the parent template importing this
1025
+ template. :meth:`join_path` can be used to implement name
1026
+ transformations with this.
1027
+ :param globals: Extend the environment :attr:`globals` with
1028
+ these extra variables available for all renders of this
1029
+ template. If the template has already been loaded and
1030
+ cached, its globals are updated with any new items.
1031
+
1032
+ .. versionchanged:: 3.0
1033
+ If a template is loaded from cache, ``globals`` will update
1034
+ the template's globals instead of ignoring the new values.
1035
+
1036
+ .. versionchanged:: 2.11
1037
+ If ``names`` is :class:`Undefined`, an :exc:`UndefinedError`
1038
+ is raised instead. If no templates were found and ``names``
1039
+ contains :class:`Undefined`, the message is more helpful.
1040
+
1041
+ .. versionchanged:: 2.4
1042
+ If ``names`` contains a :class:`Template` object it is
1043
+ returned unchanged.
1044
+
1045
+ .. versionadded:: 2.3
1046
+ """
1047
+ if isinstance(names, Undefined):
1048
+ names._fail_with_undefined_error()
1049
+
1050
+ if not names:
1051
+ raise TemplatesNotFound(
1052
+ message="Tried to select from an empty list of templates."
1053
+ )
1054
+
1055
+ for name in names:
1056
+ if isinstance(name, Template):
1057
+ return name
1058
+ if parent is not None:
1059
+ name = self.join_path(name, parent)
1060
+ try:
1061
+ return self._load_template(name, globals)
1062
+ except (TemplateNotFound, UndefinedError):
1063
+ pass
1064
+ raise TemplatesNotFound(names) # type: ignore
1065
+
1066
+ @internalcode
1067
+ def get_or_select_template(
1068
+ self,
1069
+ template_name_or_list: t.Union[
1070
+ str, "Template", t.List[t.Union[str, "Template"]]
1071
+ ],
1072
+ parent: t.Optional[str] = None,
1073
+ globals: t.Optional[t.MutableMapping[str, t.Any]] = None,
1074
+ ) -> "Template":
1075
+ """Use :meth:`select_template` if an iterable of template names
1076
+ is given, or :meth:`get_template` if one name is given.
1077
+
1078
+ .. versionadded:: 2.3
1079
+ """
1080
+ if isinstance(template_name_or_list, (str, Undefined)):
1081
+ return self.get_template(template_name_or_list, parent, globals)
1082
+ elif isinstance(template_name_or_list, Template):
1083
+ return template_name_or_list
1084
+ return self.select_template(template_name_or_list, parent, globals)
1085
+
1086
+ def from_string(
1087
+ self,
1088
+ source: t.Union[str, nodes.Template],
1089
+ globals: t.Optional[t.MutableMapping[str, t.Any]] = None,
1090
+ template_class: t.Optional[t.Type["Template"]] = None,
1091
+ ) -> "Template":
1092
+ """Load a template from a source string without using
1093
+ :attr:`loader`.
1094
+
1095
+ :param source: Jinja source to compile into a template.
1096
+ :param globals: Extend the environment :attr:`globals` with
1097
+ these extra variables available for all renders of this
1098
+ template. If the template has already been loaded and
1099
+ cached, its globals are updated with any new items.
1100
+ :param template_class: Return an instance of this
1101
+ :class:`Template` class.
1102
+ """
1103
+ gs = self.make_globals(globals)
1104
+ cls = template_class or self.template_class
1105
+ return cls.from_code(self, self.compile(source), gs, None)
1106
+
1107
+ def make_globals(
1108
+ self, d: t.Optional[t.MutableMapping[str, t.Any]]
1109
+ ) -> t.MutableMapping[str, t.Any]:
1110
+ """Make the globals map for a template. Any given template
1111
+ globals overlay the environment :attr:`globals`.
1112
+
1113
+ Returns a :class:`collections.ChainMap`. This allows any changes
1114
+ to a template's globals to only affect that template, while
1115
+ changes to the environment's globals are still reflected.
1116
+ However, avoid modifying any globals after a template is loaded.
1117
+
1118
+ :param d: Dict of template-specific globals.
1119
+
1120
+ .. versionchanged:: 3.0
1121
+ Use :class:`collections.ChainMap` to always prevent mutating
1122
+ environment globals.
1123
+ """
1124
+ if d is None:
1125
+ d = {}
1126
+
1127
+ return ChainMap(d, self.globals)
1128
+
1129
+
1130
+ class Template:
1131
+ """A compiled template that can be rendered.
1132
+
1133
+ Use the methods on :class:`Environment` to create or load templates.
1134
+ The environment is used to configure how templates are compiled and
1135
+ behave.
1136
+
1137
+ It is also possible to create a template object directly. This is
1138
+ not usually recommended. The constructor takes most of the same
1139
+ arguments as :class:`Environment`. All templates created with the
1140
+ same environment arguments share the same ephemeral ``Environment``
1141
+ instance behind the scenes.
1142
+
1143
+ A template object should be considered immutable. Modifications on
1144
+ the object are not supported.
1145
+ """
1146
+
1147
+ #: Type of environment to create when creating a template directly
1148
+ #: rather than through an existing environment.
1149
+ environment_class: t.Type[Environment] = Environment
1150
+
1151
+ environment: Environment
1152
+ globals: t.MutableMapping[str, t.Any]
1153
+ name: t.Optional[str]
1154
+ filename: t.Optional[str]
1155
+ blocks: t.Dict[str, t.Callable[[Context], t.Iterator[str]]]
1156
+ root_render_func: t.Callable[[Context], t.Iterator[str]]
1157
+ _module: t.Optional["TemplateModule"]
1158
+ _debug_info: str
1159
+ _uptodate: t.Optional[t.Callable[[], bool]]
1160
+
1161
+ def __new__(
1162
+ cls,
1163
+ source: t.Union[str, nodes.Template],
1164
+ block_start_string: str = BLOCK_START_STRING,
1165
+ block_end_string: str = BLOCK_END_STRING,
1166
+ variable_start_string: str = VARIABLE_START_STRING,
1167
+ variable_end_string: str = VARIABLE_END_STRING,
1168
+ comment_start_string: str = COMMENT_START_STRING,
1169
+ comment_end_string: str = COMMENT_END_STRING,
1170
+ line_statement_prefix: t.Optional[str] = LINE_STATEMENT_PREFIX,
1171
+ line_comment_prefix: t.Optional[str] = LINE_COMMENT_PREFIX,
1172
+ trim_blocks: bool = TRIM_BLOCKS,
1173
+ lstrip_blocks: bool = LSTRIP_BLOCKS,
1174
+ newline_sequence: "te.Literal['\\n', '\\r\\n', '\\r']" = NEWLINE_SEQUENCE,
1175
+ keep_trailing_newline: bool = KEEP_TRAILING_NEWLINE,
1176
+ extensions: t.Sequence[t.Union[str, t.Type["Extension"]]] = (),
1177
+ optimized: bool = True,
1178
+ undefined: t.Type[Undefined] = Undefined,
1179
+ finalize: t.Optional[t.Callable[..., t.Any]] = None,
1180
+ autoescape: t.Union[bool, t.Callable[[t.Optional[str]], bool]] = False,
1181
+ enable_async: bool = False,
1182
+ ) -> t.Any: # it returns a `Template`, but this breaks the sphinx build...
1183
+ env = get_spontaneous_environment(
1184
+ cls.environment_class, # type: ignore
1185
+ block_start_string,
1186
+ block_end_string,
1187
+ variable_start_string,
1188
+ variable_end_string,
1189
+ comment_start_string,
1190
+ comment_end_string,
1191
+ line_statement_prefix,
1192
+ line_comment_prefix,
1193
+ trim_blocks,
1194
+ lstrip_blocks,
1195
+ newline_sequence,
1196
+ keep_trailing_newline,
1197
+ frozenset(extensions),
1198
+ optimized,
1199
+ undefined, # type: ignore
1200
+ finalize,
1201
+ autoescape,
1202
+ None,
1203
+ 0,
1204
+ False,
1205
+ None,
1206
+ enable_async,
1207
+ )
1208
+ return env.from_string(source, template_class=cls)
1209
+
1210
+ @classmethod
1211
+ def from_code(
1212
+ cls,
1213
+ environment: Environment,
1214
+ code: CodeType,
1215
+ globals: t.MutableMapping[str, t.Any],
1216
+ uptodate: t.Optional[t.Callable[[], bool]] = None,
1217
+ ) -> "Template":
1218
+ """Creates a template object from compiled code and the globals. This
1219
+ is used by the loaders and environment to create a template object.
1220
+ """
1221
+ namespace = {"environment": environment, "__file__": code.co_filename}
1222
+ exec(code, namespace)
1223
+ rv = cls._from_namespace(environment, namespace, globals)
1224
+ rv._uptodate = uptodate
1225
+ return rv
1226
+
1227
+ @classmethod
1228
+ def from_module_dict(
1229
+ cls,
1230
+ environment: Environment,
1231
+ module_dict: t.MutableMapping[str, t.Any],
1232
+ globals: t.MutableMapping[str, t.Any],
1233
+ ) -> "Template":
1234
+ """Creates a template object from a module. This is used by the
1235
+ module loader to create a template object.
1236
+
1237
+ .. versionadded:: 2.4
1238
+ """
1239
+ return cls._from_namespace(environment, module_dict, globals)
1240
+
1241
+ @classmethod
1242
+ def _from_namespace(
1243
+ cls,
1244
+ environment: Environment,
1245
+ namespace: t.MutableMapping[str, t.Any],
1246
+ globals: t.MutableMapping[str, t.Any],
1247
+ ) -> "Template":
1248
+ t: "Template" = object.__new__(cls)
1249
+ t.environment = environment
1250
+ t.globals = globals
1251
+ t.name = namespace["name"]
1252
+ t.filename = namespace["__file__"]
1253
+ t.blocks = namespace["blocks"]
1254
+
1255
+ # render function and module
1256
+ t.root_render_func = namespace["root"] # type: ignore
1257
+ t._module = None
1258
+
1259
+ # debug and loader helpers
1260
+ t._debug_info = namespace["debug_info"]
1261
+ t._uptodate = None
1262
+
1263
+ # store the reference
1264
+ namespace["environment"] = environment
1265
+ namespace["__jinja_template__"] = t
1266
+
1267
+ return t
1268
+
1269
+ def render(self, *args: t.Any, **kwargs: t.Any) -> str:
1270
+ """This method accepts the same arguments as the `dict` constructor:
1271
+ A dict, a dict subclass or some keyword arguments. If no arguments
1272
+ are given the context will be empty. These two calls do the same::
1273
+
1274
+ template.render(knights='that say nih')
1275
+ template.render({'knights': 'that say nih'})
1276
+
1277
+ This will return the rendered template as a string.
1278
+ """
1279
+ if self.environment.is_async:
1280
+ import asyncio
1281
+
1282
+ close = False
1283
+
1284
+ try:
1285
+ loop = asyncio.get_running_loop()
1286
+ except RuntimeError:
1287
+ loop = asyncio.new_event_loop()
1288
+ close = True
1289
+
1290
+ try:
1291
+ return loop.run_until_complete(self.render_async(*args, **kwargs))
1292
+ finally:
1293
+ if close:
1294
+ loop.close()
1295
+
1296
+ ctx = self.new_context(dict(*args, **kwargs))
1297
+
1298
+ try:
1299
+ return self.environment.concat(self.root_render_func(ctx)) # type: ignore
1300
+ except Exception:
1301
+ self.environment.handle_exception()
1302
+
1303
+ async def render_async(self, *args: t.Any, **kwargs: t.Any) -> str:
1304
+ """This works similar to :meth:`render` but returns a coroutine
1305
+ that when awaited returns the entire rendered template string. This
1306
+ requires the async feature to be enabled.
1307
+
1308
+ Example usage::
1309
+
1310
+ await template.render_async(knights='that say nih; asynchronously')
1311
+ """
1312
+ if not self.environment.is_async:
1313
+ raise RuntimeError(
1314
+ "The environment was not created with async mode enabled."
1315
+ )
1316
+
1317
+ ctx = self.new_context(dict(*args, **kwargs))
1318
+
1319
+ try:
1320
+ return self.environment.concat( # type: ignore
1321
+ [n async for n in self.root_render_func(ctx)] # type: ignore
1322
+ )
1323
+ except Exception:
1324
+ return self.environment.handle_exception()
1325
+
1326
+ def stream(self, *args: t.Any, **kwargs: t.Any) -> "TemplateStream":
1327
+ """Works exactly like :meth:`generate` but returns a
1328
+ :class:`TemplateStream`.
1329
+ """
1330
+ return TemplateStream(self.generate(*args, **kwargs))
1331
+
1332
+ def generate(self, *args: t.Any, **kwargs: t.Any) -> t.Iterator[str]:
1333
+ """For very large templates it can be useful to not render the whole
1334
+ template at once but evaluate each statement after another and yield
1335
+ piece for piece. This method basically does exactly that and returns
1336
+ a generator that yields one item after another as strings.
1337
+
1338
+ It accepts the same arguments as :meth:`render`.
1339
+ """
1340
+ if self.environment.is_async:
1341
+ import asyncio
1342
+
1343
+ async def to_list() -> t.List[str]:
1344
+ return [x async for x in self.generate_async(*args, **kwargs)]
1345
+
1346
+ yield from asyncio.run(to_list())
1347
+ return
1348
+
1349
+ ctx = self.new_context(dict(*args, **kwargs))
1350
+
1351
+ try:
1352
+ yield from self.root_render_func(ctx) # type: ignore
1353
+ except Exception:
1354
+ yield self.environment.handle_exception()
1355
+
1356
+ async def generate_async(
1357
+ self, *args: t.Any, **kwargs: t.Any
1358
+ ) -> t.AsyncIterator[str]:
1359
+ """An async version of :meth:`generate`. Works very similarly but
1360
+ returns an async iterator instead.
1361
+ """
1362
+ if not self.environment.is_async:
1363
+ raise RuntimeError(
1364
+ "The environment was not created with async mode enabled."
1365
+ )
1366
+
1367
+ ctx = self.new_context(dict(*args, **kwargs))
1368
+
1369
+ try:
1370
+ async for event in self.root_render_func(ctx): # type: ignore
1371
+ yield event
1372
+ except Exception:
1373
+ yield self.environment.handle_exception()
1374
+
1375
+ def new_context(
1376
+ self,
1377
+ vars: t.Optional[t.Dict[str, t.Any]] = None,
1378
+ shared: bool = False,
1379
+ locals: t.Optional[t.Mapping[str, t.Any]] = None,
1380
+ ) -> Context:
1381
+ """Create a new :class:`Context` for this template. The vars
1382
+ provided will be passed to the template. Per default the globals
1383
+ are added to the context. If shared is set to `True` the data
1384
+ is passed as is to the context without adding the globals.
1385
+
1386
+ `locals` can be a dict of local variables for internal usage.
1387
+ """
1388
+ return new_context(
1389
+ self.environment, self.name, self.blocks, vars, shared, self.globals, locals
1390
+ )
1391
+
1392
+ def make_module(
1393
+ self,
1394
+ vars: t.Optional[t.Dict[str, t.Any]] = None,
1395
+ shared: bool = False,
1396
+ locals: t.Optional[t.Mapping[str, t.Any]] = None,
1397
+ ) -> "TemplateModule":
1398
+ """This method works like the :attr:`module` attribute when called
1399
+ without arguments but it will evaluate the template on every call
1400
+ rather than caching it. It's also possible to provide
1401
+ a dict which is then used as context. The arguments are the same
1402
+ as for the :meth:`new_context` method.
1403
+ """
1404
+ ctx = self.new_context(vars, shared, locals)
1405
+ return TemplateModule(self, ctx)
1406
+
1407
+ async def make_module_async(
1408
+ self,
1409
+ vars: t.Optional[t.Dict[str, t.Any]] = None,
1410
+ shared: bool = False,
1411
+ locals: t.Optional[t.Mapping[str, t.Any]] = None,
1412
+ ) -> "TemplateModule":
1413
+ """As template module creation can invoke template code for
1414
+ asynchronous executions this method must be used instead of the
1415
+ normal :meth:`make_module` one. Likewise the module attribute
1416
+ becomes unavailable in async mode.
1417
+ """
1418
+ ctx = self.new_context(vars, shared, locals)
1419
+ return TemplateModule(
1420
+ self, ctx, [x async for x in self.root_render_func(ctx)] # type: ignore
1421
+ )
1422
+
1423
+ @internalcode
1424
+ def _get_default_module(self, ctx: t.Optional[Context] = None) -> "TemplateModule":
1425
+ """If a context is passed in, this means that the template was
1426
+ imported. Imported templates have access to the current
1427
+ template's globals by default, but they can only be accessed via
1428
+ the context during runtime.
1429
+
1430
+ If there are new globals, we need to create a new module because
1431
+ the cached module is already rendered and will not have access
1432
+ to globals from the current context. This new module is not
1433
+ cached because the template can be imported elsewhere, and it
1434
+ should have access to only the current template's globals.
1435
+ """
1436
+ if self.environment.is_async:
1437
+ raise RuntimeError("Module is not available in async mode.")
1438
+
1439
+ if ctx is not None:
1440
+ keys = ctx.globals_keys - self.globals.keys()
1441
+
1442
+ if keys:
1443
+ return self.make_module({k: ctx.parent[k] for k in keys})
1444
+
1445
+ if self._module is None:
1446
+ self._module = self.make_module()
1447
+
1448
+ return self._module
1449
+
1450
+ async def _get_default_module_async(
1451
+ self, ctx: t.Optional[Context] = None
1452
+ ) -> "TemplateModule":
1453
+ if ctx is not None:
1454
+ keys = ctx.globals_keys - self.globals.keys()
1455
+
1456
+ if keys:
1457
+ return await self.make_module_async({k: ctx.parent[k] for k in keys})
1458
+
1459
+ if self._module is None:
1460
+ self._module = await self.make_module_async()
1461
+
1462
+ return self._module
1463
+
1464
+ @property
1465
+ def module(self) -> "TemplateModule":
1466
+ """The template as module. This is used for imports in the
1467
+ template runtime but is also useful if one wants to access
1468
+ exported template variables from the Python layer:
1469
+
1470
+ >>> t = Template('{% macro foo() %}42{% endmacro %}23')
1471
+ >>> str(t.module)
1472
+ '23'
1473
+ >>> t.module.foo() == u'42'
1474
+ True
1475
+
1476
+ This attribute is not available if async mode is enabled.
1477
+ """
1478
+ return self._get_default_module()
1479
+
1480
+ def get_corresponding_lineno(self, lineno: int) -> int:
1481
+ """Return the source line number of a line number in the
1482
+ generated bytecode as they are not in sync.
1483
+ """
1484
+ for template_line, code_line in reversed(self.debug_info):
1485
+ if code_line <= lineno:
1486
+ return template_line
1487
+ return 1
1488
+
1489
+ @property
1490
+ def is_up_to_date(self) -> bool:
1491
+ """If this variable is `False` there is a newer version available."""
1492
+ if self._uptodate is None:
1493
+ return True
1494
+ return self._uptodate()
1495
+
1496
+ @property
1497
+ def debug_info(self) -> t.List[t.Tuple[int, int]]:
1498
+ """The debug info mapping."""
1499
+ if self._debug_info:
1500
+ return [
1501
+ tuple(map(int, x.split("="))) # type: ignore
1502
+ for x in self._debug_info.split("&")
1503
+ ]
1504
+
1505
+ return []
1506
+
1507
+ def __repr__(self) -> str:
1508
+ if self.name is None:
1509
+ name = f"memory:{id(self):x}"
1510
+ else:
1511
+ name = repr(self.name)
1512
+ return f"<{type(self).__name__} {name}>"
1513
+
1514
+
1515
+ class TemplateModule:
1516
+ """Represents an imported template. All the exported names of the
1517
+ template are available as attributes on this object. Additionally
1518
+ converting it into a string renders the contents.
1519
+ """
1520
+
1521
+ def __init__(
1522
+ self,
1523
+ template: Template,
1524
+ context: Context,
1525
+ body_stream: t.Optional[t.Iterable[str]] = None,
1526
+ ) -> None:
1527
+ if body_stream is None:
1528
+ if context.environment.is_async:
1529
+ raise RuntimeError(
1530
+ "Async mode requires a body stream to be passed to"
1531
+ " a template module. Use the async methods of the"
1532
+ " API you are using."
1533
+ )
1534
+
1535
+ body_stream = list(template.root_render_func(context)) # type: ignore
1536
+
1537
+ self._body_stream = body_stream
1538
+ self.__dict__.update(context.get_exported())
1539
+ self.__name__ = template.name
1540
+
1541
+ def __html__(self) -> Markup:
1542
+ return Markup(concat(self._body_stream))
1543
+
1544
+ def __str__(self) -> str:
1545
+ return concat(self._body_stream)
1546
+
1547
+ def __repr__(self) -> str:
1548
+ if self.__name__ is None:
1549
+ name = f"memory:{id(self):x}"
1550
+ else:
1551
+ name = repr(self.__name__)
1552
+ return f"<{type(self).__name__} {name}>"
1553
+
1554
+
1555
+ class TemplateExpression:
1556
+ """The :meth:`jinja2.Environment.compile_expression` method returns an
1557
+ instance of this object. It encapsulates the expression-like access
1558
+ to the template with an expression it wraps.
1559
+ """
1560
+
1561
+ def __init__(self, template: Template, undefined_to_none: bool) -> None:
1562
+ self._template = template
1563
+ self._undefined_to_none = undefined_to_none
1564
+
1565
+ def __call__(self, *args: t.Any, **kwargs: t.Any) -> t.Optional[t.Any]:
1566
+ context = self._template.new_context(dict(*args, **kwargs))
1567
+ consume(self._template.root_render_func(context)) # type: ignore
1568
+ rv = context.vars["result"]
1569
+ if self._undefined_to_none and isinstance(rv, Undefined):
1570
+ rv = None
1571
+ return rv
1572
+
1573
+
1574
+ class TemplateStream:
1575
+ """A template stream works pretty much like an ordinary python generator
1576
+ but it can buffer multiple items to reduce the number of total iterations.
1577
+ Per default the output is unbuffered which means that for every unbuffered
1578
+ instruction in the template one string is yielded.
1579
+
1580
+ If buffering is enabled with a buffer size of 5, five items are combined
1581
+ into a new string. This is mainly useful if you are streaming
1582
+ big templates to a client via WSGI which flushes after each iteration.
1583
+ """
1584
+
1585
+ def __init__(self, gen: t.Iterator[str]) -> None:
1586
+ self._gen = gen
1587
+ self.disable_buffering()
1588
+
1589
+ def dump(
1590
+ self,
1591
+ fp: t.Union[str, t.IO],
1592
+ encoding: t.Optional[str] = None,
1593
+ errors: t.Optional[str] = "strict",
1594
+ ) -> None:
1595
+ """Dump the complete stream into a file or file-like object.
1596
+ Per default strings are written, if you want to encode
1597
+ before writing specify an `encoding`.
1598
+
1599
+ Example usage::
1600
+
1601
+ Template('Hello {{ name }}!').stream(name='foo').dump('hello.html')
1602
+ """
1603
+ close = False
1604
+
1605
+ if isinstance(fp, str):
1606
+ if encoding is None:
1607
+ encoding = "utf-8"
1608
+
1609
+ fp = open(fp, "wb")
1610
+ close = True
1611
+ try:
1612
+ if encoding is not None:
1613
+ iterable = (x.encode(encoding, errors) for x in self) # type: ignore
1614
+ else:
1615
+ iterable = self # type: ignore
1616
+
1617
+ if hasattr(fp, "writelines"):
1618
+ fp.writelines(iterable)
1619
+ else:
1620
+ for item in iterable:
1621
+ fp.write(item)
1622
+ finally:
1623
+ if close:
1624
+ fp.close()
1625
+
1626
+ def disable_buffering(self) -> None:
1627
+ """Disable the output buffering."""
1628
+ self._next = partial(next, self._gen)
1629
+ self.buffered = False
1630
+
1631
+ def _buffered_generator(self, size: int) -> t.Iterator[str]:
1632
+ buf: t.List[str] = []
1633
+ c_size = 0
1634
+ push = buf.append
1635
+
1636
+ while True:
1637
+ try:
1638
+ while c_size < size:
1639
+ c = next(self._gen)
1640
+ push(c)
1641
+ if c:
1642
+ c_size += 1
1643
+ except StopIteration:
1644
+ if not c_size:
1645
+ return
1646
+ yield concat(buf)
1647
+ del buf[:]
1648
+ c_size = 0
1649
+
1650
+ def enable_buffering(self, size: int = 5) -> None:
1651
+ """Enable buffering. Buffer `size` items before yielding them."""
1652
+ if size <= 1:
1653
+ raise ValueError("buffer size too small")
1654
+
1655
+ self.buffered = True
1656
+ self._next = partial(next, self._buffered_generator(size))
1657
+
1658
+ def __iter__(self) -> "TemplateStream":
1659
+ return self
1660
+
1661
+ def __next__(self) -> str:
1662
+ return self._next() # type: ignore
1663
+
1664
+
1665
+ # hook in default template class. if anyone reads this comment: ignore that
1666
+ # it's possible to use custom templates ;-)
1667
+ Environment.template_class = Template
lib/python3.11/site-packages/jinja2/exceptions.py ADDED
@@ -0,0 +1,166 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import typing as t
2
+
3
+ if t.TYPE_CHECKING:
4
+ from .runtime import Undefined
5
+
6
+
7
+ class TemplateError(Exception):
8
+ """Baseclass for all template errors."""
9
+
10
+ def __init__(self, message: t.Optional[str] = None) -> None:
11
+ super().__init__(message)
12
+
13
+ @property
14
+ def message(self) -> t.Optional[str]:
15
+ return self.args[0] if self.args else None
16
+
17
+
18
+ class TemplateNotFound(IOError, LookupError, TemplateError):
19
+ """Raised if a template does not exist.
20
+
21
+ .. versionchanged:: 2.11
22
+ If the given name is :class:`Undefined` and no message was
23
+ provided, an :exc:`UndefinedError` is raised.
24
+ """
25
+
26
+ # Silence the Python warning about message being deprecated since
27
+ # it's not valid here.
28
+ message: t.Optional[str] = None
29
+
30
+ def __init__(
31
+ self,
32
+ name: t.Optional[t.Union[str, "Undefined"]],
33
+ message: t.Optional[str] = None,
34
+ ) -> None:
35
+ IOError.__init__(self, name)
36
+
37
+ if message is None:
38
+ from .runtime import Undefined
39
+
40
+ if isinstance(name, Undefined):
41
+ name._fail_with_undefined_error()
42
+
43
+ message = name
44
+
45
+ self.message = message
46
+ self.name = name
47
+ self.templates = [name]
48
+
49
+ def __str__(self) -> str:
50
+ return str(self.message)
51
+
52
+
53
+ class TemplatesNotFound(TemplateNotFound):
54
+ """Like :class:`TemplateNotFound` but raised if multiple templates
55
+ are selected. This is a subclass of :class:`TemplateNotFound`
56
+ exception, so just catching the base exception will catch both.
57
+
58
+ .. versionchanged:: 2.11
59
+ If a name in the list of names is :class:`Undefined`, a message
60
+ about it being undefined is shown rather than the empty string.
61
+
62
+ .. versionadded:: 2.2
63
+ """
64
+
65
+ def __init__(
66
+ self,
67
+ names: t.Sequence[t.Union[str, "Undefined"]] = (),
68
+ message: t.Optional[str] = None,
69
+ ) -> None:
70
+ if message is None:
71
+ from .runtime import Undefined
72
+
73
+ parts = []
74
+
75
+ for name in names:
76
+ if isinstance(name, Undefined):
77
+ parts.append(name._undefined_message)
78
+ else:
79
+ parts.append(name)
80
+
81
+ parts_str = ", ".join(map(str, parts))
82
+ message = f"none of the templates given were found: {parts_str}"
83
+
84
+ super().__init__(names[-1] if names else None, message)
85
+ self.templates = list(names)
86
+
87
+
88
+ class TemplateSyntaxError(TemplateError):
89
+ """Raised to tell the user that there is a problem with the template."""
90
+
91
+ def __init__(
92
+ self,
93
+ message: str,
94
+ lineno: int,
95
+ name: t.Optional[str] = None,
96
+ filename: t.Optional[str] = None,
97
+ ) -> None:
98
+ super().__init__(message)
99
+ self.lineno = lineno
100
+ self.name = name
101
+ self.filename = filename
102
+ self.source: t.Optional[str] = None
103
+
104
+ # this is set to True if the debug.translate_syntax_error
105
+ # function translated the syntax error into a new traceback
106
+ self.translated = False
107
+
108
+ def __str__(self) -> str:
109
+ # for translated errors we only return the message
110
+ if self.translated:
111
+ return t.cast(str, self.message)
112
+
113
+ # otherwise attach some stuff
114
+ location = f"line {self.lineno}"
115
+ name = self.filename or self.name
116
+ if name:
117
+ location = f'File "{name}", {location}'
118
+ lines = [t.cast(str, self.message), " " + location]
119
+
120
+ # if the source is set, add the line to the output
121
+ if self.source is not None:
122
+ try:
123
+ line = self.source.splitlines()[self.lineno - 1]
124
+ except IndexError:
125
+ pass
126
+ else:
127
+ lines.append(" " + line.strip())
128
+
129
+ return "\n".join(lines)
130
+
131
+ def __reduce__(self): # type: ignore
132
+ # https://bugs.python.org/issue1692335 Exceptions that take
133
+ # multiple required arguments have problems with pickling.
134
+ # Without this, raises TypeError: __init__() missing 1 required
135
+ # positional argument: 'lineno'
136
+ return self.__class__, (self.message, self.lineno, self.name, self.filename)
137
+
138
+
139
+ class TemplateAssertionError(TemplateSyntaxError):
140
+ """Like a template syntax error, but covers cases where something in the
141
+ template caused an error at compile time that wasn't necessarily caused
142
+ by a syntax error. However it's a direct subclass of
143
+ :exc:`TemplateSyntaxError` and has the same attributes.
144
+ """
145
+
146
+
147
+ class TemplateRuntimeError(TemplateError):
148
+ """A generic runtime error in the template engine. Under some situations
149
+ Jinja may raise this exception.
150
+ """
151
+
152
+
153
+ class UndefinedError(TemplateRuntimeError):
154
+ """Raised if a template tries to operate on :class:`Undefined`."""
155
+
156
+
157
+ class SecurityError(TemplateRuntimeError):
158
+ """Raised if a template tries to do something insecure if the
159
+ sandbox is enabled.
160
+ """
161
+
162
+
163
+ class FilterArgumentError(TemplateRuntimeError):
164
+ """This error is raised if a filter was called with inappropriate
165
+ arguments
166
+ """
lib/python3.11/site-packages/jinja2/ext.py ADDED
@@ -0,0 +1,859 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Extension API for adding custom tags and behavior."""
2
+ import pprint
3
+ import re
4
+ import typing as t
5
+
6
+ from markupsafe import Markup
7
+
8
+ from . import defaults
9
+ from . import nodes
10
+ from .environment import Environment
11
+ from .exceptions import TemplateAssertionError
12
+ from .exceptions import TemplateSyntaxError
13
+ from .runtime import concat # type: ignore
14
+ from .runtime import Context
15
+ from .runtime import Undefined
16
+ from .utils import import_string
17
+ from .utils import pass_context
18
+
19
+ if t.TYPE_CHECKING:
20
+ import typing_extensions as te
21
+ from .lexer import Token
22
+ from .lexer import TokenStream
23
+ from .parser import Parser
24
+
25
+ class _TranslationsBasic(te.Protocol):
26
+ def gettext(self, message: str) -> str:
27
+ ...
28
+
29
+ def ngettext(self, singular: str, plural: str, n: int) -> str:
30
+ pass
31
+
32
+ class _TranslationsContext(_TranslationsBasic):
33
+ def pgettext(self, context: str, message: str) -> str:
34
+ ...
35
+
36
+ def npgettext(self, context: str, singular: str, plural: str, n: int) -> str:
37
+ ...
38
+
39
+ _SupportedTranslations = t.Union[_TranslationsBasic, _TranslationsContext]
40
+
41
+
42
+ # I18N functions available in Jinja templates. If the I18N library
43
+ # provides ugettext, it will be assigned to gettext.
44
+ GETTEXT_FUNCTIONS: t.Tuple[str, ...] = (
45
+ "_",
46
+ "gettext",
47
+ "ngettext",
48
+ "pgettext",
49
+ "npgettext",
50
+ )
51
+ _ws_re = re.compile(r"\s*\n\s*")
52
+
53
+
54
+ class Extension:
55
+ """Extensions can be used to add extra functionality to the Jinja template
56
+ system at the parser level. Custom extensions are bound to an environment
57
+ but may not store environment specific data on `self`. The reason for
58
+ this is that an extension can be bound to another environment (for
59
+ overlays) by creating a copy and reassigning the `environment` attribute.
60
+
61
+ As extensions are created by the environment they cannot accept any
62
+ arguments for configuration. One may want to work around that by using
63
+ a factory function, but that is not possible as extensions are identified
64
+ by their import name. The correct way to configure the extension is
65
+ storing the configuration values on the environment. Because this way the
66
+ environment ends up acting as central configuration storage the
67
+ attributes may clash which is why extensions have to ensure that the names
68
+ they choose for configuration are not too generic. ``prefix`` for example
69
+ is a terrible name, ``fragment_cache_prefix`` on the other hand is a good
70
+ name as includes the name of the extension (fragment cache).
71
+ """
72
+
73
+ identifier: t.ClassVar[str]
74
+
75
+ def __init_subclass__(cls) -> None:
76
+ cls.identifier = f"{cls.__module__}.{cls.__name__}"
77
+
78
+ #: if this extension parses this is the list of tags it's listening to.
79
+ tags: t.Set[str] = set()
80
+
81
+ #: the priority of that extension. This is especially useful for
82
+ #: extensions that preprocess values. A lower value means higher
83
+ #: priority.
84
+ #:
85
+ #: .. versionadded:: 2.4
86
+ priority = 100
87
+
88
+ def __init__(self, environment: Environment) -> None:
89
+ self.environment = environment
90
+
91
+ def bind(self, environment: Environment) -> "Extension":
92
+ """Create a copy of this extension bound to another environment."""
93
+ rv = object.__new__(self.__class__)
94
+ rv.__dict__.update(self.__dict__)
95
+ rv.environment = environment
96
+ return rv
97
+
98
+ def preprocess(
99
+ self, source: str, name: t.Optional[str], filename: t.Optional[str] = None
100
+ ) -> str:
101
+ """This method is called before the actual lexing and can be used to
102
+ preprocess the source. The `filename` is optional. The return value
103
+ must be the preprocessed source.
104
+ """
105
+ return source
106
+
107
+ def filter_stream(
108
+ self, stream: "TokenStream"
109
+ ) -> t.Union["TokenStream", t.Iterable["Token"]]:
110
+ """It's passed a :class:`~jinja2.lexer.TokenStream` that can be used
111
+ to filter tokens returned. This method has to return an iterable of
112
+ :class:`~jinja2.lexer.Token`\\s, but it doesn't have to return a
113
+ :class:`~jinja2.lexer.TokenStream`.
114
+ """
115
+ return stream
116
+
117
+ def parse(self, parser: "Parser") -> t.Union[nodes.Node, t.List[nodes.Node]]:
118
+ """If any of the :attr:`tags` matched this method is called with the
119
+ parser as first argument. The token the parser stream is pointing at
120
+ is the name token that matched. This method has to return one or a
121
+ list of multiple nodes.
122
+ """
123
+ raise NotImplementedError()
124
+
125
+ def attr(
126
+ self, name: str, lineno: t.Optional[int] = None
127
+ ) -> nodes.ExtensionAttribute:
128
+ """Return an attribute node for the current extension. This is useful
129
+ to pass constants on extensions to generated template code.
130
+
131
+ ::
132
+
133
+ self.attr('_my_attribute', lineno=lineno)
134
+ """
135
+ return nodes.ExtensionAttribute(self.identifier, name, lineno=lineno)
136
+
137
+ def call_method(
138
+ self,
139
+ name: str,
140
+ args: t.Optional[t.List[nodes.Expr]] = None,
141
+ kwargs: t.Optional[t.List[nodes.Keyword]] = None,
142
+ dyn_args: t.Optional[nodes.Expr] = None,
143
+ dyn_kwargs: t.Optional[nodes.Expr] = None,
144
+ lineno: t.Optional[int] = None,
145
+ ) -> nodes.Call:
146
+ """Call a method of the extension. This is a shortcut for
147
+ :meth:`attr` + :class:`jinja2.nodes.Call`.
148
+ """
149
+ if args is None:
150
+ args = []
151
+ if kwargs is None:
152
+ kwargs = []
153
+ return nodes.Call(
154
+ self.attr(name, lineno=lineno),
155
+ args,
156
+ kwargs,
157
+ dyn_args,
158
+ dyn_kwargs,
159
+ lineno=lineno,
160
+ )
161
+
162
+
163
+ @pass_context
164
+ def _gettext_alias(
165
+ __context: Context, *args: t.Any, **kwargs: t.Any
166
+ ) -> t.Union[t.Any, Undefined]:
167
+ return __context.call(__context.resolve("gettext"), *args, **kwargs)
168
+
169
+
170
+ def _make_new_gettext(func: t.Callable[[str], str]) -> t.Callable[..., str]:
171
+ @pass_context
172
+ def gettext(__context: Context, __string: str, **variables: t.Any) -> str:
173
+ rv = __context.call(func, __string)
174
+ if __context.eval_ctx.autoescape:
175
+ rv = Markup(rv)
176
+ # Always treat as a format string, even if there are no
177
+ # variables. This makes translation strings more consistent
178
+ # and predictable. This requires escaping
179
+ return rv % variables # type: ignore
180
+
181
+ return gettext
182
+
183
+
184
+ def _make_new_ngettext(func: t.Callable[[str, str, int], str]) -> t.Callable[..., str]:
185
+ @pass_context
186
+ def ngettext(
187
+ __context: Context,
188
+ __singular: str,
189
+ __plural: str,
190
+ __num: int,
191
+ **variables: t.Any,
192
+ ) -> str:
193
+ variables.setdefault("num", __num)
194
+ rv = __context.call(func, __singular, __plural, __num)
195
+ if __context.eval_ctx.autoescape:
196
+ rv = Markup(rv)
197
+ # Always treat as a format string, see gettext comment above.
198
+ return rv % variables # type: ignore
199
+
200
+ return ngettext
201
+
202
+
203
+ def _make_new_pgettext(func: t.Callable[[str, str], str]) -> t.Callable[..., str]:
204
+ @pass_context
205
+ def pgettext(
206
+ __context: Context, __string_ctx: str, __string: str, **variables: t.Any
207
+ ) -> str:
208
+ variables.setdefault("context", __string_ctx)
209
+ rv = __context.call(func, __string_ctx, __string)
210
+
211
+ if __context.eval_ctx.autoescape:
212
+ rv = Markup(rv)
213
+
214
+ # Always treat as a format string, see gettext comment above.
215
+ return rv % variables # type: ignore
216
+
217
+ return pgettext
218
+
219
+
220
+ def _make_new_npgettext(
221
+ func: t.Callable[[str, str, str, int], str]
222
+ ) -> t.Callable[..., str]:
223
+ @pass_context
224
+ def npgettext(
225
+ __context: Context,
226
+ __string_ctx: str,
227
+ __singular: str,
228
+ __plural: str,
229
+ __num: int,
230
+ **variables: t.Any,
231
+ ) -> str:
232
+ variables.setdefault("context", __string_ctx)
233
+ variables.setdefault("num", __num)
234
+ rv = __context.call(func, __string_ctx, __singular, __plural, __num)
235
+
236
+ if __context.eval_ctx.autoescape:
237
+ rv = Markup(rv)
238
+
239
+ # Always treat as a format string, see gettext comment above.
240
+ return rv % variables # type: ignore
241
+
242
+ return npgettext
243
+
244
+
245
+ class InternationalizationExtension(Extension):
246
+ """This extension adds gettext support to Jinja."""
247
+
248
+ tags = {"trans"}
249
+
250
+ # TODO: the i18n extension is currently reevaluating values in a few
251
+ # situations. Take this example:
252
+ # {% trans count=something() %}{{ count }} foo{% pluralize
253
+ # %}{{ count }} fooss{% endtrans %}
254
+ # something is called twice here. One time for the gettext value and
255
+ # the other time for the n-parameter of the ngettext function.
256
+
257
+ def __init__(self, environment: Environment) -> None:
258
+ super().__init__(environment)
259
+ environment.globals["_"] = _gettext_alias
260
+ environment.extend(
261
+ install_gettext_translations=self._install,
262
+ install_null_translations=self._install_null,
263
+ install_gettext_callables=self._install_callables,
264
+ uninstall_gettext_translations=self._uninstall,
265
+ extract_translations=self._extract,
266
+ newstyle_gettext=False,
267
+ )
268
+
269
+ def _install(
270
+ self, translations: "_SupportedTranslations", newstyle: t.Optional[bool] = None
271
+ ) -> None:
272
+ # ugettext and ungettext are preferred in case the I18N library
273
+ # is providing compatibility with older Python versions.
274
+ gettext = getattr(translations, "ugettext", None)
275
+ if gettext is None:
276
+ gettext = translations.gettext
277
+ ngettext = getattr(translations, "ungettext", None)
278
+ if ngettext is None:
279
+ ngettext = translations.ngettext
280
+
281
+ pgettext = getattr(translations, "pgettext", None)
282
+ npgettext = getattr(translations, "npgettext", None)
283
+ self._install_callables(
284
+ gettext, ngettext, newstyle=newstyle, pgettext=pgettext, npgettext=npgettext
285
+ )
286
+
287
+ def _install_null(self, newstyle: t.Optional[bool] = None) -> None:
288
+ import gettext
289
+
290
+ translations = gettext.NullTranslations()
291
+
292
+ if hasattr(translations, "pgettext"):
293
+ # Python < 3.8
294
+ pgettext = translations.pgettext # type: ignore
295
+ else:
296
+
297
+ def pgettext(c: str, s: str) -> str:
298
+ return s
299
+
300
+ if hasattr(translations, "npgettext"):
301
+ npgettext = translations.npgettext # type: ignore
302
+ else:
303
+
304
+ def npgettext(c: str, s: str, p: str, n: int) -> str:
305
+ return s if n == 1 else p
306
+
307
+ self._install_callables(
308
+ gettext=translations.gettext,
309
+ ngettext=translations.ngettext,
310
+ newstyle=newstyle,
311
+ pgettext=pgettext,
312
+ npgettext=npgettext,
313
+ )
314
+
315
+ def _install_callables(
316
+ self,
317
+ gettext: t.Callable[[str], str],
318
+ ngettext: t.Callable[[str, str, int], str],
319
+ newstyle: t.Optional[bool] = None,
320
+ pgettext: t.Optional[t.Callable[[str, str], str]] = None,
321
+ npgettext: t.Optional[t.Callable[[str, str, str, int], str]] = None,
322
+ ) -> None:
323
+ if newstyle is not None:
324
+ self.environment.newstyle_gettext = newstyle # type: ignore
325
+ if self.environment.newstyle_gettext: # type: ignore
326
+ gettext = _make_new_gettext(gettext)
327
+ ngettext = _make_new_ngettext(ngettext)
328
+
329
+ if pgettext is not None:
330
+ pgettext = _make_new_pgettext(pgettext)
331
+
332
+ if npgettext is not None:
333
+ npgettext = _make_new_npgettext(npgettext)
334
+
335
+ self.environment.globals.update(
336
+ gettext=gettext, ngettext=ngettext, pgettext=pgettext, npgettext=npgettext
337
+ )
338
+
339
+ def _uninstall(self, translations: "_SupportedTranslations") -> None:
340
+ for key in ("gettext", "ngettext", "pgettext", "npgettext"):
341
+ self.environment.globals.pop(key, None)
342
+
343
+ def _extract(
344
+ self,
345
+ source: t.Union[str, nodes.Template],
346
+ gettext_functions: t.Sequence[str] = GETTEXT_FUNCTIONS,
347
+ ) -> t.Iterator[
348
+ t.Tuple[int, str, t.Union[t.Optional[str], t.Tuple[t.Optional[str], ...]]]
349
+ ]:
350
+ if isinstance(source, str):
351
+ source = self.environment.parse(source)
352
+ return extract_from_ast(source, gettext_functions)
353
+
354
+ def parse(self, parser: "Parser") -> t.Union[nodes.Node, t.List[nodes.Node]]:
355
+ """Parse a translatable tag."""
356
+ lineno = next(parser.stream).lineno
357
+
358
+ context = None
359
+ context_token = parser.stream.next_if("string")
360
+
361
+ if context_token is not None:
362
+ context = context_token.value
363
+
364
+ # find all the variables referenced. Additionally a variable can be
365
+ # defined in the body of the trans block too, but this is checked at
366
+ # a later state.
367
+ plural_expr: t.Optional[nodes.Expr] = None
368
+ plural_expr_assignment: t.Optional[nodes.Assign] = None
369
+ num_called_num = False
370
+ variables: t.Dict[str, nodes.Expr] = {}
371
+ trimmed = None
372
+ while parser.stream.current.type != "block_end":
373
+ if variables:
374
+ parser.stream.expect("comma")
375
+
376
+ # skip colon for python compatibility
377
+ if parser.stream.skip_if("colon"):
378
+ break
379
+
380
+ token = parser.stream.expect("name")
381
+ if token.value in variables:
382
+ parser.fail(
383
+ f"translatable variable {token.value!r} defined twice.",
384
+ token.lineno,
385
+ exc=TemplateAssertionError,
386
+ )
387
+
388
+ # expressions
389
+ if parser.stream.current.type == "assign":
390
+ next(parser.stream)
391
+ variables[token.value] = var = parser.parse_expression()
392
+ elif trimmed is None and token.value in ("trimmed", "notrimmed"):
393
+ trimmed = token.value == "trimmed"
394
+ continue
395
+ else:
396
+ variables[token.value] = var = nodes.Name(token.value, "load")
397
+
398
+ if plural_expr is None:
399
+ if isinstance(var, nodes.Call):
400
+ plural_expr = nodes.Name("_trans", "load")
401
+ variables[token.value] = plural_expr
402
+ plural_expr_assignment = nodes.Assign(
403
+ nodes.Name("_trans", "store"), var
404
+ )
405
+ else:
406
+ plural_expr = var
407
+ num_called_num = token.value == "num"
408
+
409
+ parser.stream.expect("block_end")
410
+
411
+ plural = None
412
+ have_plural = False
413
+ referenced = set()
414
+
415
+ # now parse until endtrans or pluralize
416
+ singular_names, singular = self._parse_block(parser, True)
417
+ if singular_names:
418
+ referenced.update(singular_names)
419
+ if plural_expr is None:
420
+ plural_expr = nodes.Name(singular_names[0], "load")
421
+ num_called_num = singular_names[0] == "num"
422
+
423
+ # if we have a pluralize block, we parse that too
424
+ if parser.stream.current.test("name:pluralize"):
425
+ have_plural = True
426
+ next(parser.stream)
427
+ if parser.stream.current.type != "block_end":
428
+ token = parser.stream.expect("name")
429
+ if token.value not in variables:
430
+ parser.fail(
431
+ f"unknown variable {token.value!r} for pluralization",
432
+ token.lineno,
433
+ exc=TemplateAssertionError,
434
+ )
435
+ plural_expr = variables[token.value]
436
+ num_called_num = token.value == "num"
437
+ parser.stream.expect("block_end")
438
+ plural_names, plural = self._parse_block(parser, False)
439
+ next(parser.stream)
440
+ referenced.update(plural_names)
441
+ else:
442
+ next(parser.stream)
443
+
444
+ # register free names as simple name expressions
445
+ for name in referenced:
446
+ if name not in variables:
447
+ variables[name] = nodes.Name(name, "load")
448
+
449
+ if not have_plural:
450
+ plural_expr = None
451
+ elif plural_expr is None:
452
+ parser.fail("pluralize without variables", lineno)
453
+
454
+ if trimmed is None:
455
+ trimmed = self.environment.policies["ext.i18n.trimmed"]
456
+ if trimmed:
457
+ singular = self._trim_whitespace(singular)
458
+ if plural:
459
+ plural = self._trim_whitespace(plural)
460
+
461
+ node = self._make_node(
462
+ singular,
463
+ plural,
464
+ context,
465
+ variables,
466
+ plural_expr,
467
+ bool(referenced),
468
+ num_called_num and have_plural,
469
+ )
470
+ node.set_lineno(lineno)
471
+ if plural_expr_assignment is not None:
472
+ return [plural_expr_assignment, node]
473
+ else:
474
+ return node
475
+
476
+ def _trim_whitespace(self, string: str, _ws_re: t.Pattern[str] = _ws_re) -> str:
477
+ return _ws_re.sub(" ", string.strip())
478
+
479
+ def _parse_block(
480
+ self, parser: "Parser", allow_pluralize: bool
481
+ ) -> t.Tuple[t.List[str], str]:
482
+ """Parse until the next block tag with a given name."""
483
+ referenced = []
484
+ buf = []
485
+
486
+ while True:
487
+ if parser.stream.current.type == "data":
488
+ buf.append(parser.stream.current.value.replace("%", "%%"))
489
+ next(parser.stream)
490
+ elif parser.stream.current.type == "variable_begin":
491
+ next(parser.stream)
492
+ name = parser.stream.expect("name").value
493
+ referenced.append(name)
494
+ buf.append(f"%({name})s")
495
+ parser.stream.expect("variable_end")
496
+ elif parser.stream.current.type == "block_begin":
497
+ next(parser.stream)
498
+ if parser.stream.current.test("name:endtrans"):
499
+ break
500
+ elif parser.stream.current.test("name:pluralize"):
501
+ if allow_pluralize:
502
+ break
503
+ parser.fail(
504
+ "a translatable section can have only one pluralize section"
505
+ )
506
+ parser.fail(
507
+ "control structures in translatable sections are not allowed"
508
+ )
509
+ elif parser.stream.eos:
510
+ parser.fail("unclosed translation block")
511
+ else:
512
+ raise RuntimeError("internal parser error")
513
+
514
+ return referenced, concat(buf)
515
+
516
+ def _make_node(
517
+ self,
518
+ singular: str,
519
+ plural: t.Optional[str],
520
+ context: t.Optional[str],
521
+ variables: t.Dict[str, nodes.Expr],
522
+ plural_expr: t.Optional[nodes.Expr],
523
+ vars_referenced: bool,
524
+ num_called_num: bool,
525
+ ) -> nodes.Output:
526
+ """Generates a useful node from the data provided."""
527
+ newstyle = self.environment.newstyle_gettext # type: ignore
528
+ node: nodes.Expr
529
+
530
+ # no variables referenced? no need to escape for old style
531
+ # gettext invocations only if there are vars.
532
+ if not vars_referenced and not newstyle:
533
+ singular = singular.replace("%%", "%")
534
+ if plural:
535
+ plural = plural.replace("%%", "%")
536
+
537
+ func_name = "gettext"
538
+ func_args: t.List[nodes.Expr] = [nodes.Const(singular)]
539
+
540
+ if context is not None:
541
+ func_args.insert(0, nodes.Const(context))
542
+ func_name = f"p{func_name}"
543
+
544
+ if plural_expr is not None:
545
+ func_name = f"n{func_name}"
546
+ func_args.extend((nodes.Const(plural), plural_expr))
547
+
548
+ node = nodes.Call(nodes.Name(func_name, "load"), func_args, [], None, None)
549
+
550
+ # in case newstyle gettext is used, the method is powerful
551
+ # enough to handle the variable expansion and autoescape
552
+ # handling itself
553
+ if newstyle:
554
+ for key, value in variables.items():
555
+ # the function adds that later anyways in case num was
556
+ # called num, so just skip it.
557
+ if num_called_num and key == "num":
558
+ continue
559
+ node.kwargs.append(nodes.Keyword(key, value))
560
+
561
+ # otherwise do that here
562
+ else:
563
+ # mark the return value as safe if we are in an
564
+ # environment with autoescaping turned on
565
+ node = nodes.MarkSafeIfAutoescape(node)
566
+ if variables:
567
+ node = nodes.Mod(
568
+ node,
569
+ nodes.Dict(
570
+ [
571
+ nodes.Pair(nodes.Const(key), value)
572
+ for key, value in variables.items()
573
+ ]
574
+ ),
575
+ )
576
+ return nodes.Output([node])
577
+
578
+
579
+ class ExprStmtExtension(Extension):
580
+ """Adds a `do` tag to Jinja that works like the print statement just
581
+ that it doesn't print the return value.
582
+ """
583
+
584
+ tags = {"do"}
585
+
586
+ def parse(self, parser: "Parser") -> nodes.ExprStmt:
587
+ node = nodes.ExprStmt(lineno=next(parser.stream).lineno)
588
+ node.node = parser.parse_tuple()
589
+ return node
590
+
591
+
592
+ class LoopControlExtension(Extension):
593
+ """Adds break and continue to the template engine."""
594
+
595
+ tags = {"break", "continue"}
596
+
597
+ def parse(self, parser: "Parser") -> t.Union[nodes.Break, nodes.Continue]:
598
+ token = next(parser.stream)
599
+ if token.value == "break":
600
+ return nodes.Break(lineno=token.lineno)
601
+ return nodes.Continue(lineno=token.lineno)
602
+
603
+
604
+ class DebugExtension(Extension):
605
+ """A ``{% debug %}`` tag that dumps the available variables,
606
+ filters, and tests.
607
+
608
+ .. code-block:: html+jinja
609
+
610
+ <pre>{% debug %}</pre>
611
+
612
+ .. code-block:: text
613
+
614
+ {'context': {'cycler': <class 'jinja2.utils.Cycler'>,
615
+ ...,
616
+ 'namespace': <class 'jinja2.utils.Namespace'>},
617
+ 'filters': ['abs', 'attr', 'batch', 'capitalize', 'center', 'count', 'd',
618
+ ..., 'urlencode', 'urlize', 'wordcount', 'wordwrap', 'xmlattr'],
619
+ 'tests': ['!=', '<', '<=', '==', '>', '>=', 'callable', 'defined',
620
+ ..., 'odd', 'sameas', 'sequence', 'string', 'undefined', 'upper']}
621
+
622
+ .. versionadded:: 2.11.0
623
+ """
624
+
625
+ tags = {"debug"}
626
+
627
+ def parse(self, parser: "Parser") -> nodes.Output:
628
+ lineno = parser.stream.expect("name:debug").lineno
629
+ context = nodes.ContextReference()
630
+ result = self.call_method("_render", [context], lineno=lineno)
631
+ return nodes.Output([result], lineno=lineno)
632
+
633
+ def _render(self, context: Context) -> str:
634
+ result = {
635
+ "context": context.get_all(),
636
+ "filters": sorted(self.environment.filters.keys()),
637
+ "tests": sorted(self.environment.tests.keys()),
638
+ }
639
+
640
+ # Set the depth since the intent is to show the top few names.
641
+ return pprint.pformat(result, depth=3, compact=True)
642
+
643
+
644
+ def extract_from_ast(
645
+ ast: nodes.Template,
646
+ gettext_functions: t.Sequence[str] = GETTEXT_FUNCTIONS,
647
+ babel_style: bool = True,
648
+ ) -> t.Iterator[
649
+ t.Tuple[int, str, t.Union[t.Optional[str], t.Tuple[t.Optional[str], ...]]]
650
+ ]:
651
+ """Extract localizable strings from the given template node. Per
652
+ default this function returns matches in babel style that means non string
653
+ parameters as well as keyword arguments are returned as `None`. This
654
+ allows Babel to figure out what you really meant if you are using
655
+ gettext functions that allow keyword arguments for placeholder expansion.
656
+ If you don't want that behavior set the `babel_style` parameter to `False`
657
+ which causes only strings to be returned and parameters are always stored
658
+ in tuples. As a consequence invalid gettext calls (calls without a single
659
+ string parameter or string parameters after non-string parameters) are
660
+ skipped.
661
+
662
+ This example explains the behavior:
663
+
664
+ >>> from jinja2 import Environment
665
+ >>> env = Environment()
666
+ >>> node = env.parse('{{ (_("foo"), _(), ngettext("foo", "bar", 42)) }}')
667
+ >>> list(extract_from_ast(node))
668
+ [(1, '_', 'foo'), (1, '_', ()), (1, 'ngettext', ('foo', 'bar', None))]
669
+ >>> list(extract_from_ast(node, babel_style=False))
670
+ [(1, '_', ('foo',)), (1, 'ngettext', ('foo', 'bar'))]
671
+
672
+ For every string found this function yields a ``(lineno, function,
673
+ message)`` tuple, where:
674
+
675
+ * ``lineno`` is the number of the line on which the string was found,
676
+ * ``function`` is the name of the ``gettext`` function used (if the
677
+ string was extracted from embedded Python code), and
678
+ * ``message`` is the string, or a tuple of strings for functions
679
+ with multiple string arguments.
680
+
681
+ This extraction function operates on the AST and is because of that unable
682
+ to extract any comments. For comment support you have to use the babel
683
+ extraction interface or extract comments yourself.
684
+ """
685
+ out: t.Union[t.Optional[str], t.Tuple[t.Optional[str], ...]]
686
+
687
+ for node in ast.find_all(nodes.Call):
688
+ if (
689
+ not isinstance(node.node, nodes.Name)
690
+ or node.node.name not in gettext_functions
691
+ ):
692
+ continue
693
+
694
+ strings: t.List[t.Optional[str]] = []
695
+
696
+ for arg in node.args:
697
+ if isinstance(arg, nodes.Const) and isinstance(arg.value, str):
698
+ strings.append(arg.value)
699
+ else:
700
+ strings.append(None)
701
+
702
+ for _ in node.kwargs:
703
+ strings.append(None)
704
+ if node.dyn_args is not None:
705
+ strings.append(None)
706
+ if node.dyn_kwargs is not None:
707
+ strings.append(None)
708
+
709
+ if not babel_style:
710
+ out = tuple(x for x in strings if x is not None)
711
+
712
+ if not out:
713
+ continue
714
+ else:
715
+ if len(strings) == 1:
716
+ out = strings[0]
717
+ else:
718
+ out = tuple(strings)
719
+
720
+ yield node.lineno, node.node.name, out
721
+
722
+
723
+ class _CommentFinder:
724
+ """Helper class to find comments in a token stream. Can only
725
+ find comments for gettext calls forwards. Once the comment
726
+ from line 4 is found, a comment for line 1 will not return a
727
+ usable value.
728
+ """
729
+
730
+ def __init__(
731
+ self, tokens: t.Sequence[t.Tuple[int, str, str]], comment_tags: t.Sequence[str]
732
+ ) -> None:
733
+ self.tokens = tokens
734
+ self.comment_tags = comment_tags
735
+ self.offset = 0
736
+ self.last_lineno = 0
737
+
738
+ def find_backwards(self, offset: int) -> t.List[str]:
739
+ try:
740
+ for _, token_type, token_value in reversed(
741
+ self.tokens[self.offset : offset]
742
+ ):
743
+ if token_type in ("comment", "linecomment"):
744
+ try:
745
+ prefix, comment = token_value.split(None, 1)
746
+ except ValueError:
747
+ continue
748
+ if prefix in self.comment_tags:
749
+ return [comment.rstrip()]
750
+ return []
751
+ finally:
752
+ self.offset = offset
753
+
754
+ def find_comments(self, lineno: int) -> t.List[str]:
755
+ if not self.comment_tags or self.last_lineno > lineno:
756
+ return []
757
+ for idx, (token_lineno, _, _) in enumerate(self.tokens[self.offset :]):
758
+ if token_lineno > lineno:
759
+ return self.find_backwards(self.offset + idx)
760
+ return self.find_backwards(len(self.tokens))
761
+
762
+
763
+ def babel_extract(
764
+ fileobj: t.BinaryIO,
765
+ keywords: t.Sequence[str],
766
+ comment_tags: t.Sequence[str],
767
+ options: t.Dict[str, t.Any],
768
+ ) -> t.Iterator[
769
+ t.Tuple[
770
+ int, str, t.Union[t.Optional[str], t.Tuple[t.Optional[str], ...]], t.List[str]
771
+ ]
772
+ ]:
773
+ """Babel extraction method for Jinja templates.
774
+
775
+ .. versionchanged:: 2.3
776
+ Basic support for translation comments was added. If `comment_tags`
777
+ is now set to a list of keywords for extraction, the extractor will
778
+ try to find the best preceding comment that begins with one of the
779
+ keywords. For best results, make sure to not have more than one
780
+ gettext call in one line of code and the matching comment in the
781
+ same line or the line before.
782
+
783
+ .. versionchanged:: 2.5.1
784
+ The `newstyle_gettext` flag can be set to `True` to enable newstyle
785
+ gettext calls.
786
+
787
+ .. versionchanged:: 2.7
788
+ A `silent` option can now be provided. If set to `False` template
789
+ syntax errors are propagated instead of being ignored.
790
+
791
+ :param fileobj: the file-like object the messages should be extracted from
792
+ :param keywords: a list of keywords (i.e. function names) that should be
793
+ recognized as translation functions
794
+ :param comment_tags: a list of translator tags to search for and include
795
+ in the results.
796
+ :param options: a dictionary of additional options (optional)
797
+ :return: an iterator over ``(lineno, funcname, message, comments)`` tuples.
798
+ (comments will be empty currently)
799
+ """
800
+ extensions: t.Dict[t.Type[Extension], None] = {}
801
+
802
+ for extension_name in options.get("extensions", "").split(","):
803
+ extension_name = extension_name.strip()
804
+
805
+ if not extension_name:
806
+ continue
807
+
808
+ extensions[import_string(extension_name)] = None
809
+
810
+ if InternationalizationExtension not in extensions:
811
+ extensions[InternationalizationExtension] = None
812
+
813
+ def getbool(options: t.Mapping[str, str], key: str, default: bool = False) -> bool:
814
+ return options.get(key, str(default)).lower() in {"1", "on", "yes", "true"}
815
+
816
+ silent = getbool(options, "silent", True)
817
+ environment = Environment(
818
+ options.get("block_start_string", defaults.BLOCK_START_STRING),
819
+ options.get("block_end_string", defaults.BLOCK_END_STRING),
820
+ options.get("variable_start_string", defaults.VARIABLE_START_STRING),
821
+ options.get("variable_end_string", defaults.VARIABLE_END_STRING),
822
+ options.get("comment_start_string", defaults.COMMENT_START_STRING),
823
+ options.get("comment_end_string", defaults.COMMENT_END_STRING),
824
+ options.get("line_statement_prefix") or defaults.LINE_STATEMENT_PREFIX,
825
+ options.get("line_comment_prefix") or defaults.LINE_COMMENT_PREFIX,
826
+ getbool(options, "trim_blocks", defaults.TRIM_BLOCKS),
827
+ getbool(options, "lstrip_blocks", defaults.LSTRIP_BLOCKS),
828
+ defaults.NEWLINE_SEQUENCE,
829
+ getbool(options, "keep_trailing_newline", defaults.KEEP_TRAILING_NEWLINE),
830
+ tuple(extensions),
831
+ cache_size=0,
832
+ auto_reload=False,
833
+ )
834
+
835
+ if getbool(options, "trimmed"):
836
+ environment.policies["ext.i18n.trimmed"] = True
837
+ if getbool(options, "newstyle_gettext"):
838
+ environment.newstyle_gettext = True # type: ignore
839
+
840
+ source = fileobj.read().decode(options.get("encoding", "utf-8"))
841
+ try:
842
+ node = environment.parse(source)
843
+ tokens = list(environment.lex(environment.preprocess(source)))
844
+ except TemplateSyntaxError:
845
+ if not silent:
846
+ raise
847
+ # skip templates with syntax errors
848
+ return
849
+
850
+ finder = _CommentFinder(tokens, comment_tags)
851
+ for lineno, func, message in extract_from_ast(node, keywords):
852
+ yield lineno, func, message, finder.find_comments(lineno)
853
+
854
+
855
+ #: nicer import names
856
+ i18n = InternationalizationExtension
857
+ do = ExprStmtExtension
858
+ loopcontrols = LoopControlExtension
859
+ debug = DebugExtension
lib/python3.11/site-packages/jinja2/filters.py ADDED
@@ -0,0 +1,1840 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Built-in template filters used with the ``|`` operator."""
2
+ import math
3
+ import random
4
+ import re
5
+ import typing
6
+ import typing as t
7
+ from collections import abc
8
+ from itertools import chain
9
+ from itertools import groupby
10
+
11
+ from markupsafe import escape
12
+ from markupsafe import Markup
13
+ from markupsafe import soft_str
14
+
15
+ from .async_utils import async_variant
16
+ from .async_utils import auto_aiter
17
+ from .async_utils import auto_await
18
+ from .async_utils import auto_to_list
19
+ from .exceptions import FilterArgumentError
20
+ from .runtime import Undefined
21
+ from .utils import htmlsafe_json_dumps
22
+ from .utils import pass_context
23
+ from .utils import pass_environment
24
+ from .utils import pass_eval_context
25
+ from .utils import pformat
26
+ from .utils import url_quote
27
+ from .utils import urlize
28
+
29
+ if t.TYPE_CHECKING:
30
+ import typing_extensions as te
31
+ from .environment import Environment
32
+ from .nodes import EvalContext
33
+ from .runtime import Context
34
+ from .sandbox import SandboxedEnvironment # noqa: F401
35
+
36
+ class HasHTML(te.Protocol):
37
+ def __html__(self) -> str:
38
+ pass
39
+
40
+
41
+ F = t.TypeVar("F", bound=t.Callable[..., t.Any])
42
+ K = t.TypeVar("K")
43
+ V = t.TypeVar("V")
44
+
45
+
46
+ def ignore_case(value: V) -> V:
47
+ """For use as a postprocessor for :func:`make_attrgetter`. Converts strings
48
+ to lowercase and returns other types as-is."""
49
+ if isinstance(value, str):
50
+ return t.cast(V, value.lower())
51
+
52
+ return value
53
+
54
+
55
+ def make_attrgetter(
56
+ environment: "Environment",
57
+ attribute: t.Optional[t.Union[str, int]],
58
+ postprocess: t.Optional[t.Callable[[t.Any], t.Any]] = None,
59
+ default: t.Optional[t.Any] = None,
60
+ ) -> t.Callable[[t.Any], t.Any]:
61
+ """Returns a callable that looks up the given attribute from a
62
+ passed object with the rules of the environment. Dots are allowed
63
+ to access attributes of attributes. Integer parts in paths are
64
+ looked up as integers.
65
+ """
66
+ parts = _prepare_attribute_parts(attribute)
67
+
68
+ def attrgetter(item: t.Any) -> t.Any:
69
+ for part in parts:
70
+ item = environment.getitem(item, part)
71
+
72
+ if default is not None and isinstance(item, Undefined):
73
+ item = default
74
+
75
+ if postprocess is not None:
76
+ item = postprocess(item)
77
+
78
+ return item
79
+
80
+ return attrgetter
81
+
82
+
83
+ def make_multi_attrgetter(
84
+ environment: "Environment",
85
+ attribute: t.Optional[t.Union[str, int]],
86
+ postprocess: t.Optional[t.Callable[[t.Any], t.Any]] = None,
87
+ ) -> t.Callable[[t.Any], t.List[t.Any]]:
88
+ """Returns a callable that looks up the given comma separated
89
+ attributes from a passed object with the rules of the environment.
90
+ Dots are allowed to access attributes of each attribute. Integer
91
+ parts in paths are looked up as integers.
92
+
93
+ The value returned by the returned callable is a list of extracted
94
+ attribute values.
95
+
96
+ Examples of attribute: "attr1,attr2", "attr1.inner1.0,attr2.inner2.0", etc.
97
+ """
98
+ if isinstance(attribute, str):
99
+ split: t.Sequence[t.Union[str, int, None]] = attribute.split(",")
100
+ else:
101
+ split = [attribute]
102
+
103
+ parts = [_prepare_attribute_parts(item) for item in split]
104
+
105
+ def attrgetter(item: t.Any) -> t.List[t.Any]:
106
+ items = [None] * len(parts)
107
+
108
+ for i, attribute_part in enumerate(parts):
109
+ item_i = item
110
+
111
+ for part in attribute_part:
112
+ item_i = environment.getitem(item_i, part)
113
+
114
+ if postprocess is not None:
115
+ item_i = postprocess(item_i)
116
+
117
+ items[i] = item_i
118
+
119
+ return items
120
+
121
+ return attrgetter
122
+
123
+
124
+ def _prepare_attribute_parts(
125
+ attr: t.Optional[t.Union[str, int]]
126
+ ) -> t.List[t.Union[str, int]]:
127
+ if attr is None:
128
+ return []
129
+
130
+ if isinstance(attr, str):
131
+ return [int(x) if x.isdigit() else x for x in attr.split(".")]
132
+
133
+ return [attr]
134
+
135
+
136
+ def do_forceescape(value: "t.Union[str, HasHTML]") -> Markup:
137
+ """Enforce HTML escaping. This will probably double escape variables."""
138
+ if hasattr(value, "__html__"):
139
+ value = t.cast("HasHTML", value).__html__()
140
+
141
+ return escape(str(value))
142
+
143
+
144
+ def do_urlencode(
145
+ value: t.Union[str, t.Mapping[str, t.Any], t.Iterable[t.Tuple[str, t.Any]]]
146
+ ) -> str:
147
+ """Quote data for use in a URL path or query using UTF-8.
148
+
149
+ Basic wrapper around :func:`urllib.parse.quote` when given a
150
+ string, or :func:`urllib.parse.urlencode` for a dict or iterable.
151
+
152
+ :param value: Data to quote. A string will be quoted directly. A
153
+ dict or iterable of ``(key, value)`` pairs will be joined as a
154
+ query string.
155
+
156
+ When given a string, "/" is not quoted. HTTP servers treat "/" and
157
+ "%2F" equivalently in paths. If you need quoted slashes, use the
158
+ ``|replace("/", "%2F")`` filter.
159
+
160
+ .. versionadded:: 2.7
161
+ """
162
+ if isinstance(value, str) or not isinstance(value, abc.Iterable):
163
+ return url_quote(value)
164
+
165
+ if isinstance(value, dict):
166
+ items: t.Iterable[t.Tuple[str, t.Any]] = value.items()
167
+ else:
168
+ items = value # type: ignore
169
+
170
+ return "&".join(
171
+ f"{url_quote(k, for_qs=True)}={url_quote(v, for_qs=True)}" for k, v in items
172
+ )
173
+
174
+
175
+ @pass_eval_context
176
+ def do_replace(
177
+ eval_ctx: "EvalContext", s: str, old: str, new: str, count: t.Optional[int] = None
178
+ ) -> str:
179
+ """Return a copy of the value with all occurrences of a substring
180
+ replaced with a new one. The first argument is the substring
181
+ that should be replaced, the second is the replacement string.
182
+ If the optional third argument ``count`` is given, only the first
183
+ ``count`` occurrences are replaced:
184
+
185
+ .. sourcecode:: jinja
186
+
187
+ {{ "Hello World"|replace("Hello", "Goodbye") }}
188
+ -> Goodbye World
189
+
190
+ {{ "aaaaargh"|replace("a", "d'oh, ", 2) }}
191
+ -> d'oh, d'oh, aaargh
192
+ """
193
+ if count is None:
194
+ count = -1
195
+
196
+ if not eval_ctx.autoescape:
197
+ return str(s).replace(str(old), str(new), count)
198
+
199
+ if (
200
+ hasattr(old, "__html__")
201
+ or hasattr(new, "__html__")
202
+ and not hasattr(s, "__html__")
203
+ ):
204
+ s = escape(s)
205
+ else:
206
+ s = soft_str(s)
207
+
208
+ return s.replace(soft_str(old), soft_str(new), count)
209
+
210
+
211
+ def do_upper(s: str) -> str:
212
+ """Convert a value to uppercase."""
213
+ return soft_str(s).upper()
214
+
215
+
216
+ def do_lower(s: str) -> str:
217
+ """Convert a value to lowercase."""
218
+ return soft_str(s).lower()
219
+
220
+
221
+ def do_items(value: t.Union[t.Mapping[K, V], Undefined]) -> t.Iterator[t.Tuple[K, V]]:
222
+ """Return an iterator over the ``(key, value)`` items of a mapping.
223
+
224
+ ``x|items`` is the same as ``x.items()``, except if ``x`` is
225
+ undefined an empty iterator is returned.
226
+
227
+ This filter is useful if you expect the template to be rendered with
228
+ an implementation of Jinja in another programming language that does
229
+ not have a ``.items()`` method on its mapping type.
230
+
231
+ .. code-block:: html+jinja
232
+
233
+ <dl>
234
+ {% for key, value in my_dict|items %}
235
+ <dt>{{ key }}
236
+ <dd>{{ value }}
237
+ {% endfor %}
238
+ </dl>
239
+
240
+ .. versionadded:: 3.1
241
+ """
242
+ if isinstance(value, Undefined):
243
+ return
244
+
245
+ if not isinstance(value, abc.Mapping):
246
+ raise TypeError("Can only get item pairs from a mapping.")
247
+
248
+ yield from value.items()
249
+
250
+
251
+ @pass_eval_context
252
+ def do_xmlattr(
253
+ eval_ctx: "EvalContext", d: t.Mapping[str, t.Any], autospace: bool = True
254
+ ) -> str:
255
+ """Create an SGML/XML attribute string based on the items in a dict.
256
+ All values that are neither `none` nor `undefined` are automatically
257
+ escaped:
258
+
259
+ .. sourcecode:: html+jinja
260
+
261
+ <ul{{ {'class': 'my_list', 'missing': none,
262
+ 'id': 'list-%d'|format(variable)}|xmlattr }}>
263
+ ...
264
+ </ul>
265
+
266
+ Results in something like this:
267
+
268
+ .. sourcecode:: html
269
+
270
+ <ul class="my_list" id="list-42">
271
+ ...
272
+ </ul>
273
+
274
+ As you can see it automatically prepends a space in front of the item
275
+ if the filter returned something unless the second parameter is false.
276
+ """
277
+ rv = " ".join(
278
+ f'{escape(key)}="{escape(value)}"'
279
+ for key, value in d.items()
280
+ if value is not None and not isinstance(value, Undefined)
281
+ )
282
+
283
+ if autospace and rv:
284
+ rv = " " + rv
285
+
286
+ if eval_ctx.autoescape:
287
+ rv = Markup(rv)
288
+
289
+ return rv
290
+
291
+
292
+ def do_capitalize(s: str) -> str:
293
+ """Capitalize a value. The first character will be uppercase, all others
294
+ lowercase.
295
+ """
296
+ return soft_str(s).capitalize()
297
+
298
+
299
+ _word_beginning_split_re = re.compile(r"([-\s({\[<]+)")
300
+
301
+
302
+ def do_title(s: str) -> str:
303
+ """Return a titlecased version of the value. I.e. words will start with
304
+ uppercase letters, all remaining characters are lowercase.
305
+ """
306
+ return "".join(
307
+ [
308
+ item[0].upper() + item[1:].lower()
309
+ for item in _word_beginning_split_re.split(soft_str(s))
310
+ if item
311
+ ]
312
+ )
313
+
314
+
315
+ def do_dictsort(
316
+ value: t.Mapping[K, V],
317
+ case_sensitive: bool = False,
318
+ by: 'te.Literal["key", "value"]' = "key",
319
+ reverse: bool = False,
320
+ ) -> t.List[t.Tuple[K, V]]:
321
+ """Sort a dict and yield (key, value) pairs. Python dicts may not
322
+ be in the order you want to display them in, so sort them first.
323
+
324
+ .. sourcecode:: jinja
325
+
326
+ {% for key, value in mydict|dictsort %}
327
+ sort the dict by key, case insensitive
328
+
329
+ {% for key, value in mydict|dictsort(reverse=true) %}
330
+ sort the dict by key, case insensitive, reverse order
331
+
332
+ {% for key, value in mydict|dictsort(true) %}
333
+ sort the dict by key, case sensitive
334
+
335
+ {% for key, value in mydict|dictsort(false, 'value') %}
336
+ sort the dict by value, case insensitive
337
+ """
338
+ if by == "key":
339
+ pos = 0
340
+ elif by == "value":
341
+ pos = 1
342
+ else:
343
+ raise FilterArgumentError('You can only sort by either "key" or "value"')
344
+
345
+ def sort_func(item: t.Tuple[t.Any, t.Any]) -> t.Any:
346
+ value = item[pos]
347
+
348
+ if not case_sensitive:
349
+ value = ignore_case(value)
350
+
351
+ return value
352
+
353
+ return sorted(value.items(), key=sort_func, reverse=reverse)
354
+
355
+
356
+ @pass_environment
357
+ def do_sort(
358
+ environment: "Environment",
359
+ value: "t.Iterable[V]",
360
+ reverse: bool = False,
361
+ case_sensitive: bool = False,
362
+ attribute: t.Optional[t.Union[str, int]] = None,
363
+ ) -> "t.List[V]":
364
+ """Sort an iterable using Python's :func:`sorted`.
365
+
366
+ .. sourcecode:: jinja
367
+
368
+ {% for city in cities|sort %}
369
+ ...
370
+ {% endfor %}
371
+
372
+ :param reverse: Sort descending instead of ascending.
373
+ :param case_sensitive: When sorting strings, sort upper and lower
374
+ case separately.
375
+ :param attribute: When sorting objects or dicts, an attribute or
376
+ key to sort by. Can use dot notation like ``"address.city"``.
377
+ Can be a list of attributes like ``"age,name"``.
378
+
379
+ The sort is stable, it does not change the relative order of
380
+ elements that compare equal. This makes it is possible to chain
381
+ sorts on different attributes and ordering.
382
+
383
+ .. sourcecode:: jinja
384
+
385
+ {% for user in users|sort(attribute="name")
386
+ |sort(reverse=true, attribute="age") %}
387
+ ...
388
+ {% endfor %}
389
+
390
+ As a shortcut to chaining when the direction is the same for all
391
+ attributes, pass a comma separate list of attributes.
392
+
393
+ .. sourcecode:: jinja
394
+
395
+ {% for user in users|sort(attribute="age,name") %}
396
+ ...
397
+ {% endfor %}
398
+
399
+ .. versionchanged:: 2.11.0
400
+ The ``attribute`` parameter can be a comma separated list of
401
+ attributes, e.g. ``"age,name"``.
402
+
403
+ .. versionchanged:: 2.6
404
+ The ``attribute`` parameter was added.
405
+ """
406
+ key_func = make_multi_attrgetter(
407
+ environment, attribute, postprocess=ignore_case if not case_sensitive else None
408
+ )
409
+ return sorted(value, key=key_func, reverse=reverse)
410
+
411
+
412
+ @pass_environment
413
+ def do_unique(
414
+ environment: "Environment",
415
+ value: "t.Iterable[V]",
416
+ case_sensitive: bool = False,
417
+ attribute: t.Optional[t.Union[str, int]] = None,
418
+ ) -> "t.Iterator[V]":
419
+ """Returns a list of unique items from the given iterable.
420
+
421
+ .. sourcecode:: jinja
422
+
423
+ {{ ['foo', 'bar', 'foobar', 'FooBar']|unique|list }}
424
+ -> ['foo', 'bar', 'foobar']
425
+
426
+ The unique items are yielded in the same order as their first occurrence in
427
+ the iterable passed to the filter.
428
+
429
+ :param case_sensitive: Treat upper and lower case strings as distinct.
430
+ :param attribute: Filter objects with unique values for this attribute.
431
+ """
432
+ getter = make_attrgetter(
433
+ environment, attribute, postprocess=ignore_case if not case_sensitive else None
434
+ )
435
+ seen = set()
436
+
437
+ for item in value:
438
+ key = getter(item)
439
+
440
+ if key not in seen:
441
+ seen.add(key)
442
+ yield item
443
+
444
+
445
+ def _min_or_max(
446
+ environment: "Environment",
447
+ value: "t.Iterable[V]",
448
+ func: "t.Callable[..., V]",
449
+ case_sensitive: bool,
450
+ attribute: t.Optional[t.Union[str, int]],
451
+ ) -> "t.Union[V, Undefined]":
452
+ it = iter(value)
453
+
454
+ try:
455
+ first = next(it)
456
+ except StopIteration:
457
+ return environment.undefined("No aggregated item, sequence was empty.")
458
+
459
+ key_func = make_attrgetter(
460
+ environment, attribute, postprocess=ignore_case if not case_sensitive else None
461
+ )
462
+ return func(chain([first], it), key=key_func)
463
+
464
+
465
+ @pass_environment
466
+ def do_min(
467
+ environment: "Environment",
468
+ value: "t.Iterable[V]",
469
+ case_sensitive: bool = False,
470
+ attribute: t.Optional[t.Union[str, int]] = None,
471
+ ) -> "t.Union[V, Undefined]":
472
+ """Return the smallest item from the sequence.
473
+
474
+ .. sourcecode:: jinja
475
+
476
+ {{ [1, 2, 3]|min }}
477
+ -> 1
478
+
479
+ :param case_sensitive: Treat upper and lower case strings as distinct.
480
+ :param attribute: Get the object with the min value of this attribute.
481
+ """
482
+ return _min_or_max(environment, value, min, case_sensitive, attribute)
483
+
484
+
485
+ @pass_environment
486
+ def do_max(
487
+ environment: "Environment",
488
+ value: "t.Iterable[V]",
489
+ case_sensitive: bool = False,
490
+ attribute: t.Optional[t.Union[str, int]] = None,
491
+ ) -> "t.Union[V, Undefined]":
492
+ """Return the largest item from the sequence.
493
+
494
+ .. sourcecode:: jinja
495
+
496
+ {{ [1, 2, 3]|max }}
497
+ -> 3
498
+
499
+ :param case_sensitive: Treat upper and lower case strings as distinct.
500
+ :param attribute: Get the object with the max value of this attribute.
501
+ """
502
+ return _min_or_max(environment, value, max, case_sensitive, attribute)
503
+
504
+
505
+ def do_default(
506
+ value: V,
507
+ default_value: V = "", # type: ignore
508
+ boolean: bool = False,
509
+ ) -> V:
510
+ """If the value is undefined it will return the passed default value,
511
+ otherwise the value of the variable:
512
+
513
+ .. sourcecode:: jinja
514
+
515
+ {{ my_variable|default('my_variable is not defined') }}
516
+
517
+ This will output the value of ``my_variable`` if the variable was
518
+ defined, otherwise ``'my_variable is not defined'``. If you want
519
+ to use default with variables that evaluate to false you have to
520
+ set the second parameter to `true`:
521
+
522
+ .. sourcecode:: jinja
523
+
524
+ {{ ''|default('the string was empty', true) }}
525
+
526
+ .. versionchanged:: 2.11
527
+ It's now possible to configure the :class:`~jinja2.Environment` with
528
+ :class:`~jinja2.ChainableUndefined` to make the `default` filter work
529
+ on nested elements and attributes that may contain undefined values
530
+ in the chain without getting an :exc:`~jinja2.UndefinedError`.
531
+ """
532
+ if isinstance(value, Undefined) or (boolean and not value):
533
+ return default_value
534
+
535
+ return value
536
+
537
+
538
+ @pass_eval_context
539
+ def sync_do_join(
540
+ eval_ctx: "EvalContext",
541
+ value: t.Iterable,
542
+ d: str = "",
543
+ attribute: t.Optional[t.Union[str, int]] = None,
544
+ ) -> str:
545
+ """Return a string which is the concatenation of the strings in the
546
+ sequence. The separator between elements is an empty string per
547
+ default, you can define it with the optional parameter:
548
+
549
+ .. sourcecode:: jinja
550
+
551
+ {{ [1, 2, 3]|join('|') }}
552
+ -> 1|2|3
553
+
554
+ {{ [1, 2, 3]|join }}
555
+ -> 123
556
+
557
+ It is also possible to join certain attributes of an object:
558
+
559
+ .. sourcecode:: jinja
560
+
561
+ {{ users|join(', ', attribute='username') }}
562
+
563
+ .. versionadded:: 2.6
564
+ The `attribute` parameter was added.
565
+ """
566
+ if attribute is not None:
567
+ value = map(make_attrgetter(eval_ctx.environment, attribute), value)
568
+
569
+ # no automatic escaping? joining is a lot easier then
570
+ if not eval_ctx.autoescape:
571
+ return str(d).join(map(str, value))
572
+
573
+ # if the delimiter doesn't have an html representation we check
574
+ # if any of the items has. If yes we do a coercion to Markup
575
+ if not hasattr(d, "__html__"):
576
+ value = list(value)
577
+ do_escape = False
578
+
579
+ for idx, item in enumerate(value):
580
+ if hasattr(item, "__html__"):
581
+ do_escape = True
582
+ else:
583
+ value[idx] = str(item)
584
+
585
+ if do_escape:
586
+ d = escape(d)
587
+ else:
588
+ d = str(d)
589
+
590
+ return d.join(value)
591
+
592
+ # no html involved, to normal joining
593
+ return soft_str(d).join(map(soft_str, value))
594
+
595
+
596
+ @async_variant(sync_do_join) # type: ignore
597
+ async def do_join(
598
+ eval_ctx: "EvalContext",
599
+ value: t.Union[t.AsyncIterable, t.Iterable],
600
+ d: str = "",
601
+ attribute: t.Optional[t.Union[str, int]] = None,
602
+ ) -> str:
603
+ return sync_do_join(eval_ctx, await auto_to_list(value), d, attribute)
604
+
605
+
606
+ def do_center(value: str, width: int = 80) -> str:
607
+ """Centers the value in a field of a given width."""
608
+ return soft_str(value).center(width)
609
+
610
+
611
+ @pass_environment
612
+ def sync_do_first(
613
+ environment: "Environment", seq: "t.Iterable[V]"
614
+ ) -> "t.Union[V, Undefined]":
615
+ """Return the first item of a sequence."""
616
+ try:
617
+ return next(iter(seq))
618
+ except StopIteration:
619
+ return environment.undefined("No first item, sequence was empty.")
620
+
621
+
622
+ @async_variant(sync_do_first) # type: ignore
623
+ async def do_first(
624
+ environment: "Environment", seq: "t.Union[t.AsyncIterable[V], t.Iterable[V]]"
625
+ ) -> "t.Union[V, Undefined]":
626
+ try:
627
+ return await auto_aiter(seq).__anext__()
628
+ except StopAsyncIteration:
629
+ return environment.undefined("No first item, sequence was empty.")
630
+
631
+
632
+ @pass_environment
633
+ def do_last(
634
+ environment: "Environment", seq: "t.Reversible[V]"
635
+ ) -> "t.Union[V, Undefined]":
636
+ """Return the last item of a sequence.
637
+
638
+ Note: Does not work with generators. You may want to explicitly
639
+ convert it to a list:
640
+
641
+ .. sourcecode:: jinja
642
+
643
+ {{ data | selectattr('name', '==', 'Jinja') | list | last }}
644
+ """
645
+ try:
646
+ return next(iter(reversed(seq)))
647
+ except StopIteration:
648
+ return environment.undefined("No last item, sequence was empty.")
649
+
650
+
651
+ # No async do_last, it may not be safe in async mode.
652
+
653
+
654
+ @pass_context
655
+ def do_random(context: "Context", seq: "t.Sequence[V]") -> "t.Union[V, Undefined]":
656
+ """Return a random item from the sequence."""
657
+ try:
658
+ return random.choice(seq)
659
+ except IndexError:
660
+ return context.environment.undefined("No random item, sequence was empty.")
661
+
662
+
663
+ def do_filesizeformat(value: t.Union[str, float, int], binary: bool = False) -> str:
664
+ """Format the value like a 'human-readable' file size (i.e. 13 kB,
665
+ 4.1 MB, 102 Bytes, etc). Per default decimal prefixes are used (Mega,
666
+ Giga, etc.), if the second parameter is set to `True` the binary
667
+ prefixes are used (Mebi, Gibi).
668
+ """
669
+ bytes = float(value)
670
+ base = 1024 if binary else 1000
671
+ prefixes = [
672
+ ("KiB" if binary else "kB"),
673
+ ("MiB" if binary else "MB"),
674
+ ("GiB" if binary else "GB"),
675
+ ("TiB" if binary else "TB"),
676
+ ("PiB" if binary else "PB"),
677
+ ("EiB" if binary else "EB"),
678
+ ("ZiB" if binary else "ZB"),
679
+ ("YiB" if binary else "YB"),
680
+ ]
681
+
682
+ if bytes == 1:
683
+ return "1 Byte"
684
+ elif bytes < base:
685
+ return f"{int(bytes)} Bytes"
686
+ else:
687
+ for i, prefix in enumerate(prefixes):
688
+ unit = base ** (i + 2)
689
+
690
+ if bytes < unit:
691
+ return f"{base * bytes / unit:.1f} {prefix}"
692
+
693
+ return f"{base * bytes / unit:.1f} {prefix}"
694
+
695
+
696
+ def do_pprint(value: t.Any) -> str:
697
+ """Pretty print a variable. Useful for debugging."""
698
+ return pformat(value)
699
+
700
+
701
+ _uri_scheme_re = re.compile(r"^([\w.+-]{2,}:(/){0,2})$")
702
+
703
+
704
+ @pass_eval_context
705
+ def do_urlize(
706
+ eval_ctx: "EvalContext",
707
+ value: str,
708
+ trim_url_limit: t.Optional[int] = None,
709
+ nofollow: bool = False,
710
+ target: t.Optional[str] = None,
711
+ rel: t.Optional[str] = None,
712
+ extra_schemes: t.Optional[t.Iterable[str]] = None,
713
+ ) -> str:
714
+ """Convert URLs in text into clickable links.
715
+
716
+ This may not recognize links in some situations. Usually, a more
717
+ comprehensive formatter, such as a Markdown library, is a better
718
+ choice.
719
+
720
+ Works on ``http://``, ``https://``, ``www.``, ``mailto:``, and email
721
+ addresses. Links with trailing punctuation (periods, commas, closing
722
+ parentheses) and leading punctuation (opening parentheses) are
723
+ recognized excluding the punctuation. Email addresses that include
724
+ header fields are not recognized (for example,
725
+ ``mailto:address@example.com?cc=copy@example.com``).
726
+
727
+ :param value: Original text containing URLs to link.
728
+ :param trim_url_limit: Shorten displayed URL values to this length.
729
+ :param nofollow: Add the ``rel=nofollow`` attribute to links.
730
+ :param target: Add the ``target`` attribute to links.
731
+ :param rel: Add the ``rel`` attribute to links.
732
+ :param extra_schemes: Recognize URLs that start with these schemes
733
+ in addition to the default behavior. Defaults to
734
+ ``env.policies["urlize.extra_schemes"]``, which defaults to no
735
+ extra schemes.
736
+
737
+ .. versionchanged:: 3.0
738
+ The ``extra_schemes`` parameter was added.
739
+
740
+ .. versionchanged:: 3.0
741
+ Generate ``https://`` links for URLs without a scheme.
742
+
743
+ .. versionchanged:: 3.0
744
+ The parsing rules were updated. Recognize email addresses with
745
+ or without the ``mailto:`` scheme. Validate IP addresses. Ignore
746
+ parentheses and brackets in more cases.
747
+
748
+ .. versionchanged:: 2.8
749
+ The ``target`` parameter was added.
750
+ """
751
+ policies = eval_ctx.environment.policies
752
+ rel_parts = set((rel or "").split())
753
+
754
+ if nofollow:
755
+ rel_parts.add("nofollow")
756
+
757
+ rel_parts.update((policies["urlize.rel"] or "").split())
758
+ rel = " ".join(sorted(rel_parts)) or None
759
+
760
+ if target is None:
761
+ target = policies["urlize.target"]
762
+
763
+ if extra_schemes is None:
764
+ extra_schemes = policies["urlize.extra_schemes"] or ()
765
+
766
+ for scheme in extra_schemes:
767
+ if _uri_scheme_re.fullmatch(scheme) is None:
768
+ raise FilterArgumentError(f"{scheme!r} is not a valid URI scheme prefix.")
769
+
770
+ rv = urlize(
771
+ value,
772
+ trim_url_limit=trim_url_limit,
773
+ rel=rel,
774
+ target=target,
775
+ extra_schemes=extra_schemes,
776
+ )
777
+
778
+ if eval_ctx.autoescape:
779
+ rv = Markup(rv)
780
+
781
+ return rv
782
+
783
+
784
+ def do_indent(
785
+ s: str, width: t.Union[int, str] = 4, first: bool = False, blank: bool = False
786
+ ) -> str:
787
+ """Return a copy of the string with each line indented by 4 spaces. The
788
+ first line and blank lines are not indented by default.
789
+
790
+ :param width: Number of spaces, or a string, to indent by.
791
+ :param first: Don't skip indenting the first line.
792
+ :param blank: Don't skip indenting empty lines.
793
+
794
+ .. versionchanged:: 3.0
795
+ ``width`` can be a string.
796
+
797
+ .. versionchanged:: 2.10
798
+ Blank lines are not indented by default.
799
+
800
+ Rename the ``indentfirst`` argument to ``first``.
801
+ """
802
+ if isinstance(width, str):
803
+ indention = width
804
+ else:
805
+ indention = " " * width
806
+
807
+ newline = "\n"
808
+
809
+ if isinstance(s, Markup):
810
+ indention = Markup(indention)
811
+ newline = Markup(newline)
812
+
813
+ s += newline # this quirk is necessary for splitlines method
814
+
815
+ if blank:
816
+ rv = (newline + indention).join(s.splitlines())
817
+ else:
818
+ lines = s.splitlines()
819
+ rv = lines.pop(0)
820
+
821
+ if lines:
822
+ rv += newline + newline.join(
823
+ indention + line if line else line for line in lines
824
+ )
825
+
826
+ if first:
827
+ rv = indention + rv
828
+
829
+ return rv
830
+
831
+
832
+ @pass_environment
833
+ def do_truncate(
834
+ env: "Environment",
835
+ s: str,
836
+ length: int = 255,
837
+ killwords: bool = False,
838
+ end: str = "...",
839
+ leeway: t.Optional[int] = None,
840
+ ) -> str:
841
+ """Return a truncated copy of the string. The length is specified
842
+ with the first parameter which defaults to ``255``. If the second
843
+ parameter is ``true`` the filter will cut the text at length. Otherwise
844
+ it will discard the last word. If the text was in fact
845
+ truncated it will append an ellipsis sign (``"..."``). If you want a
846
+ different ellipsis sign than ``"..."`` you can specify it using the
847
+ third parameter. Strings that only exceed the length by the tolerance
848
+ margin given in the fourth parameter will not be truncated.
849
+
850
+ .. sourcecode:: jinja
851
+
852
+ {{ "foo bar baz qux"|truncate(9) }}
853
+ -> "foo..."
854
+ {{ "foo bar baz qux"|truncate(9, True) }}
855
+ -> "foo ba..."
856
+ {{ "foo bar baz qux"|truncate(11) }}
857
+ -> "foo bar baz qux"
858
+ {{ "foo bar baz qux"|truncate(11, False, '...', 0) }}
859
+ -> "foo bar..."
860
+
861
+ The default leeway on newer Jinja versions is 5 and was 0 before but
862
+ can be reconfigured globally.
863
+ """
864
+ if leeway is None:
865
+ leeway = env.policies["truncate.leeway"]
866
+
867
+ assert length >= len(end), f"expected length >= {len(end)}, got {length}"
868
+ assert leeway >= 0, f"expected leeway >= 0, got {leeway}"
869
+
870
+ if len(s) <= length + leeway:
871
+ return s
872
+
873
+ if killwords:
874
+ return s[: length - len(end)] + end
875
+
876
+ result = s[: length - len(end)].rsplit(" ", 1)[0]
877
+ return result + end
878
+
879
+
880
+ @pass_environment
881
+ def do_wordwrap(
882
+ environment: "Environment",
883
+ s: str,
884
+ width: int = 79,
885
+ break_long_words: bool = True,
886
+ wrapstring: t.Optional[str] = None,
887
+ break_on_hyphens: bool = True,
888
+ ) -> str:
889
+ """Wrap a string to the given width. Existing newlines are treated
890
+ as paragraphs to be wrapped separately.
891
+
892
+ :param s: Original text to wrap.
893
+ :param width: Maximum length of wrapped lines.
894
+ :param break_long_words: If a word is longer than ``width``, break
895
+ it across lines.
896
+ :param break_on_hyphens: If a word contains hyphens, it may be split
897
+ across lines.
898
+ :param wrapstring: String to join each wrapped line. Defaults to
899
+ :attr:`Environment.newline_sequence`.
900
+
901
+ .. versionchanged:: 2.11
902
+ Existing newlines are treated as paragraphs wrapped separately.
903
+
904
+ .. versionchanged:: 2.11
905
+ Added the ``break_on_hyphens`` parameter.
906
+
907
+ .. versionchanged:: 2.7
908
+ Added the ``wrapstring`` parameter.
909
+ """
910
+ import textwrap
911
+
912
+ if wrapstring is None:
913
+ wrapstring = environment.newline_sequence
914
+
915
+ # textwrap.wrap doesn't consider existing newlines when wrapping.
916
+ # If the string has a newline before width, wrap will still insert
917
+ # a newline at width, resulting in a short line. Instead, split and
918
+ # wrap each paragraph individually.
919
+ return wrapstring.join(
920
+ [
921
+ wrapstring.join(
922
+ textwrap.wrap(
923
+ line,
924
+ width=width,
925
+ expand_tabs=False,
926
+ replace_whitespace=False,
927
+ break_long_words=break_long_words,
928
+ break_on_hyphens=break_on_hyphens,
929
+ )
930
+ )
931
+ for line in s.splitlines()
932
+ ]
933
+ )
934
+
935
+
936
+ _word_re = re.compile(r"\w+")
937
+
938
+
939
+ def do_wordcount(s: str) -> int:
940
+ """Count the words in that string."""
941
+ return len(_word_re.findall(soft_str(s)))
942
+
943
+
944
+ def do_int(value: t.Any, default: int = 0, base: int = 10) -> int:
945
+ """Convert the value into an integer. If the
946
+ conversion doesn't work it will return ``0``. You can
947
+ override this default using the first parameter. You
948
+ can also override the default base (10) in the second
949
+ parameter, which handles input with prefixes such as
950
+ 0b, 0o and 0x for bases 2, 8 and 16 respectively.
951
+ The base is ignored for decimal numbers and non-string values.
952
+ """
953
+ try:
954
+ if isinstance(value, str):
955
+ return int(value, base)
956
+
957
+ return int(value)
958
+ except (TypeError, ValueError):
959
+ # this quirk is necessary so that "42.23"|int gives 42.
960
+ try:
961
+ return int(float(value))
962
+ except (TypeError, ValueError):
963
+ return default
964
+
965
+
966
+ def do_float(value: t.Any, default: float = 0.0) -> float:
967
+ """Convert the value into a floating point number. If the
968
+ conversion doesn't work it will return ``0.0``. You can
969
+ override this default using the first parameter.
970
+ """
971
+ try:
972
+ return float(value)
973
+ except (TypeError, ValueError):
974
+ return default
975
+
976
+
977
+ def do_format(value: str, *args: t.Any, **kwargs: t.Any) -> str:
978
+ """Apply the given values to a `printf-style`_ format string, like
979
+ ``string % values``.
980
+
981
+ .. sourcecode:: jinja
982
+
983
+ {{ "%s, %s!"|format(greeting, name) }}
984
+ Hello, World!
985
+
986
+ In most cases it should be more convenient and efficient to use the
987
+ ``%`` operator or :meth:`str.format`.
988
+
989
+ .. code-block:: text
990
+
991
+ {{ "%s, %s!" % (greeting, name) }}
992
+ {{ "{}, {}!".format(greeting, name) }}
993
+
994
+ .. _printf-style: https://docs.python.org/library/stdtypes.html
995
+ #printf-style-string-formatting
996
+ """
997
+ if args and kwargs:
998
+ raise FilterArgumentError(
999
+ "can't handle positional and keyword arguments at the same time"
1000
+ )
1001
+
1002
+ return soft_str(value) % (kwargs or args)
1003
+
1004
+
1005
+ def do_trim(value: str, chars: t.Optional[str] = None) -> str:
1006
+ """Strip leading and trailing characters, by default whitespace."""
1007
+ return soft_str(value).strip(chars)
1008
+
1009
+
1010
+ def do_striptags(value: "t.Union[str, HasHTML]") -> str:
1011
+ """Strip SGML/XML tags and replace adjacent whitespace by one space."""
1012
+ if hasattr(value, "__html__"):
1013
+ value = t.cast("HasHTML", value).__html__()
1014
+
1015
+ return Markup(str(value)).striptags()
1016
+
1017
+
1018
+ def sync_do_slice(
1019
+ value: "t.Collection[V]", slices: int, fill_with: "t.Optional[V]" = None
1020
+ ) -> "t.Iterator[t.List[V]]":
1021
+ """Slice an iterator and return a list of lists containing
1022
+ those items. Useful if you want to create a div containing
1023
+ three ul tags that represent columns:
1024
+
1025
+ .. sourcecode:: html+jinja
1026
+
1027
+ <div class="columnwrapper">
1028
+ {%- for column in items|slice(3) %}
1029
+ <ul class="column-{{ loop.index }}">
1030
+ {%- for item in column %}
1031
+ <li>{{ item }}</li>
1032
+ {%- endfor %}
1033
+ </ul>
1034
+ {%- endfor %}
1035
+ </div>
1036
+
1037
+ If you pass it a second argument it's used to fill missing
1038
+ values on the last iteration.
1039
+ """
1040
+ seq = list(value)
1041
+ length = len(seq)
1042
+ items_per_slice = length // slices
1043
+ slices_with_extra = length % slices
1044
+ offset = 0
1045
+
1046
+ for slice_number in range(slices):
1047
+ start = offset + slice_number * items_per_slice
1048
+
1049
+ if slice_number < slices_with_extra:
1050
+ offset += 1
1051
+
1052
+ end = offset + (slice_number + 1) * items_per_slice
1053
+ tmp = seq[start:end]
1054
+
1055
+ if fill_with is not None and slice_number >= slices_with_extra:
1056
+ tmp.append(fill_with)
1057
+
1058
+ yield tmp
1059
+
1060
+
1061
+ @async_variant(sync_do_slice) # type: ignore
1062
+ async def do_slice(
1063
+ value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]",
1064
+ slices: int,
1065
+ fill_with: t.Optional[t.Any] = None,
1066
+ ) -> "t.Iterator[t.List[V]]":
1067
+ return sync_do_slice(await auto_to_list(value), slices, fill_with)
1068
+
1069
+
1070
+ def do_batch(
1071
+ value: "t.Iterable[V]", linecount: int, fill_with: "t.Optional[V]" = None
1072
+ ) -> "t.Iterator[t.List[V]]":
1073
+ """
1074
+ A filter that batches items. It works pretty much like `slice`
1075
+ just the other way round. It returns a list of lists with the
1076
+ given number of items. If you provide a second parameter this
1077
+ is used to fill up missing items. See this example:
1078
+
1079
+ .. sourcecode:: html+jinja
1080
+
1081
+ <table>
1082
+ {%- for row in items|batch(3, '&nbsp;') %}
1083
+ <tr>
1084
+ {%- for column in row %}
1085
+ <td>{{ column }}</td>
1086
+ {%- endfor %}
1087
+ </tr>
1088
+ {%- endfor %}
1089
+ </table>
1090
+ """
1091
+ tmp: "t.List[V]" = []
1092
+
1093
+ for item in value:
1094
+ if len(tmp) == linecount:
1095
+ yield tmp
1096
+ tmp = []
1097
+
1098
+ tmp.append(item)
1099
+
1100
+ if tmp:
1101
+ if fill_with is not None and len(tmp) < linecount:
1102
+ tmp += [fill_with] * (linecount - len(tmp))
1103
+
1104
+ yield tmp
1105
+
1106
+
1107
+ def do_round(
1108
+ value: float,
1109
+ precision: int = 0,
1110
+ method: 'te.Literal["common", "ceil", "floor"]' = "common",
1111
+ ) -> float:
1112
+ """Round the number to a given precision. The first
1113
+ parameter specifies the precision (default is ``0``), the
1114
+ second the rounding method:
1115
+
1116
+ - ``'common'`` rounds either up or down
1117
+ - ``'ceil'`` always rounds up
1118
+ - ``'floor'`` always rounds down
1119
+
1120
+ If you don't specify a method ``'common'`` is used.
1121
+
1122
+ .. sourcecode:: jinja
1123
+
1124
+ {{ 42.55|round }}
1125
+ -> 43.0
1126
+ {{ 42.55|round(1, 'floor') }}
1127
+ -> 42.5
1128
+
1129
+ Note that even if rounded to 0 precision, a float is returned. If
1130
+ you need a real integer, pipe it through `int`:
1131
+
1132
+ .. sourcecode:: jinja
1133
+
1134
+ {{ 42.55|round|int }}
1135
+ -> 43
1136
+ """
1137
+ if method not in {"common", "ceil", "floor"}:
1138
+ raise FilterArgumentError("method must be common, ceil or floor")
1139
+
1140
+ if method == "common":
1141
+ return round(value, precision)
1142
+
1143
+ func = getattr(math, method)
1144
+ return t.cast(float, func(value * (10**precision)) / (10**precision))
1145
+
1146
+
1147
+ class _GroupTuple(t.NamedTuple):
1148
+ grouper: t.Any
1149
+ list: t.List
1150
+
1151
+ # Use the regular tuple repr to hide this subclass if users print
1152
+ # out the value during debugging.
1153
+ def __repr__(self) -> str:
1154
+ return tuple.__repr__(self)
1155
+
1156
+ def __str__(self) -> str:
1157
+ return tuple.__str__(self)
1158
+
1159
+
1160
+ @pass_environment
1161
+ def sync_do_groupby(
1162
+ environment: "Environment",
1163
+ value: "t.Iterable[V]",
1164
+ attribute: t.Union[str, int],
1165
+ default: t.Optional[t.Any] = None,
1166
+ case_sensitive: bool = False,
1167
+ ) -> "t.List[_GroupTuple]":
1168
+ """Group a sequence of objects by an attribute using Python's
1169
+ :func:`itertools.groupby`. The attribute can use dot notation for
1170
+ nested access, like ``"address.city"``. Unlike Python's ``groupby``,
1171
+ the values are sorted first so only one group is returned for each
1172
+ unique value.
1173
+
1174
+ For example, a list of ``User`` objects with a ``city`` attribute
1175
+ can be rendered in groups. In this example, ``grouper`` refers to
1176
+ the ``city`` value of the group.
1177
+
1178
+ .. sourcecode:: html+jinja
1179
+
1180
+ <ul>{% for city, items in users|groupby("city") %}
1181
+ <li>{{ city }}
1182
+ <ul>{% for user in items %}
1183
+ <li>{{ user.name }}
1184
+ {% endfor %}</ul>
1185
+ </li>
1186
+ {% endfor %}</ul>
1187
+
1188
+ ``groupby`` yields namedtuples of ``(grouper, list)``, which
1189
+ can be used instead of the tuple unpacking above. ``grouper`` is the
1190
+ value of the attribute, and ``list`` is the items with that value.
1191
+
1192
+ .. sourcecode:: html+jinja
1193
+
1194
+ <ul>{% for group in users|groupby("city") %}
1195
+ <li>{{ group.grouper }}: {{ group.list|join(", ") }}
1196
+ {% endfor %}</ul>
1197
+
1198
+ You can specify a ``default`` value to use if an object in the list
1199
+ does not have the given attribute.
1200
+
1201
+ .. sourcecode:: jinja
1202
+
1203
+ <ul>{% for city, items in users|groupby("city", default="NY") %}
1204
+ <li>{{ city }}: {{ items|map(attribute="name")|join(", ") }}</li>
1205
+ {% endfor %}</ul>
1206
+
1207
+ Like the :func:`~jinja-filters.sort` filter, sorting and grouping is
1208
+ case-insensitive by default. The ``key`` for each group will have
1209
+ the case of the first item in that group of values. For example, if
1210
+ a list of users has cities ``["CA", "NY", "ca"]``, the "CA" group
1211
+ will have two values. This can be disabled by passing
1212
+ ``case_sensitive=True``.
1213
+
1214
+ .. versionchanged:: 3.1
1215
+ Added the ``case_sensitive`` parameter. Sorting and grouping is
1216
+ case-insensitive by default, matching other filters that do
1217
+ comparisons.
1218
+
1219
+ .. versionchanged:: 3.0
1220
+ Added the ``default`` parameter.
1221
+
1222
+ .. versionchanged:: 2.6
1223
+ The attribute supports dot notation for nested access.
1224
+ """
1225
+ expr = make_attrgetter(
1226
+ environment,
1227
+ attribute,
1228
+ postprocess=ignore_case if not case_sensitive else None,
1229
+ default=default,
1230
+ )
1231
+ out = [
1232
+ _GroupTuple(key, list(values))
1233
+ for key, values in groupby(sorted(value, key=expr), expr)
1234
+ ]
1235
+
1236
+ if not case_sensitive:
1237
+ # Return the real key from the first value instead of the lowercase key.
1238
+ output_expr = make_attrgetter(environment, attribute, default=default)
1239
+ out = [_GroupTuple(output_expr(values[0]), values) for _, values in out]
1240
+
1241
+ return out
1242
+
1243
+
1244
+ @async_variant(sync_do_groupby) # type: ignore
1245
+ async def do_groupby(
1246
+ environment: "Environment",
1247
+ value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]",
1248
+ attribute: t.Union[str, int],
1249
+ default: t.Optional[t.Any] = None,
1250
+ case_sensitive: bool = False,
1251
+ ) -> "t.List[_GroupTuple]":
1252
+ expr = make_attrgetter(
1253
+ environment,
1254
+ attribute,
1255
+ postprocess=ignore_case if not case_sensitive else None,
1256
+ default=default,
1257
+ )
1258
+ out = [
1259
+ _GroupTuple(key, await auto_to_list(values))
1260
+ for key, values in groupby(sorted(await auto_to_list(value), key=expr), expr)
1261
+ ]
1262
+
1263
+ if not case_sensitive:
1264
+ # Return the real key from the first value instead of the lowercase key.
1265
+ output_expr = make_attrgetter(environment, attribute, default=default)
1266
+ out = [_GroupTuple(output_expr(values[0]), values) for _, values in out]
1267
+
1268
+ return out
1269
+
1270
+
1271
+ @pass_environment
1272
+ def sync_do_sum(
1273
+ environment: "Environment",
1274
+ iterable: "t.Iterable[V]",
1275
+ attribute: t.Optional[t.Union[str, int]] = None,
1276
+ start: V = 0, # type: ignore
1277
+ ) -> V:
1278
+ """Returns the sum of a sequence of numbers plus the value of parameter
1279
+ 'start' (which defaults to 0). When the sequence is empty it returns
1280
+ start.
1281
+
1282
+ It is also possible to sum up only certain attributes:
1283
+
1284
+ .. sourcecode:: jinja
1285
+
1286
+ Total: {{ items|sum(attribute='price') }}
1287
+
1288
+ .. versionchanged:: 2.6
1289
+ The ``attribute`` parameter was added to allow summing up over
1290
+ attributes. Also the ``start`` parameter was moved on to the right.
1291
+ """
1292
+ if attribute is not None:
1293
+ iterable = map(make_attrgetter(environment, attribute), iterable)
1294
+
1295
+ return sum(iterable, start) # type: ignore[no-any-return, call-overload]
1296
+
1297
+
1298
+ @async_variant(sync_do_sum) # type: ignore
1299
+ async def do_sum(
1300
+ environment: "Environment",
1301
+ iterable: "t.Union[t.AsyncIterable[V], t.Iterable[V]]",
1302
+ attribute: t.Optional[t.Union[str, int]] = None,
1303
+ start: V = 0, # type: ignore
1304
+ ) -> V:
1305
+ rv = start
1306
+
1307
+ if attribute is not None:
1308
+ func = make_attrgetter(environment, attribute)
1309
+ else:
1310
+
1311
+ def func(x: V) -> V:
1312
+ return x
1313
+
1314
+ async for item in auto_aiter(iterable):
1315
+ rv += func(item)
1316
+
1317
+ return rv
1318
+
1319
+
1320
+ def sync_do_list(value: "t.Iterable[V]") -> "t.List[V]":
1321
+ """Convert the value into a list. If it was a string the returned list
1322
+ will be a list of characters.
1323
+ """
1324
+ return list(value)
1325
+
1326
+
1327
+ @async_variant(sync_do_list) # type: ignore
1328
+ async def do_list(value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]") -> "t.List[V]":
1329
+ return await auto_to_list(value)
1330
+
1331
+
1332
+ def do_mark_safe(value: str) -> Markup:
1333
+ """Mark the value as safe which means that in an environment with automatic
1334
+ escaping enabled this variable will not be escaped.
1335
+ """
1336
+ return Markup(value)
1337
+
1338
+
1339
+ def do_mark_unsafe(value: str) -> str:
1340
+ """Mark a value as unsafe. This is the reverse operation for :func:`safe`."""
1341
+ return str(value)
1342
+
1343
+
1344
+ @typing.overload
1345
+ def do_reverse(value: str) -> str:
1346
+ ...
1347
+
1348
+
1349
+ @typing.overload
1350
+ def do_reverse(value: "t.Iterable[V]") -> "t.Iterable[V]":
1351
+ ...
1352
+
1353
+
1354
+ def do_reverse(value: t.Union[str, t.Iterable[V]]) -> t.Union[str, t.Iterable[V]]:
1355
+ """Reverse the object or return an iterator that iterates over it the other
1356
+ way round.
1357
+ """
1358
+ if isinstance(value, str):
1359
+ return value[::-1]
1360
+
1361
+ try:
1362
+ return reversed(value) # type: ignore
1363
+ except TypeError:
1364
+ try:
1365
+ rv = list(value)
1366
+ rv.reverse()
1367
+ return rv
1368
+ except TypeError as e:
1369
+ raise FilterArgumentError("argument must be iterable") from e
1370
+
1371
+
1372
+ @pass_environment
1373
+ def do_attr(
1374
+ environment: "Environment", obj: t.Any, name: str
1375
+ ) -> t.Union[Undefined, t.Any]:
1376
+ """Get an attribute of an object. ``foo|attr("bar")`` works like
1377
+ ``foo.bar`` just that always an attribute is returned and items are not
1378
+ looked up.
1379
+
1380
+ See :ref:`Notes on subscriptions <notes-on-subscriptions>` for more details.
1381
+ """
1382
+ try:
1383
+ name = str(name)
1384
+ except UnicodeError:
1385
+ pass
1386
+ else:
1387
+ try:
1388
+ value = getattr(obj, name)
1389
+ except AttributeError:
1390
+ pass
1391
+ else:
1392
+ if environment.sandboxed:
1393
+ environment = t.cast("SandboxedEnvironment", environment)
1394
+
1395
+ if not environment.is_safe_attribute(obj, name, value):
1396
+ return environment.unsafe_undefined(obj, name)
1397
+
1398
+ return value
1399
+
1400
+ return environment.undefined(obj=obj, name=name)
1401
+
1402
+
1403
+ @typing.overload
1404
+ def sync_do_map(
1405
+ context: "Context", value: t.Iterable, name: str, *args: t.Any, **kwargs: t.Any
1406
+ ) -> t.Iterable:
1407
+ ...
1408
+
1409
+
1410
+ @typing.overload
1411
+ def sync_do_map(
1412
+ context: "Context",
1413
+ value: t.Iterable,
1414
+ *,
1415
+ attribute: str = ...,
1416
+ default: t.Optional[t.Any] = None,
1417
+ ) -> t.Iterable:
1418
+ ...
1419
+
1420
+
1421
+ @pass_context
1422
+ def sync_do_map(
1423
+ context: "Context", value: t.Iterable, *args: t.Any, **kwargs: t.Any
1424
+ ) -> t.Iterable:
1425
+ """Applies a filter on a sequence of objects or looks up an attribute.
1426
+ This is useful when dealing with lists of objects but you are really
1427
+ only interested in a certain value of it.
1428
+
1429
+ The basic usage is mapping on an attribute. Imagine you have a list
1430
+ of users but you are only interested in a list of usernames:
1431
+
1432
+ .. sourcecode:: jinja
1433
+
1434
+ Users on this page: {{ users|map(attribute='username')|join(', ') }}
1435
+
1436
+ You can specify a ``default`` value to use if an object in the list
1437
+ does not have the given attribute.
1438
+
1439
+ .. sourcecode:: jinja
1440
+
1441
+ {{ users|map(attribute="username", default="Anonymous")|join(", ") }}
1442
+
1443
+ Alternatively you can let it invoke a filter by passing the name of the
1444
+ filter and the arguments afterwards. A good example would be applying a
1445
+ text conversion filter on a sequence:
1446
+
1447
+ .. sourcecode:: jinja
1448
+
1449
+ Users on this page: {{ titles|map('lower')|join(', ') }}
1450
+
1451
+ Similar to a generator comprehension such as:
1452
+
1453
+ .. code-block:: python
1454
+
1455
+ (u.username for u in users)
1456
+ (getattr(u, "username", "Anonymous") for u in users)
1457
+ (do_lower(x) for x in titles)
1458
+
1459
+ .. versionchanged:: 2.11.0
1460
+ Added the ``default`` parameter.
1461
+
1462
+ .. versionadded:: 2.7
1463
+ """
1464
+ if value:
1465
+ func = prepare_map(context, args, kwargs)
1466
+
1467
+ for item in value:
1468
+ yield func(item)
1469
+
1470
+
1471
+ @typing.overload
1472
+ def do_map(
1473
+ context: "Context",
1474
+ value: t.Union[t.AsyncIterable, t.Iterable],
1475
+ name: str,
1476
+ *args: t.Any,
1477
+ **kwargs: t.Any,
1478
+ ) -> t.Iterable:
1479
+ ...
1480
+
1481
+
1482
+ @typing.overload
1483
+ def do_map(
1484
+ context: "Context",
1485
+ value: t.Union[t.AsyncIterable, t.Iterable],
1486
+ *,
1487
+ attribute: str = ...,
1488
+ default: t.Optional[t.Any] = None,
1489
+ ) -> t.Iterable:
1490
+ ...
1491
+
1492
+
1493
+ @async_variant(sync_do_map) # type: ignore
1494
+ async def do_map(
1495
+ context: "Context",
1496
+ value: t.Union[t.AsyncIterable, t.Iterable],
1497
+ *args: t.Any,
1498
+ **kwargs: t.Any,
1499
+ ) -> t.AsyncIterable:
1500
+ if value:
1501
+ func = prepare_map(context, args, kwargs)
1502
+
1503
+ async for item in auto_aiter(value):
1504
+ yield await auto_await(func(item))
1505
+
1506
+
1507
+ @pass_context
1508
+ def sync_do_select(
1509
+ context: "Context", value: "t.Iterable[V]", *args: t.Any, **kwargs: t.Any
1510
+ ) -> "t.Iterator[V]":
1511
+ """Filters a sequence of objects by applying a test to each object,
1512
+ and only selecting the objects with the test succeeding.
1513
+
1514
+ If no test is specified, each object will be evaluated as a boolean.
1515
+
1516
+ Example usage:
1517
+
1518
+ .. sourcecode:: jinja
1519
+
1520
+ {{ numbers|select("odd") }}
1521
+ {{ numbers|select("odd") }}
1522
+ {{ numbers|select("divisibleby", 3) }}
1523
+ {{ numbers|select("lessthan", 42) }}
1524
+ {{ strings|select("equalto", "mystring") }}
1525
+
1526
+ Similar to a generator comprehension such as:
1527
+
1528
+ .. code-block:: python
1529
+
1530
+ (n for n in numbers if test_odd(n))
1531
+ (n for n in numbers if test_divisibleby(n, 3))
1532
+
1533
+ .. versionadded:: 2.7
1534
+ """
1535
+ return select_or_reject(context, value, args, kwargs, lambda x: x, False)
1536
+
1537
+
1538
+ @async_variant(sync_do_select) # type: ignore
1539
+ async def do_select(
1540
+ context: "Context",
1541
+ value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]",
1542
+ *args: t.Any,
1543
+ **kwargs: t.Any,
1544
+ ) -> "t.AsyncIterator[V]":
1545
+ return async_select_or_reject(context, value, args, kwargs, lambda x: x, False)
1546
+
1547
+
1548
+ @pass_context
1549
+ def sync_do_reject(
1550
+ context: "Context", value: "t.Iterable[V]", *args: t.Any, **kwargs: t.Any
1551
+ ) -> "t.Iterator[V]":
1552
+ """Filters a sequence of objects by applying a test to each object,
1553
+ and rejecting the objects with the test succeeding.
1554
+
1555
+ If no test is specified, each object will be evaluated as a boolean.
1556
+
1557
+ Example usage:
1558
+
1559
+ .. sourcecode:: jinja
1560
+
1561
+ {{ numbers|reject("odd") }}
1562
+
1563
+ Similar to a generator comprehension such as:
1564
+
1565
+ .. code-block:: python
1566
+
1567
+ (n for n in numbers if not test_odd(n))
1568
+
1569
+ .. versionadded:: 2.7
1570
+ """
1571
+ return select_or_reject(context, value, args, kwargs, lambda x: not x, False)
1572
+
1573
+
1574
+ @async_variant(sync_do_reject) # type: ignore
1575
+ async def do_reject(
1576
+ context: "Context",
1577
+ value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]",
1578
+ *args: t.Any,
1579
+ **kwargs: t.Any,
1580
+ ) -> "t.AsyncIterator[V]":
1581
+ return async_select_or_reject(context, value, args, kwargs, lambda x: not x, False)
1582
+
1583
+
1584
+ @pass_context
1585
+ def sync_do_selectattr(
1586
+ context: "Context", value: "t.Iterable[V]", *args: t.Any, **kwargs: t.Any
1587
+ ) -> "t.Iterator[V]":
1588
+ """Filters a sequence of objects by applying a test to the specified
1589
+ attribute of each object, and only selecting the objects with the
1590
+ test succeeding.
1591
+
1592
+ If no test is specified, the attribute's value will be evaluated as
1593
+ a boolean.
1594
+
1595
+ Example usage:
1596
+
1597
+ .. sourcecode:: jinja
1598
+
1599
+ {{ users|selectattr("is_active") }}
1600
+ {{ users|selectattr("email", "none") }}
1601
+
1602
+ Similar to a generator comprehension such as:
1603
+
1604
+ .. code-block:: python
1605
+
1606
+ (u for user in users if user.is_active)
1607
+ (u for user in users if test_none(user.email))
1608
+
1609
+ .. versionadded:: 2.7
1610
+ """
1611
+ return select_or_reject(context, value, args, kwargs, lambda x: x, True)
1612
+
1613
+
1614
+ @async_variant(sync_do_selectattr) # type: ignore
1615
+ async def do_selectattr(
1616
+ context: "Context",
1617
+ value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]",
1618
+ *args: t.Any,
1619
+ **kwargs: t.Any,
1620
+ ) -> "t.AsyncIterator[V]":
1621
+ return async_select_or_reject(context, value, args, kwargs, lambda x: x, True)
1622
+
1623
+
1624
+ @pass_context
1625
+ def sync_do_rejectattr(
1626
+ context: "Context", value: "t.Iterable[V]", *args: t.Any, **kwargs: t.Any
1627
+ ) -> "t.Iterator[V]":
1628
+ """Filters a sequence of objects by applying a test to the specified
1629
+ attribute of each object, and rejecting the objects with the test
1630
+ succeeding.
1631
+
1632
+ If no test is specified, the attribute's value will be evaluated as
1633
+ a boolean.
1634
+
1635
+ .. sourcecode:: jinja
1636
+
1637
+ {{ users|rejectattr("is_active") }}
1638
+ {{ users|rejectattr("email", "none") }}
1639
+
1640
+ Similar to a generator comprehension such as:
1641
+
1642
+ .. code-block:: python
1643
+
1644
+ (u for user in users if not user.is_active)
1645
+ (u for user in users if not test_none(user.email))
1646
+
1647
+ .. versionadded:: 2.7
1648
+ """
1649
+ return select_or_reject(context, value, args, kwargs, lambda x: not x, True)
1650
+
1651
+
1652
+ @async_variant(sync_do_rejectattr) # type: ignore
1653
+ async def do_rejectattr(
1654
+ context: "Context",
1655
+ value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]",
1656
+ *args: t.Any,
1657
+ **kwargs: t.Any,
1658
+ ) -> "t.AsyncIterator[V]":
1659
+ return async_select_or_reject(context, value, args, kwargs, lambda x: not x, True)
1660
+
1661
+
1662
+ @pass_eval_context
1663
+ def do_tojson(
1664
+ eval_ctx: "EvalContext", value: t.Any, indent: t.Optional[int] = None
1665
+ ) -> Markup:
1666
+ """Serialize an object to a string of JSON, and mark it safe to
1667
+ render in HTML. This filter is only for use in HTML documents.
1668
+
1669
+ The returned string is safe to render in HTML documents and
1670
+ ``<script>`` tags. The exception is in HTML attributes that are
1671
+ double quoted; either use single quotes or the ``|forceescape``
1672
+ filter.
1673
+
1674
+ :param value: The object to serialize to JSON.
1675
+ :param indent: The ``indent`` parameter passed to ``dumps``, for
1676
+ pretty-printing the value.
1677
+
1678
+ .. versionadded:: 2.9
1679
+ """
1680
+ policies = eval_ctx.environment.policies
1681
+ dumps = policies["json.dumps_function"]
1682
+ kwargs = policies["json.dumps_kwargs"]
1683
+
1684
+ if indent is not None:
1685
+ kwargs = kwargs.copy()
1686
+ kwargs["indent"] = indent
1687
+
1688
+ return htmlsafe_json_dumps(value, dumps=dumps, **kwargs)
1689
+
1690
+
1691
+ def prepare_map(
1692
+ context: "Context", args: t.Tuple, kwargs: t.Dict[str, t.Any]
1693
+ ) -> t.Callable[[t.Any], t.Any]:
1694
+ if not args and "attribute" in kwargs:
1695
+ attribute = kwargs.pop("attribute")
1696
+ default = kwargs.pop("default", None)
1697
+
1698
+ if kwargs:
1699
+ raise FilterArgumentError(
1700
+ f"Unexpected keyword argument {next(iter(kwargs))!r}"
1701
+ )
1702
+
1703
+ func = make_attrgetter(context.environment, attribute, default=default)
1704
+ else:
1705
+ try:
1706
+ name = args[0]
1707
+ args = args[1:]
1708
+ except LookupError:
1709
+ raise FilterArgumentError("map requires a filter argument") from None
1710
+
1711
+ def func(item: t.Any) -> t.Any:
1712
+ return context.environment.call_filter(
1713
+ name, item, args, kwargs, context=context
1714
+ )
1715
+
1716
+ return func
1717
+
1718
+
1719
+ def prepare_select_or_reject(
1720
+ context: "Context",
1721
+ args: t.Tuple,
1722
+ kwargs: t.Dict[str, t.Any],
1723
+ modfunc: t.Callable[[t.Any], t.Any],
1724
+ lookup_attr: bool,
1725
+ ) -> t.Callable[[t.Any], t.Any]:
1726
+ if lookup_attr:
1727
+ try:
1728
+ attr = args[0]
1729
+ except LookupError:
1730
+ raise FilterArgumentError("Missing parameter for attribute name") from None
1731
+
1732
+ transfunc = make_attrgetter(context.environment, attr)
1733
+ off = 1
1734
+ else:
1735
+ off = 0
1736
+
1737
+ def transfunc(x: V) -> V:
1738
+ return x
1739
+
1740
+ try:
1741
+ name = args[off]
1742
+ args = args[1 + off :]
1743
+
1744
+ def func(item: t.Any) -> t.Any:
1745
+ return context.environment.call_test(name, item, args, kwargs)
1746
+
1747
+ except LookupError:
1748
+ func = bool # type: ignore
1749
+
1750
+ return lambda item: modfunc(func(transfunc(item)))
1751
+
1752
+
1753
+ def select_or_reject(
1754
+ context: "Context",
1755
+ value: "t.Iterable[V]",
1756
+ args: t.Tuple,
1757
+ kwargs: t.Dict[str, t.Any],
1758
+ modfunc: t.Callable[[t.Any], t.Any],
1759
+ lookup_attr: bool,
1760
+ ) -> "t.Iterator[V]":
1761
+ if value:
1762
+ func = prepare_select_or_reject(context, args, kwargs, modfunc, lookup_attr)
1763
+
1764
+ for item in value:
1765
+ if func(item):
1766
+ yield item
1767
+
1768
+
1769
+ async def async_select_or_reject(
1770
+ context: "Context",
1771
+ value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]",
1772
+ args: t.Tuple,
1773
+ kwargs: t.Dict[str, t.Any],
1774
+ modfunc: t.Callable[[t.Any], t.Any],
1775
+ lookup_attr: bool,
1776
+ ) -> "t.AsyncIterator[V]":
1777
+ if value:
1778
+ func = prepare_select_or_reject(context, args, kwargs, modfunc, lookup_attr)
1779
+
1780
+ async for item in auto_aiter(value):
1781
+ if func(item):
1782
+ yield item
1783
+
1784
+
1785
+ FILTERS = {
1786
+ "abs": abs,
1787
+ "attr": do_attr,
1788
+ "batch": do_batch,
1789
+ "capitalize": do_capitalize,
1790
+ "center": do_center,
1791
+ "count": len,
1792
+ "d": do_default,
1793
+ "default": do_default,
1794
+ "dictsort": do_dictsort,
1795
+ "e": escape,
1796
+ "escape": escape,
1797
+ "filesizeformat": do_filesizeformat,
1798
+ "first": do_first,
1799
+ "float": do_float,
1800
+ "forceescape": do_forceescape,
1801
+ "format": do_format,
1802
+ "groupby": do_groupby,
1803
+ "indent": do_indent,
1804
+ "int": do_int,
1805
+ "join": do_join,
1806
+ "last": do_last,
1807
+ "length": len,
1808
+ "list": do_list,
1809
+ "lower": do_lower,
1810
+ "items": do_items,
1811
+ "map": do_map,
1812
+ "min": do_min,
1813
+ "max": do_max,
1814
+ "pprint": do_pprint,
1815
+ "random": do_random,
1816
+ "reject": do_reject,
1817
+ "rejectattr": do_rejectattr,
1818
+ "replace": do_replace,
1819
+ "reverse": do_reverse,
1820
+ "round": do_round,
1821
+ "safe": do_mark_safe,
1822
+ "select": do_select,
1823
+ "selectattr": do_selectattr,
1824
+ "slice": do_slice,
1825
+ "sort": do_sort,
1826
+ "string": soft_str,
1827
+ "striptags": do_striptags,
1828
+ "sum": do_sum,
1829
+ "title": do_title,
1830
+ "trim": do_trim,
1831
+ "truncate": do_truncate,
1832
+ "unique": do_unique,
1833
+ "upper": do_upper,
1834
+ "urlencode": do_urlencode,
1835
+ "urlize": do_urlize,
1836
+ "wordcount": do_wordcount,
1837
+ "wordwrap": do_wordwrap,
1838
+ "xmlattr": do_xmlattr,
1839
+ "tojson": do_tojson,
1840
+ }
lib/python3.11/site-packages/jinja2/idtracking.py ADDED
@@ -0,0 +1,318 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import typing as t
2
+
3
+ from . import nodes
4
+ from .visitor import NodeVisitor
5
+
6
+ VAR_LOAD_PARAMETER = "param"
7
+ VAR_LOAD_RESOLVE = "resolve"
8
+ VAR_LOAD_ALIAS = "alias"
9
+ VAR_LOAD_UNDEFINED = "undefined"
10
+
11
+
12
+ def find_symbols(
13
+ nodes: t.Iterable[nodes.Node], parent_symbols: t.Optional["Symbols"] = None
14
+ ) -> "Symbols":
15
+ sym = Symbols(parent=parent_symbols)
16
+ visitor = FrameSymbolVisitor(sym)
17
+ for node in nodes:
18
+ visitor.visit(node)
19
+ return sym
20
+
21
+
22
+ def symbols_for_node(
23
+ node: nodes.Node, parent_symbols: t.Optional["Symbols"] = None
24
+ ) -> "Symbols":
25
+ sym = Symbols(parent=parent_symbols)
26
+ sym.analyze_node(node)
27
+ return sym
28
+
29
+
30
+ class Symbols:
31
+ def __init__(
32
+ self, parent: t.Optional["Symbols"] = None, level: t.Optional[int] = None
33
+ ) -> None:
34
+ if level is None:
35
+ if parent is None:
36
+ level = 0
37
+ else:
38
+ level = parent.level + 1
39
+
40
+ self.level: int = level
41
+ self.parent = parent
42
+ self.refs: t.Dict[str, str] = {}
43
+ self.loads: t.Dict[str, t.Any] = {}
44
+ self.stores: t.Set[str] = set()
45
+
46
+ def analyze_node(self, node: nodes.Node, **kwargs: t.Any) -> None:
47
+ visitor = RootVisitor(self)
48
+ visitor.visit(node, **kwargs)
49
+
50
+ def _define_ref(
51
+ self, name: str, load: t.Optional[t.Tuple[str, t.Optional[str]]] = None
52
+ ) -> str:
53
+ ident = f"l_{self.level}_{name}"
54
+ self.refs[name] = ident
55
+ if load is not None:
56
+ self.loads[ident] = load
57
+ return ident
58
+
59
+ def find_load(self, target: str) -> t.Optional[t.Any]:
60
+ if target in self.loads:
61
+ return self.loads[target]
62
+
63
+ if self.parent is not None:
64
+ return self.parent.find_load(target)
65
+
66
+ return None
67
+
68
+ def find_ref(self, name: str) -> t.Optional[str]:
69
+ if name in self.refs:
70
+ return self.refs[name]
71
+
72
+ if self.parent is not None:
73
+ return self.parent.find_ref(name)
74
+
75
+ return None
76
+
77
+ def ref(self, name: str) -> str:
78
+ rv = self.find_ref(name)
79
+ if rv is None:
80
+ raise AssertionError(
81
+ "Tried to resolve a name to a reference that was"
82
+ f" unknown to the frame ({name!r})"
83
+ )
84
+ return rv
85
+
86
+ def copy(self) -> "Symbols":
87
+ rv = object.__new__(self.__class__)
88
+ rv.__dict__.update(self.__dict__)
89
+ rv.refs = self.refs.copy()
90
+ rv.loads = self.loads.copy()
91
+ rv.stores = self.stores.copy()
92
+ return rv
93
+
94
+ def store(self, name: str) -> None:
95
+ self.stores.add(name)
96
+
97
+ # If we have not see the name referenced yet, we need to figure
98
+ # out what to set it to.
99
+ if name not in self.refs:
100
+ # If there is a parent scope we check if the name has a
101
+ # reference there. If it does it means we might have to alias
102
+ # to a variable there.
103
+ if self.parent is not None:
104
+ outer_ref = self.parent.find_ref(name)
105
+ if outer_ref is not None:
106
+ self._define_ref(name, load=(VAR_LOAD_ALIAS, outer_ref))
107
+ return
108
+
109
+ # Otherwise we can just set it to undefined.
110
+ self._define_ref(name, load=(VAR_LOAD_UNDEFINED, None))
111
+
112
+ def declare_parameter(self, name: str) -> str:
113
+ self.stores.add(name)
114
+ return self._define_ref(name, load=(VAR_LOAD_PARAMETER, None))
115
+
116
+ def load(self, name: str) -> None:
117
+ if self.find_ref(name) is None:
118
+ self._define_ref(name, load=(VAR_LOAD_RESOLVE, name))
119
+
120
+ def branch_update(self, branch_symbols: t.Sequence["Symbols"]) -> None:
121
+ stores: t.Dict[str, int] = {}
122
+ for branch in branch_symbols:
123
+ for target in branch.stores:
124
+ if target in self.stores:
125
+ continue
126
+ stores[target] = stores.get(target, 0) + 1
127
+
128
+ for sym in branch_symbols:
129
+ self.refs.update(sym.refs)
130
+ self.loads.update(sym.loads)
131
+ self.stores.update(sym.stores)
132
+
133
+ for name, branch_count in stores.items():
134
+ if branch_count == len(branch_symbols):
135
+ continue
136
+
137
+ target = self.find_ref(name) # type: ignore
138
+ assert target is not None, "should not happen"
139
+
140
+ if self.parent is not None:
141
+ outer_target = self.parent.find_ref(name)
142
+ if outer_target is not None:
143
+ self.loads[target] = (VAR_LOAD_ALIAS, outer_target)
144
+ continue
145
+ self.loads[target] = (VAR_LOAD_RESOLVE, name)
146
+
147
+ def dump_stores(self) -> t.Dict[str, str]:
148
+ rv: t.Dict[str, str] = {}
149
+ node: t.Optional["Symbols"] = self
150
+
151
+ while node is not None:
152
+ for name in sorted(node.stores):
153
+ if name not in rv:
154
+ rv[name] = self.find_ref(name) # type: ignore
155
+
156
+ node = node.parent
157
+
158
+ return rv
159
+
160
+ def dump_param_targets(self) -> t.Set[str]:
161
+ rv = set()
162
+ node: t.Optional["Symbols"] = self
163
+
164
+ while node is not None:
165
+ for target, (instr, _) in self.loads.items():
166
+ if instr == VAR_LOAD_PARAMETER:
167
+ rv.add(target)
168
+
169
+ node = node.parent
170
+
171
+ return rv
172
+
173
+
174
+ class RootVisitor(NodeVisitor):
175
+ def __init__(self, symbols: "Symbols") -> None:
176
+ self.sym_visitor = FrameSymbolVisitor(symbols)
177
+
178
+ def _simple_visit(self, node: nodes.Node, **kwargs: t.Any) -> None:
179
+ for child in node.iter_child_nodes():
180
+ self.sym_visitor.visit(child)
181
+
182
+ visit_Template = _simple_visit
183
+ visit_Block = _simple_visit
184
+ visit_Macro = _simple_visit
185
+ visit_FilterBlock = _simple_visit
186
+ visit_Scope = _simple_visit
187
+ visit_If = _simple_visit
188
+ visit_ScopedEvalContextModifier = _simple_visit
189
+
190
+ def visit_AssignBlock(self, node: nodes.AssignBlock, **kwargs: t.Any) -> None:
191
+ for child in node.body:
192
+ self.sym_visitor.visit(child)
193
+
194
+ def visit_CallBlock(self, node: nodes.CallBlock, **kwargs: t.Any) -> None:
195
+ for child in node.iter_child_nodes(exclude=("call",)):
196
+ self.sym_visitor.visit(child)
197
+
198
+ def visit_OverlayScope(self, node: nodes.OverlayScope, **kwargs: t.Any) -> None:
199
+ for child in node.body:
200
+ self.sym_visitor.visit(child)
201
+
202
+ def visit_For(
203
+ self, node: nodes.For, for_branch: str = "body", **kwargs: t.Any
204
+ ) -> None:
205
+ if for_branch == "body":
206
+ self.sym_visitor.visit(node.target, store_as_param=True)
207
+ branch = node.body
208
+ elif for_branch == "else":
209
+ branch = node.else_
210
+ elif for_branch == "test":
211
+ self.sym_visitor.visit(node.target, store_as_param=True)
212
+ if node.test is not None:
213
+ self.sym_visitor.visit(node.test)
214
+ return
215
+ else:
216
+ raise RuntimeError("Unknown for branch")
217
+
218
+ if branch:
219
+ for item in branch:
220
+ self.sym_visitor.visit(item)
221
+
222
+ def visit_With(self, node: nodes.With, **kwargs: t.Any) -> None:
223
+ for target in node.targets:
224
+ self.sym_visitor.visit(target)
225
+ for child in node.body:
226
+ self.sym_visitor.visit(child)
227
+
228
+ def generic_visit(self, node: nodes.Node, *args: t.Any, **kwargs: t.Any) -> None:
229
+ raise NotImplementedError(f"Cannot find symbols for {type(node).__name__!r}")
230
+
231
+
232
+ class FrameSymbolVisitor(NodeVisitor):
233
+ """A visitor for `Frame.inspect`."""
234
+
235
+ def __init__(self, symbols: "Symbols") -> None:
236
+ self.symbols = symbols
237
+
238
+ def visit_Name(
239
+ self, node: nodes.Name, store_as_param: bool = False, **kwargs: t.Any
240
+ ) -> None:
241
+ """All assignments to names go through this function."""
242
+ if store_as_param or node.ctx == "param":
243
+ self.symbols.declare_parameter(node.name)
244
+ elif node.ctx == "store":
245
+ self.symbols.store(node.name)
246
+ elif node.ctx == "load":
247
+ self.symbols.load(node.name)
248
+
249
+ def visit_NSRef(self, node: nodes.NSRef, **kwargs: t.Any) -> None:
250
+ self.symbols.load(node.name)
251
+
252
+ def visit_If(self, node: nodes.If, **kwargs: t.Any) -> None:
253
+ self.visit(node.test, **kwargs)
254
+ original_symbols = self.symbols
255
+
256
+ def inner_visit(nodes: t.Iterable[nodes.Node]) -> "Symbols":
257
+ self.symbols = rv = original_symbols.copy()
258
+
259
+ for subnode in nodes:
260
+ self.visit(subnode, **kwargs)
261
+
262
+ self.symbols = original_symbols
263
+ return rv
264
+
265
+ body_symbols = inner_visit(node.body)
266
+ elif_symbols = inner_visit(node.elif_)
267
+ else_symbols = inner_visit(node.else_ or ())
268
+ self.symbols.branch_update([body_symbols, elif_symbols, else_symbols])
269
+
270
+ def visit_Macro(self, node: nodes.Macro, **kwargs: t.Any) -> None:
271
+ self.symbols.store(node.name)
272
+
273
+ def visit_Import(self, node: nodes.Import, **kwargs: t.Any) -> None:
274
+ self.generic_visit(node, **kwargs)
275
+ self.symbols.store(node.target)
276
+
277
+ def visit_FromImport(self, node: nodes.FromImport, **kwargs: t.Any) -> None:
278
+ self.generic_visit(node, **kwargs)
279
+
280
+ for name in node.names:
281
+ if isinstance(name, tuple):
282
+ self.symbols.store(name[1])
283
+ else:
284
+ self.symbols.store(name)
285
+
286
+ def visit_Assign(self, node: nodes.Assign, **kwargs: t.Any) -> None:
287
+ """Visit assignments in the correct order."""
288
+ self.visit(node.node, **kwargs)
289
+ self.visit(node.target, **kwargs)
290
+
291
+ def visit_For(self, node: nodes.For, **kwargs: t.Any) -> None:
292
+ """Visiting stops at for blocks. However the block sequence
293
+ is visited as part of the outer scope.
294
+ """
295
+ self.visit(node.iter, **kwargs)
296
+
297
+ def visit_CallBlock(self, node: nodes.CallBlock, **kwargs: t.Any) -> None:
298
+ self.visit(node.call, **kwargs)
299
+
300
+ def visit_FilterBlock(self, node: nodes.FilterBlock, **kwargs: t.Any) -> None:
301
+ self.visit(node.filter, **kwargs)
302
+
303
+ def visit_With(self, node: nodes.With, **kwargs: t.Any) -> None:
304
+ for target in node.values:
305
+ self.visit(target)
306
+
307
+ def visit_AssignBlock(self, node: nodes.AssignBlock, **kwargs: t.Any) -> None:
308
+ """Stop visiting at block assigns."""
309
+ self.visit(node.target, **kwargs)
310
+
311
+ def visit_Scope(self, node: nodes.Scope, **kwargs: t.Any) -> None:
312
+ """Stop visiting at scopes."""
313
+
314
+ def visit_Block(self, node: nodes.Block, **kwargs: t.Any) -> None:
315
+ """Stop visiting at blocks."""
316
+
317
+ def visit_OverlayScope(self, node: nodes.OverlayScope, **kwargs: t.Any) -> None:
318
+ """Do not visit into overlay scopes."""
lib/python3.11/site-packages/jinja2/lexer.py ADDED
@@ -0,0 +1,866 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Implements a Jinja / Python combination lexer. The ``Lexer`` class
2
+ is used to do some preprocessing. It filters out invalid operators like
3
+ the bitshift operators we don't allow in templates. It separates
4
+ template code and python code in expressions.
5
+ """
6
+ import re
7
+ import typing as t
8
+ from ast import literal_eval
9
+ from collections import deque
10
+ from sys import intern
11
+
12
+ from ._identifier import pattern as name_re
13
+ from .exceptions import TemplateSyntaxError
14
+ from .utils import LRUCache
15
+
16
+ if t.TYPE_CHECKING:
17
+ import typing_extensions as te
18
+ from .environment import Environment
19
+
20
+ # cache for the lexers. Exists in order to be able to have multiple
21
+ # environments with the same lexer
22
+ _lexer_cache: t.MutableMapping[t.Tuple, "Lexer"] = LRUCache(50) # type: ignore
23
+
24
+ # static regular expressions
25
+ whitespace_re = re.compile(r"\s+")
26
+ newline_re = re.compile(r"(\r\n|\r|\n)")
27
+ string_re = re.compile(
28
+ r"('([^'\\]*(?:\\.[^'\\]*)*)'" r'|"([^"\\]*(?:\\.[^"\\]*)*)")', re.S
29
+ )
30
+ integer_re = re.compile(
31
+ r"""
32
+ (
33
+ 0b(_?[0-1])+ # binary
34
+ |
35
+ 0o(_?[0-7])+ # octal
36
+ |
37
+ 0x(_?[\da-f])+ # hex
38
+ |
39
+ [1-9](_?\d)* # decimal
40
+ |
41
+ 0(_?0)* # decimal zero
42
+ )
43
+ """,
44
+ re.IGNORECASE | re.VERBOSE,
45
+ )
46
+ float_re = re.compile(
47
+ r"""
48
+ (?<!\.) # doesn't start with a .
49
+ (\d+_)*\d+ # digits, possibly _ separated
50
+ (
51
+ (\.(\d+_)*\d+)? # optional fractional part
52
+ e[+\-]?(\d+_)*\d+ # exponent part
53
+ |
54
+ \.(\d+_)*\d+ # required fractional part
55
+ )
56
+ """,
57
+ re.IGNORECASE | re.VERBOSE,
58
+ )
59
+
60
+ # internal the tokens and keep references to them
61
+ TOKEN_ADD = intern("add")
62
+ TOKEN_ASSIGN = intern("assign")
63
+ TOKEN_COLON = intern("colon")
64
+ TOKEN_COMMA = intern("comma")
65
+ TOKEN_DIV = intern("div")
66
+ TOKEN_DOT = intern("dot")
67
+ TOKEN_EQ = intern("eq")
68
+ TOKEN_FLOORDIV = intern("floordiv")
69
+ TOKEN_GT = intern("gt")
70
+ TOKEN_GTEQ = intern("gteq")
71
+ TOKEN_LBRACE = intern("lbrace")
72
+ TOKEN_LBRACKET = intern("lbracket")
73
+ TOKEN_LPAREN = intern("lparen")
74
+ TOKEN_LT = intern("lt")
75
+ TOKEN_LTEQ = intern("lteq")
76
+ TOKEN_MOD = intern("mod")
77
+ TOKEN_MUL = intern("mul")
78
+ TOKEN_NE = intern("ne")
79
+ TOKEN_PIPE = intern("pipe")
80
+ TOKEN_POW = intern("pow")
81
+ TOKEN_RBRACE = intern("rbrace")
82
+ TOKEN_RBRACKET = intern("rbracket")
83
+ TOKEN_RPAREN = intern("rparen")
84
+ TOKEN_SEMICOLON = intern("semicolon")
85
+ TOKEN_SUB = intern("sub")
86
+ TOKEN_TILDE = intern("tilde")
87
+ TOKEN_WHITESPACE = intern("whitespace")
88
+ TOKEN_FLOAT = intern("float")
89
+ TOKEN_INTEGER = intern("integer")
90
+ TOKEN_NAME = intern("name")
91
+ TOKEN_STRING = intern("string")
92
+ TOKEN_OPERATOR = intern("operator")
93
+ TOKEN_BLOCK_BEGIN = intern("block_begin")
94
+ TOKEN_BLOCK_END = intern("block_end")
95
+ TOKEN_VARIABLE_BEGIN = intern("variable_begin")
96
+ TOKEN_VARIABLE_END = intern("variable_end")
97
+ TOKEN_RAW_BEGIN = intern("raw_begin")
98
+ TOKEN_RAW_END = intern("raw_end")
99
+ TOKEN_COMMENT_BEGIN = intern("comment_begin")
100
+ TOKEN_COMMENT_END = intern("comment_end")
101
+ TOKEN_COMMENT = intern("comment")
102
+ TOKEN_LINESTATEMENT_BEGIN = intern("linestatement_begin")
103
+ TOKEN_LINESTATEMENT_END = intern("linestatement_end")
104
+ TOKEN_LINECOMMENT_BEGIN = intern("linecomment_begin")
105
+ TOKEN_LINECOMMENT_END = intern("linecomment_end")
106
+ TOKEN_LINECOMMENT = intern("linecomment")
107
+ TOKEN_DATA = intern("data")
108
+ TOKEN_INITIAL = intern("initial")
109
+ TOKEN_EOF = intern("eof")
110
+
111
+ # bind operators to token types
112
+ operators = {
113
+ "+": TOKEN_ADD,
114
+ "-": TOKEN_SUB,
115
+ "/": TOKEN_DIV,
116
+ "//": TOKEN_FLOORDIV,
117
+ "*": TOKEN_MUL,
118
+ "%": TOKEN_MOD,
119
+ "**": TOKEN_POW,
120
+ "~": TOKEN_TILDE,
121
+ "[": TOKEN_LBRACKET,
122
+ "]": TOKEN_RBRACKET,
123
+ "(": TOKEN_LPAREN,
124
+ ")": TOKEN_RPAREN,
125
+ "{": TOKEN_LBRACE,
126
+ "}": TOKEN_RBRACE,
127
+ "==": TOKEN_EQ,
128
+ "!=": TOKEN_NE,
129
+ ">": TOKEN_GT,
130
+ ">=": TOKEN_GTEQ,
131
+ "<": TOKEN_LT,
132
+ "<=": TOKEN_LTEQ,
133
+ "=": TOKEN_ASSIGN,
134
+ ".": TOKEN_DOT,
135
+ ":": TOKEN_COLON,
136
+ "|": TOKEN_PIPE,
137
+ ",": TOKEN_COMMA,
138
+ ";": TOKEN_SEMICOLON,
139
+ }
140
+
141
+ reverse_operators = {v: k for k, v in operators.items()}
142
+ assert len(operators) == len(reverse_operators), "operators dropped"
143
+ operator_re = re.compile(
144
+ f"({'|'.join(re.escape(x) for x in sorted(operators, key=lambda x: -len(x)))})"
145
+ )
146
+
147
+ ignored_tokens = frozenset(
148
+ [
149
+ TOKEN_COMMENT_BEGIN,
150
+ TOKEN_COMMENT,
151
+ TOKEN_COMMENT_END,
152
+ TOKEN_WHITESPACE,
153
+ TOKEN_LINECOMMENT_BEGIN,
154
+ TOKEN_LINECOMMENT_END,
155
+ TOKEN_LINECOMMENT,
156
+ ]
157
+ )
158
+ ignore_if_empty = frozenset(
159
+ [TOKEN_WHITESPACE, TOKEN_DATA, TOKEN_COMMENT, TOKEN_LINECOMMENT]
160
+ )
161
+
162
+
163
+ def _describe_token_type(token_type: str) -> str:
164
+ if token_type in reverse_operators:
165
+ return reverse_operators[token_type]
166
+
167
+ return {
168
+ TOKEN_COMMENT_BEGIN: "begin of comment",
169
+ TOKEN_COMMENT_END: "end of comment",
170
+ TOKEN_COMMENT: "comment",
171
+ TOKEN_LINECOMMENT: "comment",
172
+ TOKEN_BLOCK_BEGIN: "begin of statement block",
173
+ TOKEN_BLOCK_END: "end of statement block",
174
+ TOKEN_VARIABLE_BEGIN: "begin of print statement",
175
+ TOKEN_VARIABLE_END: "end of print statement",
176
+ TOKEN_LINESTATEMENT_BEGIN: "begin of line statement",
177
+ TOKEN_LINESTATEMENT_END: "end of line statement",
178
+ TOKEN_DATA: "template data / text",
179
+ TOKEN_EOF: "end of template",
180
+ }.get(token_type, token_type)
181
+
182
+
183
+ def describe_token(token: "Token") -> str:
184
+ """Returns a description of the token."""
185
+ if token.type == TOKEN_NAME:
186
+ return token.value
187
+
188
+ return _describe_token_type(token.type)
189
+
190
+
191
+ def describe_token_expr(expr: str) -> str:
192
+ """Like `describe_token` but for token expressions."""
193
+ if ":" in expr:
194
+ type, value = expr.split(":", 1)
195
+
196
+ if type == TOKEN_NAME:
197
+ return value
198
+ else:
199
+ type = expr
200
+
201
+ return _describe_token_type(type)
202
+
203
+
204
+ def count_newlines(value: str) -> int:
205
+ """Count the number of newline characters in the string. This is
206
+ useful for extensions that filter a stream.
207
+ """
208
+ return len(newline_re.findall(value))
209
+
210
+
211
+ def compile_rules(environment: "Environment") -> t.List[t.Tuple[str, str]]:
212
+ """Compiles all the rules from the environment into a list of rules."""
213
+ e = re.escape
214
+ rules = [
215
+ (
216
+ len(environment.comment_start_string),
217
+ TOKEN_COMMENT_BEGIN,
218
+ e(environment.comment_start_string),
219
+ ),
220
+ (
221
+ len(environment.block_start_string),
222
+ TOKEN_BLOCK_BEGIN,
223
+ e(environment.block_start_string),
224
+ ),
225
+ (
226
+ len(environment.variable_start_string),
227
+ TOKEN_VARIABLE_BEGIN,
228
+ e(environment.variable_start_string),
229
+ ),
230
+ ]
231
+
232
+ if environment.line_statement_prefix is not None:
233
+ rules.append(
234
+ (
235
+ len(environment.line_statement_prefix),
236
+ TOKEN_LINESTATEMENT_BEGIN,
237
+ r"^[ \t\v]*" + e(environment.line_statement_prefix),
238
+ )
239
+ )
240
+ if environment.line_comment_prefix is not None:
241
+ rules.append(
242
+ (
243
+ len(environment.line_comment_prefix),
244
+ TOKEN_LINECOMMENT_BEGIN,
245
+ r"(?:^|(?<=\S))[^\S\r\n]*" + e(environment.line_comment_prefix),
246
+ )
247
+ )
248
+
249
+ return [x[1:] for x in sorted(rules, reverse=True)]
250
+
251
+
252
+ class Failure:
253
+ """Class that raises a `TemplateSyntaxError` if called.
254
+ Used by the `Lexer` to specify known errors.
255
+ """
256
+
257
+ def __init__(
258
+ self, message: str, cls: t.Type[TemplateSyntaxError] = TemplateSyntaxError
259
+ ) -> None:
260
+ self.message = message
261
+ self.error_class = cls
262
+
263
+ def __call__(self, lineno: int, filename: str) -> "te.NoReturn":
264
+ raise self.error_class(self.message, lineno, filename)
265
+
266
+
267
+ class Token(t.NamedTuple):
268
+ lineno: int
269
+ type: str
270
+ value: str
271
+
272
+ def __str__(self) -> str:
273
+ return describe_token(self)
274
+
275
+ def test(self, expr: str) -> bool:
276
+ """Test a token against a token expression. This can either be a
277
+ token type or ``'token_type:token_value'``. This can only test
278
+ against string values and types.
279
+ """
280
+ # here we do a regular string equality check as test_any is usually
281
+ # passed an iterable of not interned strings.
282
+ if self.type == expr:
283
+ return True
284
+
285
+ if ":" in expr:
286
+ return expr.split(":", 1) == [self.type, self.value]
287
+
288
+ return False
289
+
290
+ def test_any(self, *iterable: str) -> bool:
291
+ """Test against multiple token expressions."""
292
+ return any(self.test(expr) for expr in iterable)
293
+
294
+
295
+ class TokenStreamIterator:
296
+ """The iterator for tokenstreams. Iterate over the stream
297
+ until the eof token is reached.
298
+ """
299
+
300
+ def __init__(self, stream: "TokenStream") -> None:
301
+ self.stream = stream
302
+
303
+ def __iter__(self) -> "TokenStreamIterator":
304
+ return self
305
+
306
+ def __next__(self) -> Token:
307
+ token = self.stream.current
308
+
309
+ if token.type is TOKEN_EOF:
310
+ self.stream.close()
311
+ raise StopIteration
312
+
313
+ next(self.stream)
314
+ return token
315
+
316
+
317
+ class TokenStream:
318
+ """A token stream is an iterable that yields :class:`Token`\\s. The
319
+ parser however does not iterate over it but calls :meth:`next` to go
320
+ one token ahead. The current active token is stored as :attr:`current`.
321
+ """
322
+
323
+ def __init__(
324
+ self,
325
+ generator: t.Iterable[Token],
326
+ name: t.Optional[str],
327
+ filename: t.Optional[str],
328
+ ):
329
+ self._iter = iter(generator)
330
+ self._pushed: "te.Deque[Token]" = deque()
331
+ self.name = name
332
+ self.filename = filename
333
+ self.closed = False
334
+ self.current = Token(1, TOKEN_INITIAL, "")
335
+ next(self)
336
+
337
+ def __iter__(self) -> TokenStreamIterator:
338
+ return TokenStreamIterator(self)
339
+
340
+ def __bool__(self) -> bool:
341
+ return bool(self._pushed) or self.current.type is not TOKEN_EOF
342
+
343
+ @property
344
+ def eos(self) -> bool:
345
+ """Are we at the end of the stream?"""
346
+ return not self
347
+
348
+ def push(self, token: Token) -> None:
349
+ """Push a token back to the stream."""
350
+ self._pushed.append(token)
351
+
352
+ def look(self) -> Token:
353
+ """Look at the next token."""
354
+ old_token = next(self)
355
+ result = self.current
356
+ self.push(result)
357
+ self.current = old_token
358
+ return result
359
+
360
+ def skip(self, n: int = 1) -> None:
361
+ """Got n tokens ahead."""
362
+ for _ in range(n):
363
+ next(self)
364
+
365
+ def next_if(self, expr: str) -> t.Optional[Token]:
366
+ """Perform the token test and return the token if it matched.
367
+ Otherwise the return value is `None`.
368
+ """
369
+ if self.current.test(expr):
370
+ return next(self)
371
+
372
+ return None
373
+
374
+ def skip_if(self, expr: str) -> bool:
375
+ """Like :meth:`next_if` but only returns `True` or `False`."""
376
+ return self.next_if(expr) is not None
377
+
378
+ def __next__(self) -> Token:
379
+ """Go one token ahead and return the old one.
380
+
381
+ Use the built-in :func:`next` instead of calling this directly.
382
+ """
383
+ rv = self.current
384
+
385
+ if self._pushed:
386
+ self.current = self._pushed.popleft()
387
+ elif self.current.type is not TOKEN_EOF:
388
+ try:
389
+ self.current = next(self._iter)
390
+ except StopIteration:
391
+ self.close()
392
+
393
+ return rv
394
+
395
+ def close(self) -> None:
396
+ """Close the stream."""
397
+ self.current = Token(self.current.lineno, TOKEN_EOF, "")
398
+ self._iter = iter(())
399
+ self.closed = True
400
+
401
+ def expect(self, expr: str) -> Token:
402
+ """Expect a given token type and return it. This accepts the same
403
+ argument as :meth:`jinja2.lexer.Token.test`.
404
+ """
405
+ if not self.current.test(expr):
406
+ expr = describe_token_expr(expr)
407
+
408
+ if self.current.type is TOKEN_EOF:
409
+ raise TemplateSyntaxError(
410
+ f"unexpected end of template, expected {expr!r}.",
411
+ self.current.lineno,
412
+ self.name,
413
+ self.filename,
414
+ )
415
+
416
+ raise TemplateSyntaxError(
417
+ f"expected token {expr!r}, got {describe_token(self.current)!r}",
418
+ self.current.lineno,
419
+ self.name,
420
+ self.filename,
421
+ )
422
+
423
+ return next(self)
424
+
425
+
426
+ def get_lexer(environment: "Environment") -> "Lexer":
427
+ """Return a lexer which is probably cached."""
428
+ key = (
429
+ environment.block_start_string,
430
+ environment.block_end_string,
431
+ environment.variable_start_string,
432
+ environment.variable_end_string,
433
+ environment.comment_start_string,
434
+ environment.comment_end_string,
435
+ environment.line_statement_prefix,
436
+ environment.line_comment_prefix,
437
+ environment.trim_blocks,
438
+ environment.lstrip_blocks,
439
+ environment.newline_sequence,
440
+ environment.keep_trailing_newline,
441
+ )
442
+ lexer = _lexer_cache.get(key)
443
+
444
+ if lexer is None:
445
+ _lexer_cache[key] = lexer = Lexer(environment)
446
+
447
+ return lexer
448
+
449
+
450
+ class OptionalLStrip(tuple):
451
+ """A special tuple for marking a point in the state that can have
452
+ lstrip applied.
453
+ """
454
+
455
+ __slots__ = ()
456
+
457
+ # Even though it looks like a no-op, creating instances fails
458
+ # without this.
459
+ def __new__(cls, *members, **kwargs): # type: ignore
460
+ return super().__new__(cls, members)
461
+
462
+
463
+ class _Rule(t.NamedTuple):
464
+ pattern: t.Pattern[str]
465
+ tokens: t.Union[str, t.Tuple[str, ...], t.Tuple[Failure]]
466
+ command: t.Optional[str]
467
+
468
+
469
+ class Lexer:
470
+ """Class that implements a lexer for a given environment. Automatically
471
+ created by the environment class, usually you don't have to do that.
472
+
473
+ Note that the lexer is not automatically bound to an environment.
474
+ Multiple environments can share the same lexer.
475
+ """
476
+
477
+ def __init__(self, environment: "Environment") -> None:
478
+ # shortcuts
479
+ e = re.escape
480
+
481
+ def c(x: str) -> t.Pattern[str]:
482
+ return re.compile(x, re.M | re.S)
483
+
484
+ # lexing rules for tags
485
+ tag_rules: t.List[_Rule] = [
486
+ _Rule(whitespace_re, TOKEN_WHITESPACE, None),
487
+ _Rule(float_re, TOKEN_FLOAT, None),
488
+ _Rule(integer_re, TOKEN_INTEGER, None),
489
+ _Rule(name_re, TOKEN_NAME, None),
490
+ _Rule(string_re, TOKEN_STRING, None),
491
+ _Rule(operator_re, TOKEN_OPERATOR, None),
492
+ ]
493
+
494
+ # assemble the root lexing rule. because "|" is ungreedy
495
+ # we have to sort by length so that the lexer continues working
496
+ # as expected when we have parsing rules like <% for block and
497
+ # <%= for variables. (if someone wants asp like syntax)
498
+ # variables are just part of the rules if variable processing
499
+ # is required.
500
+ root_tag_rules = compile_rules(environment)
501
+
502
+ block_start_re = e(environment.block_start_string)
503
+ block_end_re = e(environment.block_end_string)
504
+ comment_end_re = e(environment.comment_end_string)
505
+ variable_end_re = e(environment.variable_end_string)
506
+
507
+ # block suffix if trimming is enabled
508
+ block_suffix_re = "\\n?" if environment.trim_blocks else ""
509
+
510
+ self.lstrip_blocks = environment.lstrip_blocks
511
+
512
+ self.newline_sequence = environment.newline_sequence
513
+ self.keep_trailing_newline = environment.keep_trailing_newline
514
+
515
+ root_raw_re = (
516
+ rf"(?P<raw_begin>{block_start_re}(\-|\+|)\s*raw\s*"
517
+ rf"(?:\-{block_end_re}\s*|{block_end_re}))"
518
+ )
519
+ root_parts_re = "|".join(
520
+ [root_raw_re] + [rf"(?P<{n}>{r}(\-|\+|))" for n, r in root_tag_rules]
521
+ )
522
+
523
+ # global lexing rules
524
+ self.rules: t.Dict[str, t.List[_Rule]] = {
525
+ "root": [
526
+ # directives
527
+ _Rule(
528
+ c(rf"(.*?)(?:{root_parts_re})"),
529
+ OptionalLStrip(TOKEN_DATA, "#bygroup"), # type: ignore
530
+ "#bygroup",
531
+ ),
532
+ # data
533
+ _Rule(c(".+"), TOKEN_DATA, None),
534
+ ],
535
+ # comments
536
+ TOKEN_COMMENT_BEGIN: [
537
+ _Rule(
538
+ c(
539
+ rf"(.*?)((?:\+{comment_end_re}|\-{comment_end_re}\s*"
540
+ rf"|{comment_end_re}{block_suffix_re}))"
541
+ ),
542
+ (TOKEN_COMMENT, TOKEN_COMMENT_END),
543
+ "#pop",
544
+ ),
545
+ _Rule(c(r"(.)"), (Failure("Missing end of comment tag"),), None),
546
+ ],
547
+ # blocks
548
+ TOKEN_BLOCK_BEGIN: [
549
+ _Rule(
550
+ c(
551
+ rf"(?:\+{block_end_re}|\-{block_end_re}\s*"
552
+ rf"|{block_end_re}{block_suffix_re})"
553
+ ),
554
+ TOKEN_BLOCK_END,
555
+ "#pop",
556
+ ),
557
+ ]
558
+ + tag_rules,
559
+ # variables
560
+ TOKEN_VARIABLE_BEGIN: [
561
+ _Rule(
562
+ c(rf"\-{variable_end_re}\s*|{variable_end_re}"),
563
+ TOKEN_VARIABLE_END,
564
+ "#pop",
565
+ )
566
+ ]
567
+ + tag_rules,
568
+ # raw block
569
+ TOKEN_RAW_BEGIN: [
570
+ _Rule(
571
+ c(
572
+ rf"(.*?)((?:{block_start_re}(\-|\+|))\s*endraw\s*"
573
+ rf"(?:\+{block_end_re}|\-{block_end_re}\s*"
574
+ rf"|{block_end_re}{block_suffix_re}))"
575
+ ),
576
+ OptionalLStrip(TOKEN_DATA, TOKEN_RAW_END), # type: ignore
577
+ "#pop",
578
+ ),
579
+ _Rule(c(r"(.)"), (Failure("Missing end of raw directive"),), None),
580
+ ],
581
+ # line statements
582
+ TOKEN_LINESTATEMENT_BEGIN: [
583
+ _Rule(c(r"\s*(\n|$)"), TOKEN_LINESTATEMENT_END, "#pop")
584
+ ]
585
+ + tag_rules,
586
+ # line comments
587
+ TOKEN_LINECOMMENT_BEGIN: [
588
+ _Rule(
589
+ c(r"(.*?)()(?=\n|$)"),
590
+ (TOKEN_LINECOMMENT, TOKEN_LINECOMMENT_END),
591
+ "#pop",
592
+ )
593
+ ],
594
+ }
595
+
596
+ def _normalize_newlines(self, value: str) -> str:
597
+ """Replace all newlines with the configured sequence in strings
598
+ and template data.
599
+ """
600
+ return newline_re.sub(self.newline_sequence, value)
601
+
602
+ def tokenize(
603
+ self,
604
+ source: str,
605
+ name: t.Optional[str] = None,
606
+ filename: t.Optional[str] = None,
607
+ state: t.Optional[str] = None,
608
+ ) -> TokenStream:
609
+ """Calls tokeniter + tokenize and wraps it in a token stream."""
610
+ stream = self.tokeniter(source, name, filename, state)
611
+ return TokenStream(self.wrap(stream, name, filename), name, filename)
612
+
613
+ def wrap(
614
+ self,
615
+ stream: t.Iterable[t.Tuple[int, str, str]],
616
+ name: t.Optional[str] = None,
617
+ filename: t.Optional[str] = None,
618
+ ) -> t.Iterator[Token]:
619
+ """This is called with the stream as returned by `tokenize` and wraps
620
+ every token in a :class:`Token` and converts the value.
621
+ """
622
+ for lineno, token, value_str in stream:
623
+ if token in ignored_tokens:
624
+ continue
625
+
626
+ value: t.Any = value_str
627
+
628
+ if token == TOKEN_LINESTATEMENT_BEGIN:
629
+ token = TOKEN_BLOCK_BEGIN
630
+ elif token == TOKEN_LINESTATEMENT_END:
631
+ token = TOKEN_BLOCK_END
632
+ # we are not interested in those tokens in the parser
633
+ elif token in (TOKEN_RAW_BEGIN, TOKEN_RAW_END):
634
+ continue
635
+ elif token == TOKEN_DATA:
636
+ value = self._normalize_newlines(value_str)
637
+ elif token == "keyword":
638
+ token = value_str
639
+ elif token == TOKEN_NAME:
640
+ value = value_str
641
+
642
+ if not value.isidentifier():
643
+ raise TemplateSyntaxError(
644
+ "Invalid character in identifier", lineno, name, filename
645
+ )
646
+ elif token == TOKEN_STRING:
647
+ # try to unescape string
648
+ try:
649
+ value = (
650
+ self._normalize_newlines(value_str[1:-1])
651
+ .encode("ascii", "backslashreplace")
652
+ .decode("unicode-escape")
653
+ )
654
+ except Exception as e:
655
+ msg = str(e).split(":")[-1].strip()
656
+ raise TemplateSyntaxError(msg, lineno, name, filename) from e
657
+ elif token == TOKEN_INTEGER:
658
+ value = int(value_str.replace("_", ""), 0)
659
+ elif token == TOKEN_FLOAT:
660
+ # remove all "_" first to support more Python versions
661
+ value = literal_eval(value_str.replace("_", ""))
662
+ elif token == TOKEN_OPERATOR:
663
+ token = operators[value_str]
664
+
665
+ yield Token(lineno, token, value)
666
+
667
+ def tokeniter(
668
+ self,
669
+ source: str,
670
+ name: t.Optional[str],
671
+ filename: t.Optional[str] = None,
672
+ state: t.Optional[str] = None,
673
+ ) -> t.Iterator[t.Tuple[int, str, str]]:
674
+ """This method tokenizes the text and returns the tokens in a
675
+ generator. Use this method if you just want to tokenize a template.
676
+
677
+ .. versionchanged:: 3.0
678
+ Only ``\\n``, ``\\r\\n`` and ``\\r`` are treated as line
679
+ breaks.
680
+ """
681
+ lines = newline_re.split(source)[::2]
682
+
683
+ if not self.keep_trailing_newline and lines[-1] == "":
684
+ del lines[-1]
685
+
686
+ source = "\n".join(lines)
687
+ pos = 0
688
+ lineno = 1
689
+ stack = ["root"]
690
+
691
+ if state is not None and state != "root":
692
+ assert state in ("variable", "block"), "invalid state"
693
+ stack.append(state + "_begin")
694
+
695
+ statetokens = self.rules[stack[-1]]
696
+ source_length = len(source)
697
+ balancing_stack: t.List[str] = []
698
+ newlines_stripped = 0
699
+ line_starting = True
700
+
701
+ while True:
702
+ # tokenizer loop
703
+ for regex, tokens, new_state in statetokens:
704
+ m = regex.match(source, pos)
705
+
706
+ # if no match we try again with the next rule
707
+ if m is None:
708
+ continue
709
+
710
+ # we only match blocks and variables if braces / parentheses
711
+ # are balanced. continue parsing with the lower rule which
712
+ # is the operator rule. do this only if the end tags look
713
+ # like operators
714
+ if balancing_stack and tokens in (
715
+ TOKEN_VARIABLE_END,
716
+ TOKEN_BLOCK_END,
717
+ TOKEN_LINESTATEMENT_END,
718
+ ):
719
+ continue
720
+
721
+ # tuples support more options
722
+ if isinstance(tokens, tuple):
723
+ groups: t.Sequence[str] = m.groups()
724
+
725
+ if isinstance(tokens, OptionalLStrip):
726
+ # Rule supports lstrip. Match will look like
727
+ # text, block type, whitespace control, type, control, ...
728
+ text = groups[0]
729
+ # Skipping the text and first type, every other group is the
730
+ # whitespace control for each type. One of the groups will be
731
+ # -, +, or empty string instead of None.
732
+ strip_sign = next(g for g in groups[2::2] if g is not None)
733
+
734
+ if strip_sign == "-":
735
+ # Strip all whitespace between the text and the tag.
736
+ stripped = text.rstrip()
737
+ newlines_stripped = text[len(stripped) :].count("\n")
738
+ groups = [stripped, *groups[1:]]
739
+ elif (
740
+ # Not marked for preserving whitespace.
741
+ strip_sign != "+"
742
+ # lstrip is enabled.
743
+ and self.lstrip_blocks
744
+ # Not a variable expression.
745
+ and not m.groupdict().get(TOKEN_VARIABLE_BEGIN)
746
+ ):
747
+ # The start of text between the last newline and the tag.
748
+ l_pos = text.rfind("\n") + 1
749
+
750
+ if l_pos > 0 or line_starting:
751
+ # If there's only whitespace between the newline and the
752
+ # tag, strip it.
753
+ if whitespace_re.fullmatch(text, l_pos):
754
+ groups = [text[:l_pos], *groups[1:]]
755
+
756
+ for idx, token in enumerate(tokens):
757
+ # failure group
758
+ if token.__class__ is Failure:
759
+ raise token(lineno, filename)
760
+ # bygroup is a bit more complex, in that case we
761
+ # yield for the current token the first named
762
+ # group that matched
763
+ elif token == "#bygroup":
764
+ for key, value in m.groupdict().items():
765
+ if value is not None:
766
+ yield lineno, key, value
767
+ lineno += value.count("\n")
768
+ break
769
+ else:
770
+ raise RuntimeError(
771
+ f"{regex!r} wanted to resolve the token dynamically"
772
+ " but no group matched"
773
+ )
774
+ # normal group
775
+ else:
776
+ data = groups[idx]
777
+
778
+ if data or token not in ignore_if_empty:
779
+ yield lineno, token, data
780
+
781
+ lineno += data.count("\n") + newlines_stripped
782
+ newlines_stripped = 0
783
+
784
+ # strings as token just are yielded as it.
785
+ else:
786
+ data = m.group()
787
+
788
+ # update brace/parentheses balance
789
+ if tokens == TOKEN_OPERATOR:
790
+ if data == "{":
791
+ balancing_stack.append("}")
792
+ elif data == "(":
793
+ balancing_stack.append(")")
794
+ elif data == "[":
795
+ balancing_stack.append("]")
796
+ elif data in ("}", ")", "]"):
797
+ if not balancing_stack:
798
+ raise TemplateSyntaxError(
799
+ f"unexpected '{data}'", lineno, name, filename
800
+ )
801
+
802
+ expected_op = balancing_stack.pop()
803
+
804
+ if expected_op != data:
805
+ raise TemplateSyntaxError(
806
+ f"unexpected '{data}', expected '{expected_op}'",
807
+ lineno,
808
+ name,
809
+ filename,
810
+ )
811
+
812
+ # yield items
813
+ if data or tokens not in ignore_if_empty:
814
+ yield lineno, tokens, data
815
+
816
+ lineno += data.count("\n")
817
+
818
+ line_starting = m.group()[-1:] == "\n"
819
+ # fetch new position into new variable so that we can check
820
+ # if there is a internal parsing error which would result
821
+ # in an infinite loop
822
+ pos2 = m.end()
823
+
824
+ # handle state changes
825
+ if new_state is not None:
826
+ # remove the uppermost state
827
+ if new_state == "#pop":
828
+ stack.pop()
829
+ # resolve the new state by group checking
830
+ elif new_state == "#bygroup":
831
+ for key, value in m.groupdict().items():
832
+ if value is not None:
833
+ stack.append(key)
834
+ break
835
+ else:
836
+ raise RuntimeError(
837
+ f"{regex!r} wanted to resolve the new state dynamically"
838
+ f" but no group matched"
839
+ )
840
+ # direct state name given
841
+ else:
842
+ stack.append(new_state)
843
+
844
+ statetokens = self.rules[stack[-1]]
845
+ # we are still at the same position and no stack change.
846
+ # this means a loop without break condition, avoid that and
847
+ # raise error
848
+ elif pos2 == pos:
849
+ raise RuntimeError(
850
+ f"{regex!r} yielded empty string without stack change"
851
+ )
852
+
853
+ # publish new function and start again
854
+ pos = pos2
855
+ break
856
+ # if loop terminated without break we haven't found a single match
857
+ # either we are at the end of the file or we have a problem
858
+ else:
859
+ # end of text
860
+ if pos >= source_length:
861
+ return
862
+
863
+ # something went wrong
864
+ raise TemplateSyntaxError(
865
+ f"unexpected char {source[pos]!r} at {pos}", lineno, name, filename
866
+ )
lib/python3.11/site-packages/jinja2/loaders.py ADDED
@@ -0,0 +1,661 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """API and implementations for loading templates from different data
2
+ sources.
3
+ """
4
+ import importlib.util
5
+ import os
6
+ import posixpath
7
+ import sys
8
+ import typing as t
9
+ import weakref
10
+ import zipimport
11
+ from collections import abc
12
+ from hashlib import sha1
13
+ from importlib import import_module
14
+ from types import ModuleType
15
+
16
+ from .exceptions import TemplateNotFound
17
+ from .utils import internalcode
18
+ from .utils import open_if_exists
19
+
20
+ if t.TYPE_CHECKING:
21
+ from .environment import Environment
22
+ from .environment import Template
23
+
24
+
25
+ def split_template_path(template: str) -> t.List[str]:
26
+ """Split a path into segments and perform a sanity check. If it detects
27
+ '..' in the path it will raise a `TemplateNotFound` error.
28
+ """
29
+ pieces = []
30
+ for piece in template.split("/"):
31
+ if (
32
+ os.path.sep in piece
33
+ or (os.path.altsep and os.path.altsep in piece)
34
+ or piece == os.path.pardir
35
+ ):
36
+ raise TemplateNotFound(template)
37
+ elif piece and piece != ".":
38
+ pieces.append(piece)
39
+ return pieces
40
+
41
+
42
+ class BaseLoader:
43
+ """Baseclass for all loaders. Subclass this and override `get_source` to
44
+ implement a custom loading mechanism. The environment provides a
45
+ `get_template` method that calls the loader's `load` method to get the
46
+ :class:`Template` object.
47
+
48
+ A very basic example for a loader that looks up templates on the file
49
+ system could look like this::
50
+
51
+ from jinja2 import BaseLoader, TemplateNotFound
52
+ from os.path import join, exists, getmtime
53
+
54
+ class MyLoader(BaseLoader):
55
+
56
+ def __init__(self, path):
57
+ self.path = path
58
+
59
+ def get_source(self, environment, template):
60
+ path = join(self.path, template)
61
+ if not exists(path):
62
+ raise TemplateNotFound(template)
63
+ mtime = getmtime(path)
64
+ with open(path) as f:
65
+ source = f.read()
66
+ return source, path, lambda: mtime == getmtime(path)
67
+ """
68
+
69
+ #: if set to `False` it indicates that the loader cannot provide access
70
+ #: to the source of templates.
71
+ #:
72
+ #: .. versionadded:: 2.4
73
+ has_source_access = True
74
+
75
+ def get_source(
76
+ self, environment: "Environment", template: str
77
+ ) -> t.Tuple[str, t.Optional[str], t.Optional[t.Callable[[], bool]]]:
78
+ """Get the template source, filename and reload helper for a template.
79
+ It's passed the environment and template name and has to return a
80
+ tuple in the form ``(source, filename, uptodate)`` or raise a
81
+ `TemplateNotFound` error if it can't locate the template.
82
+
83
+ The source part of the returned tuple must be the source of the
84
+ template as a string. The filename should be the name of the
85
+ file on the filesystem if it was loaded from there, otherwise
86
+ ``None``. The filename is used by Python for the tracebacks
87
+ if no loader extension is used.
88
+
89
+ The last item in the tuple is the `uptodate` function. If auto
90
+ reloading is enabled it's always called to check if the template
91
+ changed. No arguments are passed so the function must store the
92
+ old state somewhere (for example in a closure). If it returns `False`
93
+ the template will be reloaded.
94
+ """
95
+ if not self.has_source_access:
96
+ raise RuntimeError(
97
+ f"{type(self).__name__} cannot provide access to the source"
98
+ )
99
+ raise TemplateNotFound(template)
100
+
101
+ def list_templates(self) -> t.List[str]:
102
+ """Iterates over all templates. If the loader does not support that
103
+ it should raise a :exc:`TypeError` which is the default behavior.
104
+ """
105
+ raise TypeError("this loader cannot iterate over all templates")
106
+
107
+ @internalcode
108
+ def load(
109
+ self,
110
+ environment: "Environment",
111
+ name: str,
112
+ globals: t.Optional[t.MutableMapping[str, t.Any]] = None,
113
+ ) -> "Template":
114
+ """Loads a template. This method looks up the template in the cache
115
+ or loads one by calling :meth:`get_source`. Subclasses should not
116
+ override this method as loaders working on collections of other
117
+ loaders (such as :class:`PrefixLoader` or :class:`ChoiceLoader`)
118
+ will not call this method but `get_source` directly.
119
+ """
120
+ code = None
121
+ if globals is None:
122
+ globals = {}
123
+
124
+ # first we try to get the source for this template together
125
+ # with the filename and the uptodate function.
126
+ source, filename, uptodate = self.get_source(environment, name)
127
+
128
+ # try to load the code from the bytecode cache if there is a
129
+ # bytecode cache configured.
130
+ bcc = environment.bytecode_cache
131
+ if bcc is not None:
132
+ bucket = bcc.get_bucket(environment, name, filename, source)
133
+ code = bucket.code
134
+
135
+ # if we don't have code so far (not cached, no longer up to
136
+ # date) etc. we compile the template
137
+ if code is None:
138
+ code = environment.compile(source, name, filename)
139
+
140
+ # if the bytecode cache is available and the bucket doesn't
141
+ # have a code so far, we give the bucket the new code and put
142
+ # it back to the bytecode cache.
143
+ if bcc is not None and bucket.code is None:
144
+ bucket.code = code
145
+ bcc.set_bucket(bucket)
146
+
147
+ return environment.template_class.from_code(
148
+ environment, code, globals, uptodate
149
+ )
150
+
151
+
152
+ class FileSystemLoader(BaseLoader):
153
+ """Load templates from a directory in the file system.
154
+
155
+ The path can be relative or absolute. Relative paths are relative to
156
+ the current working directory.
157
+
158
+ .. code-block:: python
159
+
160
+ loader = FileSystemLoader("templates")
161
+
162
+ A list of paths can be given. The directories will be searched in
163
+ order, stopping at the first matching template.
164
+
165
+ .. code-block:: python
166
+
167
+ loader = FileSystemLoader(["/override/templates", "/default/templates"])
168
+
169
+ :param searchpath: A path, or list of paths, to the directory that
170
+ contains the templates.
171
+ :param encoding: Use this encoding to read the text from template
172
+ files.
173
+ :param followlinks: Follow symbolic links in the path.
174
+
175
+ .. versionchanged:: 2.8
176
+ Added the ``followlinks`` parameter.
177
+ """
178
+
179
+ def __init__(
180
+ self,
181
+ searchpath: t.Union[str, os.PathLike, t.Sequence[t.Union[str, os.PathLike]]],
182
+ encoding: str = "utf-8",
183
+ followlinks: bool = False,
184
+ ) -> None:
185
+ if not isinstance(searchpath, abc.Iterable) or isinstance(searchpath, str):
186
+ searchpath = [searchpath]
187
+
188
+ self.searchpath = [os.fspath(p) for p in searchpath]
189
+ self.encoding = encoding
190
+ self.followlinks = followlinks
191
+
192
+ def get_source(
193
+ self, environment: "Environment", template: str
194
+ ) -> t.Tuple[str, str, t.Callable[[], bool]]:
195
+ pieces = split_template_path(template)
196
+ for searchpath in self.searchpath:
197
+ # Use posixpath even on Windows to avoid "drive:" or UNC
198
+ # segments breaking out of the search directory.
199
+ filename = posixpath.join(searchpath, *pieces)
200
+ f = open_if_exists(filename)
201
+ if f is None:
202
+ continue
203
+ try:
204
+ contents = f.read().decode(self.encoding)
205
+ finally:
206
+ f.close()
207
+
208
+ mtime = os.path.getmtime(filename)
209
+
210
+ def uptodate() -> bool:
211
+ try:
212
+ return os.path.getmtime(filename) == mtime
213
+ except OSError:
214
+ return False
215
+
216
+ # Use normpath to convert Windows altsep to sep.
217
+ return contents, os.path.normpath(filename), uptodate
218
+ raise TemplateNotFound(template)
219
+
220
+ def list_templates(self) -> t.List[str]:
221
+ found = set()
222
+ for searchpath in self.searchpath:
223
+ walk_dir = os.walk(searchpath, followlinks=self.followlinks)
224
+ for dirpath, _, filenames in walk_dir:
225
+ for filename in filenames:
226
+ template = (
227
+ os.path.join(dirpath, filename)[len(searchpath) :]
228
+ .strip(os.path.sep)
229
+ .replace(os.path.sep, "/")
230
+ )
231
+ if template[:2] == "./":
232
+ template = template[2:]
233
+ if template not in found:
234
+ found.add(template)
235
+ return sorted(found)
236
+
237
+
238
+ class PackageLoader(BaseLoader):
239
+ """Load templates from a directory in a Python package.
240
+
241
+ :param package_name: Import name of the package that contains the
242
+ template directory.
243
+ :param package_path: Directory within the imported package that
244
+ contains the templates.
245
+ :param encoding: Encoding of template files.
246
+
247
+ The following example looks up templates in the ``pages`` directory
248
+ within the ``project.ui`` package.
249
+
250
+ .. code-block:: python
251
+
252
+ loader = PackageLoader("project.ui", "pages")
253
+
254
+ Only packages installed as directories (standard pip behavior) or
255
+ zip/egg files (less common) are supported. The Python API for
256
+ introspecting data in packages is too limited to support other
257
+ installation methods the way this loader requires.
258
+
259
+ There is limited support for :pep:`420` namespace packages. The
260
+ template directory is assumed to only be in one namespace
261
+ contributor. Zip files contributing to a namespace are not
262
+ supported.
263
+
264
+ .. versionchanged:: 3.0
265
+ No longer uses ``setuptools`` as a dependency.
266
+
267
+ .. versionchanged:: 3.0
268
+ Limited PEP 420 namespace package support.
269
+ """
270
+
271
+ def __init__(
272
+ self,
273
+ package_name: str,
274
+ package_path: "str" = "templates",
275
+ encoding: str = "utf-8",
276
+ ) -> None:
277
+ package_path = os.path.normpath(package_path).rstrip(os.path.sep)
278
+
279
+ # normpath preserves ".", which isn't valid in zip paths.
280
+ if package_path == os.path.curdir:
281
+ package_path = ""
282
+ elif package_path[:2] == os.path.curdir + os.path.sep:
283
+ package_path = package_path[2:]
284
+
285
+ self.package_path = package_path
286
+ self.package_name = package_name
287
+ self.encoding = encoding
288
+
289
+ # Make sure the package exists. This also makes namespace
290
+ # packages work, otherwise get_loader returns None.
291
+ import_module(package_name)
292
+ spec = importlib.util.find_spec(package_name)
293
+ assert spec is not None, "An import spec was not found for the package."
294
+ loader = spec.loader
295
+ assert loader is not None, "A loader was not found for the package."
296
+ self._loader = loader
297
+ self._archive = None
298
+ template_root = None
299
+
300
+ if isinstance(loader, zipimport.zipimporter):
301
+ self._archive = loader.archive
302
+ pkgdir = next(iter(spec.submodule_search_locations)) # type: ignore
303
+ template_root = os.path.join(pkgdir, package_path).rstrip(os.path.sep)
304
+ else:
305
+ roots: t.List[str] = []
306
+
307
+ # One element for regular packages, multiple for namespace
308
+ # packages, or None for single module file.
309
+ if spec.submodule_search_locations:
310
+ roots.extend(spec.submodule_search_locations)
311
+ # A single module file, use the parent directory instead.
312
+ elif spec.origin is not None:
313
+ roots.append(os.path.dirname(spec.origin))
314
+
315
+ for root in roots:
316
+ root = os.path.join(root, package_path)
317
+
318
+ if os.path.isdir(root):
319
+ template_root = root
320
+ break
321
+
322
+ if template_root is None:
323
+ raise ValueError(
324
+ f"The {package_name!r} package was not installed in a"
325
+ " way that PackageLoader understands."
326
+ )
327
+
328
+ self._template_root = template_root
329
+
330
+ def get_source(
331
+ self, environment: "Environment", template: str
332
+ ) -> t.Tuple[str, str, t.Optional[t.Callable[[], bool]]]:
333
+ # Use posixpath even on Windows to avoid "drive:" or UNC
334
+ # segments breaking out of the search directory. Use normpath to
335
+ # convert Windows altsep to sep.
336
+ p = os.path.normpath(
337
+ posixpath.join(self._template_root, *split_template_path(template))
338
+ )
339
+ up_to_date: t.Optional[t.Callable[[], bool]]
340
+
341
+ if self._archive is None:
342
+ # Package is a directory.
343
+ if not os.path.isfile(p):
344
+ raise TemplateNotFound(template)
345
+
346
+ with open(p, "rb") as f:
347
+ source = f.read()
348
+
349
+ mtime = os.path.getmtime(p)
350
+
351
+ def up_to_date() -> bool:
352
+ return os.path.isfile(p) and os.path.getmtime(p) == mtime
353
+
354
+ else:
355
+ # Package is a zip file.
356
+ try:
357
+ source = self._loader.get_data(p) # type: ignore
358
+ except OSError as e:
359
+ raise TemplateNotFound(template) from e
360
+
361
+ # Could use the zip's mtime for all template mtimes, but
362
+ # would need to safely reload the module if it's out of
363
+ # date, so just report it as always current.
364
+ up_to_date = None
365
+
366
+ return source.decode(self.encoding), p, up_to_date
367
+
368
+ def list_templates(self) -> t.List[str]:
369
+ results: t.List[str] = []
370
+
371
+ if self._archive is None:
372
+ # Package is a directory.
373
+ offset = len(self._template_root)
374
+
375
+ for dirpath, _, filenames in os.walk(self._template_root):
376
+ dirpath = dirpath[offset:].lstrip(os.path.sep)
377
+ results.extend(
378
+ os.path.join(dirpath, name).replace(os.path.sep, "/")
379
+ for name in filenames
380
+ )
381
+ else:
382
+ if not hasattr(self._loader, "_files"):
383
+ raise TypeError(
384
+ "This zip import does not have the required"
385
+ " metadata to list templates."
386
+ )
387
+
388
+ # Package is a zip file.
389
+ prefix = (
390
+ self._template_root[len(self._archive) :].lstrip(os.path.sep)
391
+ + os.path.sep
392
+ )
393
+ offset = len(prefix)
394
+
395
+ for name in self._loader._files.keys(): # type: ignore
396
+ # Find names under the templates directory that aren't directories.
397
+ if name.startswith(prefix) and name[-1] != os.path.sep:
398
+ results.append(name[offset:].replace(os.path.sep, "/"))
399
+
400
+ results.sort()
401
+ return results
402
+
403
+
404
+ class DictLoader(BaseLoader):
405
+ """Loads a template from a Python dict mapping template names to
406
+ template source. This loader is useful for unittesting:
407
+
408
+ >>> loader = DictLoader({'index.html': 'source here'})
409
+
410
+ Because auto reloading is rarely useful this is disabled per default.
411
+ """
412
+
413
+ def __init__(self, mapping: t.Mapping[str, str]) -> None:
414
+ self.mapping = mapping
415
+
416
+ def get_source(
417
+ self, environment: "Environment", template: str
418
+ ) -> t.Tuple[str, None, t.Callable[[], bool]]:
419
+ if template in self.mapping:
420
+ source = self.mapping[template]
421
+ return source, None, lambda: source == self.mapping.get(template)
422
+ raise TemplateNotFound(template)
423
+
424
+ def list_templates(self) -> t.List[str]:
425
+ return sorted(self.mapping)
426
+
427
+
428
+ class FunctionLoader(BaseLoader):
429
+ """A loader that is passed a function which does the loading. The
430
+ function receives the name of the template and has to return either
431
+ a string with the template source, a tuple in the form ``(source,
432
+ filename, uptodatefunc)`` or `None` if the template does not exist.
433
+
434
+ >>> def load_template(name):
435
+ ... if name == 'index.html':
436
+ ... return '...'
437
+ ...
438
+ >>> loader = FunctionLoader(load_template)
439
+
440
+ The `uptodatefunc` is a function that is called if autoreload is enabled
441
+ and has to return `True` if the template is still up to date. For more
442
+ details have a look at :meth:`BaseLoader.get_source` which has the same
443
+ return value.
444
+ """
445
+
446
+ def __init__(
447
+ self,
448
+ load_func: t.Callable[
449
+ [str],
450
+ t.Optional[
451
+ t.Union[
452
+ str, t.Tuple[str, t.Optional[str], t.Optional[t.Callable[[], bool]]]
453
+ ]
454
+ ],
455
+ ],
456
+ ) -> None:
457
+ self.load_func = load_func
458
+
459
+ def get_source(
460
+ self, environment: "Environment", template: str
461
+ ) -> t.Tuple[str, t.Optional[str], t.Optional[t.Callable[[], bool]]]:
462
+ rv = self.load_func(template)
463
+
464
+ if rv is None:
465
+ raise TemplateNotFound(template)
466
+
467
+ if isinstance(rv, str):
468
+ return rv, None, None
469
+
470
+ return rv
471
+
472
+
473
+ class PrefixLoader(BaseLoader):
474
+ """A loader that is passed a dict of loaders where each loader is bound
475
+ to a prefix. The prefix is delimited from the template by a slash per
476
+ default, which can be changed by setting the `delimiter` argument to
477
+ something else::
478
+
479
+ loader = PrefixLoader({
480
+ 'app1': PackageLoader('mypackage.app1'),
481
+ 'app2': PackageLoader('mypackage.app2')
482
+ })
483
+
484
+ By loading ``'app1/index.html'`` the file from the app1 package is loaded,
485
+ by loading ``'app2/index.html'`` the file from the second.
486
+ """
487
+
488
+ def __init__(
489
+ self, mapping: t.Mapping[str, BaseLoader], delimiter: str = "/"
490
+ ) -> None:
491
+ self.mapping = mapping
492
+ self.delimiter = delimiter
493
+
494
+ def get_loader(self, template: str) -> t.Tuple[BaseLoader, str]:
495
+ try:
496
+ prefix, name = template.split(self.delimiter, 1)
497
+ loader = self.mapping[prefix]
498
+ except (ValueError, KeyError) as e:
499
+ raise TemplateNotFound(template) from e
500
+ return loader, name
501
+
502
+ def get_source(
503
+ self, environment: "Environment", template: str
504
+ ) -> t.Tuple[str, t.Optional[str], t.Optional[t.Callable[[], bool]]]:
505
+ loader, name = self.get_loader(template)
506
+ try:
507
+ return loader.get_source(environment, name)
508
+ except TemplateNotFound as e:
509
+ # re-raise the exception with the correct filename here.
510
+ # (the one that includes the prefix)
511
+ raise TemplateNotFound(template) from e
512
+
513
+ @internalcode
514
+ def load(
515
+ self,
516
+ environment: "Environment",
517
+ name: str,
518
+ globals: t.Optional[t.MutableMapping[str, t.Any]] = None,
519
+ ) -> "Template":
520
+ loader, local_name = self.get_loader(name)
521
+ try:
522
+ return loader.load(environment, local_name, globals)
523
+ except TemplateNotFound as e:
524
+ # re-raise the exception with the correct filename here.
525
+ # (the one that includes the prefix)
526
+ raise TemplateNotFound(name) from e
527
+
528
+ def list_templates(self) -> t.List[str]:
529
+ result = []
530
+ for prefix, loader in self.mapping.items():
531
+ for template in loader.list_templates():
532
+ result.append(prefix + self.delimiter + template)
533
+ return result
534
+
535
+
536
+ class ChoiceLoader(BaseLoader):
537
+ """This loader works like the `PrefixLoader` just that no prefix is
538
+ specified. If a template could not be found by one loader the next one
539
+ is tried.
540
+
541
+ >>> loader = ChoiceLoader([
542
+ ... FileSystemLoader('/path/to/user/templates'),
543
+ ... FileSystemLoader('/path/to/system/templates')
544
+ ... ])
545
+
546
+ This is useful if you want to allow users to override builtin templates
547
+ from a different location.
548
+ """
549
+
550
+ def __init__(self, loaders: t.Sequence[BaseLoader]) -> None:
551
+ self.loaders = loaders
552
+
553
+ def get_source(
554
+ self, environment: "Environment", template: str
555
+ ) -> t.Tuple[str, t.Optional[str], t.Optional[t.Callable[[], bool]]]:
556
+ for loader in self.loaders:
557
+ try:
558
+ return loader.get_source(environment, template)
559
+ except TemplateNotFound:
560
+ pass
561
+ raise TemplateNotFound(template)
562
+
563
+ @internalcode
564
+ def load(
565
+ self,
566
+ environment: "Environment",
567
+ name: str,
568
+ globals: t.Optional[t.MutableMapping[str, t.Any]] = None,
569
+ ) -> "Template":
570
+ for loader in self.loaders:
571
+ try:
572
+ return loader.load(environment, name, globals)
573
+ except TemplateNotFound:
574
+ pass
575
+ raise TemplateNotFound(name)
576
+
577
+ def list_templates(self) -> t.List[str]:
578
+ found = set()
579
+ for loader in self.loaders:
580
+ found.update(loader.list_templates())
581
+ return sorted(found)
582
+
583
+
584
+ class _TemplateModule(ModuleType):
585
+ """Like a normal module but with support for weak references"""
586
+
587
+
588
+ class ModuleLoader(BaseLoader):
589
+ """This loader loads templates from precompiled templates.
590
+
591
+ Example usage:
592
+
593
+ >>> loader = ChoiceLoader([
594
+ ... ModuleLoader('/path/to/compiled/templates'),
595
+ ... FileSystemLoader('/path/to/templates')
596
+ ... ])
597
+
598
+ Templates can be precompiled with :meth:`Environment.compile_templates`.
599
+ """
600
+
601
+ has_source_access = False
602
+
603
+ def __init__(
604
+ self, path: t.Union[str, os.PathLike, t.Sequence[t.Union[str, os.PathLike]]]
605
+ ) -> None:
606
+ package_name = f"_jinja2_module_templates_{id(self):x}"
607
+
608
+ # create a fake module that looks for the templates in the
609
+ # path given.
610
+ mod = _TemplateModule(package_name)
611
+
612
+ if not isinstance(path, abc.Iterable) or isinstance(path, str):
613
+ path = [path]
614
+
615
+ mod.__path__ = [os.fspath(p) for p in path]
616
+
617
+ sys.modules[package_name] = weakref.proxy(
618
+ mod, lambda x: sys.modules.pop(package_name, None)
619
+ )
620
+
621
+ # the only strong reference, the sys.modules entry is weak
622
+ # so that the garbage collector can remove it once the
623
+ # loader that created it goes out of business.
624
+ self.module = mod
625
+ self.package_name = package_name
626
+
627
+ @staticmethod
628
+ def get_template_key(name: str) -> str:
629
+ return "tmpl_" + sha1(name.encode("utf-8")).hexdigest()
630
+
631
+ @staticmethod
632
+ def get_module_filename(name: str) -> str:
633
+ return ModuleLoader.get_template_key(name) + ".py"
634
+
635
+ @internalcode
636
+ def load(
637
+ self,
638
+ environment: "Environment",
639
+ name: str,
640
+ globals: t.Optional[t.MutableMapping[str, t.Any]] = None,
641
+ ) -> "Template":
642
+ key = self.get_template_key(name)
643
+ module = f"{self.package_name}.{key}"
644
+ mod = getattr(self.module, module, None)
645
+
646
+ if mod is None:
647
+ try:
648
+ mod = __import__(module, None, None, ["root"])
649
+ except ImportError as e:
650
+ raise TemplateNotFound(name) from e
651
+
652
+ # remove the entry from sys.modules, we only want the attribute
653
+ # on the module object we have stored on the loader.
654
+ sys.modules.pop(module, None)
655
+
656
+ if globals is None:
657
+ globals = {}
658
+
659
+ return environment.template_class.from_module_dict(
660
+ environment, mod.__dict__, globals
661
+ )
lib/python3.11/site-packages/jinja2/meta.py ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Functions that expose information about templates that might be
2
+ interesting for introspection.
3
+ """
4
+ import typing as t
5
+
6
+ from . import nodes
7
+ from .compiler import CodeGenerator
8
+ from .compiler import Frame
9
+
10
+ if t.TYPE_CHECKING:
11
+ from .environment import Environment
12
+
13
+
14
+ class TrackingCodeGenerator(CodeGenerator):
15
+ """We abuse the code generator for introspection."""
16
+
17
+ def __init__(self, environment: "Environment") -> None:
18
+ super().__init__(environment, "<introspection>", "<introspection>")
19
+ self.undeclared_identifiers: t.Set[str] = set()
20
+
21
+ def write(self, x: str) -> None:
22
+ """Don't write."""
23
+
24
+ def enter_frame(self, frame: Frame) -> None:
25
+ """Remember all undeclared identifiers."""
26
+ super().enter_frame(frame)
27
+
28
+ for _, (action, param) in frame.symbols.loads.items():
29
+ if action == "resolve" and param not in self.environment.globals:
30
+ self.undeclared_identifiers.add(param)
31
+
32
+
33
+ def find_undeclared_variables(ast: nodes.Template) -> t.Set[str]:
34
+ """Returns a set of all variables in the AST that will be looked up from
35
+ the context at runtime. Because at compile time it's not known which
36
+ variables will be used depending on the path the execution takes at
37
+ runtime, all variables are returned.
38
+
39
+ >>> from jinja2 import Environment, meta
40
+ >>> env = Environment()
41
+ >>> ast = env.parse('{% set foo = 42 %}{{ bar + foo }}')
42
+ >>> meta.find_undeclared_variables(ast) == {'bar'}
43
+ True
44
+
45
+ .. admonition:: Implementation
46
+
47
+ Internally the code generator is used for finding undeclared variables.
48
+ This is good to know because the code generator might raise a
49
+ :exc:`TemplateAssertionError` during compilation and as a matter of
50
+ fact this function can currently raise that exception as well.
51
+ """
52
+ codegen = TrackingCodeGenerator(ast.environment) # type: ignore
53
+ codegen.visit(ast)
54
+ return codegen.undeclared_identifiers
55
+
56
+
57
+ _ref_types = (nodes.Extends, nodes.FromImport, nodes.Import, nodes.Include)
58
+ _RefType = t.Union[nodes.Extends, nodes.FromImport, nodes.Import, nodes.Include]
59
+
60
+
61
+ def find_referenced_templates(ast: nodes.Template) -> t.Iterator[t.Optional[str]]:
62
+ """Finds all the referenced templates from the AST. This will return an
63
+ iterator over all the hardcoded template extensions, inclusions and
64
+ imports. If dynamic inheritance or inclusion is used, `None` will be
65
+ yielded.
66
+
67
+ >>> from jinja2 import Environment, meta
68
+ >>> env = Environment()
69
+ >>> ast = env.parse('{% extends "layout.html" %}{% include helper %}')
70
+ >>> list(meta.find_referenced_templates(ast))
71
+ ['layout.html', None]
72
+
73
+ This function is useful for dependency tracking. For example if you want
74
+ to rebuild parts of the website after a layout template has changed.
75
+ """
76
+ template_name: t.Any
77
+
78
+ for node in ast.find_all(_ref_types):
79
+ template: nodes.Expr = node.template # type: ignore
80
+
81
+ if not isinstance(template, nodes.Const):
82
+ # a tuple with some non consts in there
83
+ if isinstance(template, (nodes.Tuple, nodes.List)):
84
+ for template_name in template.items:
85
+ # something const, only yield the strings and ignore
86
+ # non-string consts that really just make no sense
87
+ if isinstance(template_name, nodes.Const):
88
+ if isinstance(template_name.value, str):
89
+ yield template_name.value
90
+ # something dynamic in there
91
+ else:
92
+ yield None
93
+ # something dynamic we don't know about here
94
+ else:
95
+ yield None
96
+ continue
97
+ # constant is a basestring, direct template name
98
+ if isinstance(template.value, str):
99
+ yield template.value
100
+ # a tuple or list (latter *should* not happen) made of consts,
101
+ # yield the consts that are strings. We could warn here for
102
+ # non string values
103
+ elif isinstance(node, nodes.Include) and isinstance(
104
+ template.value, (tuple, list)
105
+ ):
106
+ for template_name in template.value:
107
+ if isinstance(template_name, str):
108
+ yield template_name
109
+ # something else we don't care about, we could warn here
110
+ else:
111
+ yield None
lib/python3.11/site-packages/jinja2/nativetypes.py ADDED
@@ -0,0 +1,130 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import typing as t
2
+ from ast import literal_eval
3
+ from ast import parse
4
+ from itertools import chain
5
+ from itertools import islice
6
+ from types import GeneratorType
7
+
8
+ from . import nodes
9
+ from .compiler import CodeGenerator
10
+ from .compiler import Frame
11
+ from .compiler import has_safe_repr
12
+ from .environment import Environment
13
+ from .environment import Template
14
+
15
+
16
+ def native_concat(values: t.Iterable[t.Any]) -> t.Optional[t.Any]:
17
+ """Return a native Python type from the list of compiled nodes. If
18
+ the result is a single node, its value is returned. Otherwise, the
19
+ nodes are concatenated as strings. If the result can be parsed with
20
+ :func:`ast.literal_eval`, the parsed value is returned. Otherwise,
21
+ the string is returned.
22
+
23
+ :param values: Iterable of outputs to concatenate.
24
+ """
25
+ head = list(islice(values, 2))
26
+
27
+ if not head:
28
+ return None
29
+
30
+ if len(head) == 1:
31
+ raw = head[0]
32
+ if not isinstance(raw, str):
33
+ return raw
34
+ else:
35
+ if isinstance(values, GeneratorType):
36
+ values = chain(head, values)
37
+ raw = "".join([str(v) for v in values])
38
+
39
+ try:
40
+ return literal_eval(
41
+ # In Python 3.10+ ast.literal_eval removes leading spaces/tabs
42
+ # from the given string. For backwards compatibility we need to
43
+ # parse the string ourselves without removing leading spaces/tabs.
44
+ parse(raw, mode="eval")
45
+ )
46
+ except (ValueError, SyntaxError, MemoryError):
47
+ return raw
48
+
49
+
50
+ class NativeCodeGenerator(CodeGenerator):
51
+ """A code generator which renders Python types by not adding
52
+ ``str()`` around output nodes.
53
+ """
54
+
55
+ @staticmethod
56
+ def _default_finalize(value: t.Any) -> t.Any:
57
+ return value
58
+
59
+ def _output_const_repr(self, group: t.Iterable[t.Any]) -> str:
60
+ return repr("".join([str(v) for v in group]))
61
+
62
+ def _output_child_to_const(
63
+ self, node: nodes.Expr, frame: Frame, finalize: CodeGenerator._FinalizeInfo
64
+ ) -> t.Any:
65
+ const = node.as_const(frame.eval_ctx)
66
+
67
+ if not has_safe_repr(const):
68
+ raise nodes.Impossible()
69
+
70
+ if isinstance(node, nodes.TemplateData):
71
+ return const
72
+
73
+ return finalize.const(const) # type: ignore
74
+
75
+ def _output_child_pre(
76
+ self, node: nodes.Expr, frame: Frame, finalize: CodeGenerator._FinalizeInfo
77
+ ) -> None:
78
+ if finalize.src is not None:
79
+ self.write(finalize.src)
80
+
81
+ def _output_child_post(
82
+ self, node: nodes.Expr, frame: Frame, finalize: CodeGenerator._FinalizeInfo
83
+ ) -> None:
84
+ if finalize.src is not None:
85
+ self.write(")")
86
+
87
+
88
+ class NativeEnvironment(Environment):
89
+ """An environment that renders templates to native Python types."""
90
+
91
+ code_generator_class = NativeCodeGenerator
92
+ concat = staticmethod(native_concat) # type: ignore
93
+
94
+
95
+ class NativeTemplate(Template):
96
+ environment_class = NativeEnvironment
97
+
98
+ def render(self, *args: t.Any, **kwargs: t.Any) -> t.Any:
99
+ """Render the template to produce a native Python type. If the
100
+ result is a single node, its value is returned. Otherwise, the
101
+ nodes are concatenated as strings. If the result can be parsed
102
+ with :func:`ast.literal_eval`, the parsed value is returned.
103
+ Otherwise, the string is returned.
104
+ """
105
+ ctx = self.new_context(dict(*args, **kwargs))
106
+
107
+ try:
108
+ return self.environment_class.concat( # type: ignore
109
+ self.root_render_func(ctx) # type: ignore
110
+ )
111
+ except Exception:
112
+ return self.environment.handle_exception()
113
+
114
+ async def render_async(self, *args: t.Any, **kwargs: t.Any) -> t.Any:
115
+ if not self.environment.is_async:
116
+ raise RuntimeError(
117
+ "The environment was not created with async mode enabled."
118
+ )
119
+
120
+ ctx = self.new_context(dict(*args, **kwargs))
121
+
122
+ try:
123
+ return self.environment_class.concat( # type: ignore
124
+ [n async for n in self.root_render_func(ctx)] # type: ignore
125
+ )
126
+ except Exception:
127
+ return self.environment.handle_exception()
128
+
129
+
130
+ NativeEnvironment.template_class = NativeTemplate
lib/python3.11/site-packages/jinja2/nodes.py ADDED
@@ -0,0 +1,1204 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """AST nodes generated by the parser for the compiler. Also provides
2
+ some node tree helper functions used by the parser and compiler in order
3
+ to normalize nodes.
4
+ """
5
+ import inspect
6
+ import operator
7
+ import typing as t
8
+ from collections import deque
9
+
10
+ from markupsafe import Markup
11
+
12
+ from .utils import _PassArg
13
+
14
+ if t.TYPE_CHECKING:
15
+ import typing_extensions as te
16
+ from .environment import Environment
17
+
18
+ _NodeBound = t.TypeVar("_NodeBound", bound="Node")
19
+
20
+ _binop_to_func: t.Dict[str, t.Callable[[t.Any, t.Any], t.Any]] = {
21
+ "*": operator.mul,
22
+ "/": operator.truediv,
23
+ "//": operator.floordiv,
24
+ "**": operator.pow,
25
+ "%": operator.mod,
26
+ "+": operator.add,
27
+ "-": operator.sub,
28
+ }
29
+
30
+ _uaop_to_func: t.Dict[str, t.Callable[[t.Any], t.Any]] = {
31
+ "not": operator.not_,
32
+ "+": operator.pos,
33
+ "-": operator.neg,
34
+ }
35
+
36
+ _cmpop_to_func: t.Dict[str, t.Callable[[t.Any, t.Any], t.Any]] = {
37
+ "eq": operator.eq,
38
+ "ne": operator.ne,
39
+ "gt": operator.gt,
40
+ "gteq": operator.ge,
41
+ "lt": operator.lt,
42
+ "lteq": operator.le,
43
+ "in": lambda a, b: a in b,
44
+ "notin": lambda a, b: a not in b,
45
+ }
46
+
47
+
48
+ class Impossible(Exception):
49
+ """Raised if the node could not perform a requested action."""
50
+
51
+
52
+ class NodeType(type):
53
+ """A metaclass for nodes that handles the field and attribute
54
+ inheritance. fields and attributes from the parent class are
55
+ automatically forwarded to the child."""
56
+
57
+ def __new__(mcs, name, bases, d): # type: ignore
58
+ for attr in "fields", "attributes":
59
+ storage = []
60
+ storage.extend(getattr(bases[0] if bases else object, attr, ()))
61
+ storage.extend(d.get(attr, ()))
62
+ assert len(bases) <= 1, "multiple inheritance not allowed"
63
+ assert len(storage) == len(set(storage)), "layout conflict"
64
+ d[attr] = tuple(storage)
65
+ d.setdefault("abstract", False)
66
+ return type.__new__(mcs, name, bases, d)
67
+
68
+
69
+ class EvalContext:
70
+ """Holds evaluation time information. Custom attributes can be attached
71
+ to it in extensions.
72
+ """
73
+
74
+ def __init__(
75
+ self, environment: "Environment", template_name: t.Optional[str] = None
76
+ ) -> None:
77
+ self.environment = environment
78
+ if callable(environment.autoescape):
79
+ self.autoescape = environment.autoescape(template_name)
80
+ else:
81
+ self.autoescape = environment.autoescape
82
+ self.volatile = False
83
+
84
+ def save(self) -> t.Mapping[str, t.Any]:
85
+ return self.__dict__.copy()
86
+
87
+ def revert(self, old: t.Mapping[str, t.Any]) -> None:
88
+ self.__dict__.clear()
89
+ self.__dict__.update(old)
90
+
91
+
92
+ def get_eval_context(node: "Node", ctx: t.Optional[EvalContext]) -> EvalContext:
93
+ if ctx is None:
94
+ if node.environment is None:
95
+ raise RuntimeError(
96
+ "if no eval context is passed, the node must have an"
97
+ " attached environment."
98
+ )
99
+ return EvalContext(node.environment)
100
+ return ctx
101
+
102
+
103
+ class Node(metaclass=NodeType):
104
+ """Baseclass for all Jinja nodes. There are a number of nodes available
105
+ of different types. There are four major types:
106
+
107
+ - :class:`Stmt`: statements
108
+ - :class:`Expr`: expressions
109
+ - :class:`Helper`: helper nodes
110
+ - :class:`Template`: the outermost wrapper node
111
+
112
+ All nodes have fields and attributes. Fields may be other nodes, lists,
113
+ or arbitrary values. Fields are passed to the constructor as regular
114
+ positional arguments, attributes as keyword arguments. Each node has
115
+ two attributes: `lineno` (the line number of the node) and `environment`.
116
+ The `environment` attribute is set at the end of the parsing process for
117
+ all nodes automatically.
118
+ """
119
+
120
+ fields: t.Tuple[str, ...] = ()
121
+ attributes: t.Tuple[str, ...] = ("lineno", "environment")
122
+ abstract = True
123
+
124
+ lineno: int
125
+ environment: t.Optional["Environment"]
126
+
127
+ def __init__(self, *fields: t.Any, **attributes: t.Any) -> None:
128
+ if self.abstract:
129
+ raise TypeError("abstract nodes are not instantiable")
130
+ if fields:
131
+ if len(fields) != len(self.fields):
132
+ if not self.fields:
133
+ raise TypeError(f"{type(self).__name__!r} takes 0 arguments")
134
+ raise TypeError(
135
+ f"{type(self).__name__!r} takes 0 or {len(self.fields)}"
136
+ f" argument{'s' if len(self.fields) != 1 else ''}"
137
+ )
138
+ for name, arg in zip(self.fields, fields):
139
+ setattr(self, name, arg)
140
+ for attr in self.attributes:
141
+ setattr(self, attr, attributes.pop(attr, None))
142
+ if attributes:
143
+ raise TypeError(f"unknown attribute {next(iter(attributes))!r}")
144
+
145
+ def iter_fields(
146
+ self,
147
+ exclude: t.Optional[t.Container[str]] = None,
148
+ only: t.Optional[t.Container[str]] = None,
149
+ ) -> t.Iterator[t.Tuple[str, t.Any]]:
150
+ """This method iterates over all fields that are defined and yields
151
+ ``(key, value)`` tuples. Per default all fields are returned, but
152
+ it's possible to limit that to some fields by providing the `only`
153
+ parameter or to exclude some using the `exclude` parameter. Both
154
+ should be sets or tuples of field names.
155
+ """
156
+ for name in self.fields:
157
+ if (
158
+ (exclude is None and only is None)
159
+ or (exclude is not None and name not in exclude)
160
+ or (only is not None and name in only)
161
+ ):
162
+ try:
163
+ yield name, getattr(self, name)
164
+ except AttributeError:
165
+ pass
166
+
167
+ def iter_child_nodes(
168
+ self,
169
+ exclude: t.Optional[t.Container[str]] = None,
170
+ only: t.Optional[t.Container[str]] = None,
171
+ ) -> t.Iterator["Node"]:
172
+ """Iterates over all direct child nodes of the node. This iterates
173
+ over all fields and yields the values of they are nodes. If the value
174
+ of a field is a list all the nodes in that list are returned.
175
+ """
176
+ for _, item in self.iter_fields(exclude, only):
177
+ if isinstance(item, list):
178
+ for n in item:
179
+ if isinstance(n, Node):
180
+ yield n
181
+ elif isinstance(item, Node):
182
+ yield item
183
+
184
+ def find(self, node_type: t.Type[_NodeBound]) -> t.Optional[_NodeBound]:
185
+ """Find the first node of a given type. If no such node exists the
186
+ return value is `None`.
187
+ """
188
+ for result in self.find_all(node_type):
189
+ return result
190
+
191
+ return None
192
+
193
+ def find_all(
194
+ self, node_type: t.Union[t.Type[_NodeBound], t.Tuple[t.Type[_NodeBound], ...]]
195
+ ) -> t.Iterator[_NodeBound]:
196
+ """Find all the nodes of a given type. If the type is a tuple,
197
+ the check is performed for any of the tuple items.
198
+ """
199
+ for child in self.iter_child_nodes():
200
+ if isinstance(child, node_type):
201
+ yield child # type: ignore
202
+ yield from child.find_all(node_type)
203
+
204
+ def set_ctx(self, ctx: str) -> "Node":
205
+ """Reset the context of a node and all child nodes. Per default the
206
+ parser will all generate nodes that have a 'load' context as it's the
207
+ most common one. This method is used in the parser to set assignment
208
+ targets and other nodes to a store context.
209
+ """
210
+ todo = deque([self])
211
+ while todo:
212
+ node = todo.popleft()
213
+ if "ctx" in node.fields:
214
+ node.ctx = ctx # type: ignore
215
+ todo.extend(node.iter_child_nodes())
216
+ return self
217
+
218
+ def set_lineno(self, lineno: int, override: bool = False) -> "Node":
219
+ """Set the line numbers of the node and children."""
220
+ todo = deque([self])
221
+ while todo:
222
+ node = todo.popleft()
223
+ if "lineno" in node.attributes:
224
+ if node.lineno is None or override:
225
+ node.lineno = lineno
226
+ todo.extend(node.iter_child_nodes())
227
+ return self
228
+
229
+ def set_environment(self, environment: "Environment") -> "Node":
230
+ """Set the environment for all nodes."""
231
+ todo = deque([self])
232
+ while todo:
233
+ node = todo.popleft()
234
+ node.environment = environment
235
+ todo.extend(node.iter_child_nodes())
236
+ return self
237
+
238
+ def __eq__(self, other: t.Any) -> bool:
239
+ if type(self) is not type(other):
240
+ return NotImplemented
241
+
242
+ return tuple(self.iter_fields()) == tuple(other.iter_fields())
243
+
244
+ __hash__ = object.__hash__
245
+
246
+ def __repr__(self) -> str:
247
+ args_str = ", ".join(f"{a}={getattr(self, a, None)!r}" for a in self.fields)
248
+ return f"{type(self).__name__}({args_str})"
249
+
250
+ def dump(self) -> str:
251
+ def _dump(node: t.Union[Node, t.Any]) -> None:
252
+ if not isinstance(node, Node):
253
+ buf.append(repr(node))
254
+ return
255
+
256
+ buf.append(f"nodes.{type(node).__name__}(")
257
+ if not node.fields:
258
+ buf.append(")")
259
+ return
260
+ for idx, field in enumerate(node.fields):
261
+ if idx:
262
+ buf.append(", ")
263
+ value = getattr(node, field)
264
+ if isinstance(value, list):
265
+ buf.append("[")
266
+ for idx, item in enumerate(value):
267
+ if idx:
268
+ buf.append(", ")
269
+ _dump(item)
270
+ buf.append("]")
271
+ else:
272
+ _dump(value)
273
+ buf.append(")")
274
+
275
+ buf: t.List[str] = []
276
+ _dump(self)
277
+ return "".join(buf)
278
+
279
+
280
+ class Stmt(Node):
281
+ """Base node for all statements."""
282
+
283
+ abstract = True
284
+
285
+
286
+ class Helper(Node):
287
+ """Nodes that exist in a specific context only."""
288
+
289
+ abstract = True
290
+
291
+
292
+ class Template(Node):
293
+ """Node that represents a template. This must be the outermost node that
294
+ is passed to the compiler.
295
+ """
296
+
297
+ fields = ("body",)
298
+ body: t.List[Node]
299
+
300
+
301
+ class Output(Stmt):
302
+ """A node that holds multiple expressions which are then printed out.
303
+ This is used both for the `print` statement and the regular template data.
304
+ """
305
+
306
+ fields = ("nodes",)
307
+ nodes: t.List["Expr"]
308
+
309
+
310
+ class Extends(Stmt):
311
+ """Represents an extends statement."""
312
+
313
+ fields = ("template",)
314
+ template: "Expr"
315
+
316
+
317
+ class For(Stmt):
318
+ """The for loop. `target` is the target for the iteration (usually a
319
+ :class:`Name` or :class:`Tuple`), `iter` the iterable. `body` is a list
320
+ of nodes that are used as loop-body, and `else_` a list of nodes for the
321
+ `else` block. If no else node exists it has to be an empty list.
322
+
323
+ For filtered nodes an expression can be stored as `test`, otherwise `None`.
324
+ """
325
+
326
+ fields = ("target", "iter", "body", "else_", "test", "recursive")
327
+ target: Node
328
+ iter: Node
329
+ body: t.List[Node]
330
+ else_: t.List[Node]
331
+ test: t.Optional[Node]
332
+ recursive: bool
333
+
334
+
335
+ class If(Stmt):
336
+ """If `test` is true, `body` is rendered, else `else_`."""
337
+
338
+ fields = ("test", "body", "elif_", "else_")
339
+ test: Node
340
+ body: t.List[Node]
341
+ elif_: t.List["If"]
342
+ else_: t.List[Node]
343
+
344
+
345
+ class Macro(Stmt):
346
+ """A macro definition. `name` is the name of the macro, `args` a list of
347
+ arguments and `defaults` a list of defaults if there are any. `body` is
348
+ a list of nodes for the macro body.
349
+ """
350
+
351
+ fields = ("name", "args", "defaults", "body")
352
+ name: str
353
+ args: t.List["Name"]
354
+ defaults: t.List["Expr"]
355
+ body: t.List[Node]
356
+
357
+
358
+ class CallBlock(Stmt):
359
+ """Like a macro without a name but a call instead. `call` is called with
360
+ the unnamed macro as `caller` argument this node holds.
361
+ """
362
+
363
+ fields = ("call", "args", "defaults", "body")
364
+ call: "Call"
365
+ args: t.List["Name"]
366
+ defaults: t.List["Expr"]
367
+ body: t.List[Node]
368
+
369
+
370
+ class FilterBlock(Stmt):
371
+ """Node for filter sections."""
372
+
373
+ fields = ("body", "filter")
374
+ body: t.List[Node]
375
+ filter: "Filter"
376
+
377
+
378
+ class With(Stmt):
379
+ """Specific node for with statements. In older versions of Jinja the
380
+ with statement was implemented on the base of the `Scope` node instead.
381
+
382
+ .. versionadded:: 2.9.3
383
+ """
384
+
385
+ fields = ("targets", "values", "body")
386
+ targets: t.List["Expr"]
387
+ values: t.List["Expr"]
388
+ body: t.List[Node]
389
+
390
+
391
+ class Block(Stmt):
392
+ """A node that represents a block.
393
+
394
+ .. versionchanged:: 3.0.0
395
+ the `required` field was added.
396
+ """
397
+
398
+ fields = ("name", "body", "scoped", "required")
399
+ name: str
400
+ body: t.List[Node]
401
+ scoped: bool
402
+ required: bool
403
+
404
+
405
+ class Include(Stmt):
406
+ """A node that represents the include tag."""
407
+
408
+ fields = ("template", "with_context", "ignore_missing")
409
+ template: "Expr"
410
+ with_context: bool
411
+ ignore_missing: bool
412
+
413
+
414
+ class Import(Stmt):
415
+ """A node that represents the import tag."""
416
+
417
+ fields = ("template", "target", "with_context")
418
+ template: "Expr"
419
+ target: str
420
+ with_context: bool
421
+
422
+
423
+ class FromImport(Stmt):
424
+ """A node that represents the from import tag. It's important to not
425
+ pass unsafe names to the name attribute. The compiler translates the
426
+ attribute lookups directly into getattr calls and does *not* use the
427
+ subscript callback of the interface. As exported variables may not
428
+ start with double underscores (which the parser asserts) this is not a
429
+ problem for regular Jinja code, but if this node is used in an extension
430
+ extra care must be taken.
431
+
432
+ The list of names may contain tuples if aliases are wanted.
433
+ """
434
+
435
+ fields = ("template", "names", "with_context")
436
+ template: "Expr"
437
+ names: t.List[t.Union[str, t.Tuple[str, str]]]
438
+ with_context: bool
439
+
440
+
441
+ class ExprStmt(Stmt):
442
+ """A statement that evaluates an expression and discards the result."""
443
+
444
+ fields = ("node",)
445
+ node: Node
446
+
447
+
448
+ class Assign(Stmt):
449
+ """Assigns an expression to a target."""
450
+
451
+ fields = ("target", "node")
452
+ target: "Expr"
453
+ node: Node
454
+
455
+
456
+ class AssignBlock(Stmt):
457
+ """Assigns a block to a target."""
458
+
459
+ fields = ("target", "filter", "body")
460
+ target: "Expr"
461
+ filter: t.Optional["Filter"]
462
+ body: t.List[Node]
463
+
464
+
465
+ class Expr(Node):
466
+ """Baseclass for all expressions."""
467
+
468
+ abstract = True
469
+
470
+ def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> t.Any:
471
+ """Return the value of the expression as constant or raise
472
+ :exc:`Impossible` if this was not possible.
473
+
474
+ An :class:`EvalContext` can be provided, if none is given
475
+ a default context is created which requires the nodes to have
476
+ an attached environment.
477
+
478
+ .. versionchanged:: 2.4
479
+ the `eval_ctx` parameter was added.
480
+ """
481
+ raise Impossible()
482
+
483
+ def can_assign(self) -> bool:
484
+ """Check if it's possible to assign something to this node."""
485
+ return False
486
+
487
+
488
+ class BinExpr(Expr):
489
+ """Baseclass for all binary expressions."""
490
+
491
+ fields = ("left", "right")
492
+ left: Expr
493
+ right: Expr
494
+ operator: str
495
+ abstract = True
496
+
497
+ def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> t.Any:
498
+ eval_ctx = get_eval_context(self, eval_ctx)
499
+
500
+ # intercepted operators cannot be folded at compile time
501
+ if (
502
+ eval_ctx.environment.sandboxed
503
+ and self.operator in eval_ctx.environment.intercepted_binops # type: ignore
504
+ ):
505
+ raise Impossible()
506
+ f = _binop_to_func[self.operator]
507
+ try:
508
+ return f(self.left.as_const(eval_ctx), self.right.as_const(eval_ctx))
509
+ except Exception as e:
510
+ raise Impossible() from e
511
+
512
+
513
+ class UnaryExpr(Expr):
514
+ """Baseclass for all unary expressions."""
515
+
516
+ fields = ("node",)
517
+ node: Expr
518
+ operator: str
519
+ abstract = True
520
+
521
+ def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> t.Any:
522
+ eval_ctx = get_eval_context(self, eval_ctx)
523
+
524
+ # intercepted operators cannot be folded at compile time
525
+ if (
526
+ eval_ctx.environment.sandboxed
527
+ and self.operator in eval_ctx.environment.intercepted_unops # type: ignore
528
+ ):
529
+ raise Impossible()
530
+ f = _uaop_to_func[self.operator]
531
+ try:
532
+ return f(self.node.as_const(eval_ctx))
533
+ except Exception as e:
534
+ raise Impossible() from e
535
+
536
+
537
+ class Name(Expr):
538
+ """Looks up a name or stores a value in a name.
539
+ The `ctx` of the node can be one of the following values:
540
+
541
+ - `store`: store a value in the name
542
+ - `load`: load that name
543
+ - `param`: like `store` but if the name was defined as function parameter.
544
+ """
545
+
546
+ fields = ("name", "ctx")
547
+ name: str
548
+ ctx: str
549
+
550
+ def can_assign(self) -> bool:
551
+ return self.name not in {"true", "false", "none", "True", "False", "None"}
552
+
553
+
554
+ class NSRef(Expr):
555
+ """Reference to a namespace value assignment"""
556
+
557
+ fields = ("name", "attr")
558
+ name: str
559
+ attr: str
560
+
561
+ def can_assign(self) -> bool:
562
+ # We don't need any special checks here; NSRef assignments have a
563
+ # runtime check to ensure the target is a namespace object which will
564
+ # have been checked already as it is created using a normal assignment
565
+ # which goes through a `Name` node.
566
+ return True
567
+
568
+
569
+ class Literal(Expr):
570
+ """Baseclass for literals."""
571
+
572
+ abstract = True
573
+
574
+
575
+ class Const(Literal):
576
+ """All constant values. The parser will return this node for simple
577
+ constants such as ``42`` or ``"foo"`` but it can be used to store more
578
+ complex values such as lists too. Only constants with a safe
579
+ representation (objects where ``eval(repr(x)) == x`` is true).
580
+ """
581
+
582
+ fields = ("value",)
583
+ value: t.Any
584
+
585
+ def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> t.Any:
586
+ return self.value
587
+
588
+ @classmethod
589
+ def from_untrusted(
590
+ cls,
591
+ value: t.Any,
592
+ lineno: t.Optional[int] = None,
593
+ environment: "t.Optional[Environment]" = None,
594
+ ) -> "Const":
595
+ """Return a const object if the value is representable as
596
+ constant value in the generated code, otherwise it will raise
597
+ an `Impossible` exception.
598
+ """
599
+ from .compiler import has_safe_repr
600
+
601
+ if not has_safe_repr(value):
602
+ raise Impossible()
603
+ return cls(value, lineno=lineno, environment=environment)
604
+
605
+
606
+ class TemplateData(Literal):
607
+ """A constant template string."""
608
+
609
+ fields = ("data",)
610
+ data: str
611
+
612
+ def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> str:
613
+ eval_ctx = get_eval_context(self, eval_ctx)
614
+ if eval_ctx.volatile:
615
+ raise Impossible()
616
+ if eval_ctx.autoescape:
617
+ return Markup(self.data)
618
+ return self.data
619
+
620
+
621
+ class Tuple(Literal):
622
+ """For loop unpacking and some other things like multiple arguments
623
+ for subscripts. Like for :class:`Name` `ctx` specifies if the tuple
624
+ is used for loading the names or storing.
625
+ """
626
+
627
+ fields = ("items", "ctx")
628
+ items: t.List[Expr]
629
+ ctx: str
630
+
631
+ def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> t.Tuple[t.Any, ...]:
632
+ eval_ctx = get_eval_context(self, eval_ctx)
633
+ return tuple(x.as_const(eval_ctx) for x in self.items)
634
+
635
+ def can_assign(self) -> bool:
636
+ for item in self.items:
637
+ if not item.can_assign():
638
+ return False
639
+ return True
640
+
641
+
642
+ class List(Literal):
643
+ """Any list literal such as ``[1, 2, 3]``"""
644
+
645
+ fields = ("items",)
646
+ items: t.List[Expr]
647
+
648
+ def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> t.List[t.Any]:
649
+ eval_ctx = get_eval_context(self, eval_ctx)
650
+ return [x.as_const(eval_ctx) for x in self.items]
651
+
652
+
653
+ class Dict(Literal):
654
+ """Any dict literal such as ``{1: 2, 3: 4}``. The items must be a list of
655
+ :class:`Pair` nodes.
656
+ """
657
+
658
+ fields = ("items",)
659
+ items: t.List["Pair"]
660
+
661
+ def as_const(
662
+ self, eval_ctx: t.Optional[EvalContext] = None
663
+ ) -> t.Dict[t.Any, t.Any]:
664
+ eval_ctx = get_eval_context(self, eval_ctx)
665
+ return dict(x.as_const(eval_ctx) for x in self.items)
666
+
667
+
668
+ class Pair(Helper):
669
+ """A key, value pair for dicts."""
670
+
671
+ fields = ("key", "value")
672
+ key: Expr
673
+ value: Expr
674
+
675
+ def as_const(
676
+ self, eval_ctx: t.Optional[EvalContext] = None
677
+ ) -> t.Tuple[t.Any, t.Any]:
678
+ eval_ctx = get_eval_context(self, eval_ctx)
679
+ return self.key.as_const(eval_ctx), self.value.as_const(eval_ctx)
680
+
681
+
682
+ class Keyword(Helper):
683
+ """A key, value pair for keyword arguments where key is a string."""
684
+
685
+ fields = ("key", "value")
686
+ key: str
687
+ value: Expr
688
+
689
+ def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> t.Tuple[str, t.Any]:
690
+ eval_ctx = get_eval_context(self, eval_ctx)
691
+ return self.key, self.value.as_const(eval_ctx)
692
+
693
+
694
+ class CondExpr(Expr):
695
+ """A conditional expression (inline if expression). (``{{
696
+ foo if bar else baz }}``)
697
+ """
698
+
699
+ fields = ("test", "expr1", "expr2")
700
+ test: Expr
701
+ expr1: Expr
702
+ expr2: t.Optional[Expr]
703
+
704
+ def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> t.Any:
705
+ eval_ctx = get_eval_context(self, eval_ctx)
706
+ if self.test.as_const(eval_ctx):
707
+ return self.expr1.as_const(eval_ctx)
708
+
709
+ # if we evaluate to an undefined object, we better do that at runtime
710
+ if self.expr2 is None:
711
+ raise Impossible()
712
+
713
+ return self.expr2.as_const(eval_ctx)
714
+
715
+
716
+ def args_as_const(
717
+ node: t.Union["_FilterTestCommon", "Call"], eval_ctx: t.Optional[EvalContext]
718
+ ) -> t.Tuple[t.List[t.Any], t.Dict[t.Any, t.Any]]:
719
+ args = [x.as_const(eval_ctx) for x in node.args]
720
+ kwargs = dict(x.as_const(eval_ctx) for x in node.kwargs)
721
+
722
+ if node.dyn_args is not None:
723
+ try:
724
+ args.extend(node.dyn_args.as_const(eval_ctx))
725
+ except Exception as e:
726
+ raise Impossible() from e
727
+
728
+ if node.dyn_kwargs is not None:
729
+ try:
730
+ kwargs.update(node.dyn_kwargs.as_const(eval_ctx))
731
+ except Exception as e:
732
+ raise Impossible() from e
733
+
734
+ return args, kwargs
735
+
736
+
737
+ class _FilterTestCommon(Expr):
738
+ fields = ("node", "name", "args", "kwargs", "dyn_args", "dyn_kwargs")
739
+ node: Expr
740
+ name: str
741
+ args: t.List[Expr]
742
+ kwargs: t.List[Pair]
743
+ dyn_args: t.Optional[Expr]
744
+ dyn_kwargs: t.Optional[Expr]
745
+ abstract = True
746
+ _is_filter = True
747
+
748
+ def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> t.Any:
749
+ eval_ctx = get_eval_context(self, eval_ctx)
750
+
751
+ if eval_ctx.volatile:
752
+ raise Impossible()
753
+
754
+ if self._is_filter:
755
+ env_map = eval_ctx.environment.filters
756
+ else:
757
+ env_map = eval_ctx.environment.tests
758
+
759
+ func = env_map.get(self.name)
760
+ pass_arg = _PassArg.from_obj(func) # type: ignore
761
+
762
+ if func is None or pass_arg is _PassArg.context:
763
+ raise Impossible()
764
+
765
+ if eval_ctx.environment.is_async and (
766
+ getattr(func, "jinja_async_variant", False) is True
767
+ or inspect.iscoroutinefunction(func)
768
+ ):
769
+ raise Impossible()
770
+
771
+ args, kwargs = args_as_const(self, eval_ctx)
772
+ args.insert(0, self.node.as_const(eval_ctx))
773
+
774
+ if pass_arg is _PassArg.eval_context:
775
+ args.insert(0, eval_ctx)
776
+ elif pass_arg is _PassArg.environment:
777
+ args.insert(0, eval_ctx.environment)
778
+
779
+ try:
780
+ return func(*args, **kwargs)
781
+ except Exception as e:
782
+ raise Impossible() from e
783
+
784
+
785
+ class Filter(_FilterTestCommon):
786
+ """Apply a filter to an expression. ``name`` is the name of the
787
+ filter, the other fields are the same as :class:`Call`.
788
+
789
+ If ``node`` is ``None``, the filter is being used in a filter block
790
+ and is applied to the content of the block.
791
+ """
792
+
793
+ node: t.Optional[Expr] # type: ignore
794
+
795
+ def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> t.Any:
796
+ if self.node is None:
797
+ raise Impossible()
798
+
799
+ return super().as_const(eval_ctx=eval_ctx)
800
+
801
+
802
+ class Test(_FilterTestCommon):
803
+ """Apply a test to an expression. ``name`` is the name of the test,
804
+ the other field are the same as :class:`Call`.
805
+
806
+ .. versionchanged:: 3.0
807
+ ``as_const`` shares the same logic for filters and tests. Tests
808
+ check for volatile, async, and ``@pass_context`` etc.
809
+ decorators.
810
+ """
811
+
812
+ _is_filter = False
813
+
814
+
815
+ class Call(Expr):
816
+ """Calls an expression. `args` is a list of arguments, `kwargs` a list
817
+ of keyword arguments (list of :class:`Keyword` nodes), and `dyn_args`
818
+ and `dyn_kwargs` has to be either `None` or a node that is used as
819
+ node for dynamic positional (``*args``) or keyword (``**kwargs``)
820
+ arguments.
821
+ """
822
+
823
+ fields = ("node", "args", "kwargs", "dyn_args", "dyn_kwargs")
824
+ node: Expr
825
+ args: t.List[Expr]
826
+ kwargs: t.List[Keyword]
827
+ dyn_args: t.Optional[Expr]
828
+ dyn_kwargs: t.Optional[Expr]
829
+
830
+
831
+ class Getitem(Expr):
832
+ """Get an attribute or item from an expression and prefer the item."""
833
+
834
+ fields = ("node", "arg", "ctx")
835
+ node: Expr
836
+ arg: Expr
837
+ ctx: str
838
+
839
+ def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> t.Any:
840
+ if self.ctx != "load":
841
+ raise Impossible()
842
+
843
+ eval_ctx = get_eval_context(self, eval_ctx)
844
+
845
+ try:
846
+ return eval_ctx.environment.getitem(
847
+ self.node.as_const(eval_ctx), self.arg.as_const(eval_ctx)
848
+ )
849
+ except Exception as e:
850
+ raise Impossible() from e
851
+
852
+
853
+ class Getattr(Expr):
854
+ """Get an attribute or item from an expression that is a ascii-only
855
+ bytestring and prefer the attribute.
856
+ """
857
+
858
+ fields = ("node", "attr", "ctx")
859
+ node: Expr
860
+ attr: str
861
+ ctx: str
862
+
863
+ def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> t.Any:
864
+ if self.ctx != "load":
865
+ raise Impossible()
866
+
867
+ eval_ctx = get_eval_context(self, eval_ctx)
868
+
869
+ try:
870
+ return eval_ctx.environment.getattr(self.node.as_const(eval_ctx), self.attr)
871
+ except Exception as e:
872
+ raise Impossible() from e
873
+
874
+
875
+ class Slice(Expr):
876
+ """Represents a slice object. This must only be used as argument for
877
+ :class:`Subscript`.
878
+ """
879
+
880
+ fields = ("start", "stop", "step")
881
+ start: t.Optional[Expr]
882
+ stop: t.Optional[Expr]
883
+ step: t.Optional[Expr]
884
+
885
+ def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> slice:
886
+ eval_ctx = get_eval_context(self, eval_ctx)
887
+
888
+ def const(obj: t.Optional[Expr]) -> t.Optional[t.Any]:
889
+ if obj is None:
890
+ return None
891
+ return obj.as_const(eval_ctx)
892
+
893
+ return slice(const(self.start), const(self.stop), const(self.step))
894
+
895
+
896
+ class Concat(Expr):
897
+ """Concatenates the list of expressions provided after converting
898
+ them to strings.
899
+ """
900
+
901
+ fields = ("nodes",)
902
+ nodes: t.List[Expr]
903
+
904
+ def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> str:
905
+ eval_ctx = get_eval_context(self, eval_ctx)
906
+ return "".join(str(x.as_const(eval_ctx)) for x in self.nodes)
907
+
908
+
909
+ class Compare(Expr):
910
+ """Compares an expression with some other expressions. `ops` must be a
911
+ list of :class:`Operand`\\s.
912
+ """
913
+
914
+ fields = ("expr", "ops")
915
+ expr: Expr
916
+ ops: t.List["Operand"]
917
+
918
+ def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> t.Any:
919
+ eval_ctx = get_eval_context(self, eval_ctx)
920
+ result = value = self.expr.as_const(eval_ctx)
921
+
922
+ try:
923
+ for op in self.ops:
924
+ new_value = op.expr.as_const(eval_ctx)
925
+ result = _cmpop_to_func[op.op](value, new_value)
926
+
927
+ if not result:
928
+ return False
929
+
930
+ value = new_value
931
+ except Exception as e:
932
+ raise Impossible() from e
933
+
934
+ return result
935
+
936
+
937
+ class Operand(Helper):
938
+ """Holds an operator and an expression."""
939
+
940
+ fields = ("op", "expr")
941
+ op: str
942
+ expr: Expr
943
+
944
+
945
+ class Mul(BinExpr):
946
+ """Multiplies the left with the right node."""
947
+
948
+ operator = "*"
949
+
950
+
951
+ class Div(BinExpr):
952
+ """Divides the left by the right node."""
953
+
954
+ operator = "/"
955
+
956
+
957
+ class FloorDiv(BinExpr):
958
+ """Divides the left by the right node and converts the
959
+ result into an integer by truncating.
960
+ """
961
+
962
+ operator = "//"
963
+
964
+
965
+ class Add(BinExpr):
966
+ """Add the left to the right node."""
967
+
968
+ operator = "+"
969
+
970
+
971
+ class Sub(BinExpr):
972
+ """Subtract the right from the left node."""
973
+
974
+ operator = "-"
975
+
976
+
977
+ class Mod(BinExpr):
978
+ """Left modulo right."""
979
+
980
+ operator = "%"
981
+
982
+
983
+ class Pow(BinExpr):
984
+ """Left to the power of right."""
985
+
986
+ operator = "**"
987
+
988
+
989
+ class And(BinExpr):
990
+ """Short circuited AND."""
991
+
992
+ operator = "and"
993
+
994
+ def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> t.Any:
995
+ eval_ctx = get_eval_context(self, eval_ctx)
996
+ return self.left.as_const(eval_ctx) and self.right.as_const(eval_ctx)
997
+
998
+
999
+ class Or(BinExpr):
1000
+ """Short circuited OR."""
1001
+
1002
+ operator = "or"
1003
+
1004
+ def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> t.Any:
1005
+ eval_ctx = get_eval_context(self, eval_ctx)
1006
+ return self.left.as_const(eval_ctx) or self.right.as_const(eval_ctx)
1007
+
1008
+
1009
+ class Not(UnaryExpr):
1010
+ """Negate the expression."""
1011
+
1012
+ operator = "not"
1013
+
1014
+
1015
+ class Neg(UnaryExpr):
1016
+ """Make the expression negative."""
1017
+
1018
+ operator = "-"
1019
+
1020
+
1021
+ class Pos(UnaryExpr):
1022
+ """Make the expression positive (noop for most expressions)"""
1023
+
1024
+ operator = "+"
1025
+
1026
+
1027
+ # Helpers for extensions
1028
+
1029
+
1030
+ class EnvironmentAttribute(Expr):
1031
+ """Loads an attribute from the environment object. This is useful for
1032
+ extensions that want to call a callback stored on the environment.
1033
+ """
1034
+
1035
+ fields = ("name",)
1036
+ name: str
1037
+
1038
+
1039
+ class ExtensionAttribute(Expr):
1040
+ """Returns the attribute of an extension bound to the environment.
1041
+ The identifier is the identifier of the :class:`Extension`.
1042
+
1043
+ This node is usually constructed by calling the
1044
+ :meth:`~jinja2.ext.Extension.attr` method on an extension.
1045
+ """
1046
+
1047
+ fields = ("identifier", "name")
1048
+ identifier: str
1049
+ name: str
1050
+
1051
+
1052
+ class ImportedName(Expr):
1053
+ """If created with an import name the import name is returned on node
1054
+ access. For example ``ImportedName('cgi.escape')`` returns the `escape`
1055
+ function from the cgi module on evaluation. Imports are optimized by the
1056
+ compiler so there is no need to assign them to local variables.
1057
+ """
1058
+
1059
+ fields = ("importname",)
1060
+ importname: str
1061
+
1062
+
1063
+ class InternalName(Expr):
1064
+ """An internal name in the compiler. You cannot create these nodes
1065
+ yourself but the parser provides a
1066
+ :meth:`~jinja2.parser.Parser.free_identifier` method that creates
1067
+ a new identifier for you. This identifier is not available from the
1068
+ template and is not treated specially by the compiler.
1069
+ """
1070
+
1071
+ fields = ("name",)
1072
+ name: str
1073
+
1074
+ def __init__(self) -> None:
1075
+ raise TypeError(
1076
+ "Can't create internal names. Use the "
1077
+ "`free_identifier` method on a parser."
1078
+ )
1079
+
1080
+
1081
+ class MarkSafe(Expr):
1082
+ """Mark the wrapped expression as safe (wrap it as `Markup`)."""
1083
+
1084
+ fields = ("expr",)
1085
+ expr: Expr
1086
+
1087
+ def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> Markup:
1088
+ eval_ctx = get_eval_context(self, eval_ctx)
1089
+ return Markup(self.expr.as_const(eval_ctx))
1090
+
1091
+
1092
+ class MarkSafeIfAutoescape(Expr):
1093
+ """Mark the wrapped expression as safe (wrap it as `Markup`) but
1094
+ only if autoescaping is active.
1095
+
1096
+ .. versionadded:: 2.5
1097
+ """
1098
+
1099
+ fields = ("expr",)
1100
+ expr: Expr
1101
+
1102
+ def as_const(
1103
+ self, eval_ctx: t.Optional[EvalContext] = None
1104
+ ) -> t.Union[Markup, t.Any]:
1105
+ eval_ctx = get_eval_context(self, eval_ctx)
1106
+ if eval_ctx.volatile:
1107
+ raise Impossible()
1108
+ expr = self.expr.as_const(eval_ctx)
1109
+ if eval_ctx.autoescape:
1110
+ return Markup(expr)
1111
+ return expr
1112
+
1113
+
1114
+ class ContextReference(Expr):
1115
+ """Returns the current template context. It can be used like a
1116
+ :class:`Name` node, with a ``'load'`` ctx and will return the
1117
+ current :class:`~jinja2.runtime.Context` object.
1118
+
1119
+ Here an example that assigns the current template name to a
1120
+ variable named `foo`::
1121
+
1122
+ Assign(Name('foo', ctx='store'),
1123
+ Getattr(ContextReference(), 'name'))
1124
+
1125
+ This is basically equivalent to using the
1126
+ :func:`~jinja2.pass_context` decorator when using the high-level
1127
+ API, which causes a reference to the context to be passed as the
1128
+ first argument to a function.
1129
+ """
1130
+
1131
+
1132
+ class DerivedContextReference(Expr):
1133
+ """Return the current template context including locals. Behaves
1134
+ exactly like :class:`ContextReference`, but includes local
1135
+ variables, such as from a ``for`` loop.
1136
+
1137
+ .. versionadded:: 2.11
1138
+ """
1139
+
1140
+
1141
+ class Continue(Stmt):
1142
+ """Continue a loop."""
1143
+
1144
+
1145
+ class Break(Stmt):
1146
+ """Break a loop."""
1147
+
1148
+
1149
+ class Scope(Stmt):
1150
+ """An artificial scope."""
1151
+
1152
+ fields = ("body",)
1153
+ body: t.List[Node]
1154
+
1155
+
1156
+ class OverlayScope(Stmt):
1157
+ """An overlay scope for extensions. This is a largely unoptimized scope
1158
+ that however can be used to introduce completely arbitrary variables into
1159
+ a sub scope from a dictionary or dictionary like object. The `context`
1160
+ field has to evaluate to a dictionary object.
1161
+
1162
+ Example usage::
1163
+
1164
+ OverlayScope(context=self.call_method('get_context'),
1165
+ body=[...])
1166
+
1167
+ .. versionadded:: 2.10
1168
+ """
1169
+
1170
+ fields = ("context", "body")
1171
+ context: Expr
1172
+ body: t.List[Node]
1173
+
1174
+
1175
+ class EvalContextModifier(Stmt):
1176
+ """Modifies the eval context. For each option that should be modified,
1177
+ a :class:`Keyword` has to be added to the :attr:`options` list.
1178
+
1179
+ Example to change the `autoescape` setting::
1180
+
1181
+ EvalContextModifier(options=[Keyword('autoescape', Const(True))])
1182
+ """
1183
+
1184
+ fields = ("options",)
1185
+ options: t.List[Keyword]
1186
+
1187
+
1188
+ class ScopedEvalContextModifier(EvalContextModifier):
1189
+ """Modifies the eval context and reverts it later. Works exactly like
1190
+ :class:`EvalContextModifier` but will only modify the
1191
+ :class:`~jinja2.nodes.EvalContext` for nodes in the :attr:`body`.
1192
+ """
1193
+
1194
+ fields = ("body",)
1195
+ body: t.List[Node]
1196
+
1197
+
1198
+ # make sure nobody creates custom nodes
1199
+ def _failing_new(*args: t.Any, **kwargs: t.Any) -> "te.NoReturn":
1200
+ raise TypeError("can't create custom node types")
1201
+
1202
+
1203
+ NodeType.__new__ = staticmethod(_failing_new) # type: ignore
1204
+ del _failing_new
lib/python3.11/site-packages/jinja2/optimizer.py ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """The optimizer tries to constant fold expressions and modify the AST
2
+ in place so that it should be faster to evaluate.
3
+
4
+ Because the AST does not contain all the scoping information and the
5
+ compiler has to find that out, we cannot do all the optimizations we
6
+ want. For example, loop unrolling doesn't work because unrolled loops
7
+ would have a different scope. The solution would be a second syntax tree
8
+ that stored the scoping rules.
9
+ """
10
+ import typing as t
11
+
12
+ from . import nodes
13
+ from .visitor import NodeTransformer
14
+
15
+ if t.TYPE_CHECKING:
16
+ from .environment import Environment
17
+
18
+
19
+ def optimize(node: nodes.Node, environment: "Environment") -> nodes.Node:
20
+ """The context hint can be used to perform an static optimization
21
+ based on the context given."""
22
+ optimizer = Optimizer(environment)
23
+ return t.cast(nodes.Node, optimizer.visit(node))
24
+
25
+
26
+ class Optimizer(NodeTransformer):
27
+ def __init__(self, environment: "t.Optional[Environment]") -> None:
28
+ self.environment = environment
29
+
30
+ def generic_visit(
31
+ self, node: nodes.Node, *args: t.Any, **kwargs: t.Any
32
+ ) -> nodes.Node:
33
+ node = super().generic_visit(node, *args, **kwargs)
34
+
35
+ # Do constant folding. Some other nodes besides Expr have
36
+ # as_const, but folding them causes errors later on.
37
+ if isinstance(node, nodes.Expr):
38
+ try:
39
+ return nodes.Const.from_untrusted(
40
+ node.as_const(args[0] if args else None),
41
+ lineno=node.lineno,
42
+ environment=self.environment,
43
+ )
44
+ except nodes.Impossible:
45
+ pass
46
+
47
+ return node
lib/python3.11/site-packages/jinja2/parser.py ADDED
@@ -0,0 +1,1032 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Parse tokens from the lexer into nodes for the compiler."""
2
+ import typing
3
+ import typing as t
4
+
5
+ from . import nodes
6
+ from .exceptions import TemplateAssertionError
7
+ from .exceptions import TemplateSyntaxError
8
+ from .lexer import describe_token
9
+ from .lexer import describe_token_expr
10
+
11
+ if t.TYPE_CHECKING:
12
+ import typing_extensions as te
13
+ from .environment import Environment
14
+
15
+ _ImportInclude = t.TypeVar("_ImportInclude", nodes.Import, nodes.Include)
16
+ _MacroCall = t.TypeVar("_MacroCall", nodes.Macro, nodes.CallBlock)
17
+
18
+ _statement_keywords = frozenset(
19
+ [
20
+ "for",
21
+ "if",
22
+ "block",
23
+ "extends",
24
+ "print",
25
+ "macro",
26
+ "include",
27
+ "from",
28
+ "import",
29
+ "set",
30
+ "with",
31
+ "autoescape",
32
+ ]
33
+ )
34
+ _compare_operators = frozenset(["eq", "ne", "lt", "lteq", "gt", "gteq"])
35
+
36
+ _math_nodes: t.Dict[str, t.Type[nodes.Expr]] = {
37
+ "add": nodes.Add,
38
+ "sub": nodes.Sub,
39
+ "mul": nodes.Mul,
40
+ "div": nodes.Div,
41
+ "floordiv": nodes.FloorDiv,
42
+ "mod": nodes.Mod,
43
+ }
44
+
45
+
46
+ class Parser:
47
+ """This is the central parsing class Jinja uses. It's passed to
48
+ extensions and can be used to parse expressions or statements.
49
+ """
50
+
51
+ def __init__(
52
+ self,
53
+ environment: "Environment",
54
+ source: str,
55
+ name: t.Optional[str] = None,
56
+ filename: t.Optional[str] = None,
57
+ state: t.Optional[str] = None,
58
+ ) -> None:
59
+ self.environment = environment
60
+ self.stream = environment._tokenize(source, name, filename, state)
61
+ self.name = name
62
+ self.filename = filename
63
+ self.closed = False
64
+ self.extensions: t.Dict[
65
+ str, t.Callable[["Parser"], t.Union[nodes.Node, t.List[nodes.Node]]]
66
+ ] = {}
67
+ for extension in environment.iter_extensions():
68
+ for tag in extension.tags:
69
+ self.extensions[tag] = extension.parse
70
+ self._last_identifier = 0
71
+ self._tag_stack: t.List[str] = []
72
+ self._end_token_stack: t.List[t.Tuple[str, ...]] = []
73
+
74
+ def fail(
75
+ self,
76
+ msg: str,
77
+ lineno: t.Optional[int] = None,
78
+ exc: t.Type[TemplateSyntaxError] = TemplateSyntaxError,
79
+ ) -> "te.NoReturn":
80
+ """Convenience method that raises `exc` with the message, passed
81
+ line number or last line number as well as the current name and
82
+ filename.
83
+ """
84
+ if lineno is None:
85
+ lineno = self.stream.current.lineno
86
+ raise exc(msg, lineno, self.name, self.filename)
87
+
88
+ def _fail_ut_eof(
89
+ self,
90
+ name: t.Optional[str],
91
+ end_token_stack: t.List[t.Tuple[str, ...]],
92
+ lineno: t.Optional[int],
93
+ ) -> "te.NoReturn":
94
+ expected: t.Set[str] = set()
95
+ for exprs in end_token_stack:
96
+ expected.update(map(describe_token_expr, exprs))
97
+ if end_token_stack:
98
+ currently_looking: t.Optional[str] = " or ".join(
99
+ map(repr, map(describe_token_expr, end_token_stack[-1]))
100
+ )
101
+ else:
102
+ currently_looking = None
103
+
104
+ if name is None:
105
+ message = ["Unexpected end of template."]
106
+ else:
107
+ message = [f"Encountered unknown tag {name!r}."]
108
+
109
+ if currently_looking:
110
+ if name is not None and name in expected:
111
+ message.append(
112
+ "You probably made a nesting mistake. Jinja is expecting this tag,"
113
+ f" but currently looking for {currently_looking}."
114
+ )
115
+ else:
116
+ message.append(
117
+ f"Jinja was looking for the following tags: {currently_looking}."
118
+ )
119
+
120
+ if self._tag_stack:
121
+ message.append(
122
+ "The innermost block that needs to be closed is"
123
+ f" {self._tag_stack[-1]!r}."
124
+ )
125
+
126
+ self.fail(" ".join(message), lineno)
127
+
128
+ def fail_unknown_tag(
129
+ self, name: str, lineno: t.Optional[int] = None
130
+ ) -> "te.NoReturn":
131
+ """Called if the parser encounters an unknown tag. Tries to fail
132
+ with a human readable error message that could help to identify
133
+ the problem.
134
+ """
135
+ self._fail_ut_eof(name, self._end_token_stack, lineno)
136
+
137
+ def fail_eof(
138
+ self,
139
+ end_tokens: t.Optional[t.Tuple[str, ...]] = None,
140
+ lineno: t.Optional[int] = None,
141
+ ) -> "te.NoReturn":
142
+ """Like fail_unknown_tag but for end of template situations."""
143
+ stack = list(self._end_token_stack)
144
+ if end_tokens is not None:
145
+ stack.append(end_tokens)
146
+ self._fail_ut_eof(None, stack, lineno)
147
+
148
+ def is_tuple_end(
149
+ self, extra_end_rules: t.Optional[t.Tuple[str, ...]] = None
150
+ ) -> bool:
151
+ """Are we at the end of a tuple?"""
152
+ if self.stream.current.type in ("variable_end", "block_end", "rparen"):
153
+ return True
154
+ elif extra_end_rules is not None:
155
+ return self.stream.current.test_any(extra_end_rules) # type: ignore
156
+ return False
157
+
158
+ def free_identifier(self, lineno: t.Optional[int] = None) -> nodes.InternalName:
159
+ """Return a new free identifier as :class:`~jinja2.nodes.InternalName`."""
160
+ self._last_identifier += 1
161
+ rv = object.__new__(nodes.InternalName)
162
+ nodes.Node.__init__(rv, f"fi{self._last_identifier}", lineno=lineno)
163
+ return rv
164
+
165
+ def parse_statement(self) -> t.Union[nodes.Node, t.List[nodes.Node]]:
166
+ """Parse a single statement."""
167
+ token = self.stream.current
168
+ if token.type != "name":
169
+ self.fail("tag name expected", token.lineno)
170
+ self._tag_stack.append(token.value)
171
+ pop_tag = True
172
+ try:
173
+ if token.value in _statement_keywords:
174
+ f = getattr(self, f"parse_{self.stream.current.value}")
175
+ return f() # type: ignore
176
+ if token.value == "call":
177
+ return self.parse_call_block()
178
+ if token.value == "filter":
179
+ return self.parse_filter_block()
180
+ ext = self.extensions.get(token.value)
181
+ if ext is not None:
182
+ return ext(self)
183
+
184
+ # did not work out, remove the token we pushed by accident
185
+ # from the stack so that the unknown tag fail function can
186
+ # produce a proper error message.
187
+ self._tag_stack.pop()
188
+ pop_tag = False
189
+ self.fail_unknown_tag(token.value, token.lineno)
190
+ finally:
191
+ if pop_tag:
192
+ self._tag_stack.pop()
193
+
194
+ def parse_statements(
195
+ self, end_tokens: t.Tuple[str, ...], drop_needle: bool = False
196
+ ) -> t.List[nodes.Node]:
197
+ """Parse multiple statements into a list until one of the end tokens
198
+ is reached. This is used to parse the body of statements as it also
199
+ parses template data if appropriate. The parser checks first if the
200
+ current token is a colon and skips it if there is one. Then it checks
201
+ for the block end and parses until if one of the `end_tokens` is
202
+ reached. Per default the active token in the stream at the end of
203
+ the call is the matched end token. If this is not wanted `drop_needle`
204
+ can be set to `True` and the end token is removed.
205
+ """
206
+ # the first token may be a colon for python compatibility
207
+ self.stream.skip_if("colon")
208
+
209
+ # in the future it would be possible to add whole code sections
210
+ # by adding some sort of end of statement token and parsing those here.
211
+ self.stream.expect("block_end")
212
+ result = self.subparse(end_tokens)
213
+
214
+ # we reached the end of the template too early, the subparser
215
+ # does not check for this, so we do that now
216
+ if self.stream.current.type == "eof":
217
+ self.fail_eof(end_tokens)
218
+
219
+ if drop_needle:
220
+ next(self.stream)
221
+ return result
222
+
223
+ def parse_set(self) -> t.Union[nodes.Assign, nodes.AssignBlock]:
224
+ """Parse an assign statement."""
225
+ lineno = next(self.stream).lineno
226
+ target = self.parse_assign_target(with_namespace=True)
227
+ if self.stream.skip_if("assign"):
228
+ expr = self.parse_tuple()
229
+ return nodes.Assign(target, expr, lineno=lineno)
230
+ filter_node = self.parse_filter(None)
231
+ body = self.parse_statements(("name:endset",), drop_needle=True)
232
+ return nodes.AssignBlock(target, filter_node, body, lineno=lineno)
233
+
234
+ def parse_for(self) -> nodes.For:
235
+ """Parse a for loop."""
236
+ lineno = self.stream.expect("name:for").lineno
237
+ target = self.parse_assign_target(extra_end_rules=("name:in",))
238
+ self.stream.expect("name:in")
239
+ iter = self.parse_tuple(
240
+ with_condexpr=False, extra_end_rules=("name:recursive",)
241
+ )
242
+ test = None
243
+ if self.stream.skip_if("name:if"):
244
+ test = self.parse_expression()
245
+ recursive = self.stream.skip_if("name:recursive")
246
+ body = self.parse_statements(("name:endfor", "name:else"))
247
+ if next(self.stream).value == "endfor":
248
+ else_ = []
249
+ else:
250
+ else_ = self.parse_statements(("name:endfor",), drop_needle=True)
251
+ return nodes.For(target, iter, body, else_, test, recursive, lineno=lineno)
252
+
253
+ def parse_if(self) -> nodes.If:
254
+ """Parse an if construct."""
255
+ node = result = nodes.If(lineno=self.stream.expect("name:if").lineno)
256
+ while True:
257
+ node.test = self.parse_tuple(with_condexpr=False)
258
+ node.body = self.parse_statements(("name:elif", "name:else", "name:endif"))
259
+ node.elif_ = []
260
+ node.else_ = []
261
+ token = next(self.stream)
262
+ if token.test("name:elif"):
263
+ node = nodes.If(lineno=self.stream.current.lineno)
264
+ result.elif_.append(node)
265
+ continue
266
+ elif token.test("name:else"):
267
+ result.else_ = self.parse_statements(("name:endif",), drop_needle=True)
268
+ break
269
+ return result
270
+
271
+ def parse_with(self) -> nodes.With:
272
+ node = nodes.With(lineno=next(self.stream).lineno)
273
+ targets: t.List[nodes.Expr] = []
274
+ values: t.List[nodes.Expr] = []
275
+ while self.stream.current.type != "block_end":
276
+ if targets:
277
+ self.stream.expect("comma")
278
+ target = self.parse_assign_target()
279
+ target.set_ctx("param")
280
+ targets.append(target)
281
+ self.stream.expect("assign")
282
+ values.append(self.parse_expression())
283
+ node.targets = targets
284
+ node.values = values
285
+ node.body = self.parse_statements(("name:endwith",), drop_needle=True)
286
+ return node
287
+
288
+ def parse_autoescape(self) -> nodes.Scope:
289
+ node = nodes.ScopedEvalContextModifier(lineno=next(self.stream).lineno)
290
+ node.options = [nodes.Keyword("autoescape", self.parse_expression())]
291
+ node.body = self.parse_statements(("name:endautoescape",), drop_needle=True)
292
+ return nodes.Scope([node])
293
+
294
+ def parse_block(self) -> nodes.Block:
295
+ node = nodes.Block(lineno=next(self.stream).lineno)
296
+ node.name = self.stream.expect("name").value
297
+ node.scoped = self.stream.skip_if("name:scoped")
298
+ node.required = self.stream.skip_if("name:required")
299
+
300
+ # common problem people encounter when switching from django
301
+ # to jinja. we do not support hyphens in block names, so let's
302
+ # raise a nicer error message in that case.
303
+ if self.stream.current.type == "sub":
304
+ self.fail(
305
+ "Block names in Jinja have to be valid Python identifiers and may not"
306
+ " contain hyphens, use an underscore instead."
307
+ )
308
+
309
+ node.body = self.parse_statements(("name:endblock",), drop_needle=True)
310
+
311
+ # enforce that required blocks only contain whitespace or comments
312
+ # by asserting that the body, if not empty, is just TemplateData nodes
313
+ # with whitespace data
314
+ if node.required and not all(
315
+ isinstance(child, nodes.TemplateData) and child.data.isspace()
316
+ for body in node.body
317
+ for child in body.nodes # type: ignore
318
+ ):
319
+ self.fail("Required blocks can only contain comments or whitespace")
320
+
321
+ self.stream.skip_if("name:" + node.name)
322
+ return node
323
+
324
+ def parse_extends(self) -> nodes.Extends:
325
+ node = nodes.Extends(lineno=next(self.stream).lineno)
326
+ node.template = self.parse_expression()
327
+ return node
328
+
329
+ def parse_import_context(
330
+ self, node: _ImportInclude, default: bool
331
+ ) -> _ImportInclude:
332
+ if self.stream.current.test_any(
333
+ "name:with", "name:without"
334
+ ) and self.stream.look().test("name:context"):
335
+ node.with_context = next(self.stream).value == "with"
336
+ self.stream.skip()
337
+ else:
338
+ node.with_context = default
339
+ return node
340
+
341
+ def parse_include(self) -> nodes.Include:
342
+ node = nodes.Include(lineno=next(self.stream).lineno)
343
+ node.template = self.parse_expression()
344
+ if self.stream.current.test("name:ignore") and self.stream.look().test(
345
+ "name:missing"
346
+ ):
347
+ node.ignore_missing = True
348
+ self.stream.skip(2)
349
+ else:
350
+ node.ignore_missing = False
351
+ return self.parse_import_context(node, True)
352
+
353
+ def parse_import(self) -> nodes.Import:
354
+ node = nodes.Import(lineno=next(self.stream).lineno)
355
+ node.template = self.parse_expression()
356
+ self.stream.expect("name:as")
357
+ node.target = self.parse_assign_target(name_only=True).name
358
+ return self.parse_import_context(node, False)
359
+
360
+ def parse_from(self) -> nodes.FromImport:
361
+ node = nodes.FromImport(lineno=next(self.stream).lineno)
362
+ node.template = self.parse_expression()
363
+ self.stream.expect("name:import")
364
+ node.names = []
365
+
366
+ def parse_context() -> bool:
367
+ if self.stream.current.value in {
368
+ "with",
369
+ "without",
370
+ } and self.stream.look().test("name:context"):
371
+ node.with_context = next(self.stream).value == "with"
372
+ self.stream.skip()
373
+ return True
374
+ return False
375
+
376
+ while True:
377
+ if node.names:
378
+ self.stream.expect("comma")
379
+ if self.stream.current.type == "name":
380
+ if parse_context():
381
+ break
382
+ target = self.parse_assign_target(name_only=True)
383
+ if target.name.startswith("_"):
384
+ self.fail(
385
+ "names starting with an underline can not be imported",
386
+ target.lineno,
387
+ exc=TemplateAssertionError,
388
+ )
389
+ if self.stream.skip_if("name:as"):
390
+ alias = self.parse_assign_target(name_only=True)
391
+ node.names.append((target.name, alias.name))
392
+ else:
393
+ node.names.append(target.name)
394
+ if parse_context() or self.stream.current.type != "comma":
395
+ break
396
+ else:
397
+ self.stream.expect("name")
398
+ if not hasattr(node, "with_context"):
399
+ node.with_context = False
400
+ return node
401
+
402
+ def parse_signature(self, node: _MacroCall) -> None:
403
+ args = node.args = []
404
+ defaults = node.defaults = []
405
+ self.stream.expect("lparen")
406
+ while self.stream.current.type != "rparen":
407
+ if args:
408
+ self.stream.expect("comma")
409
+ arg = self.parse_assign_target(name_only=True)
410
+ arg.set_ctx("param")
411
+ if self.stream.skip_if("assign"):
412
+ defaults.append(self.parse_expression())
413
+ elif defaults:
414
+ self.fail("non-default argument follows default argument")
415
+ args.append(arg)
416
+ self.stream.expect("rparen")
417
+
418
+ def parse_call_block(self) -> nodes.CallBlock:
419
+ node = nodes.CallBlock(lineno=next(self.stream).lineno)
420
+ if self.stream.current.type == "lparen":
421
+ self.parse_signature(node)
422
+ else:
423
+ node.args = []
424
+ node.defaults = []
425
+
426
+ call_node = self.parse_expression()
427
+ if not isinstance(call_node, nodes.Call):
428
+ self.fail("expected call", node.lineno)
429
+ node.call = call_node
430
+ node.body = self.parse_statements(("name:endcall",), drop_needle=True)
431
+ return node
432
+
433
+ def parse_filter_block(self) -> nodes.FilterBlock:
434
+ node = nodes.FilterBlock(lineno=next(self.stream).lineno)
435
+ node.filter = self.parse_filter(None, start_inline=True) # type: ignore
436
+ node.body = self.parse_statements(("name:endfilter",), drop_needle=True)
437
+ return node
438
+
439
+ def parse_macro(self) -> nodes.Macro:
440
+ node = nodes.Macro(lineno=next(self.stream).lineno)
441
+ node.name = self.parse_assign_target(name_only=True).name
442
+ self.parse_signature(node)
443
+ node.body = self.parse_statements(("name:endmacro",), drop_needle=True)
444
+ return node
445
+
446
+ def parse_print(self) -> nodes.Output:
447
+ node = nodes.Output(lineno=next(self.stream).lineno)
448
+ node.nodes = []
449
+ while self.stream.current.type != "block_end":
450
+ if node.nodes:
451
+ self.stream.expect("comma")
452
+ node.nodes.append(self.parse_expression())
453
+ return node
454
+
455
+ @typing.overload
456
+ def parse_assign_target(
457
+ self, with_tuple: bool = ..., name_only: "te.Literal[True]" = ...
458
+ ) -> nodes.Name:
459
+ ...
460
+
461
+ @typing.overload
462
+ def parse_assign_target(
463
+ self,
464
+ with_tuple: bool = True,
465
+ name_only: bool = False,
466
+ extra_end_rules: t.Optional[t.Tuple[str, ...]] = None,
467
+ with_namespace: bool = False,
468
+ ) -> t.Union[nodes.NSRef, nodes.Name, nodes.Tuple]:
469
+ ...
470
+
471
+ def parse_assign_target(
472
+ self,
473
+ with_tuple: bool = True,
474
+ name_only: bool = False,
475
+ extra_end_rules: t.Optional[t.Tuple[str, ...]] = None,
476
+ with_namespace: bool = False,
477
+ ) -> t.Union[nodes.NSRef, nodes.Name, nodes.Tuple]:
478
+ """Parse an assignment target. As Jinja allows assignments to
479
+ tuples, this function can parse all allowed assignment targets. Per
480
+ default assignments to tuples are parsed, that can be disable however
481
+ by setting `with_tuple` to `False`. If only assignments to names are
482
+ wanted `name_only` can be set to `True`. The `extra_end_rules`
483
+ parameter is forwarded to the tuple parsing function. If
484
+ `with_namespace` is enabled, a namespace assignment may be parsed.
485
+ """
486
+ target: nodes.Expr
487
+
488
+ if with_namespace and self.stream.look().type == "dot":
489
+ token = self.stream.expect("name")
490
+ next(self.stream) # dot
491
+ attr = self.stream.expect("name")
492
+ target = nodes.NSRef(token.value, attr.value, lineno=token.lineno)
493
+ elif name_only:
494
+ token = self.stream.expect("name")
495
+ target = nodes.Name(token.value, "store", lineno=token.lineno)
496
+ else:
497
+ if with_tuple:
498
+ target = self.parse_tuple(
499
+ simplified=True, extra_end_rules=extra_end_rules
500
+ )
501
+ else:
502
+ target = self.parse_primary()
503
+
504
+ target.set_ctx("store")
505
+
506
+ if not target.can_assign():
507
+ self.fail(
508
+ f"can't assign to {type(target).__name__.lower()!r}", target.lineno
509
+ )
510
+
511
+ return target # type: ignore
512
+
513
+ def parse_expression(self, with_condexpr: bool = True) -> nodes.Expr:
514
+ """Parse an expression. Per default all expressions are parsed, if
515
+ the optional `with_condexpr` parameter is set to `False` conditional
516
+ expressions are not parsed.
517
+ """
518
+ if with_condexpr:
519
+ return self.parse_condexpr()
520
+ return self.parse_or()
521
+
522
+ def parse_condexpr(self) -> nodes.Expr:
523
+ lineno = self.stream.current.lineno
524
+ expr1 = self.parse_or()
525
+ expr3: t.Optional[nodes.Expr]
526
+
527
+ while self.stream.skip_if("name:if"):
528
+ expr2 = self.parse_or()
529
+ if self.stream.skip_if("name:else"):
530
+ expr3 = self.parse_condexpr()
531
+ else:
532
+ expr3 = None
533
+ expr1 = nodes.CondExpr(expr2, expr1, expr3, lineno=lineno)
534
+ lineno = self.stream.current.lineno
535
+ return expr1
536
+
537
+ def parse_or(self) -> nodes.Expr:
538
+ lineno = self.stream.current.lineno
539
+ left = self.parse_and()
540
+ while self.stream.skip_if("name:or"):
541
+ right = self.parse_and()
542
+ left = nodes.Or(left, right, lineno=lineno)
543
+ lineno = self.stream.current.lineno
544
+ return left
545
+
546
+ def parse_and(self) -> nodes.Expr:
547
+ lineno = self.stream.current.lineno
548
+ left = self.parse_not()
549
+ while self.stream.skip_if("name:and"):
550
+ right = self.parse_not()
551
+ left = nodes.And(left, right, lineno=lineno)
552
+ lineno = self.stream.current.lineno
553
+ return left
554
+
555
+ def parse_not(self) -> nodes.Expr:
556
+ if self.stream.current.test("name:not"):
557
+ lineno = next(self.stream).lineno
558
+ return nodes.Not(self.parse_not(), lineno=lineno)
559
+ return self.parse_compare()
560
+
561
+ def parse_compare(self) -> nodes.Expr:
562
+ lineno = self.stream.current.lineno
563
+ expr = self.parse_math1()
564
+ ops = []
565
+ while True:
566
+ token_type = self.stream.current.type
567
+ if token_type in _compare_operators:
568
+ next(self.stream)
569
+ ops.append(nodes.Operand(token_type, self.parse_math1()))
570
+ elif self.stream.skip_if("name:in"):
571
+ ops.append(nodes.Operand("in", self.parse_math1()))
572
+ elif self.stream.current.test("name:not") and self.stream.look().test(
573
+ "name:in"
574
+ ):
575
+ self.stream.skip(2)
576
+ ops.append(nodes.Operand("notin", self.parse_math1()))
577
+ else:
578
+ break
579
+ lineno = self.stream.current.lineno
580
+ if not ops:
581
+ return expr
582
+ return nodes.Compare(expr, ops, lineno=lineno)
583
+
584
+ def parse_math1(self) -> nodes.Expr:
585
+ lineno = self.stream.current.lineno
586
+ left = self.parse_concat()
587
+ while self.stream.current.type in ("add", "sub"):
588
+ cls = _math_nodes[self.stream.current.type]
589
+ next(self.stream)
590
+ right = self.parse_concat()
591
+ left = cls(left, right, lineno=lineno)
592
+ lineno = self.stream.current.lineno
593
+ return left
594
+
595
+ def parse_concat(self) -> nodes.Expr:
596
+ lineno = self.stream.current.lineno
597
+ args = [self.parse_math2()]
598
+ while self.stream.current.type == "tilde":
599
+ next(self.stream)
600
+ args.append(self.parse_math2())
601
+ if len(args) == 1:
602
+ return args[0]
603
+ return nodes.Concat(args, lineno=lineno)
604
+
605
+ def parse_math2(self) -> nodes.Expr:
606
+ lineno = self.stream.current.lineno
607
+ left = self.parse_pow()
608
+ while self.stream.current.type in ("mul", "div", "floordiv", "mod"):
609
+ cls = _math_nodes[self.stream.current.type]
610
+ next(self.stream)
611
+ right = self.parse_pow()
612
+ left = cls(left, right, lineno=lineno)
613
+ lineno = self.stream.current.lineno
614
+ return left
615
+
616
+ def parse_pow(self) -> nodes.Expr:
617
+ lineno = self.stream.current.lineno
618
+ left = self.parse_unary()
619
+ while self.stream.current.type == "pow":
620
+ next(self.stream)
621
+ right = self.parse_unary()
622
+ left = nodes.Pow(left, right, lineno=lineno)
623
+ lineno = self.stream.current.lineno
624
+ return left
625
+
626
+ def parse_unary(self, with_filter: bool = True) -> nodes.Expr:
627
+ token_type = self.stream.current.type
628
+ lineno = self.stream.current.lineno
629
+ node: nodes.Expr
630
+
631
+ if token_type == "sub":
632
+ next(self.stream)
633
+ node = nodes.Neg(self.parse_unary(False), lineno=lineno)
634
+ elif token_type == "add":
635
+ next(self.stream)
636
+ node = nodes.Pos(self.parse_unary(False), lineno=lineno)
637
+ else:
638
+ node = self.parse_primary()
639
+ node = self.parse_postfix(node)
640
+ if with_filter:
641
+ node = self.parse_filter_expr(node)
642
+ return node
643
+
644
+ def parse_primary(self) -> nodes.Expr:
645
+ token = self.stream.current
646
+ node: nodes.Expr
647
+ if token.type == "name":
648
+ if token.value in ("true", "false", "True", "False"):
649
+ node = nodes.Const(token.value in ("true", "True"), lineno=token.lineno)
650
+ elif token.value in ("none", "None"):
651
+ node = nodes.Const(None, lineno=token.lineno)
652
+ else:
653
+ node = nodes.Name(token.value, "load", lineno=token.lineno)
654
+ next(self.stream)
655
+ elif token.type == "string":
656
+ next(self.stream)
657
+ buf = [token.value]
658
+ lineno = token.lineno
659
+ while self.stream.current.type == "string":
660
+ buf.append(self.stream.current.value)
661
+ next(self.stream)
662
+ node = nodes.Const("".join(buf), lineno=lineno)
663
+ elif token.type in ("integer", "float"):
664
+ next(self.stream)
665
+ node = nodes.Const(token.value, lineno=token.lineno)
666
+ elif token.type == "lparen":
667
+ next(self.stream)
668
+ node = self.parse_tuple(explicit_parentheses=True)
669
+ self.stream.expect("rparen")
670
+ elif token.type == "lbracket":
671
+ node = self.parse_list()
672
+ elif token.type == "lbrace":
673
+ node = self.parse_dict()
674
+ else:
675
+ self.fail(f"unexpected {describe_token(token)!r}", token.lineno)
676
+ return node
677
+
678
+ def parse_tuple(
679
+ self,
680
+ simplified: bool = False,
681
+ with_condexpr: bool = True,
682
+ extra_end_rules: t.Optional[t.Tuple[str, ...]] = None,
683
+ explicit_parentheses: bool = False,
684
+ ) -> t.Union[nodes.Tuple, nodes.Expr]:
685
+ """Works like `parse_expression` but if multiple expressions are
686
+ delimited by a comma a :class:`~jinja2.nodes.Tuple` node is created.
687
+ This method could also return a regular expression instead of a tuple
688
+ if no commas where found.
689
+
690
+ The default parsing mode is a full tuple. If `simplified` is `True`
691
+ only names and literals are parsed. The `no_condexpr` parameter is
692
+ forwarded to :meth:`parse_expression`.
693
+
694
+ Because tuples do not require delimiters and may end in a bogus comma
695
+ an extra hint is needed that marks the end of a tuple. For example
696
+ for loops support tuples between `for` and `in`. In that case the
697
+ `extra_end_rules` is set to ``['name:in']``.
698
+
699
+ `explicit_parentheses` is true if the parsing was triggered by an
700
+ expression in parentheses. This is used to figure out if an empty
701
+ tuple is a valid expression or not.
702
+ """
703
+ lineno = self.stream.current.lineno
704
+ if simplified:
705
+ parse = self.parse_primary
706
+ elif with_condexpr:
707
+ parse = self.parse_expression
708
+ else:
709
+
710
+ def parse() -> nodes.Expr:
711
+ return self.parse_expression(with_condexpr=False)
712
+
713
+ args: t.List[nodes.Expr] = []
714
+ is_tuple = False
715
+
716
+ while True:
717
+ if args:
718
+ self.stream.expect("comma")
719
+ if self.is_tuple_end(extra_end_rules):
720
+ break
721
+ args.append(parse())
722
+ if self.stream.current.type == "comma":
723
+ is_tuple = True
724
+ else:
725
+ break
726
+ lineno = self.stream.current.lineno
727
+
728
+ if not is_tuple:
729
+ if args:
730
+ return args[0]
731
+
732
+ # if we don't have explicit parentheses, an empty tuple is
733
+ # not a valid expression. This would mean nothing (literally
734
+ # nothing) in the spot of an expression would be an empty
735
+ # tuple.
736
+ if not explicit_parentheses:
737
+ self.fail(
738
+ "Expected an expression,"
739
+ f" got {describe_token(self.stream.current)!r}"
740
+ )
741
+
742
+ return nodes.Tuple(args, "load", lineno=lineno)
743
+
744
+ def parse_list(self) -> nodes.List:
745
+ token = self.stream.expect("lbracket")
746
+ items: t.List[nodes.Expr] = []
747
+ while self.stream.current.type != "rbracket":
748
+ if items:
749
+ self.stream.expect("comma")
750
+ if self.stream.current.type == "rbracket":
751
+ break
752
+ items.append(self.parse_expression())
753
+ self.stream.expect("rbracket")
754
+ return nodes.List(items, lineno=token.lineno)
755
+
756
+ def parse_dict(self) -> nodes.Dict:
757
+ token = self.stream.expect("lbrace")
758
+ items: t.List[nodes.Pair] = []
759
+ while self.stream.current.type != "rbrace":
760
+ if items:
761
+ self.stream.expect("comma")
762
+ if self.stream.current.type == "rbrace":
763
+ break
764
+ key = self.parse_expression()
765
+ self.stream.expect("colon")
766
+ value = self.parse_expression()
767
+ items.append(nodes.Pair(key, value, lineno=key.lineno))
768
+ self.stream.expect("rbrace")
769
+ return nodes.Dict(items, lineno=token.lineno)
770
+
771
+ def parse_postfix(self, node: nodes.Expr) -> nodes.Expr:
772
+ while True:
773
+ token_type = self.stream.current.type
774
+ if token_type == "dot" or token_type == "lbracket":
775
+ node = self.parse_subscript(node)
776
+ # calls are valid both after postfix expressions (getattr
777
+ # and getitem) as well as filters and tests
778
+ elif token_type == "lparen":
779
+ node = self.parse_call(node)
780
+ else:
781
+ break
782
+ return node
783
+
784
+ def parse_filter_expr(self, node: nodes.Expr) -> nodes.Expr:
785
+ while True:
786
+ token_type = self.stream.current.type
787
+ if token_type == "pipe":
788
+ node = self.parse_filter(node) # type: ignore
789
+ elif token_type == "name" and self.stream.current.value == "is":
790
+ node = self.parse_test(node)
791
+ # calls are valid both after postfix expressions (getattr
792
+ # and getitem) as well as filters and tests
793
+ elif token_type == "lparen":
794
+ node = self.parse_call(node)
795
+ else:
796
+ break
797
+ return node
798
+
799
+ def parse_subscript(
800
+ self, node: nodes.Expr
801
+ ) -> t.Union[nodes.Getattr, nodes.Getitem]:
802
+ token = next(self.stream)
803
+ arg: nodes.Expr
804
+
805
+ if token.type == "dot":
806
+ attr_token = self.stream.current
807
+ next(self.stream)
808
+ if attr_token.type == "name":
809
+ return nodes.Getattr(
810
+ node, attr_token.value, "load", lineno=token.lineno
811
+ )
812
+ elif attr_token.type != "integer":
813
+ self.fail("expected name or number", attr_token.lineno)
814
+ arg = nodes.Const(attr_token.value, lineno=attr_token.lineno)
815
+ return nodes.Getitem(node, arg, "load", lineno=token.lineno)
816
+ if token.type == "lbracket":
817
+ args: t.List[nodes.Expr] = []
818
+ while self.stream.current.type != "rbracket":
819
+ if args:
820
+ self.stream.expect("comma")
821
+ args.append(self.parse_subscribed())
822
+ self.stream.expect("rbracket")
823
+ if len(args) == 1:
824
+ arg = args[0]
825
+ else:
826
+ arg = nodes.Tuple(args, "load", lineno=token.lineno)
827
+ return nodes.Getitem(node, arg, "load", lineno=token.lineno)
828
+ self.fail("expected subscript expression", token.lineno)
829
+
830
+ def parse_subscribed(self) -> nodes.Expr:
831
+ lineno = self.stream.current.lineno
832
+ args: t.List[t.Optional[nodes.Expr]]
833
+
834
+ if self.stream.current.type == "colon":
835
+ next(self.stream)
836
+ args = [None]
837
+ else:
838
+ node = self.parse_expression()
839
+ if self.stream.current.type != "colon":
840
+ return node
841
+ next(self.stream)
842
+ args = [node]
843
+
844
+ if self.stream.current.type == "colon":
845
+ args.append(None)
846
+ elif self.stream.current.type not in ("rbracket", "comma"):
847
+ args.append(self.parse_expression())
848
+ else:
849
+ args.append(None)
850
+
851
+ if self.stream.current.type == "colon":
852
+ next(self.stream)
853
+ if self.stream.current.type not in ("rbracket", "comma"):
854
+ args.append(self.parse_expression())
855
+ else:
856
+ args.append(None)
857
+ else:
858
+ args.append(None)
859
+
860
+ return nodes.Slice(lineno=lineno, *args)
861
+
862
+ def parse_call_args(self) -> t.Tuple:
863
+ token = self.stream.expect("lparen")
864
+ args = []
865
+ kwargs = []
866
+ dyn_args = None
867
+ dyn_kwargs = None
868
+ require_comma = False
869
+
870
+ def ensure(expr: bool) -> None:
871
+ if not expr:
872
+ self.fail("invalid syntax for function call expression", token.lineno)
873
+
874
+ while self.stream.current.type != "rparen":
875
+ if require_comma:
876
+ self.stream.expect("comma")
877
+
878
+ # support for trailing comma
879
+ if self.stream.current.type == "rparen":
880
+ break
881
+
882
+ if self.stream.current.type == "mul":
883
+ ensure(dyn_args is None and dyn_kwargs is None)
884
+ next(self.stream)
885
+ dyn_args = self.parse_expression()
886
+ elif self.stream.current.type == "pow":
887
+ ensure(dyn_kwargs is None)
888
+ next(self.stream)
889
+ dyn_kwargs = self.parse_expression()
890
+ else:
891
+ if (
892
+ self.stream.current.type == "name"
893
+ and self.stream.look().type == "assign"
894
+ ):
895
+ # Parsing a kwarg
896
+ ensure(dyn_kwargs is None)
897
+ key = self.stream.current.value
898
+ self.stream.skip(2)
899
+ value = self.parse_expression()
900
+ kwargs.append(nodes.Keyword(key, value, lineno=value.lineno))
901
+ else:
902
+ # Parsing an arg
903
+ ensure(dyn_args is None and dyn_kwargs is None and not kwargs)
904
+ args.append(self.parse_expression())
905
+
906
+ require_comma = True
907
+
908
+ self.stream.expect("rparen")
909
+ return args, kwargs, dyn_args, dyn_kwargs
910
+
911
+ def parse_call(self, node: nodes.Expr) -> nodes.Call:
912
+ # The lparen will be expected in parse_call_args, but the lineno
913
+ # needs to be recorded before the stream is advanced.
914
+ token = self.stream.current
915
+ args, kwargs, dyn_args, dyn_kwargs = self.parse_call_args()
916
+ return nodes.Call(node, args, kwargs, dyn_args, dyn_kwargs, lineno=token.lineno)
917
+
918
+ def parse_filter(
919
+ self, node: t.Optional[nodes.Expr], start_inline: bool = False
920
+ ) -> t.Optional[nodes.Expr]:
921
+ while self.stream.current.type == "pipe" or start_inline:
922
+ if not start_inline:
923
+ next(self.stream)
924
+ token = self.stream.expect("name")
925
+ name = token.value
926
+ while self.stream.current.type == "dot":
927
+ next(self.stream)
928
+ name += "." + self.stream.expect("name").value
929
+ if self.stream.current.type == "lparen":
930
+ args, kwargs, dyn_args, dyn_kwargs = self.parse_call_args()
931
+ else:
932
+ args = []
933
+ kwargs = []
934
+ dyn_args = dyn_kwargs = None
935
+ node = nodes.Filter(
936
+ node, name, args, kwargs, dyn_args, dyn_kwargs, lineno=token.lineno
937
+ )
938
+ start_inline = False
939
+ return node
940
+
941
+ def parse_test(self, node: nodes.Expr) -> nodes.Expr:
942
+ token = next(self.stream)
943
+ if self.stream.current.test("name:not"):
944
+ next(self.stream)
945
+ negated = True
946
+ else:
947
+ negated = False
948
+ name = self.stream.expect("name").value
949
+ while self.stream.current.type == "dot":
950
+ next(self.stream)
951
+ name += "." + self.stream.expect("name").value
952
+ dyn_args = dyn_kwargs = None
953
+ kwargs = []
954
+ if self.stream.current.type == "lparen":
955
+ args, kwargs, dyn_args, dyn_kwargs = self.parse_call_args()
956
+ elif self.stream.current.type in {
957
+ "name",
958
+ "string",
959
+ "integer",
960
+ "float",
961
+ "lparen",
962
+ "lbracket",
963
+ "lbrace",
964
+ } and not self.stream.current.test_any("name:else", "name:or", "name:and"):
965
+ if self.stream.current.test("name:is"):
966
+ self.fail("You cannot chain multiple tests with is")
967
+ arg_node = self.parse_primary()
968
+ arg_node = self.parse_postfix(arg_node)
969
+ args = [arg_node]
970
+ else:
971
+ args = []
972
+ node = nodes.Test(
973
+ node, name, args, kwargs, dyn_args, dyn_kwargs, lineno=token.lineno
974
+ )
975
+ if negated:
976
+ node = nodes.Not(node, lineno=token.lineno)
977
+ return node
978
+
979
+ def subparse(
980
+ self, end_tokens: t.Optional[t.Tuple[str, ...]] = None
981
+ ) -> t.List[nodes.Node]:
982
+ body: t.List[nodes.Node] = []
983
+ data_buffer: t.List[nodes.Node] = []
984
+ add_data = data_buffer.append
985
+
986
+ if end_tokens is not None:
987
+ self._end_token_stack.append(end_tokens)
988
+
989
+ def flush_data() -> None:
990
+ if data_buffer:
991
+ lineno = data_buffer[0].lineno
992
+ body.append(nodes.Output(data_buffer[:], lineno=lineno))
993
+ del data_buffer[:]
994
+
995
+ try:
996
+ while self.stream:
997
+ token = self.stream.current
998
+ if token.type == "data":
999
+ if token.value:
1000
+ add_data(nodes.TemplateData(token.value, lineno=token.lineno))
1001
+ next(self.stream)
1002
+ elif token.type == "variable_begin":
1003
+ next(self.stream)
1004
+ add_data(self.parse_tuple(with_condexpr=True))
1005
+ self.stream.expect("variable_end")
1006
+ elif token.type == "block_begin":
1007
+ flush_data()
1008
+ next(self.stream)
1009
+ if end_tokens is not None and self.stream.current.test_any(
1010
+ *end_tokens
1011
+ ):
1012
+ return body
1013
+ rv = self.parse_statement()
1014
+ if isinstance(rv, list):
1015
+ body.extend(rv)
1016
+ else:
1017
+ body.append(rv)
1018
+ self.stream.expect("block_end")
1019
+ else:
1020
+ raise AssertionError("internal parsing error")
1021
+
1022
+ flush_data()
1023
+ finally:
1024
+ if end_tokens is not None:
1025
+ self._end_token_stack.pop()
1026
+ return body
1027
+
1028
+ def parse(self) -> nodes.Template:
1029
+ """Parse the whole template into a `Template` node."""
1030
+ result = nodes.Template(self.subparse(), lineno=1)
1031
+ result.set_environment(self.environment)
1032
+ return result
lib/python3.11/site-packages/jinja2/py.typed ADDED
File without changes
lib/python3.11/site-packages/jinja2/runtime.py ADDED
@@ -0,0 +1,1053 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """The runtime functions and state used by compiled templates."""
2
+ import functools
3
+ import sys
4
+ import typing as t
5
+ from collections import abc
6
+ from itertools import chain
7
+
8
+ from markupsafe import escape # noqa: F401
9
+ from markupsafe import Markup
10
+ from markupsafe import soft_str
11
+
12
+ from .async_utils import auto_aiter
13
+ from .async_utils import auto_await # noqa: F401
14
+ from .exceptions import TemplateNotFound # noqa: F401
15
+ from .exceptions import TemplateRuntimeError # noqa: F401
16
+ from .exceptions import UndefinedError
17
+ from .nodes import EvalContext
18
+ from .utils import _PassArg
19
+ from .utils import concat
20
+ from .utils import internalcode
21
+ from .utils import missing
22
+ from .utils import Namespace # noqa: F401
23
+ from .utils import object_type_repr
24
+ from .utils import pass_eval_context
25
+
26
+ V = t.TypeVar("V")
27
+ F = t.TypeVar("F", bound=t.Callable[..., t.Any])
28
+
29
+ if t.TYPE_CHECKING:
30
+ import logging
31
+ import typing_extensions as te
32
+ from .environment import Environment
33
+
34
+ class LoopRenderFunc(te.Protocol):
35
+ def __call__(
36
+ self,
37
+ reciter: t.Iterable[V],
38
+ loop_render_func: "LoopRenderFunc",
39
+ depth: int = 0,
40
+ ) -> str:
41
+ ...
42
+
43
+
44
+ # these variables are exported to the template runtime
45
+ exported = [
46
+ "LoopContext",
47
+ "TemplateReference",
48
+ "Macro",
49
+ "Markup",
50
+ "TemplateRuntimeError",
51
+ "missing",
52
+ "escape",
53
+ "markup_join",
54
+ "str_join",
55
+ "identity",
56
+ "TemplateNotFound",
57
+ "Namespace",
58
+ "Undefined",
59
+ "internalcode",
60
+ ]
61
+ async_exported = [
62
+ "AsyncLoopContext",
63
+ "auto_aiter",
64
+ "auto_await",
65
+ ]
66
+
67
+
68
+ def identity(x: V) -> V:
69
+ """Returns its argument. Useful for certain things in the
70
+ environment.
71
+ """
72
+ return x
73
+
74
+
75
+ def markup_join(seq: t.Iterable[t.Any]) -> str:
76
+ """Concatenation that escapes if necessary and converts to string."""
77
+ buf = []
78
+ iterator = map(soft_str, seq)
79
+ for arg in iterator:
80
+ buf.append(arg)
81
+ if hasattr(arg, "__html__"):
82
+ return Markup("").join(chain(buf, iterator))
83
+ return concat(buf)
84
+
85
+
86
+ def str_join(seq: t.Iterable[t.Any]) -> str:
87
+ """Simple args to string conversion and concatenation."""
88
+ return concat(map(str, seq))
89
+
90
+
91
+ def new_context(
92
+ environment: "Environment",
93
+ template_name: t.Optional[str],
94
+ blocks: t.Dict[str, t.Callable[["Context"], t.Iterator[str]]],
95
+ vars: t.Optional[t.Dict[str, t.Any]] = None,
96
+ shared: bool = False,
97
+ globals: t.Optional[t.MutableMapping[str, t.Any]] = None,
98
+ locals: t.Optional[t.Mapping[str, t.Any]] = None,
99
+ ) -> "Context":
100
+ """Internal helper for context creation."""
101
+ if vars is None:
102
+ vars = {}
103
+ if shared:
104
+ parent = vars
105
+ else:
106
+ parent = dict(globals or (), **vars)
107
+ if locals:
108
+ # if the parent is shared a copy should be created because
109
+ # we don't want to modify the dict passed
110
+ if shared:
111
+ parent = dict(parent)
112
+ for key, value in locals.items():
113
+ if value is not missing:
114
+ parent[key] = value
115
+ return environment.context_class(
116
+ environment, parent, template_name, blocks, globals=globals
117
+ )
118
+
119
+
120
+ class TemplateReference:
121
+ """The `self` in templates."""
122
+
123
+ def __init__(self, context: "Context") -> None:
124
+ self.__context = context
125
+
126
+ def __getitem__(self, name: str) -> t.Any:
127
+ blocks = self.__context.blocks[name]
128
+ return BlockReference(name, self.__context, blocks, 0)
129
+
130
+ def __repr__(self) -> str:
131
+ return f"<{type(self).__name__} {self.__context.name!r}>"
132
+
133
+
134
+ def _dict_method_all(dict_method: F) -> F:
135
+ @functools.wraps(dict_method)
136
+ def f_all(self: "Context") -> t.Any:
137
+ return dict_method(self.get_all())
138
+
139
+ return t.cast(F, f_all)
140
+
141
+
142
+ @abc.Mapping.register
143
+ class Context:
144
+ """The template context holds the variables of a template. It stores the
145
+ values passed to the template and also the names the template exports.
146
+ Creating instances is neither supported nor useful as it's created
147
+ automatically at various stages of the template evaluation and should not
148
+ be created by hand.
149
+
150
+ The context is immutable. Modifications on :attr:`parent` **must not**
151
+ happen and modifications on :attr:`vars` are allowed from generated
152
+ template code only. Template filters and global functions marked as
153
+ :func:`pass_context` get the active context passed as first argument
154
+ and are allowed to access the context read-only.
155
+
156
+ The template context supports read only dict operations (`get`,
157
+ `keys`, `values`, `items`, `iterkeys`, `itervalues`, `iteritems`,
158
+ `__getitem__`, `__contains__`). Additionally there is a :meth:`resolve`
159
+ method that doesn't fail with a `KeyError` but returns an
160
+ :class:`Undefined` object for missing variables.
161
+ """
162
+
163
+ def __init__(
164
+ self,
165
+ environment: "Environment",
166
+ parent: t.Dict[str, t.Any],
167
+ name: t.Optional[str],
168
+ blocks: t.Dict[str, t.Callable[["Context"], t.Iterator[str]]],
169
+ globals: t.Optional[t.MutableMapping[str, t.Any]] = None,
170
+ ):
171
+ self.parent = parent
172
+ self.vars: t.Dict[str, t.Any] = {}
173
+ self.environment: "Environment" = environment
174
+ self.eval_ctx = EvalContext(self.environment, name)
175
+ self.exported_vars: t.Set[str] = set()
176
+ self.name = name
177
+ self.globals_keys = set() if globals is None else set(globals)
178
+
179
+ # create the initial mapping of blocks. Whenever template inheritance
180
+ # takes place the runtime will update this mapping with the new blocks
181
+ # from the template.
182
+ self.blocks = {k: [v] for k, v in blocks.items()}
183
+
184
+ def super(
185
+ self, name: str, current: t.Callable[["Context"], t.Iterator[str]]
186
+ ) -> t.Union["BlockReference", "Undefined"]:
187
+ """Render a parent block."""
188
+ try:
189
+ blocks = self.blocks[name]
190
+ index = blocks.index(current) + 1
191
+ blocks[index]
192
+ except LookupError:
193
+ return self.environment.undefined(
194
+ f"there is no parent block called {name!r}.", name="super"
195
+ )
196
+ return BlockReference(name, self, blocks, index)
197
+
198
+ def get(self, key: str, default: t.Any = None) -> t.Any:
199
+ """Look up a variable by name, or return a default if the key is
200
+ not found.
201
+
202
+ :param key: The variable name to look up.
203
+ :param default: The value to return if the key is not found.
204
+ """
205
+ try:
206
+ return self[key]
207
+ except KeyError:
208
+ return default
209
+
210
+ def resolve(self, key: str) -> t.Union[t.Any, "Undefined"]:
211
+ """Look up a variable by name, or return an :class:`Undefined`
212
+ object if the key is not found.
213
+
214
+ If you need to add custom behavior, override
215
+ :meth:`resolve_or_missing`, not this method. The various lookup
216
+ functions use that method, not this one.
217
+
218
+ :param key: The variable name to look up.
219
+ """
220
+ rv = self.resolve_or_missing(key)
221
+
222
+ if rv is missing:
223
+ return self.environment.undefined(name=key)
224
+
225
+ return rv
226
+
227
+ def resolve_or_missing(self, key: str) -> t.Any:
228
+ """Look up a variable by name, or return a ``missing`` sentinel
229
+ if the key is not found.
230
+
231
+ Override this method to add custom lookup behavior.
232
+ :meth:`resolve`, :meth:`get`, and :meth:`__getitem__` use this
233
+ method. Don't call this method directly.
234
+
235
+ :param key: The variable name to look up.
236
+ """
237
+ if key in self.vars:
238
+ return self.vars[key]
239
+
240
+ if key in self.parent:
241
+ return self.parent[key]
242
+
243
+ return missing
244
+
245
+ def get_exported(self) -> t.Dict[str, t.Any]:
246
+ """Get a new dict with the exported variables."""
247
+ return {k: self.vars[k] for k in self.exported_vars}
248
+
249
+ def get_all(self) -> t.Dict[str, t.Any]:
250
+ """Return the complete context as dict including the exported
251
+ variables. For optimizations reasons this might not return an
252
+ actual copy so be careful with using it.
253
+ """
254
+ if not self.vars:
255
+ return self.parent
256
+ if not self.parent:
257
+ return self.vars
258
+ return dict(self.parent, **self.vars)
259
+
260
+ @internalcode
261
+ def call(
262
+ __self, __obj: t.Callable, *args: t.Any, **kwargs: t.Any # noqa: B902
263
+ ) -> t.Union[t.Any, "Undefined"]:
264
+ """Call the callable with the arguments and keyword arguments
265
+ provided but inject the active context or environment as first
266
+ argument if the callable has :func:`pass_context` or
267
+ :func:`pass_environment`.
268
+ """
269
+ if __debug__:
270
+ __traceback_hide__ = True # noqa
271
+
272
+ # Allow callable classes to take a context
273
+ if (
274
+ hasattr(__obj, "__call__") # noqa: B004
275
+ and _PassArg.from_obj(__obj.__call__) is not None # type: ignore
276
+ ):
277
+ __obj = __obj.__call__ # type: ignore
278
+
279
+ pass_arg = _PassArg.from_obj(__obj)
280
+
281
+ if pass_arg is _PassArg.context:
282
+ # the active context should have access to variables set in
283
+ # loops and blocks without mutating the context itself
284
+ if kwargs.get("_loop_vars"):
285
+ __self = __self.derived(kwargs["_loop_vars"])
286
+ if kwargs.get("_block_vars"):
287
+ __self = __self.derived(kwargs["_block_vars"])
288
+ args = (__self,) + args
289
+ elif pass_arg is _PassArg.eval_context:
290
+ args = (__self.eval_ctx,) + args
291
+ elif pass_arg is _PassArg.environment:
292
+ args = (__self.environment,) + args
293
+
294
+ kwargs.pop("_block_vars", None)
295
+ kwargs.pop("_loop_vars", None)
296
+
297
+ try:
298
+ return __obj(*args, **kwargs)
299
+ except StopIteration:
300
+ return __self.environment.undefined(
301
+ "value was undefined because a callable raised a"
302
+ " StopIteration exception"
303
+ )
304
+
305
+ def derived(self, locals: t.Optional[t.Dict[str, t.Any]] = None) -> "Context":
306
+ """Internal helper function to create a derived context. This is
307
+ used in situations where the system needs a new context in the same
308
+ template that is independent.
309
+ """
310
+ context = new_context(
311
+ self.environment, self.name, {}, self.get_all(), True, None, locals
312
+ )
313
+ context.eval_ctx = self.eval_ctx
314
+ context.blocks.update((k, list(v)) for k, v in self.blocks.items())
315
+ return context
316
+
317
+ keys = _dict_method_all(dict.keys)
318
+ values = _dict_method_all(dict.values)
319
+ items = _dict_method_all(dict.items)
320
+
321
+ def __contains__(self, name: str) -> bool:
322
+ return name in self.vars or name in self.parent
323
+
324
+ def __getitem__(self, key: str) -> t.Any:
325
+ """Look up a variable by name with ``[]`` syntax, or raise a
326
+ ``KeyError`` if the key is not found.
327
+ """
328
+ item = self.resolve_or_missing(key)
329
+
330
+ if item is missing:
331
+ raise KeyError(key)
332
+
333
+ return item
334
+
335
+ def __repr__(self) -> str:
336
+ return f"<{type(self).__name__} {self.get_all()!r} of {self.name!r}>"
337
+
338
+
339
+ class BlockReference:
340
+ """One block on a template reference."""
341
+
342
+ def __init__(
343
+ self,
344
+ name: str,
345
+ context: "Context",
346
+ stack: t.List[t.Callable[["Context"], t.Iterator[str]]],
347
+ depth: int,
348
+ ) -> None:
349
+ self.name = name
350
+ self._context = context
351
+ self._stack = stack
352
+ self._depth = depth
353
+
354
+ @property
355
+ def super(self) -> t.Union["BlockReference", "Undefined"]:
356
+ """Super the block."""
357
+ if self._depth + 1 >= len(self._stack):
358
+ return self._context.environment.undefined(
359
+ f"there is no parent block called {self.name!r}.", name="super"
360
+ )
361
+ return BlockReference(self.name, self._context, self._stack, self._depth + 1)
362
+
363
+ @internalcode
364
+ async def _async_call(self) -> str:
365
+ rv = concat(
366
+ [x async for x in self._stack[self._depth](self._context)] # type: ignore
367
+ )
368
+
369
+ if self._context.eval_ctx.autoescape:
370
+ return Markup(rv)
371
+
372
+ return rv
373
+
374
+ @internalcode
375
+ def __call__(self) -> str:
376
+ if self._context.environment.is_async:
377
+ return self._async_call() # type: ignore
378
+
379
+ rv = concat(self._stack[self._depth](self._context))
380
+
381
+ if self._context.eval_ctx.autoescape:
382
+ return Markup(rv)
383
+
384
+ return rv
385
+
386
+
387
+ class LoopContext:
388
+ """A wrapper iterable for dynamic ``for`` loops, with information
389
+ about the loop and iteration.
390
+ """
391
+
392
+ #: Current iteration of the loop, starting at 0.
393
+ index0 = -1
394
+
395
+ _length: t.Optional[int] = None
396
+ _after: t.Any = missing
397
+ _current: t.Any = missing
398
+ _before: t.Any = missing
399
+ _last_changed_value: t.Any = missing
400
+
401
+ def __init__(
402
+ self,
403
+ iterable: t.Iterable[V],
404
+ undefined: t.Type["Undefined"],
405
+ recurse: t.Optional["LoopRenderFunc"] = None,
406
+ depth0: int = 0,
407
+ ) -> None:
408
+ """
409
+ :param iterable: Iterable to wrap.
410
+ :param undefined: :class:`Undefined` class to use for next and
411
+ previous items.
412
+ :param recurse: The function to render the loop body when the
413
+ loop is marked recursive.
414
+ :param depth0: Incremented when looping recursively.
415
+ """
416
+ self._iterable = iterable
417
+ self._iterator = self._to_iterator(iterable)
418
+ self._undefined = undefined
419
+ self._recurse = recurse
420
+ #: How many levels deep a recursive loop currently is, starting at 0.
421
+ self.depth0 = depth0
422
+
423
+ @staticmethod
424
+ def _to_iterator(iterable: t.Iterable[V]) -> t.Iterator[V]:
425
+ return iter(iterable)
426
+
427
+ @property
428
+ def length(self) -> int:
429
+ """Length of the iterable.
430
+
431
+ If the iterable is a generator or otherwise does not have a
432
+ size, it is eagerly evaluated to get a size.
433
+ """
434
+ if self._length is not None:
435
+ return self._length
436
+
437
+ try:
438
+ self._length = len(self._iterable) # type: ignore
439
+ except TypeError:
440
+ iterable = list(self._iterator)
441
+ self._iterator = self._to_iterator(iterable)
442
+ self._length = len(iterable) + self.index + (self._after is not missing)
443
+
444
+ return self._length
445
+
446
+ def __len__(self) -> int:
447
+ return self.length
448
+
449
+ @property
450
+ def depth(self) -> int:
451
+ """How many levels deep a recursive loop currently is, starting at 1."""
452
+ return self.depth0 + 1
453
+
454
+ @property
455
+ def index(self) -> int:
456
+ """Current iteration of the loop, starting at 1."""
457
+ return self.index0 + 1
458
+
459
+ @property
460
+ def revindex0(self) -> int:
461
+ """Number of iterations from the end of the loop, ending at 0.
462
+
463
+ Requires calculating :attr:`length`.
464
+ """
465
+ return self.length - self.index
466
+
467
+ @property
468
+ def revindex(self) -> int:
469
+ """Number of iterations from the end of the loop, ending at 1.
470
+
471
+ Requires calculating :attr:`length`.
472
+ """
473
+ return self.length - self.index0
474
+
475
+ @property
476
+ def first(self) -> bool:
477
+ """Whether this is the first iteration of the loop."""
478
+ return self.index0 == 0
479
+
480
+ def _peek_next(self) -> t.Any:
481
+ """Return the next element in the iterable, or :data:`missing`
482
+ if the iterable is exhausted. Only peeks one item ahead, caching
483
+ the result in :attr:`_last` for use in subsequent checks. The
484
+ cache is reset when :meth:`__next__` is called.
485
+ """
486
+ if self._after is not missing:
487
+ return self._after
488
+
489
+ self._after = next(self._iterator, missing)
490
+ return self._after
491
+
492
+ @property
493
+ def last(self) -> bool:
494
+ """Whether this is the last iteration of the loop.
495
+
496
+ Causes the iterable to advance early. See
497
+ :func:`itertools.groupby` for issues this can cause.
498
+ The :func:`groupby` filter avoids that issue.
499
+ """
500
+ return self._peek_next() is missing
501
+
502
+ @property
503
+ def previtem(self) -> t.Union[t.Any, "Undefined"]:
504
+ """The item in the previous iteration. Undefined during the
505
+ first iteration.
506
+ """
507
+ if self.first:
508
+ return self._undefined("there is no previous item")
509
+
510
+ return self._before
511
+
512
+ @property
513
+ def nextitem(self) -> t.Union[t.Any, "Undefined"]:
514
+ """The item in the next iteration. Undefined during the last
515
+ iteration.
516
+
517
+ Causes the iterable to advance early. See
518
+ :func:`itertools.groupby` for issues this can cause.
519
+ The :func:`jinja-filters.groupby` filter avoids that issue.
520
+ """
521
+ rv = self._peek_next()
522
+
523
+ if rv is missing:
524
+ return self._undefined("there is no next item")
525
+
526
+ return rv
527
+
528
+ def cycle(self, *args: V) -> V:
529
+ """Return a value from the given args, cycling through based on
530
+ the current :attr:`index0`.
531
+
532
+ :param args: One or more values to cycle through.
533
+ """
534
+ if not args:
535
+ raise TypeError("no items for cycling given")
536
+
537
+ return args[self.index0 % len(args)]
538
+
539
+ def changed(self, *value: t.Any) -> bool:
540
+ """Return ``True`` if previously called with a different value
541
+ (including when called for the first time).
542
+
543
+ :param value: One or more values to compare to the last call.
544
+ """
545
+ if self._last_changed_value != value:
546
+ self._last_changed_value = value
547
+ return True
548
+
549
+ return False
550
+
551
+ def __iter__(self) -> "LoopContext":
552
+ return self
553
+
554
+ def __next__(self) -> t.Tuple[t.Any, "LoopContext"]:
555
+ if self._after is not missing:
556
+ rv = self._after
557
+ self._after = missing
558
+ else:
559
+ rv = next(self._iterator)
560
+
561
+ self.index0 += 1
562
+ self._before = self._current
563
+ self._current = rv
564
+ return rv, self
565
+
566
+ @internalcode
567
+ def __call__(self, iterable: t.Iterable[V]) -> str:
568
+ """When iterating over nested data, render the body of the loop
569
+ recursively with the given inner iterable data.
570
+
571
+ The loop must have the ``recursive`` marker for this to work.
572
+ """
573
+ if self._recurse is None:
574
+ raise TypeError(
575
+ "The loop must have the 'recursive' marker to be called recursively."
576
+ )
577
+
578
+ return self._recurse(iterable, self._recurse, depth=self.depth)
579
+
580
+ def __repr__(self) -> str:
581
+ return f"<{type(self).__name__} {self.index}/{self.length}>"
582
+
583
+
584
+ class AsyncLoopContext(LoopContext):
585
+ _iterator: t.AsyncIterator[t.Any] # type: ignore
586
+
587
+ @staticmethod
588
+ def _to_iterator( # type: ignore
589
+ iterable: t.Union[t.Iterable[V], t.AsyncIterable[V]]
590
+ ) -> t.AsyncIterator[V]:
591
+ return auto_aiter(iterable)
592
+
593
+ @property
594
+ async def length(self) -> int: # type: ignore
595
+ if self._length is not None:
596
+ return self._length
597
+
598
+ try:
599
+ self._length = len(self._iterable) # type: ignore
600
+ except TypeError:
601
+ iterable = [x async for x in self._iterator]
602
+ self._iterator = self._to_iterator(iterable)
603
+ self._length = len(iterable) + self.index + (self._after is not missing)
604
+
605
+ return self._length
606
+
607
+ @property
608
+ async def revindex0(self) -> int: # type: ignore
609
+ return await self.length - self.index
610
+
611
+ @property
612
+ async def revindex(self) -> int: # type: ignore
613
+ return await self.length - self.index0
614
+
615
+ async def _peek_next(self) -> t.Any:
616
+ if self._after is not missing:
617
+ return self._after
618
+
619
+ try:
620
+ self._after = await self._iterator.__anext__()
621
+ except StopAsyncIteration:
622
+ self._after = missing
623
+
624
+ return self._after
625
+
626
+ @property
627
+ async def last(self) -> bool: # type: ignore
628
+ return await self._peek_next() is missing
629
+
630
+ @property
631
+ async def nextitem(self) -> t.Union[t.Any, "Undefined"]:
632
+ rv = await self._peek_next()
633
+
634
+ if rv is missing:
635
+ return self._undefined("there is no next item")
636
+
637
+ return rv
638
+
639
+ def __aiter__(self) -> "AsyncLoopContext":
640
+ return self
641
+
642
+ async def __anext__(self) -> t.Tuple[t.Any, "AsyncLoopContext"]:
643
+ if self._after is not missing:
644
+ rv = self._after
645
+ self._after = missing
646
+ else:
647
+ rv = await self._iterator.__anext__()
648
+
649
+ self.index0 += 1
650
+ self._before = self._current
651
+ self._current = rv
652
+ return rv, self
653
+
654
+
655
+ class Macro:
656
+ """Wraps a macro function."""
657
+
658
+ def __init__(
659
+ self,
660
+ environment: "Environment",
661
+ func: t.Callable[..., str],
662
+ name: str,
663
+ arguments: t.List[str],
664
+ catch_kwargs: bool,
665
+ catch_varargs: bool,
666
+ caller: bool,
667
+ default_autoescape: t.Optional[bool] = None,
668
+ ):
669
+ self._environment = environment
670
+ self._func = func
671
+ self._argument_count = len(arguments)
672
+ self.name = name
673
+ self.arguments = arguments
674
+ self.catch_kwargs = catch_kwargs
675
+ self.catch_varargs = catch_varargs
676
+ self.caller = caller
677
+ self.explicit_caller = "caller" in arguments
678
+
679
+ if default_autoescape is None:
680
+ if callable(environment.autoescape):
681
+ default_autoescape = environment.autoescape(None)
682
+ else:
683
+ default_autoescape = environment.autoescape
684
+
685
+ self._default_autoescape = default_autoescape
686
+
687
+ @internalcode
688
+ @pass_eval_context
689
+ def __call__(self, *args: t.Any, **kwargs: t.Any) -> str:
690
+ # This requires a bit of explanation, In the past we used to
691
+ # decide largely based on compile-time information if a macro is
692
+ # safe or unsafe. While there was a volatile mode it was largely
693
+ # unused for deciding on escaping. This turns out to be
694
+ # problematic for macros because whether a macro is safe depends not
695
+ # on the escape mode when it was defined, but rather when it was used.
696
+ #
697
+ # Because however we export macros from the module system and
698
+ # there are historic callers that do not pass an eval context (and
699
+ # will continue to not pass one), we need to perform an instance
700
+ # check here.
701
+ #
702
+ # This is considered safe because an eval context is not a valid
703
+ # argument to callables otherwise anyway. Worst case here is
704
+ # that if no eval context is passed we fall back to the compile
705
+ # time autoescape flag.
706
+ if args and isinstance(args[0], EvalContext):
707
+ autoescape = args[0].autoescape
708
+ args = args[1:]
709
+ else:
710
+ autoescape = self._default_autoescape
711
+
712
+ # try to consume the positional arguments
713
+ arguments = list(args[: self._argument_count])
714
+ off = len(arguments)
715
+
716
+ # For information why this is necessary refer to the handling
717
+ # of caller in the `macro_body` handler in the compiler.
718
+ found_caller = False
719
+
720
+ # if the number of arguments consumed is not the number of
721
+ # arguments expected we start filling in keyword arguments
722
+ # and defaults.
723
+ if off != self._argument_count:
724
+ for name in self.arguments[len(arguments) :]:
725
+ try:
726
+ value = kwargs.pop(name)
727
+ except KeyError:
728
+ value = missing
729
+ if name == "caller":
730
+ found_caller = True
731
+ arguments.append(value)
732
+ else:
733
+ found_caller = self.explicit_caller
734
+
735
+ # it's important that the order of these arguments does not change
736
+ # if not also changed in the compiler's `function_scoping` method.
737
+ # the order is caller, keyword arguments, positional arguments!
738
+ if self.caller and not found_caller:
739
+ caller = kwargs.pop("caller", None)
740
+ if caller is None:
741
+ caller = self._environment.undefined("No caller defined", name="caller")
742
+ arguments.append(caller)
743
+
744
+ if self.catch_kwargs:
745
+ arguments.append(kwargs)
746
+ elif kwargs:
747
+ if "caller" in kwargs:
748
+ raise TypeError(
749
+ f"macro {self.name!r} was invoked with two values for the special"
750
+ " caller argument. This is most likely a bug."
751
+ )
752
+ raise TypeError(
753
+ f"macro {self.name!r} takes no keyword argument {next(iter(kwargs))!r}"
754
+ )
755
+ if self.catch_varargs:
756
+ arguments.append(args[self._argument_count :])
757
+ elif len(args) > self._argument_count:
758
+ raise TypeError(
759
+ f"macro {self.name!r} takes not more than"
760
+ f" {len(self.arguments)} argument(s)"
761
+ )
762
+
763
+ return self._invoke(arguments, autoescape)
764
+
765
+ async def _async_invoke(self, arguments: t.List[t.Any], autoescape: bool) -> str:
766
+ rv = await self._func(*arguments) # type: ignore
767
+
768
+ if autoescape:
769
+ return Markup(rv)
770
+
771
+ return rv # type: ignore
772
+
773
+ def _invoke(self, arguments: t.List[t.Any], autoescape: bool) -> str:
774
+ if self._environment.is_async:
775
+ return self._async_invoke(arguments, autoescape) # type: ignore
776
+
777
+ rv = self._func(*arguments)
778
+
779
+ if autoescape:
780
+ rv = Markup(rv)
781
+
782
+ return rv
783
+
784
+ def __repr__(self) -> str:
785
+ name = "anonymous" if self.name is None else repr(self.name)
786
+ return f"<{type(self).__name__} {name}>"
787
+
788
+
789
+ class Undefined:
790
+ """The default undefined type. This undefined type can be printed and
791
+ iterated over, but every other access will raise an :exc:`UndefinedError`:
792
+
793
+ >>> foo = Undefined(name='foo')
794
+ >>> str(foo)
795
+ ''
796
+ >>> not foo
797
+ True
798
+ >>> foo + 42
799
+ Traceback (most recent call last):
800
+ ...
801
+ jinja2.exceptions.UndefinedError: 'foo' is undefined
802
+ """
803
+
804
+ __slots__ = (
805
+ "_undefined_hint",
806
+ "_undefined_obj",
807
+ "_undefined_name",
808
+ "_undefined_exception",
809
+ )
810
+
811
+ def __init__(
812
+ self,
813
+ hint: t.Optional[str] = None,
814
+ obj: t.Any = missing,
815
+ name: t.Optional[str] = None,
816
+ exc: t.Type[TemplateRuntimeError] = UndefinedError,
817
+ ) -> None:
818
+ self._undefined_hint = hint
819
+ self._undefined_obj = obj
820
+ self._undefined_name = name
821
+ self._undefined_exception = exc
822
+
823
+ @property
824
+ def _undefined_message(self) -> str:
825
+ """Build a message about the undefined value based on how it was
826
+ accessed.
827
+ """
828
+ if self._undefined_hint:
829
+ return self._undefined_hint
830
+
831
+ if self._undefined_obj is missing:
832
+ return f"{self._undefined_name!r} is undefined"
833
+
834
+ if not isinstance(self._undefined_name, str):
835
+ return (
836
+ f"{object_type_repr(self._undefined_obj)} has no"
837
+ f" element {self._undefined_name!r}"
838
+ )
839
+
840
+ return (
841
+ f"{object_type_repr(self._undefined_obj)!r} has no"
842
+ f" attribute {self._undefined_name!r}"
843
+ )
844
+
845
+ @internalcode
846
+ def _fail_with_undefined_error(
847
+ self, *args: t.Any, **kwargs: t.Any
848
+ ) -> "te.NoReturn":
849
+ """Raise an :exc:`UndefinedError` when operations are performed
850
+ on the undefined value.
851
+ """
852
+ raise self._undefined_exception(self._undefined_message)
853
+
854
+ @internalcode
855
+ def __getattr__(self, name: str) -> t.Any:
856
+ if name[:2] == "__":
857
+ raise AttributeError(name)
858
+
859
+ return self._fail_with_undefined_error()
860
+
861
+ __add__ = __radd__ = __sub__ = __rsub__ = _fail_with_undefined_error
862
+ __mul__ = __rmul__ = __div__ = __rdiv__ = _fail_with_undefined_error
863
+ __truediv__ = __rtruediv__ = _fail_with_undefined_error
864
+ __floordiv__ = __rfloordiv__ = _fail_with_undefined_error
865
+ __mod__ = __rmod__ = _fail_with_undefined_error
866
+ __pos__ = __neg__ = _fail_with_undefined_error
867
+ __call__ = __getitem__ = _fail_with_undefined_error
868
+ __lt__ = __le__ = __gt__ = __ge__ = _fail_with_undefined_error
869
+ __int__ = __float__ = __complex__ = _fail_with_undefined_error
870
+ __pow__ = __rpow__ = _fail_with_undefined_error
871
+
872
+ def __eq__(self, other: t.Any) -> bool:
873
+ return type(self) is type(other)
874
+
875
+ def __ne__(self, other: t.Any) -> bool:
876
+ return not self.__eq__(other)
877
+
878
+ def __hash__(self) -> int:
879
+ return id(type(self))
880
+
881
+ def __str__(self) -> str:
882
+ return ""
883
+
884
+ def __len__(self) -> int:
885
+ return 0
886
+
887
+ def __iter__(self) -> t.Iterator[t.Any]:
888
+ yield from ()
889
+
890
+ async def __aiter__(self) -> t.AsyncIterator[t.Any]:
891
+ for _ in ():
892
+ yield
893
+
894
+ def __bool__(self) -> bool:
895
+ return False
896
+
897
+ def __repr__(self) -> str:
898
+ return "Undefined"
899
+
900
+
901
+ def make_logging_undefined(
902
+ logger: t.Optional["logging.Logger"] = None, base: t.Type[Undefined] = Undefined
903
+ ) -> t.Type[Undefined]:
904
+ """Given a logger object this returns a new undefined class that will
905
+ log certain failures. It will log iterations and printing. If no
906
+ logger is given a default logger is created.
907
+
908
+ Example::
909
+
910
+ logger = logging.getLogger(__name__)
911
+ LoggingUndefined = make_logging_undefined(
912
+ logger=logger,
913
+ base=Undefined
914
+ )
915
+
916
+ .. versionadded:: 2.8
917
+
918
+ :param logger: the logger to use. If not provided, a default logger
919
+ is created.
920
+ :param base: the base class to add logging functionality to. This
921
+ defaults to :class:`Undefined`.
922
+ """
923
+ if logger is None:
924
+ import logging
925
+
926
+ logger = logging.getLogger(__name__)
927
+ logger.addHandler(logging.StreamHandler(sys.stderr))
928
+
929
+ def _log_message(undef: Undefined) -> None:
930
+ logger.warning( # type: ignore
931
+ "Template variable warning: %s", undef._undefined_message
932
+ )
933
+
934
+ class LoggingUndefined(base): # type: ignore
935
+ __slots__ = ()
936
+
937
+ def _fail_with_undefined_error( # type: ignore
938
+ self, *args: t.Any, **kwargs: t.Any
939
+ ) -> "te.NoReturn":
940
+ try:
941
+ super()._fail_with_undefined_error(*args, **kwargs)
942
+ except self._undefined_exception as e:
943
+ logger.error("Template variable error: %s", e) # type: ignore
944
+ raise e
945
+
946
+ def __str__(self) -> str:
947
+ _log_message(self)
948
+ return super().__str__() # type: ignore
949
+
950
+ def __iter__(self) -> t.Iterator[t.Any]:
951
+ _log_message(self)
952
+ return super().__iter__() # type: ignore
953
+
954
+ def __bool__(self) -> bool:
955
+ _log_message(self)
956
+ return super().__bool__() # type: ignore
957
+
958
+ return LoggingUndefined
959
+
960
+
961
+ class ChainableUndefined(Undefined):
962
+ """An undefined that is chainable, where both ``__getattr__`` and
963
+ ``__getitem__`` return itself rather than raising an
964
+ :exc:`UndefinedError`.
965
+
966
+ >>> foo = ChainableUndefined(name='foo')
967
+ >>> str(foo.bar['baz'])
968
+ ''
969
+ >>> foo.bar['baz'] + 42
970
+ Traceback (most recent call last):
971
+ ...
972
+ jinja2.exceptions.UndefinedError: 'foo' is undefined
973
+
974
+ .. versionadded:: 2.11.0
975
+ """
976
+
977
+ __slots__ = ()
978
+
979
+ def __html__(self) -> str:
980
+ return str(self)
981
+
982
+ def __getattr__(self, _: str) -> "ChainableUndefined":
983
+ return self
984
+
985
+ __getitem__ = __getattr__ # type: ignore
986
+
987
+
988
+ class DebugUndefined(Undefined):
989
+ """An undefined that returns the debug info when printed.
990
+
991
+ >>> foo = DebugUndefined(name='foo')
992
+ >>> str(foo)
993
+ '{{ foo }}'
994
+ >>> not foo
995
+ True
996
+ >>> foo + 42
997
+ Traceback (most recent call last):
998
+ ...
999
+ jinja2.exceptions.UndefinedError: 'foo' is undefined
1000
+ """
1001
+
1002
+ __slots__ = ()
1003
+
1004
+ def __str__(self) -> str:
1005
+ if self._undefined_hint:
1006
+ message = f"undefined value printed: {self._undefined_hint}"
1007
+
1008
+ elif self._undefined_obj is missing:
1009
+ message = self._undefined_name # type: ignore
1010
+
1011
+ else:
1012
+ message = (
1013
+ f"no such element: {object_type_repr(self._undefined_obj)}"
1014
+ f"[{self._undefined_name!r}]"
1015
+ )
1016
+
1017
+ return f"{{{{ {message} }}}}"
1018
+
1019
+
1020
+ class StrictUndefined(Undefined):
1021
+ """An undefined that barks on print and iteration as well as boolean
1022
+ tests and all kinds of comparisons. In other words: you can do nothing
1023
+ with it except checking if it's defined using the `defined` test.
1024
+
1025
+ >>> foo = StrictUndefined(name='foo')
1026
+ >>> str(foo)
1027
+ Traceback (most recent call last):
1028
+ ...
1029
+ jinja2.exceptions.UndefinedError: 'foo' is undefined
1030
+ >>> not foo
1031
+ Traceback (most recent call last):
1032
+ ...
1033
+ jinja2.exceptions.UndefinedError: 'foo' is undefined
1034
+ >>> foo + 42
1035
+ Traceback (most recent call last):
1036
+ ...
1037
+ jinja2.exceptions.UndefinedError: 'foo' is undefined
1038
+ """
1039
+
1040
+ __slots__ = ()
1041
+ __iter__ = __str__ = __len__ = Undefined._fail_with_undefined_error
1042
+ __eq__ = __ne__ = __bool__ = __hash__ = Undefined._fail_with_undefined_error
1043
+ __contains__ = Undefined._fail_with_undefined_error
1044
+
1045
+
1046
+ # Remove slots attributes, after the metaclass is applied they are
1047
+ # unneeded and contain wrong data for subclasses.
1048
+ del (
1049
+ Undefined.__slots__,
1050
+ ChainableUndefined.__slots__,
1051
+ DebugUndefined.__slots__,
1052
+ StrictUndefined.__slots__,
1053
+ )
lib/python3.11/site-packages/jinja2/sandbox.py ADDED
@@ -0,0 +1,428 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """A sandbox layer that ensures unsafe operations cannot be performed.
2
+ Useful when the template itself comes from an untrusted source.
3
+ """
4
+ import operator
5
+ import types
6
+ import typing as t
7
+ from _string import formatter_field_name_split # type: ignore
8
+ from collections import abc
9
+ from collections import deque
10
+ from string import Formatter
11
+
12
+ from markupsafe import EscapeFormatter
13
+ from markupsafe import Markup
14
+
15
+ from .environment import Environment
16
+ from .exceptions import SecurityError
17
+ from .runtime import Context
18
+ from .runtime import Undefined
19
+
20
+ F = t.TypeVar("F", bound=t.Callable[..., t.Any])
21
+
22
+ #: maximum number of items a range may produce
23
+ MAX_RANGE = 100000
24
+
25
+ #: Unsafe function attributes.
26
+ UNSAFE_FUNCTION_ATTRIBUTES: t.Set[str] = set()
27
+
28
+ #: Unsafe method attributes. Function attributes are unsafe for methods too.
29
+ UNSAFE_METHOD_ATTRIBUTES: t.Set[str] = set()
30
+
31
+ #: unsafe generator attributes.
32
+ UNSAFE_GENERATOR_ATTRIBUTES = {"gi_frame", "gi_code"}
33
+
34
+ #: unsafe attributes on coroutines
35
+ UNSAFE_COROUTINE_ATTRIBUTES = {"cr_frame", "cr_code"}
36
+
37
+ #: unsafe attributes on async generators
38
+ UNSAFE_ASYNC_GENERATOR_ATTRIBUTES = {"ag_code", "ag_frame"}
39
+
40
+ _mutable_spec: t.Tuple[t.Tuple[t.Type, t.FrozenSet[str]], ...] = (
41
+ (
42
+ abc.MutableSet,
43
+ frozenset(
44
+ [
45
+ "add",
46
+ "clear",
47
+ "difference_update",
48
+ "discard",
49
+ "pop",
50
+ "remove",
51
+ "symmetric_difference_update",
52
+ "update",
53
+ ]
54
+ ),
55
+ ),
56
+ (
57
+ abc.MutableMapping,
58
+ frozenset(["clear", "pop", "popitem", "setdefault", "update"]),
59
+ ),
60
+ (
61
+ abc.MutableSequence,
62
+ frozenset(["append", "reverse", "insert", "sort", "extend", "remove"]),
63
+ ),
64
+ (
65
+ deque,
66
+ frozenset(
67
+ [
68
+ "append",
69
+ "appendleft",
70
+ "clear",
71
+ "extend",
72
+ "extendleft",
73
+ "pop",
74
+ "popleft",
75
+ "remove",
76
+ "rotate",
77
+ ]
78
+ ),
79
+ ),
80
+ )
81
+
82
+
83
+ def inspect_format_method(callable: t.Callable) -> t.Optional[str]:
84
+ if not isinstance(
85
+ callable, (types.MethodType, types.BuiltinMethodType)
86
+ ) or callable.__name__ not in ("format", "format_map"):
87
+ return None
88
+
89
+ obj = callable.__self__
90
+
91
+ if isinstance(obj, str):
92
+ return obj
93
+
94
+ return None
95
+
96
+
97
+ def safe_range(*args: int) -> range:
98
+ """A range that can't generate ranges with a length of more than
99
+ MAX_RANGE items.
100
+ """
101
+ rng = range(*args)
102
+
103
+ if len(rng) > MAX_RANGE:
104
+ raise OverflowError(
105
+ "Range too big. The sandbox blocks ranges larger than"
106
+ f" MAX_RANGE ({MAX_RANGE})."
107
+ )
108
+
109
+ return rng
110
+
111
+
112
+ def unsafe(f: F) -> F:
113
+ """Marks a function or method as unsafe.
114
+
115
+ .. code-block: python
116
+
117
+ @unsafe
118
+ def delete(self):
119
+ pass
120
+ """
121
+ f.unsafe_callable = True # type: ignore
122
+ return f
123
+
124
+
125
+ def is_internal_attribute(obj: t.Any, attr: str) -> bool:
126
+ """Test if the attribute given is an internal python attribute. For
127
+ example this function returns `True` for the `func_code` attribute of
128
+ python objects. This is useful if the environment method
129
+ :meth:`~SandboxedEnvironment.is_safe_attribute` is overridden.
130
+
131
+ >>> from jinja2.sandbox import is_internal_attribute
132
+ >>> is_internal_attribute(str, "mro")
133
+ True
134
+ >>> is_internal_attribute(str, "upper")
135
+ False
136
+ """
137
+ if isinstance(obj, types.FunctionType):
138
+ if attr in UNSAFE_FUNCTION_ATTRIBUTES:
139
+ return True
140
+ elif isinstance(obj, types.MethodType):
141
+ if attr in UNSAFE_FUNCTION_ATTRIBUTES or attr in UNSAFE_METHOD_ATTRIBUTES:
142
+ return True
143
+ elif isinstance(obj, type):
144
+ if attr == "mro":
145
+ return True
146
+ elif isinstance(obj, (types.CodeType, types.TracebackType, types.FrameType)):
147
+ return True
148
+ elif isinstance(obj, types.GeneratorType):
149
+ if attr in UNSAFE_GENERATOR_ATTRIBUTES:
150
+ return True
151
+ elif hasattr(types, "CoroutineType") and isinstance(obj, types.CoroutineType):
152
+ if attr in UNSAFE_COROUTINE_ATTRIBUTES:
153
+ return True
154
+ elif hasattr(types, "AsyncGeneratorType") and isinstance(
155
+ obj, types.AsyncGeneratorType
156
+ ):
157
+ if attr in UNSAFE_ASYNC_GENERATOR_ATTRIBUTES:
158
+ return True
159
+ return attr.startswith("__")
160
+
161
+
162
+ def modifies_known_mutable(obj: t.Any, attr: str) -> bool:
163
+ """This function checks if an attribute on a builtin mutable object
164
+ (list, dict, set or deque) or the corresponding ABCs would modify it
165
+ if called.
166
+
167
+ >>> modifies_known_mutable({}, "clear")
168
+ True
169
+ >>> modifies_known_mutable({}, "keys")
170
+ False
171
+ >>> modifies_known_mutable([], "append")
172
+ True
173
+ >>> modifies_known_mutable([], "index")
174
+ False
175
+
176
+ If called with an unsupported object, ``False`` is returned.
177
+
178
+ >>> modifies_known_mutable("foo", "upper")
179
+ False
180
+ """
181
+ for typespec, unsafe in _mutable_spec:
182
+ if isinstance(obj, typespec):
183
+ return attr in unsafe
184
+ return False
185
+
186
+
187
+ class SandboxedEnvironment(Environment):
188
+ """The sandboxed environment. It works like the regular environment but
189
+ tells the compiler to generate sandboxed code. Additionally subclasses of
190
+ this environment may override the methods that tell the runtime what
191
+ attributes or functions are safe to access.
192
+
193
+ If the template tries to access insecure code a :exc:`SecurityError` is
194
+ raised. However also other exceptions may occur during the rendering so
195
+ the caller has to ensure that all exceptions are caught.
196
+ """
197
+
198
+ sandboxed = True
199
+
200
+ #: default callback table for the binary operators. A copy of this is
201
+ #: available on each instance of a sandboxed environment as
202
+ #: :attr:`binop_table`
203
+ default_binop_table: t.Dict[str, t.Callable[[t.Any, t.Any], t.Any]] = {
204
+ "+": operator.add,
205
+ "-": operator.sub,
206
+ "*": operator.mul,
207
+ "/": operator.truediv,
208
+ "//": operator.floordiv,
209
+ "**": operator.pow,
210
+ "%": operator.mod,
211
+ }
212
+
213
+ #: default callback table for the unary operators. A copy of this is
214
+ #: available on each instance of a sandboxed environment as
215
+ #: :attr:`unop_table`
216
+ default_unop_table: t.Dict[str, t.Callable[[t.Any], t.Any]] = {
217
+ "+": operator.pos,
218
+ "-": operator.neg,
219
+ }
220
+
221
+ #: a set of binary operators that should be intercepted. Each operator
222
+ #: that is added to this set (empty by default) is delegated to the
223
+ #: :meth:`call_binop` method that will perform the operator. The default
224
+ #: operator callback is specified by :attr:`binop_table`.
225
+ #:
226
+ #: The following binary operators are interceptable:
227
+ #: ``//``, ``%``, ``+``, ``*``, ``-``, ``/``, and ``**``
228
+ #:
229
+ #: The default operation form the operator table corresponds to the
230
+ #: builtin function. Intercepted calls are always slower than the native
231
+ #: operator call, so make sure only to intercept the ones you are
232
+ #: interested in.
233
+ #:
234
+ #: .. versionadded:: 2.6
235
+ intercepted_binops: t.FrozenSet[str] = frozenset()
236
+
237
+ #: a set of unary operators that should be intercepted. Each operator
238
+ #: that is added to this set (empty by default) is delegated to the
239
+ #: :meth:`call_unop` method that will perform the operator. The default
240
+ #: operator callback is specified by :attr:`unop_table`.
241
+ #:
242
+ #: The following unary operators are interceptable: ``+``, ``-``
243
+ #:
244
+ #: The default operation form the operator table corresponds to the
245
+ #: builtin function. Intercepted calls are always slower than the native
246
+ #: operator call, so make sure only to intercept the ones you are
247
+ #: interested in.
248
+ #:
249
+ #: .. versionadded:: 2.6
250
+ intercepted_unops: t.FrozenSet[str] = frozenset()
251
+
252
+ def __init__(self, *args: t.Any, **kwargs: t.Any) -> None:
253
+ super().__init__(*args, **kwargs)
254
+ self.globals["range"] = safe_range
255
+ self.binop_table = self.default_binop_table.copy()
256
+ self.unop_table = self.default_unop_table.copy()
257
+
258
+ def is_safe_attribute(self, obj: t.Any, attr: str, value: t.Any) -> bool:
259
+ """The sandboxed environment will call this method to check if the
260
+ attribute of an object is safe to access. Per default all attributes
261
+ starting with an underscore are considered private as well as the
262
+ special attributes of internal python objects as returned by the
263
+ :func:`is_internal_attribute` function.
264
+ """
265
+ return not (attr.startswith("_") or is_internal_attribute(obj, attr))
266
+
267
+ def is_safe_callable(self, obj: t.Any) -> bool:
268
+ """Check if an object is safely callable. By default callables
269
+ are considered safe unless decorated with :func:`unsafe`.
270
+
271
+ This also recognizes the Django convention of setting
272
+ ``func.alters_data = True``.
273
+ """
274
+ return not (
275
+ getattr(obj, "unsafe_callable", False) or getattr(obj, "alters_data", False)
276
+ )
277
+
278
+ def call_binop(
279
+ self, context: Context, operator: str, left: t.Any, right: t.Any
280
+ ) -> t.Any:
281
+ """For intercepted binary operator calls (:meth:`intercepted_binops`)
282
+ this function is executed instead of the builtin operator. This can
283
+ be used to fine tune the behavior of certain operators.
284
+
285
+ .. versionadded:: 2.6
286
+ """
287
+ return self.binop_table[operator](left, right)
288
+
289
+ def call_unop(self, context: Context, operator: str, arg: t.Any) -> t.Any:
290
+ """For intercepted unary operator calls (:meth:`intercepted_unops`)
291
+ this function is executed instead of the builtin operator. This can
292
+ be used to fine tune the behavior of certain operators.
293
+
294
+ .. versionadded:: 2.6
295
+ """
296
+ return self.unop_table[operator](arg)
297
+
298
+ def getitem(
299
+ self, obj: t.Any, argument: t.Union[str, t.Any]
300
+ ) -> t.Union[t.Any, Undefined]:
301
+ """Subscribe an object from sandboxed code."""
302
+ try:
303
+ return obj[argument]
304
+ except (TypeError, LookupError):
305
+ if isinstance(argument, str):
306
+ try:
307
+ attr = str(argument)
308
+ except Exception:
309
+ pass
310
+ else:
311
+ try:
312
+ value = getattr(obj, attr)
313
+ except AttributeError:
314
+ pass
315
+ else:
316
+ if self.is_safe_attribute(obj, argument, value):
317
+ return value
318
+ return self.unsafe_undefined(obj, argument)
319
+ return self.undefined(obj=obj, name=argument)
320
+
321
+ def getattr(self, obj: t.Any, attribute: str) -> t.Union[t.Any, Undefined]:
322
+ """Subscribe an object from sandboxed code and prefer the
323
+ attribute. The attribute passed *must* be a bytestring.
324
+ """
325
+ try:
326
+ value = getattr(obj, attribute)
327
+ except AttributeError:
328
+ try:
329
+ return obj[attribute]
330
+ except (TypeError, LookupError):
331
+ pass
332
+ else:
333
+ if self.is_safe_attribute(obj, attribute, value):
334
+ return value
335
+ return self.unsafe_undefined(obj, attribute)
336
+ return self.undefined(obj=obj, name=attribute)
337
+
338
+ def unsafe_undefined(self, obj: t.Any, attribute: str) -> Undefined:
339
+ """Return an undefined object for unsafe attributes."""
340
+ return self.undefined(
341
+ f"access to attribute {attribute!r} of"
342
+ f" {type(obj).__name__!r} object is unsafe.",
343
+ name=attribute,
344
+ obj=obj,
345
+ exc=SecurityError,
346
+ )
347
+
348
+ def format_string(
349
+ self,
350
+ s: str,
351
+ args: t.Tuple[t.Any, ...],
352
+ kwargs: t.Dict[str, t.Any],
353
+ format_func: t.Optional[t.Callable] = None,
354
+ ) -> str:
355
+ """If a format call is detected, then this is routed through this
356
+ method so that our safety sandbox can be used for it.
357
+ """
358
+ formatter: SandboxedFormatter
359
+ if isinstance(s, Markup):
360
+ formatter = SandboxedEscapeFormatter(self, escape=s.escape)
361
+ else:
362
+ formatter = SandboxedFormatter(self)
363
+
364
+ if format_func is not None and format_func.__name__ == "format_map":
365
+ if len(args) != 1 or kwargs:
366
+ raise TypeError(
367
+ "format_map() takes exactly one argument"
368
+ f" {len(args) + (kwargs is not None)} given"
369
+ )
370
+
371
+ kwargs = args[0]
372
+ args = ()
373
+
374
+ rv = formatter.vformat(s, args, kwargs)
375
+ return type(s)(rv)
376
+
377
+ def call(
378
+ __self, # noqa: B902
379
+ __context: Context,
380
+ __obj: t.Any,
381
+ *args: t.Any,
382
+ **kwargs: t.Any,
383
+ ) -> t.Any:
384
+ """Call an object from sandboxed code."""
385
+ fmt = inspect_format_method(__obj)
386
+ if fmt is not None:
387
+ return __self.format_string(fmt, args, kwargs, __obj)
388
+
389
+ # the double prefixes are to avoid double keyword argument
390
+ # errors when proxying the call.
391
+ if not __self.is_safe_callable(__obj):
392
+ raise SecurityError(f"{__obj!r} is not safely callable")
393
+ return __context.call(__obj, *args, **kwargs)
394
+
395
+
396
+ class ImmutableSandboxedEnvironment(SandboxedEnvironment):
397
+ """Works exactly like the regular `SandboxedEnvironment` but does not
398
+ permit modifications on the builtin mutable objects `list`, `set`, and
399
+ `dict` by using the :func:`modifies_known_mutable` function.
400
+ """
401
+
402
+ def is_safe_attribute(self, obj: t.Any, attr: str, value: t.Any) -> bool:
403
+ if not super().is_safe_attribute(obj, attr, value):
404
+ return False
405
+
406
+ return not modifies_known_mutable(obj, attr)
407
+
408
+
409
+ class SandboxedFormatter(Formatter):
410
+ def __init__(self, env: Environment, **kwargs: t.Any) -> None:
411
+ self._env = env
412
+ super().__init__(**kwargs)
413
+
414
+ def get_field(
415
+ self, field_name: str, args: t.Sequence[t.Any], kwargs: t.Mapping[str, t.Any]
416
+ ) -> t.Tuple[t.Any, str]:
417
+ first, rest = formatter_field_name_split(field_name)
418
+ obj = self.get_value(first, args, kwargs)
419
+ for is_attr, i in rest:
420
+ if is_attr:
421
+ obj = self._env.getattr(obj, i)
422
+ else:
423
+ obj = self._env.getitem(obj, i)
424
+ return obj, first
425
+
426
+
427
+ class SandboxedEscapeFormatter(SandboxedFormatter, EscapeFormatter):
428
+ pass
lib/python3.11/site-packages/jinja2/tests.py ADDED
@@ -0,0 +1,255 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Built-in template tests used with the ``is`` operator."""
2
+ import operator
3
+ import typing as t
4
+ from collections import abc
5
+ from numbers import Number
6
+
7
+ from .runtime import Undefined
8
+ from .utils import pass_environment
9
+
10
+ if t.TYPE_CHECKING:
11
+ from .environment import Environment
12
+
13
+
14
+ def test_odd(value: int) -> bool:
15
+ """Return true if the variable is odd."""
16
+ return value % 2 == 1
17
+
18
+
19
+ def test_even(value: int) -> bool:
20
+ """Return true if the variable is even."""
21
+ return value % 2 == 0
22
+
23
+
24
+ def test_divisibleby(value: int, num: int) -> bool:
25
+ """Check if a variable is divisible by a number."""
26
+ return value % num == 0
27
+
28
+
29
+ def test_defined(value: t.Any) -> bool:
30
+ """Return true if the variable is defined:
31
+
32
+ .. sourcecode:: jinja
33
+
34
+ {% if variable is defined %}
35
+ value of variable: {{ variable }}
36
+ {% else %}
37
+ variable is not defined
38
+ {% endif %}
39
+
40
+ See the :func:`default` filter for a simple way to set undefined
41
+ variables.
42
+ """
43
+ return not isinstance(value, Undefined)
44
+
45
+
46
+ def test_undefined(value: t.Any) -> bool:
47
+ """Like :func:`defined` but the other way round."""
48
+ return isinstance(value, Undefined)
49
+
50
+
51
+ @pass_environment
52
+ def test_filter(env: "Environment", value: str) -> bool:
53
+ """Check if a filter exists by name. Useful if a filter may be
54
+ optionally available.
55
+
56
+ .. code-block:: jinja
57
+
58
+ {% if 'markdown' is filter %}
59
+ {{ value | markdown }}
60
+ {% else %}
61
+ {{ value }}
62
+ {% endif %}
63
+
64
+ .. versionadded:: 3.0
65
+ """
66
+ return value in env.filters
67
+
68
+
69
+ @pass_environment
70
+ def test_test(env: "Environment", value: str) -> bool:
71
+ """Check if a test exists by name. Useful if a test may be
72
+ optionally available.
73
+
74
+ .. code-block:: jinja
75
+
76
+ {% if 'loud' is test %}
77
+ {% if value is loud %}
78
+ {{ value|upper }}
79
+ {% else %}
80
+ {{ value|lower }}
81
+ {% endif %}
82
+ {% else %}
83
+ {{ value }}
84
+ {% endif %}
85
+
86
+ .. versionadded:: 3.0
87
+ """
88
+ return value in env.tests
89
+
90
+
91
+ def test_none(value: t.Any) -> bool:
92
+ """Return true if the variable is none."""
93
+ return value is None
94
+
95
+
96
+ def test_boolean(value: t.Any) -> bool:
97
+ """Return true if the object is a boolean value.
98
+
99
+ .. versionadded:: 2.11
100
+ """
101
+ return value is True or value is False
102
+
103
+
104
+ def test_false(value: t.Any) -> bool:
105
+ """Return true if the object is False.
106
+
107
+ .. versionadded:: 2.11
108
+ """
109
+ return value is False
110
+
111
+
112
+ def test_true(value: t.Any) -> bool:
113
+ """Return true if the object is True.
114
+
115
+ .. versionadded:: 2.11
116
+ """
117
+ return value is True
118
+
119
+
120
+ # NOTE: The existing 'number' test matches booleans and floats
121
+ def test_integer(value: t.Any) -> bool:
122
+ """Return true if the object is an integer.
123
+
124
+ .. versionadded:: 2.11
125
+ """
126
+ return isinstance(value, int) and value is not True and value is not False
127
+
128
+
129
+ # NOTE: The existing 'number' test matches booleans and integers
130
+ def test_float(value: t.Any) -> bool:
131
+ """Return true if the object is a float.
132
+
133
+ .. versionadded:: 2.11
134
+ """
135
+ return isinstance(value, float)
136
+
137
+
138
+ def test_lower(value: str) -> bool:
139
+ """Return true if the variable is lowercased."""
140
+ return str(value).islower()
141
+
142
+
143
+ def test_upper(value: str) -> bool:
144
+ """Return true if the variable is uppercased."""
145
+ return str(value).isupper()
146
+
147
+
148
+ def test_string(value: t.Any) -> bool:
149
+ """Return true if the object is a string."""
150
+ return isinstance(value, str)
151
+
152
+
153
+ def test_mapping(value: t.Any) -> bool:
154
+ """Return true if the object is a mapping (dict etc.).
155
+
156
+ .. versionadded:: 2.6
157
+ """
158
+ return isinstance(value, abc.Mapping)
159
+
160
+
161
+ def test_number(value: t.Any) -> bool:
162
+ """Return true if the variable is a number."""
163
+ return isinstance(value, Number)
164
+
165
+
166
+ def test_sequence(value: t.Any) -> bool:
167
+ """Return true if the variable is a sequence. Sequences are variables
168
+ that are iterable.
169
+ """
170
+ try:
171
+ len(value)
172
+ value.__getitem__
173
+ except Exception:
174
+ return False
175
+
176
+ return True
177
+
178
+
179
+ def test_sameas(value: t.Any, other: t.Any) -> bool:
180
+ """Check if an object points to the same memory address than another
181
+ object:
182
+
183
+ .. sourcecode:: jinja
184
+
185
+ {% if foo.attribute is sameas false %}
186
+ the foo attribute really is the `False` singleton
187
+ {% endif %}
188
+ """
189
+ return value is other
190
+
191
+
192
+ def test_iterable(value: t.Any) -> bool:
193
+ """Check if it's possible to iterate over an object."""
194
+ try:
195
+ iter(value)
196
+ except TypeError:
197
+ return False
198
+
199
+ return True
200
+
201
+
202
+ def test_escaped(value: t.Any) -> bool:
203
+ """Check if the value is escaped."""
204
+ return hasattr(value, "__html__")
205
+
206
+
207
+ def test_in(value: t.Any, seq: t.Container) -> bool:
208
+ """Check if value is in seq.
209
+
210
+ .. versionadded:: 2.10
211
+ """
212
+ return value in seq
213
+
214
+
215
+ TESTS = {
216
+ "odd": test_odd,
217
+ "even": test_even,
218
+ "divisibleby": test_divisibleby,
219
+ "defined": test_defined,
220
+ "undefined": test_undefined,
221
+ "filter": test_filter,
222
+ "test": test_test,
223
+ "none": test_none,
224
+ "boolean": test_boolean,
225
+ "false": test_false,
226
+ "true": test_true,
227
+ "integer": test_integer,
228
+ "float": test_float,
229
+ "lower": test_lower,
230
+ "upper": test_upper,
231
+ "string": test_string,
232
+ "mapping": test_mapping,
233
+ "number": test_number,
234
+ "sequence": test_sequence,
235
+ "iterable": test_iterable,
236
+ "callable": callable,
237
+ "sameas": test_sameas,
238
+ "escaped": test_escaped,
239
+ "in": test_in,
240
+ "==": operator.eq,
241
+ "eq": operator.eq,
242
+ "equalto": operator.eq,
243
+ "!=": operator.ne,
244
+ "ne": operator.ne,
245
+ ">": operator.gt,
246
+ "gt": operator.gt,
247
+ "greaterthan": operator.gt,
248
+ "ge": operator.ge,
249
+ ">=": operator.ge,
250
+ "<": operator.lt,
251
+ "lt": operator.lt,
252
+ "lessthan": operator.lt,
253
+ "<=": operator.le,
254
+ "le": operator.le,
255
+ }
lib/python3.11/site-packages/jinja2/utils.py ADDED
@@ -0,0 +1,755 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import enum
2
+ import json
3
+ import os
4
+ import re
5
+ import typing as t
6
+ from collections import abc
7
+ from collections import deque
8
+ from random import choice
9
+ from random import randrange
10
+ from threading import Lock
11
+ from types import CodeType
12
+ from urllib.parse import quote_from_bytes
13
+
14
+ import markupsafe
15
+
16
+ if t.TYPE_CHECKING:
17
+ import typing_extensions as te
18
+
19
+ F = t.TypeVar("F", bound=t.Callable[..., t.Any])
20
+
21
+ # special singleton representing missing values for the runtime
22
+ missing: t.Any = type("MissingType", (), {"__repr__": lambda x: "missing"})()
23
+
24
+ internal_code: t.MutableSet[CodeType] = set()
25
+
26
+ concat = "".join
27
+
28
+
29
+ def pass_context(f: F) -> F:
30
+ """Pass the :class:`~jinja2.runtime.Context` as the first argument
31
+ to the decorated function when called while rendering a template.
32
+
33
+ Can be used on functions, filters, and tests.
34
+
35
+ If only ``Context.eval_context`` is needed, use
36
+ :func:`pass_eval_context`. If only ``Context.environment`` is
37
+ needed, use :func:`pass_environment`.
38
+
39
+ .. versionadded:: 3.0.0
40
+ Replaces ``contextfunction`` and ``contextfilter``.
41
+ """
42
+ f.jinja_pass_arg = _PassArg.context # type: ignore
43
+ return f
44
+
45
+
46
+ def pass_eval_context(f: F) -> F:
47
+ """Pass the :class:`~jinja2.nodes.EvalContext` as the first argument
48
+ to the decorated function when called while rendering a template.
49
+ See :ref:`eval-context`.
50
+
51
+ Can be used on functions, filters, and tests.
52
+
53
+ If only ``EvalContext.environment`` is needed, use
54
+ :func:`pass_environment`.
55
+
56
+ .. versionadded:: 3.0.0
57
+ Replaces ``evalcontextfunction`` and ``evalcontextfilter``.
58
+ """
59
+ f.jinja_pass_arg = _PassArg.eval_context # type: ignore
60
+ return f
61
+
62
+
63
+ def pass_environment(f: F) -> F:
64
+ """Pass the :class:`~jinja2.Environment` as the first argument to
65
+ the decorated function when called while rendering a template.
66
+
67
+ Can be used on functions, filters, and tests.
68
+
69
+ .. versionadded:: 3.0.0
70
+ Replaces ``environmentfunction`` and ``environmentfilter``.
71
+ """
72
+ f.jinja_pass_arg = _PassArg.environment # type: ignore
73
+ return f
74
+
75
+
76
+ class _PassArg(enum.Enum):
77
+ context = enum.auto()
78
+ eval_context = enum.auto()
79
+ environment = enum.auto()
80
+
81
+ @classmethod
82
+ def from_obj(cls, obj: F) -> t.Optional["_PassArg"]:
83
+ if hasattr(obj, "jinja_pass_arg"):
84
+ return obj.jinja_pass_arg # type: ignore
85
+
86
+ return None
87
+
88
+
89
+ def internalcode(f: F) -> F:
90
+ """Marks the function as internally used"""
91
+ internal_code.add(f.__code__)
92
+ return f
93
+
94
+
95
+ def is_undefined(obj: t.Any) -> bool:
96
+ """Check if the object passed is undefined. This does nothing more than
97
+ performing an instance check against :class:`Undefined` but looks nicer.
98
+ This can be used for custom filters or tests that want to react to
99
+ undefined variables. For example a custom default filter can look like
100
+ this::
101
+
102
+ def default(var, default=''):
103
+ if is_undefined(var):
104
+ return default
105
+ return var
106
+ """
107
+ from .runtime import Undefined
108
+
109
+ return isinstance(obj, Undefined)
110
+
111
+
112
+ def consume(iterable: t.Iterable[t.Any]) -> None:
113
+ """Consumes an iterable without doing anything with it."""
114
+ for _ in iterable:
115
+ pass
116
+
117
+
118
+ def clear_caches() -> None:
119
+ """Jinja keeps internal caches for environments and lexers. These are
120
+ used so that Jinja doesn't have to recreate environments and lexers all
121
+ the time. Normally you don't have to care about that but if you are
122
+ measuring memory consumption you may want to clean the caches.
123
+ """
124
+ from .environment import get_spontaneous_environment
125
+ from .lexer import _lexer_cache
126
+
127
+ get_spontaneous_environment.cache_clear()
128
+ _lexer_cache.clear()
129
+
130
+
131
+ def import_string(import_name: str, silent: bool = False) -> t.Any:
132
+ """Imports an object based on a string. This is useful if you want to
133
+ use import paths as endpoints or something similar. An import path can
134
+ be specified either in dotted notation (``xml.sax.saxutils.escape``)
135
+ or with a colon as object delimiter (``xml.sax.saxutils:escape``).
136
+
137
+ If the `silent` is True the return value will be `None` if the import
138
+ fails.
139
+
140
+ :return: imported object
141
+ """
142
+ try:
143
+ if ":" in import_name:
144
+ module, obj = import_name.split(":", 1)
145
+ elif "." in import_name:
146
+ module, _, obj = import_name.rpartition(".")
147
+ else:
148
+ return __import__(import_name)
149
+ return getattr(__import__(module, None, None, [obj]), obj)
150
+ except (ImportError, AttributeError):
151
+ if not silent:
152
+ raise
153
+
154
+
155
+ def open_if_exists(filename: str, mode: str = "rb") -> t.Optional[t.IO]:
156
+ """Returns a file descriptor for the filename if that file exists,
157
+ otherwise ``None``.
158
+ """
159
+ if not os.path.isfile(filename):
160
+ return None
161
+
162
+ return open(filename, mode)
163
+
164
+
165
+ def object_type_repr(obj: t.Any) -> str:
166
+ """Returns the name of the object's type. For some recognized
167
+ singletons the name of the object is returned instead. (For
168
+ example for `None` and `Ellipsis`).
169
+ """
170
+ if obj is None:
171
+ return "None"
172
+ elif obj is Ellipsis:
173
+ return "Ellipsis"
174
+
175
+ cls = type(obj)
176
+
177
+ if cls.__module__ == "builtins":
178
+ return f"{cls.__name__} object"
179
+
180
+ return f"{cls.__module__}.{cls.__name__} object"
181
+
182
+
183
+ def pformat(obj: t.Any) -> str:
184
+ """Format an object using :func:`pprint.pformat`."""
185
+ from pprint import pformat # type: ignore
186
+
187
+ return pformat(obj)
188
+
189
+
190
+ _http_re = re.compile(
191
+ r"""
192
+ ^
193
+ (
194
+ (https?://|www\.) # scheme or www
195
+ (([\w%-]+\.)+)? # subdomain
196
+ (
197
+ [a-z]{2,63} # basic tld
198
+ |
199
+ xn--[\w%]{2,59} # idna tld
200
+ )
201
+ |
202
+ ([\w%-]{2,63}\.)+ # basic domain
203
+ (com|net|int|edu|gov|org|info|mil) # basic tld
204
+ |
205
+ (https?://) # scheme
206
+ (
207
+ (([\d]{1,3})(\.[\d]{1,3}){3}) # IPv4
208
+ |
209
+ (\[([\da-f]{0,4}:){2}([\da-f]{0,4}:?){1,6}]) # IPv6
210
+ )
211
+ )
212
+ (?::[\d]{1,5})? # port
213
+ (?:[/?#]\S*)? # path, query, and fragment
214
+ $
215
+ """,
216
+ re.IGNORECASE | re.VERBOSE,
217
+ )
218
+ _email_re = re.compile(r"^\S+@\w[\w.-]*\.\w+$")
219
+
220
+
221
+ def urlize(
222
+ text: str,
223
+ trim_url_limit: t.Optional[int] = None,
224
+ rel: t.Optional[str] = None,
225
+ target: t.Optional[str] = None,
226
+ extra_schemes: t.Optional[t.Iterable[str]] = None,
227
+ ) -> str:
228
+ """Convert URLs in text into clickable links.
229
+
230
+ This may not recognize links in some situations. Usually, a more
231
+ comprehensive formatter, such as a Markdown library, is a better
232
+ choice.
233
+
234
+ Works on ``http://``, ``https://``, ``www.``, ``mailto:``, and email
235
+ addresses. Links with trailing punctuation (periods, commas, closing
236
+ parentheses) and leading punctuation (opening parentheses) are
237
+ recognized excluding the punctuation. Email addresses that include
238
+ header fields are not recognized (for example,
239
+ ``mailto:address@example.com?cc=copy@example.com``).
240
+
241
+ :param text: Original text containing URLs to link.
242
+ :param trim_url_limit: Shorten displayed URL values to this length.
243
+ :param target: Add the ``target`` attribute to links.
244
+ :param rel: Add the ``rel`` attribute to links.
245
+ :param extra_schemes: Recognize URLs that start with these schemes
246
+ in addition to the default behavior.
247
+
248
+ .. versionchanged:: 3.0
249
+ The ``extra_schemes`` parameter was added.
250
+
251
+ .. versionchanged:: 3.0
252
+ Generate ``https://`` links for URLs without a scheme.
253
+
254
+ .. versionchanged:: 3.0
255
+ The parsing rules were updated. Recognize email addresses with
256
+ or without the ``mailto:`` scheme. Validate IP addresses. Ignore
257
+ parentheses and brackets in more cases.
258
+ """
259
+ if trim_url_limit is not None:
260
+
261
+ def trim_url(x: str) -> str:
262
+ if len(x) > trim_url_limit: # type: ignore
263
+ return f"{x[:trim_url_limit]}..."
264
+
265
+ return x
266
+
267
+ else:
268
+
269
+ def trim_url(x: str) -> str:
270
+ return x
271
+
272
+ words = re.split(r"(\s+)", str(markupsafe.escape(text)))
273
+ rel_attr = f' rel="{markupsafe.escape(rel)}"' if rel else ""
274
+ target_attr = f' target="{markupsafe.escape(target)}"' if target else ""
275
+
276
+ for i, word in enumerate(words):
277
+ head, middle, tail = "", word, ""
278
+ match = re.match(r"^([(<]|&lt;)+", middle)
279
+
280
+ if match:
281
+ head = match.group()
282
+ middle = middle[match.end() :]
283
+
284
+ # Unlike lead, which is anchored to the start of the string,
285
+ # need to check that the string ends with any of the characters
286
+ # before trying to match all of them, to avoid backtracking.
287
+ if middle.endswith((")", ">", ".", ",", "\n", "&gt;")):
288
+ match = re.search(r"([)>.,\n]|&gt;)+$", middle)
289
+
290
+ if match:
291
+ tail = match.group()
292
+ middle = middle[: match.start()]
293
+
294
+ # Prefer balancing parentheses in URLs instead of ignoring a
295
+ # trailing character.
296
+ for start_char, end_char in ("(", ")"), ("<", ">"), ("&lt;", "&gt;"):
297
+ start_count = middle.count(start_char)
298
+
299
+ if start_count <= middle.count(end_char):
300
+ # Balanced, or lighter on the left
301
+ continue
302
+
303
+ # Move as many as possible from the tail to balance
304
+ for _ in range(min(start_count, tail.count(end_char))):
305
+ end_index = tail.index(end_char) + len(end_char)
306
+ # Move anything in the tail before the end char too
307
+ middle += tail[:end_index]
308
+ tail = tail[end_index:]
309
+
310
+ if _http_re.match(middle):
311
+ if middle.startswith("https://") or middle.startswith("http://"):
312
+ middle = (
313
+ f'<a href="{middle}"{rel_attr}{target_attr}>{trim_url(middle)}</a>'
314
+ )
315
+ else:
316
+ middle = (
317
+ f'<a href="https://{middle}"{rel_attr}{target_attr}>'
318
+ f"{trim_url(middle)}</a>"
319
+ )
320
+
321
+ elif middle.startswith("mailto:") and _email_re.match(middle[7:]):
322
+ middle = f'<a href="{middle}">{middle[7:]}</a>'
323
+
324
+ elif (
325
+ "@" in middle
326
+ and not middle.startswith("www.")
327
+ and ":" not in middle
328
+ and _email_re.match(middle)
329
+ ):
330
+ middle = f'<a href="mailto:{middle}">{middle}</a>'
331
+
332
+ elif extra_schemes is not None:
333
+ for scheme in extra_schemes:
334
+ if middle != scheme and middle.startswith(scheme):
335
+ middle = f'<a href="{middle}"{rel_attr}{target_attr}>{middle}</a>'
336
+
337
+ words[i] = f"{head}{middle}{tail}"
338
+
339
+ return "".join(words)
340
+
341
+
342
+ def generate_lorem_ipsum(
343
+ n: int = 5, html: bool = True, min: int = 20, max: int = 100
344
+ ) -> str:
345
+ """Generate some lorem ipsum for the template."""
346
+ from .constants import LOREM_IPSUM_WORDS
347
+
348
+ words = LOREM_IPSUM_WORDS.split()
349
+ result = []
350
+
351
+ for _ in range(n):
352
+ next_capitalized = True
353
+ last_comma = last_fullstop = 0
354
+ word = None
355
+ last = None
356
+ p = []
357
+
358
+ # each paragraph contains out of 20 to 100 words.
359
+ for idx, _ in enumerate(range(randrange(min, max))):
360
+ while True:
361
+ word = choice(words)
362
+ if word != last:
363
+ last = word
364
+ break
365
+ if next_capitalized:
366
+ word = word.capitalize()
367
+ next_capitalized = False
368
+ # add commas
369
+ if idx - randrange(3, 8) > last_comma:
370
+ last_comma = idx
371
+ last_fullstop += 2
372
+ word += ","
373
+ # add end of sentences
374
+ if idx - randrange(10, 20) > last_fullstop:
375
+ last_comma = last_fullstop = idx
376
+ word += "."
377
+ next_capitalized = True
378
+ p.append(word)
379
+
380
+ # ensure that the paragraph ends with a dot.
381
+ p_str = " ".join(p)
382
+
383
+ if p_str.endswith(","):
384
+ p_str = p_str[:-1] + "."
385
+ elif not p_str.endswith("."):
386
+ p_str += "."
387
+
388
+ result.append(p_str)
389
+
390
+ if not html:
391
+ return "\n\n".join(result)
392
+ return markupsafe.Markup(
393
+ "\n".join(f"<p>{markupsafe.escape(x)}</p>" for x in result)
394
+ )
395
+
396
+
397
+ def url_quote(obj: t.Any, charset: str = "utf-8", for_qs: bool = False) -> str:
398
+ """Quote a string for use in a URL using the given charset.
399
+
400
+ :param obj: String or bytes to quote. Other types are converted to
401
+ string then encoded to bytes using the given charset.
402
+ :param charset: Encode text to bytes using this charset.
403
+ :param for_qs: Quote "/" and use "+" for spaces.
404
+ """
405
+ if not isinstance(obj, bytes):
406
+ if not isinstance(obj, str):
407
+ obj = str(obj)
408
+
409
+ obj = obj.encode(charset)
410
+
411
+ safe = b"" if for_qs else b"/"
412
+ rv = quote_from_bytes(obj, safe)
413
+
414
+ if for_qs:
415
+ rv = rv.replace("%20", "+")
416
+
417
+ return rv
418
+
419
+
420
+ @abc.MutableMapping.register
421
+ class LRUCache:
422
+ """A simple LRU Cache implementation."""
423
+
424
+ # this is fast for small capacities (something below 1000) but doesn't
425
+ # scale. But as long as it's only used as storage for templates this
426
+ # won't do any harm.
427
+
428
+ def __init__(self, capacity: int) -> None:
429
+ self.capacity = capacity
430
+ self._mapping: t.Dict[t.Any, t.Any] = {}
431
+ self._queue: "te.Deque[t.Any]" = deque()
432
+ self._postinit()
433
+
434
+ def _postinit(self) -> None:
435
+ # alias all queue methods for faster lookup
436
+ self._popleft = self._queue.popleft
437
+ self._pop = self._queue.pop
438
+ self._remove = self._queue.remove
439
+ self._wlock = Lock()
440
+ self._append = self._queue.append
441
+
442
+ def __getstate__(self) -> t.Mapping[str, t.Any]:
443
+ return {
444
+ "capacity": self.capacity,
445
+ "_mapping": self._mapping,
446
+ "_queue": self._queue,
447
+ }
448
+
449
+ def __setstate__(self, d: t.Mapping[str, t.Any]) -> None:
450
+ self.__dict__.update(d)
451
+ self._postinit()
452
+
453
+ def __getnewargs__(self) -> t.Tuple:
454
+ return (self.capacity,)
455
+
456
+ def copy(self) -> "LRUCache":
457
+ """Return a shallow copy of the instance."""
458
+ rv = self.__class__(self.capacity)
459
+ rv._mapping.update(self._mapping)
460
+ rv._queue.extend(self._queue)
461
+ return rv
462
+
463
+ def get(self, key: t.Any, default: t.Any = None) -> t.Any:
464
+ """Return an item from the cache dict or `default`"""
465
+ try:
466
+ return self[key]
467
+ except KeyError:
468
+ return default
469
+
470
+ def setdefault(self, key: t.Any, default: t.Any = None) -> t.Any:
471
+ """Set `default` if the key is not in the cache otherwise
472
+ leave unchanged. Return the value of this key.
473
+ """
474
+ try:
475
+ return self[key]
476
+ except KeyError:
477
+ self[key] = default
478
+ return default
479
+
480
+ def clear(self) -> None:
481
+ """Clear the cache."""
482
+ with self._wlock:
483
+ self._mapping.clear()
484
+ self._queue.clear()
485
+
486
+ def __contains__(self, key: t.Any) -> bool:
487
+ """Check if a key exists in this cache."""
488
+ return key in self._mapping
489
+
490
+ def __len__(self) -> int:
491
+ """Return the current size of the cache."""
492
+ return len(self._mapping)
493
+
494
+ def __repr__(self) -> str:
495
+ return f"<{type(self).__name__} {self._mapping!r}>"
496
+
497
+ def __getitem__(self, key: t.Any) -> t.Any:
498
+ """Get an item from the cache. Moves the item up so that it has the
499
+ highest priority then.
500
+
501
+ Raise a `KeyError` if it does not exist.
502
+ """
503
+ with self._wlock:
504
+ rv = self._mapping[key]
505
+
506
+ if self._queue[-1] != key:
507
+ try:
508
+ self._remove(key)
509
+ except ValueError:
510
+ # if something removed the key from the container
511
+ # when we read, ignore the ValueError that we would
512
+ # get otherwise.
513
+ pass
514
+
515
+ self._append(key)
516
+
517
+ return rv
518
+
519
+ def __setitem__(self, key: t.Any, value: t.Any) -> None:
520
+ """Sets the value for an item. Moves the item up so that it
521
+ has the highest priority then.
522
+ """
523
+ with self._wlock:
524
+ if key in self._mapping:
525
+ self._remove(key)
526
+ elif len(self._mapping) == self.capacity:
527
+ del self._mapping[self._popleft()]
528
+
529
+ self._append(key)
530
+ self._mapping[key] = value
531
+
532
+ def __delitem__(self, key: t.Any) -> None:
533
+ """Remove an item from the cache dict.
534
+ Raise a `KeyError` if it does not exist.
535
+ """
536
+ with self._wlock:
537
+ del self._mapping[key]
538
+
539
+ try:
540
+ self._remove(key)
541
+ except ValueError:
542
+ pass
543
+
544
+ def items(self) -> t.Iterable[t.Tuple[t.Any, t.Any]]:
545
+ """Return a list of items."""
546
+ result = [(key, self._mapping[key]) for key in list(self._queue)]
547
+ result.reverse()
548
+ return result
549
+
550
+ def values(self) -> t.Iterable[t.Any]:
551
+ """Return a list of all values."""
552
+ return [x[1] for x in self.items()]
553
+
554
+ def keys(self) -> t.Iterable[t.Any]:
555
+ """Return a list of all keys ordered by most recent usage."""
556
+ return list(self)
557
+
558
+ def __iter__(self) -> t.Iterator[t.Any]:
559
+ return reversed(tuple(self._queue))
560
+
561
+ def __reversed__(self) -> t.Iterator[t.Any]:
562
+ """Iterate over the keys in the cache dict, oldest items
563
+ coming first.
564
+ """
565
+ return iter(tuple(self._queue))
566
+
567
+ __copy__ = copy
568
+
569
+
570
+ def select_autoescape(
571
+ enabled_extensions: t.Collection[str] = ("html", "htm", "xml"),
572
+ disabled_extensions: t.Collection[str] = (),
573
+ default_for_string: bool = True,
574
+ default: bool = False,
575
+ ) -> t.Callable[[t.Optional[str]], bool]:
576
+ """Intelligently sets the initial value of autoescaping based on the
577
+ filename of the template. This is the recommended way to configure
578
+ autoescaping if you do not want to write a custom function yourself.
579
+
580
+ If you want to enable it for all templates created from strings or
581
+ for all templates with `.html` and `.xml` extensions::
582
+
583
+ from jinja2 import Environment, select_autoescape
584
+ env = Environment(autoescape=select_autoescape(
585
+ enabled_extensions=('html', 'xml'),
586
+ default_for_string=True,
587
+ ))
588
+
589
+ Example configuration to turn it on at all times except if the template
590
+ ends with `.txt`::
591
+
592
+ from jinja2 import Environment, select_autoescape
593
+ env = Environment(autoescape=select_autoescape(
594
+ disabled_extensions=('txt',),
595
+ default_for_string=True,
596
+ default=True,
597
+ ))
598
+
599
+ The `enabled_extensions` is an iterable of all the extensions that
600
+ autoescaping should be enabled for. Likewise `disabled_extensions` is
601
+ a list of all templates it should be disabled for. If a template is
602
+ loaded from a string then the default from `default_for_string` is used.
603
+ If nothing matches then the initial value of autoescaping is set to the
604
+ value of `default`.
605
+
606
+ For security reasons this function operates case insensitive.
607
+
608
+ .. versionadded:: 2.9
609
+ """
610
+ enabled_patterns = tuple(f".{x.lstrip('.').lower()}" for x in enabled_extensions)
611
+ disabled_patterns = tuple(f".{x.lstrip('.').lower()}" for x in disabled_extensions)
612
+
613
+ def autoescape(template_name: t.Optional[str]) -> bool:
614
+ if template_name is None:
615
+ return default_for_string
616
+ template_name = template_name.lower()
617
+ if template_name.endswith(enabled_patterns):
618
+ return True
619
+ if template_name.endswith(disabled_patterns):
620
+ return False
621
+ return default
622
+
623
+ return autoescape
624
+
625
+
626
+ def htmlsafe_json_dumps(
627
+ obj: t.Any, dumps: t.Optional[t.Callable[..., str]] = None, **kwargs: t.Any
628
+ ) -> markupsafe.Markup:
629
+ """Serialize an object to a string of JSON with :func:`json.dumps`,
630
+ then replace HTML-unsafe characters with Unicode escapes and mark
631
+ the result safe with :class:`~markupsafe.Markup`.
632
+
633
+ This is available in templates as the ``|tojson`` filter.
634
+
635
+ The following characters are escaped: ``<``, ``>``, ``&``, ``'``.
636
+
637
+ The returned string is safe to render in HTML documents and
638
+ ``<script>`` tags. The exception is in HTML attributes that are
639
+ double quoted; either use single quotes or the ``|forceescape``
640
+ filter.
641
+
642
+ :param obj: The object to serialize to JSON.
643
+ :param dumps: The ``dumps`` function to use. Defaults to
644
+ ``env.policies["json.dumps_function"]``, which defaults to
645
+ :func:`json.dumps`.
646
+ :param kwargs: Extra arguments to pass to ``dumps``. Merged onto
647
+ ``env.policies["json.dumps_kwargs"]``.
648
+
649
+ .. versionchanged:: 3.0
650
+ The ``dumper`` parameter is renamed to ``dumps``.
651
+
652
+ .. versionadded:: 2.9
653
+ """
654
+ if dumps is None:
655
+ dumps = json.dumps
656
+
657
+ return markupsafe.Markup(
658
+ dumps(obj, **kwargs)
659
+ .replace("<", "\\u003c")
660
+ .replace(">", "\\u003e")
661
+ .replace("&", "\\u0026")
662
+ .replace("'", "\\u0027")
663
+ )
664
+
665
+
666
+ class Cycler:
667
+ """Cycle through values by yield them one at a time, then restarting
668
+ once the end is reached. Available as ``cycler`` in templates.
669
+
670
+ Similar to ``loop.cycle``, but can be used outside loops or across
671
+ multiple loops. For example, render a list of folders and files in a
672
+ list, alternating giving them "odd" and "even" classes.
673
+
674
+ .. code-block:: html+jinja
675
+
676
+ {% set row_class = cycler("odd", "even") %}
677
+ <ul class="browser">
678
+ {% for folder in folders %}
679
+ <li class="folder {{ row_class.next() }}">{{ folder }}
680
+ {% endfor %}
681
+ {% for file in files %}
682
+ <li class="file {{ row_class.next() }}">{{ file }}
683
+ {% endfor %}
684
+ </ul>
685
+
686
+ :param items: Each positional argument will be yielded in the order
687
+ given for each cycle.
688
+
689
+ .. versionadded:: 2.1
690
+ """
691
+
692
+ def __init__(self, *items: t.Any) -> None:
693
+ if not items:
694
+ raise RuntimeError("at least one item has to be provided")
695
+ self.items = items
696
+ self.pos = 0
697
+
698
+ def reset(self) -> None:
699
+ """Resets the current item to the first item."""
700
+ self.pos = 0
701
+
702
+ @property
703
+ def current(self) -> t.Any:
704
+ """Return the current item. Equivalent to the item that will be
705
+ returned next time :meth:`next` is called.
706
+ """
707
+ return self.items[self.pos]
708
+
709
+ def next(self) -> t.Any:
710
+ """Return the current item, then advance :attr:`current` to the
711
+ next item.
712
+ """
713
+ rv = self.current
714
+ self.pos = (self.pos + 1) % len(self.items)
715
+ return rv
716
+
717
+ __next__ = next
718
+
719
+
720
+ class Joiner:
721
+ """A joining helper for templates."""
722
+
723
+ def __init__(self, sep: str = ", ") -> None:
724
+ self.sep = sep
725
+ self.used = False
726
+
727
+ def __call__(self) -> str:
728
+ if not self.used:
729
+ self.used = True
730
+ return ""
731
+ return self.sep
732
+
733
+
734
+ class Namespace:
735
+ """A namespace object that can hold arbitrary attributes. It may be
736
+ initialized from a dictionary or with keyword arguments."""
737
+
738
+ def __init__(*args: t.Any, **kwargs: t.Any) -> None: # noqa: B902
739
+ self, args = args[0], args[1:]
740
+ self.__attrs = dict(*args, **kwargs)
741
+
742
+ def __getattribute__(self, name: str) -> t.Any:
743
+ # __class__ is needed for the awaitable check in async mode
744
+ if name in {"_Namespace__attrs", "__class__"}:
745
+ return object.__getattribute__(self, name)
746
+ try:
747
+ return self.__attrs[name]
748
+ except KeyError:
749
+ raise AttributeError(name) from None
750
+
751
+ def __setitem__(self, name: str, value: t.Any) -> None:
752
+ self.__attrs[name] = value
753
+
754
+ def __repr__(self) -> str:
755
+ return f"<Namespace {self.__attrs!r}>"
lib/python3.11/site-packages/jinja2/visitor.py ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """API for traversing the AST nodes. Implemented by the compiler and
2
+ meta introspection.
3
+ """
4
+ import typing as t
5
+
6
+ from .nodes import Node
7
+
8
+ if t.TYPE_CHECKING:
9
+ import typing_extensions as te
10
+
11
+ class VisitCallable(te.Protocol):
12
+ def __call__(self, node: Node, *args: t.Any, **kwargs: t.Any) -> t.Any:
13
+ ...
14
+
15
+
16
+ class NodeVisitor:
17
+ """Walks the abstract syntax tree and call visitor functions for every
18
+ node found. The visitor functions may return values which will be
19
+ forwarded by the `visit` method.
20
+
21
+ Per default the visitor functions for the nodes are ``'visit_'`` +
22
+ class name of the node. So a `TryFinally` node visit function would
23
+ be `visit_TryFinally`. This behavior can be changed by overriding
24
+ the `get_visitor` function. If no visitor function exists for a node
25
+ (return value `None`) the `generic_visit` visitor is used instead.
26
+ """
27
+
28
+ def get_visitor(self, node: Node) -> "t.Optional[VisitCallable]":
29
+ """Return the visitor function for this node or `None` if no visitor
30
+ exists for this node. In that case the generic visit function is
31
+ used instead.
32
+ """
33
+ return getattr(self, f"visit_{type(node).__name__}", None)
34
+
35
+ def visit(self, node: Node, *args: t.Any, **kwargs: t.Any) -> t.Any:
36
+ """Visit a node."""
37
+ f = self.get_visitor(node)
38
+
39
+ if f is not None:
40
+ return f(node, *args, **kwargs)
41
+
42
+ return self.generic_visit(node, *args, **kwargs)
43
+
44
+ def generic_visit(self, node: Node, *args: t.Any, **kwargs: t.Any) -> t.Any:
45
+ """Called if no explicit visitor function exists for a node."""
46
+ for child_node in node.iter_child_nodes():
47
+ self.visit(child_node, *args, **kwargs)
48
+
49
+
50
+ class NodeTransformer(NodeVisitor):
51
+ """Walks the abstract syntax tree and allows modifications of nodes.
52
+
53
+ The `NodeTransformer` will walk the AST and use the return value of the
54
+ visitor functions to replace or remove the old node. If the return
55
+ value of the visitor function is `None` the node will be removed
56
+ from the previous location otherwise it's replaced with the return
57
+ value. The return value may be the original node in which case no
58
+ replacement takes place.
59
+ """
60
+
61
+ def generic_visit(self, node: Node, *args: t.Any, **kwargs: t.Any) -> Node:
62
+ for field, old_value in node.iter_fields():
63
+ if isinstance(old_value, list):
64
+ new_values = []
65
+ for value in old_value:
66
+ if isinstance(value, Node):
67
+ value = self.visit(value, *args, **kwargs)
68
+ if value is None:
69
+ continue
70
+ elif not isinstance(value, Node):
71
+ new_values.extend(value)
72
+ continue
73
+ new_values.append(value)
74
+ old_value[:] = new_values
75
+ elif isinstance(old_value, Node):
76
+ new_node = self.visit(old_value, *args, **kwargs)
77
+ if new_node is None:
78
+ delattr(node, field)
79
+ else:
80
+ setattr(node, field, new_node)
81
+ return node
82
+
83
+ def visit_list(self, node: Node, *args: t.Any, **kwargs: t.Any) -> t.List[Node]:
84
+ """As transformers may return lists in some places this method
85
+ can be used to enforce a list as return value.
86
+ """
87
+ rv = self.visit(node, *args, **kwargs)
88
+
89
+ if not isinstance(rv, list):
90
+ return [rv]
91
+
92
+ return rv
lib/python3.11/site-packages/llvmlite/__init__.py ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ from ._version import get_versions
2
+ __version__ = get_versions()['version']
3
+ del get_versions
lib/python3.11/site-packages/llvmlite/__pycache__/__init__.cpython-311.pyc ADDED
Binary file (355 Bytes). View file
 
lib/python3.11/site-packages/llvmlite/__pycache__/_version.cpython-311.pyc ADDED
Binary file (535 Bytes). View file
 
lib/python3.11/site-packages/llvmlite/__pycache__/utils.cpython-311.pyc ADDED
Binary file (1.13 kB). View file
 
lib/python3.11/site-packages/llvmlite/_version.py ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ # This file was generated by 'versioneer.py' (0.14) from
3
+ # revision-control system data, or from the parent directory name of an
4
+ # unpacked source archive. Distribution tarballs contain a pre-generated copy
5
+ # of this file.
6
+
7
+ version_version = '0.41.1'
8
+ version_full = '42db9b74c8e8ca012cb4394cbf941f2f9e4ae12b'
9
+ def get_versions(default={}, verbose=False):
10
+ return {'version': version_version, 'full': version_full}
11
+
lib/python3.11/site-packages/llvmlite/binding/__init__.py ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Things that rely on the LLVM library
3
+ """
4
+ from .dylib import *
5
+ from .executionengine import *
6
+ from .initfini import *
7
+ from .linker import *
8
+ from .module import *
9
+ from .options import *
10
+ from .passmanagers import *
11
+ from .targets import *
12
+ from .transforms import *
13
+ from .value import *
14
+ from .analysis import *
15
+ from .object_file import *
16
+ from .context import *
17
+ from .orcjit import *
lib/python3.11/site-packages/llvmlite/binding/__pycache__/__init__.cpython-311.pyc ADDED
Binary file (720 Bytes). View file
 
lib/python3.11/site-packages/llvmlite/binding/__pycache__/analysis.cpython-311.pyc ADDED
Binary file (3.33 kB). View file
 
lib/python3.11/site-packages/llvmlite/binding/__pycache__/common.cpython-311.pyc ADDED
Binary file (1.45 kB). View file
 
lib/python3.11/site-packages/llvmlite/binding/__pycache__/context.cpython-311.pyc ADDED
Binary file (2.27 kB). View file
 
lib/python3.11/site-packages/llvmlite/binding/__pycache__/dylib.cpython-311.pyc ADDED
Binary file (2.39 kB). View file
 
lib/python3.11/site-packages/llvmlite/binding/__pycache__/executionengine.cpython-311.pyc ADDED
Binary file (15.3 kB). View file