Загрузить файлы в «venv/Lib/site-packages/jinja2»
This commit is contained in:
38
venv/Lib/site-packages/jinja2/__init__.py
Normal file
38
venv/Lib/site-packages/jinja2/__init__.py
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
"""Jinja is a template engine written in pure Python. It provides a
|
||||||
|
non-XML syntax that supports inline expressions and an optional
|
||||||
|
sandboxed environment.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from .bccache import BytecodeCache as BytecodeCache
|
||||||
|
from .bccache import FileSystemBytecodeCache as FileSystemBytecodeCache
|
||||||
|
from .bccache import MemcachedBytecodeCache as MemcachedBytecodeCache
|
||||||
|
from .environment import Environment as Environment
|
||||||
|
from .environment import Template as Template
|
||||||
|
from .exceptions import TemplateAssertionError as TemplateAssertionError
|
||||||
|
from .exceptions import TemplateError as TemplateError
|
||||||
|
from .exceptions import TemplateNotFound as TemplateNotFound
|
||||||
|
from .exceptions import TemplateRuntimeError as TemplateRuntimeError
|
||||||
|
from .exceptions import TemplatesNotFound as TemplatesNotFound
|
||||||
|
from .exceptions import TemplateSyntaxError as TemplateSyntaxError
|
||||||
|
from .exceptions import UndefinedError as UndefinedError
|
||||||
|
from .loaders import BaseLoader as BaseLoader
|
||||||
|
from .loaders import ChoiceLoader as ChoiceLoader
|
||||||
|
from .loaders import DictLoader as DictLoader
|
||||||
|
from .loaders import FileSystemLoader as FileSystemLoader
|
||||||
|
from .loaders import FunctionLoader as FunctionLoader
|
||||||
|
from .loaders import ModuleLoader as ModuleLoader
|
||||||
|
from .loaders import PackageLoader as PackageLoader
|
||||||
|
from .loaders import PrefixLoader as PrefixLoader
|
||||||
|
from .runtime import ChainableUndefined as ChainableUndefined
|
||||||
|
from .runtime import DebugUndefined as DebugUndefined
|
||||||
|
from .runtime import make_logging_undefined as make_logging_undefined
|
||||||
|
from .runtime import StrictUndefined as StrictUndefined
|
||||||
|
from .runtime import Undefined as Undefined
|
||||||
|
from .utils import clear_caches as clear_caches
|
||||||
|
from .utils import is_undefined as is_undefined
|
||||||
|
from .utils import pass_context as pass_context
|
||||||
|
from .utils import pass_environment as pass_environment
|
||||||
|
from .utils import pass_eval_context as pass_eval_context
|
||||||
|
from .utils import select_autoescape as select_autoescape
|
||||||
|
|
||||||
|
__version__ = "3.1.6"
|
||||||
6
venv/Lib/site-packages/jinja2/_identifier.py
Normal file
6
venv/Lib/site-packages/jinja2/_identifier.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import re
|
||||||
|
|
||||||
|
# generated by scripts/generate_identifier_pattern.py
|
||||||
|
pattern = re.compile(
|
||||||
|
r"[\w·̀-ͯ·҃-֑҇-ׇֽֿׁׂׅׄؐ-ًؚ-ٰٟۖ-ۜ۟-۪ۤۧۨ-ܑۭܰ-݊ަ-ް߫-߽߳ࠖ-࠙ࠛ-ࠣࠥ-ࠧࠩ-࡙࠭-࡛࣓-ࣣ࣡-ःऺ-़ा-ॏ॑-ॗॢॣঁ-ঃ়া-ৄেৈো-্ৗৢৣ৾ਁ-ਃ਼ਾ-ੂੇੈੋ-੍ੑੰੱੵઁ-ઃ઼ા-ૅે-ૉો-્ૢૣૺ-૿ଁ-ଃ଼ା-ୄେୈୋ-୍ୖୗୢୣஂா-ூெ-ைொ-்ௗఀ-ఄా-ౄె-ైొ-్ౕౖౢౣಁ-ಃ಼ಾ-ೄೆ-ೈೊ-್ೕೖೢೣഀ-ഃ഻഼ാ-ൄെ-ൈൊ-്ൗൢൣංඃ්ා-ුූෘ-ෟෲෳัิ-ฺ็-๎ັິ-ູົຼ່-ໍ༹༘༙༵༷༾༿ཱ-྄྆྇ྍ-ྗྙ-ྼ࿆ါ-ှၖ-ၙၞ-ၠၢ-ၤၧ-ၭၱ-ၴႂ-ႍႏႚ-ႝ፝-፟ᜒ-᜔ᜲ-᜴ᝒᝓᝲᝳ឴-៓៝᠋-᠍ᢅᢆᢩᤠ-ᤫᤰ-᤻ᨗ-ᨛᩕ-ᩞ᩠-᩿᩼᪰-᪽ᬀ-ᬄ᬴-᭄᭫-᭳ᮀ-ᮂᮡ-ᮭ᯦-᯳ᰤ-᰷᳐-᳔᳒-᳨᳭ᳲ-᳴᳷-᳹᷀-᷹᷻-᷿‿⁀⁔⃐-⃥⃜⃡-⃰℘℮⳯-⵿⳱ⷠ-〪ⷿ-゙゚〯꙯ꙴ-꙽ꚞꚟ꛰꛱ꠂ꠆ꠋꠣ-ꠧꢀꢁꢴ-ꣅ꣠-꣱ꣿꤦ-꤭ꥇ-꥓ꦀ-ꦃ꦳-꧀ꧥꨩ-ꨶꩃꩌꩍꩻ-ꩽꪰꪲ-ꪴꪷꪸꪾ꪿꫁ꫫ-ꫯꫵ꫶ꯣ-ꯪ꯬꯭ﬞ︀-️︠-︯︳︴﹍-﹏_𐇽𐋠𐍶-𐍺𐨁-𐨃𐨅𐨆𐨌-𐨏𐨸-𐨿𐨺𐫦𐫥𐴤-𐽆𐴧-𐽐𑀀-𑀂𑀸-𑁆𑁿-𑂂𑂰-𑂺𑄀-𑄂𑄧-𑄴𑅅𑅆𑅳𑆀-𑆂𑆳-𑇀𑇉-𑇌𑈬-𑈷𑈾𑋟-𑋪𑌀-𑌃𑌻𑌼𑌾-𑍄𑍇𑍈𑍋-𑍍𑍗𑍢𑍣𑍦-𑍬𑍰-𑍴𑐵-𑑆𑑞𑒰-𑓃𑖯-𑖵𑖸-𑗀𑗜𑗝𑘰-𑙀𑚫-𑚷𑜝-𑜫𑠬-𑠺𑨁-𑨊𑨳-𑨹𑨻-𑨾𑩇𑩑-𑩛𑪊-𑪙𑰯-𑰶𑰸-𑰿𑲒-𑲧𑲩-𑲶𑴱-𑴶𑴺𑴼𑴽𑴿-𑵅𑵇𑶊-𑶎𑶐𑶑𑶓-𑶗𑻳-𑻶𖫰-𖫴𖬰-𖬶𖽑-𖽾𖾏-𖾒𛲝𛲞𝅥-𝅩𝅭-𝅲𝅻-𝆂𝆅-𝆋𝆪-𝆭𝉂-𝉄𝨀-𝨶𝨻-𝩬𝩵𝪄𝪛-𝪟𝪡-𝪯𞀀-𞀆𞀈-𞀘𞀛-𞀡𞀣𞀤𞀦-𞣐𞀪-𞣖𞥄-𞥊󠄀-󠇯]+" # noqa: B950
|
||||||
|
)
|
||||||
99
venv/Lib/site-packages/jinja2/async_utils.py
Normal file
99
venv/Lib/site-packages/jinja2/async_utils.py
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
import inspect
|
||||||
|
import typing as t
|
||||||
|
from functools import WRAPPER_ASSIGNMENTS
|
||||||
|
from functools import wraps
|
||||||
|
|
||||||
|
from .utils import _PassArg
|
||||||
|
from .utils import pass_eval_context
|
||||||
|
|
||||||
|
if t.TYPE_CHECKING:
|
||||||
|
import typing_extensions as te
|
||||||
|
|
||||||
|
V = t.TypeVar("V")
|
||||||
|
|
||||||
|
|
||||||
|
def async_variant(normal_func): # type: ignore
|
||||||
|
def decorator(async_func): # type: ignore
|
||||||
|
pass_arg = _PassArg.from_obj(normal_func)
|
||||||
|
need_eval_context = pass_arg is None
|
||||||
|
|
||||||
|
if pass_arg is _PassArg.environment:
|
||||||
|
|
||||||
|
def is_async(args: t.Any) -> bool:
|
||||||
|
return t.cast(bool, args[0].is_async)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
def is_async(args: t.Any) -> bool:
|
||||||
|
return t.cast(bool, args[0].environment.is_async)
|
||||||
|
|
||||||
|
# Take the doc and annotations from the sync function, but the
|
||||||
|
# name from the async function. Pallets-Sphinx-Themes
|
||||||
|
# build_function_directive expects __wrapped__ to point to the
|
||||||
|
# sync function.
|
||||||
|
async_func_attrs = ("__module__", "__name__", "__qualname__")
|
||||||
|
normal_func_attrs = tuple(set(WRAPPER_ASSIGNMENTS).difference(async_func_attrs))
|
||||||
|
|
||||||
|
@wraps(normal_func, assigned=normal_func_attrs)
|
||||||
|
@wraps(async_func, assigned=async_func_attrs, updated=())
|
||||||
|
def wrapper(*args, **kwargs): # type: ignore
|
||||||
|
b = is_async(args)
|
||||||
|
|
||||||
|
if need_eval_context:
|
||||||
|
args = args[1:]
|
||||||
|
|
||||||
|
if b:
|
||||||
|
return async_func(*args, **kwargs)
|
||||||
|
|
||||||
|
return normal_func(*args, **kwargs)
|
||||||
|
|
||||||
|
if need_eval_context:
|
||||||
|
wrapper = pass_eval_context(wrapper)
|
||||||
|
|
||||||
|
wrapper.jinja_async_variant = True # type: ignore[attr-defined]
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
|
_common_primitives = {int, float, bool, str, list, dict, tuple, type(None)}
|
||||||
|
|
||||||
|
|
||||||
|
async def auto_await(value: t.Union[t.Awaitable["V"], "V"]) -> "V":
|
||||||
|
# Avoid a costly call to isawaitable
|
||||||
|
if type(value) in _common_primitives:
|
||||||
|
return t.cast("V", value)
|
||||||
|
|
||||||
|
if inspect.isawaitable(value):
|
||||||
|
return await t.cast("t.Awaitable[V]", value)
|
||||||
|
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
class _IteratorToAsyncIterator(t.Generic[V]):
|
||||||
|
def __init__(self, iterator: "t.Iterator[V]"):
|
||||||
|
self._iterator = iterator
|
||||||
|
|
||||||
|
def __aiter__(self) -> "te.Self":
|
||||||
|
return self
|
||||||
|
|
||||||
|
async def __anext__(self) -> V:
|
||||||
|
try:
|
||||||
|
return next(self._iterator)
|
||||||
|
except StopIteration as e:
|
||||||
|
raise StopAsyncIteration(e.value) from e
|
||||||
|
|
||||||
|
|
||||||
|
def auto_aiter(
|
||||||
|
iterable: "t.Union[t.AsyncIterable[V], t.Iterable[V]]",
|
||||||
|
) -> "t.AsyncIterator[V]":
|
||||||
|
if hasattr(iterable, "__aiter__"):
|
||||||
|
return iterable.__aiter__()
|
||||||
|
else:
|
||||||
|
return _IteratorToAsyncIterator(iter(iterable))
|
||||||
|
|
||||||
|
|
||||||
|
async def auto_to_list(
|
||||||
|
value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]",
|
||||||
|
) -> t.List["V"]:
|
||||||
|
return [x async for x in auto_aiter(value)]
|
||||||
408
venv/Lib/site-packages/jinja2/bccache.py
Normal file
408
venv/Lib/site-packages/jinja2/bccache.py
Normal file
@@ -0,0 +1,408 @@
|
|||||||
|
"""The optional bytecode cache system. This is useful if you have very
|
||||||
|
complex template situations and the compilation of all those templates
|
||||||
|
slows down your application too much.
|
||||||
|
|
||||||
|
Situations where this is useful are often forking web applications that
|
||||||
|
are initialized on the first request.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import errno
|
||||||
|
import fnmatch
|
||||||
|
import marshal
|
||||||
|
import os
|
||||||
|
import pickle
|
||||||
|
import stat
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
import typing as t
|
||||||
|
from hashlib import sha1
|
||||||
|
from io import BytesIO
|
||||||
|
from types import CodeType
|
||||||
|
|
||||||
|
if t.TYPE_CHECKING:
|
||||||
|
import typing_extensions as te
|
||||||
|
|
||||||
|
from .environment import Environment
|
||||||
|
|
||||||
|
class _MemcachedClient(te.Protocol):
|
||||||
|
def get(self, key: str) -> bytes: ...
|
||||||
|
|
||||||
|
def set(
|
||||||
|
self, key: str, value: bytes, timeout: t.Optional[int] = None
|
||||||
|
) -> None: ...
|
||||||
|
|
||||||
|
|
||||||
|
bc_version = 5
|
||||||
|
# Magic bytes to identify Jinja bytecode cache files. Contains the
|
||||||
|
# Python major and minor version to avoid loading incompatible bytecode
|
||||||
|
# if a project upgrades its Python version.
|
||||||
|
bc_magic = (
|
||||||
|
b"j2"
|
||||||
|
+ pickle.dumps(bc_version, 2)
|
||||||
|
+ pickle.dumps((sys.version_info[0] << 24) | sys.version_info[1], 2)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Bucket:
|
||||||
|
"""Buckets are used to store the bytecode for one template. It's created
|
||||||
|
and initialized by the bytecode cache and passed to the loading functions.
|
||||||
|
|
||||||
|
The buckets get an internal checksum from the cache assigned and use this
|
||||||
|
to automatically reject outdated cache material. Individual bytecode
|
||||||
|
cache subclasses don't have to care about cache invalidation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, environment: "Environment", key: str, checksum: str) -> None:
|
||||||
|
self.environment = environment
|
||||||
|
self.key = key
|
||||||
|
self.checksum = checksum
|
||||||
|
self.reset()
|
||||||
|
|
||||||
|
def reset(self) -> None:
|
||||||
|
"""Resets the bucket (unloads the bytecode)."""
|
||||||
|
self.code: t.Optional[CodeType] = None
|
||||||
|
|
||||||
|
def load_bytecode(self, f: t.BinaryIO) -> None:
|
||||||
|
"""Loads bytecode from a file or file like object."""
|
||||||
|
# make sure the magic header is correct
|
||||||
|
magic = f.read(len(bc_magic))
|
||||||
|
if magic != bc_magic:
|
||||||
|
self.reset()
|
||||||
|
return
|
||||||
|
# the source code of the file changed, we need to reload
|
||||||
|
checksum = pickle.load(f)
|
||||||
|
if self.checksum != checksum:
|
||||||
|
self.reset()
|
||||||
|
return
|
||||||
|
# if marshal_load fails then we need to reload
|
||||||
|
try:
|
||||||
|
self.code = marshal.load(f)
|
||||||
|
except (EOFError, ValueError, TypeError):
|
||||||
|
self.reset()
|
||||||
|
return
|
||||||
|
|
||||||
|
def write_bytecode(self, f: t.IO[bytes]) -> None:
|
||||||
|
"""Dump the bytecode into the file or file like object passed."""
|
||||||
|
if self.code is None:
|
||||||
|
raise TypeError("can't write empty bucket")
|
||||||
|
f.write(bc_magic)
|
||||||
|
pickle.dump(self.checksum, f, 2)
|
||||||
|
marshal.dump(self.code, f)
|
||||||
|
|
||||||
|
def bytecode_from_string(self, string: bytes) -> None:
|
||||||
|
"""Load bytecode from bytes."""
|
||||||
|
self.load_bytecode(BytesIO(string))
|
||||||
|
|
||||||
|
def bytecode_to_string(self) -> bytes:
|
||||||
|
"""Return the bytecode as bytes."""
|
||||||
|
out = BytesIO()
|
||||||
|
self.write_bytecode(out)
|
||||||
|
return out.getvalue()
|
||||||
|
|
||||||
|
|
||||||
|
class BytecodeCache:
|
||||||
|
"""To implement your own bytecode cache you have to subclass this class
|
||||||
|
and override :meth:`load_bytecode` and :meth:`dump_bytecode`. Both of
|
||||||
|
these methods are passed a :class:`~jinja2.bccache.Bucket`.
|
||||||
|
|
||||||
|
A very basic bytecode cache that saves the bytecode on the file system::
|
||||||
|
|
||||||
|
from os import path
|
||||||
|
|
||||||
|
class MyCache(BytecodeCache):
|
||||||
|
|
||||||
|
def __init__(self, directory):
|
||||||
|
self.directory = directory
|
||||||
|
|
||||||
|
def load_bytecode(self, bucket):
|
||||||
|
filename = path.join(self.directory, bucket.key)
|
||||||
|
if path.exists(filename):
|
||||||
|
with open(filename, 'rb') as f:
|
||||||
|
bucket.load_bytecode(f)
|
||||||
|
|
||||||
|
def dump_bytecode(self, bucket):
|
||||||
|
filename = path.join(self.directory, bucket.key)
|
||||||
|
with open(filename, 'wb') as f:
|
||||||
|
bucket.write_bytecode(f)
|
||||||
|
|
||||||
|
A more advanced version of a filesystem based bytecode cache is part of
|
||||||
|
Jinja.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def load_bytecode(self, bucket: Bucket) -> None:
|
||||||
|
"""Subclasses have to override this method to load bytecode into a
|
||||||
|
bucket. If they are not able to find code in the cache for the
|
||||||
|
bucket, it must not do anything.
|
||||||
|
"""
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def dump_bytecode(self, bucket: Bucket) -> None:
|
||||||
|
"""Subclasses have to override this method to write the bytecode
|
||||||
|
from a bucket back to the cache. If it unable to do so it must not
|
||||||
|
fail silently but raise an exception.
|
||||||
|
"""
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def clear(self) -> None:
|
||||||
|
"""Clears the cache. This method is not used by Jinja but should be
|
||||||
|
implemented to allow applications to clear the bytecode cache used
|
||||||
|
by a particular environment.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def get_cache_key(
|
||||||
|
self, name: str, filename: t.Optional[t.Union[str]] = None
|
||||||
|
) -> str:
|
||||||
|
"""Returns the unique hash key for this template name."""
|
||||||
|
hash = sha1(name.encode("utf-8"))
|
||||||
|
|
||||||
|
if filename is not None:
|
||||||
|
hash.update(f"|{filename}".encode())
|
||||||
|
|
||||||
|
return hash.hexdigest()
|
||||||
|
|
||||||
|
def get_source_checksum(self, source: str) -> str:
|
||||||
|
"""Returns a checksum for the source."""
|
||||||
|
return sha1(source.encode("utf-8")).hexdigest()
|
||||||
|
|
||||||
|
def get_bucket(
|
||||||
|
self,
|
||||||
|
environment: "Environment",
|
||||||
|
name: str,
|
||||||
|
filename: t.Optional[str],
|
||||||
|
source: str,
|
||||||
|
) -> Bucket:
|
||||||
|
"""Return a cache bucket for the given template. All arguments are
|
||||||
|
mandatory but filename may be `None`.
|
||||||
|
"""
|
||||||
|
key = self.get_cache_key(name, filename)
|
||||||
|
checksum = self.get_source_checksum(source)
|
||||||
|
bucket = Bucket(environment, key, checksum)
|
||||||
|
self.load_bytecode(bucket)
|
||||||
|
return bucket
|
||||||
|
|
||||||
|
def set_bucket(self, bucket: Bucket) -> None:
|
||||||
|
"""Put the bucket into the cache."""
|
||||||
|
self.dump_bytecode(bucket)
|
||||||
|
|
||||||
|
|
||||||
|
class FileSystemBytecodeCache(BytecodeCache):
|
||||||
|
"""A bytecode cache that stores bytecode on the filesystem. It accepts
|
||||||
|
two arguments: The directory where the cache items are stored and a
|
||||||
|
pattern string that is used to build the filename.
|
||||||
|
|
||||||
|
If no directory is specified a default cache directory is selected. On
|
||||||
|
Windows the user's temp directory is used, on UNIX systems a directory
|
||||||
|
is created for the user in the system temp directory.
|
||||||
|
|
||||||
|
The pattern can be used to have multiple separate caches operate on the
|
||||||
|
same directory. The default pattern is ``'__jinja2_%s.cache'``. ``%s``
|
||||||
|
is replaced with the cache key.
|
||||||
|
|
||||||
|
>>> bcc = FileSystemBytecodeCache('/tmp/jinja_cache', '%s.cache')
|
||||||
|
|
||||||
|
This bytecode cache supports clearing of the cache using the clear method.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self, directory: t.Optional[str] = None, pattern: str = "__jinja2_%s.cache"
|
||||||
|
) -> None:
|
||||||
|
if directory is None:
|
||||||
|
directory = self._get_default_cache_dir()
|
||||||
|
self.directory = directory
|
||||||
|
self.pattern = pattern
|
||||||
|
|
||||||
|
def _get_default_cache_dir(self) -> str:
|
||||||
|
def _unsafe_dir() -> "te.NoReturn":
|
||||||
|
raise RuntimeError(
|
||||||
|
"Cannot determine safe temp directory. You "
|
||||||
|
"need to explicitly provide one."
|
||||||
|
)
|
||||||
|
|
||||||
|
tmpdir = tempfile.gettempdir()
|
||||||
|
|
||||||
|
# On windows the temporary directory is used specific unless
|
||||||
|
# explicitly forced otherwise. We can just use that.
|
||||||
|
if os.name == "nt":
|
||||||
|
return tmpdir
|
||||||
|
if not hasattr(os, "getuid"):
|
||||||
|
_unsafe_dir()
|
||||||
|
|
||||||
|
dirname = f"_jinja2-cache-{os.getuid()}"
|
||||||
|
actual_dir = os.path.join(tmpdir, dirname)
|
||||||
|
|
||||||
|
try:
|
||||||
|
os.mkdir(actual_dir, stat.S_IRWXU)
|
||||||
|
except OSError as e:
|
||||||
|
if e.errno != errno.EEXIST:
|
||||||
|
raise
|
||||||
|
try:
|
||||||
|
os.chmod(actual_dir, stat.S_IRWXU)
|
||||||
|
actual_dir_stat = os.lstat(actual_dir)
|
||||||
|
if (
|
||||||
|
actual_dir_stat.st_uid != os.getuid()
|
||||||
|
or not stat.S_ISDIR(actual_dir_stat.st_mode)
|
||||||
|
or stat.S_IMODE(actual_dir_stat.st_mode) != stat.S_IRWXU
|
||||||
|
):
|
||||||
|
_unsafe_dir()
|
||||||
|
except OSError as e:
|
||||||
|
if e.errno != errno.EEXIST:
|
||||||
|
raise
|
||||||
|
|
||||||
|
actual_dir_stat = os.lstat(actual_dir)
|
||||||
|
if (
|
||||||
|
actual_dir_stat.st_uid != os.getuid()
|
||||||
|
or not stat.S_ISDIR(actual_dir_stat.st_mode)
|
||||||
|
or stat.S_IMODE(actual_dir_stat.st_mode) != stat.S_IRWXU
|
||||||
|
):
|
||||||
|
_unsafe_dir()
|
||||||
|
|
||||||
|
return actual_dir
|
||||||
|
|
||||||
|
def _get_cache_filename(self, bucket: Bucket) -> str:
|
||||||
|
return os.path.join(self.directory, self.pattern % (bucket.key,))
|
||||||
|
|
||||||
|
def load_bytecode(self, bucket: Bucket) -> None:
|
||||||
|
filename = self._get_cache_filename(bucket)
|
||||||
|
|
||||||
|
# Don't test for existence before opening the file, since the
|
||||||
|
# file could disappear after the test before the open.
|
||||||
|
try:
|
||||||
|
f = open(filename, "rb")
|
||||||
|
except (FileNotFoundError, IsADirectoryError, PermissionError):
|
||||||
|
# PermissionError can occur on Windows when an operation is
|
||||||
|
# in progress, such as calling clear().
|
||||||
|
return
|
||||||
|
|
||||||
|
with f:
|
||||||
|
bucket.load_bytecode(f)
|
||||||
|
|
||||||
|
def dump_bytecode(self, bucket: Bucket) -> None:
|
||||||
|
# Write to a temporary file, then rename to the real name after
|
||||||
|
# writing. This avoids another process reading the file before
|
||||||
|
# it is fully written.
|
||||||
|
name = self._get_cache_filename(bucket)
|
||||||
|
f = tempfile.NamedTemporaryFile(
|
||||||
|
mode="wb",
|
||||||
|
dir=os.path.dirname(name),
|
||||||
|
prefix=os.path.basename(name),
|
||||||
|
suffix=".tmp",
|
||||||
|
delete=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
def remove_silent() -> None:
|
||||||
|
try:
|
||||||
|
os.remove(f.name)
|
||||||
|
except OSError:
|
||||||
|
# Another process may have called clear(). On Windows,
|
||||||
|
# another program may be holding the file open.
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
with f:
|
||||||
|
bucket.write_bytecode(f)
|
||||||
|
except BaseException:
|
||||||
|
remove_silent()
|
||||||
|
raise
|
||||||
|
|
||||||
|
try:
|
||||||
|
os.replace(f.name, name)
|
||||||
|
except OSError:
|
||||||
|
# Another process may have called clear(). On Windows,
|
||||||
|
# another program may be holding the file open.
|
||||||
|
remove_silent()
|
||||||
|
except BaseException:
|
||||||
|
remove_silent()
|
||||||
|
raise
|
||||||
|
|
||||||
|
def clear(self) -> None:
|
||||||
|
# imported lazily here because google app-engine doesn't support
|
||||||
|
# write access on the file system and the function does not exist
|
||||||
|
# normally.
|
||||||
|
from os import remove
|
||||||
|
|
||||||
|
files = fnmatch.filter(os.listdir(self.directory), self.pattern % ("*",))
|
||||||
|
for filename in files:
|
||||||
|
try:
|
||||||
|
remove(os.path.join(self.directory, filename))
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class MemcachedBytecodeCache(BytecodeCache):
|
||||||
|
"""This class implements a bytecode cache that uses a memcache cache for
|
||||||
|
storing the information. It does not enforce a specific memcache library
|
||||||
|
(tummy's memcache or cmemcache) but will accept any class that provides
|
||||||
|
the minimal interface required.
|
||||||
|
|
||||||
|
Libraries compatible with this class:
|
||||||
|
|
||||||
|
- `cachelib <https://github.com/pallets/cachelib>`_
|
||||||
|
- `python-memcached <https://pypi.org/project/python-memcached/>`_
|
||||||
|
|
||||||
|
(Unfortunately the django cache interface is not compatible because it
|
||||||
|
does not support storing binary data, only text. You can however pass
|
||||||
|
the underlying cache client to the bytecode cache which is available
|
||||||
|
as `django.core.cache.cache._client`.)
|
||||||
|
|
||||||
|
The minimal interface for the client passed to the constructor is this:
|
||||||
|
|
||||||
|
.. class:: MinimalClientInterface
|
||||||
|
|
||||||
|
.. method:: set(key, value[, timeout])
|
||||||
|
|
||||||
|
Stores the bytecode in the cache. `value` is a string and
|
||||||
|
`timeout` the timeout of the key. If timeout is not provided
|
||||||
|
a default timeout or no timeout should be assumed, if it's
|
||||||
|
provided it's an integer with the number of seconds the cache
|
||||||
|
item should exist.
|
||||||
|
|
||||||
|
.. method:: get(key)
|
||||||
|
|
||||||
|
Returns the value for the cache key. If the item does not
|
||||||
|
exist in the cache the return value must be `None`.
|
||||||
|
|
||||||
|
The other arguments to the constructor are the prefix for all keys that
|
||||||
|
is added before the actual cache key and the timeout for the bytecode in
|
||||||
|
the cache system. We recommend a high (or no) timeout.
|
||||||
|
|
||||||
|
This bytecode cache does not support clearing of used items in the cache.
|
||||||
|
The clear method is a no-operation function.
|
||||||
|
|
||||||
|
.. versionadded:: 2.7
|
||||||
|
Added support for ignoring memcache errors through the
|
||||||
|
`ignore_memcache_errors` parameter.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
client: "_MemcachedClient",
|
||||||
|
prefix: str = "jinja2/bytecode/",
|
||||||
|
timeout: t.Optional[int] = None,
|
||||||
|
ignore_memcache_errors: bool = True,
|
||||||
|
):
|
||||||
|
self.client = client
|
||||||
|
self.prefix = prefix
|
||||||
|
self.timeout = timeout
|
||||||
|
self.ignore_memcache_errors = ignore_memcache_errors
|
||||||
|
|
||||||
|
def load_bytecode(self, bucket: Bucket) -> None:
|
||||||
|
try:
|
||||||
|
code = self.client.get(self.prefix + bucket.key)
|
||||||
|
except Exception:
|
||||||
|
if not self.ignore_memcache_errors:
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
bucket.bytecode_from_string(code)
|
||||||
|
|
||||||
|
def dump_bytecode(self, bucket: Bucket) -> None:
|
||||||
|
key = self.prefix + bucket.key
|
||||||
|
value = bucket.bytecode_to_string()
|
||||||
|
|
||||||
|
try:
|
||||||
|
if self.timeout is not None:
|
||||||
|
self.client.set(key, value, self.timeout)
|
||||||
|
else:
|
||||||
|
self.client.set(key, value)
|
||||||
|
except Exception:
|
||||||
|
if not self.ignore_memcache_errors:
|
||||||
|
raise
|
||||||
1998
venv/Lib/site-packages/jinja2/compiler.py
Normal file
1998
venv/Lib/site-packages/jinja2/compiler.py
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user