diff options
| author | floatdrop <[email protected]> | 2022-02-10 16:47:15 +0300 |
|---|---|---|
| committer | Daniil Cherednik <[email protected]> | 2022-02-10 16:47:15 +0300 |
| commit | e63b84f1d39557d9e46ac380b1f388271894293c (patch) | |
| tree | 338cdaff3fb027e030b847db66df06019a0e3149 /contrib/python/Jinja2/py3/jinja2/utils.py | |
| parent | f60febb7ea449535e7b073c386c7ff0539637fc0 (diff) | |
Restoring authorship annotation for <[email protected]>. Commit 1 of 2.
Diffstat (limited to 'contrib/python/Jinja2/py3/jinja2/utils.py')
| -rw-r--r-- | contrib/python/Jinja2/py3/jinja2/utils.py | 682 |
1 files changed, 341 insertions, 341 deletions
diff --git a/contrib/python/Jinja2/py3/jinja2/utils.py b/contrib/python/Jinja2/py3/jinja2/utils.py index 567185f41fc..261b1185834 100644 --- a/contrib/python/Jinja2/py3/jinja2/utils.py +++ b/contrib/python/Jinja2/py3/jinja2/utils.py @@ -1,54 +1,54 @@ import enum import json import os -import re +import re import typing as t import warnings from collections import abc -from collections import deque +from collections import deque from random import choice from random import randrange -from threading import Lock +from threading import Lock from types import CodeType from urllib.parse import quote_from_bytes - + import markupsafe - + if t.TYPE_CHECKING: import typing_extensions as te F = t.TypeVar("F", bound=t.Callable[..., t.Any]) -# special singleton representing missing values for the runtime +# special singleton representing missing values for the runtime missing: t.Any = type("MissingType", (), {"__repr__": lambda x: "missing"})() - + internal_code: t.MutableSet[CodeType] = set() - + concat = "".join - - + + def pass_context(f: F) -> F: """Pass the :class:`~jinja2.runtime.Context` as the first argument to the decorated function when called while rendering a template. - + Can be used on functions, filters, and tests. - + If only ``Context.eval_context`` is needed, use :func:`pass_eval_context`. If only ``Context.environment`` is needed, use :func:`pass_environment`. .. versionadded:: 3.0.0 Replaces ``contextfunction`` and ``contextfilter``. - """ + """ f.jinja_pass_arg = _PassArg.context # type: ignore - return f - - + return f + + def pass_eval_context(f: F) -> F: """Pass the :class:`~jinja2.nodes.EvalContext` as the first argument to the decorated function when called while rendering a template. See :ref:`eval-context`. - + Can be used on functions, filters, and tests. If only ``EvalContext.environment`` is needed, use @@ -56,11 +56,11 @@ def pass_eval_context(f: F) -> F: .. versionadded:: 3.0.0 Replaces ``evalcontextfunction`` and ``evalcontextfilter``. - """ + """ f.jinja_pass_arg = _PassArg.eval_context # type: ignore - return f - - + return f + + def pass_environment(f: F) -> F: """Pass the :class:`~jinja2.Environment` as the first argument to the decorated function when called while rendering a template. @@ -69,11 +69,11 @@ def pass_environment(f: F) -> F: .. versionadded:: 3.0.0 Replaces ``environmentfunction`` and ``environmentfilter``. - """ + """ f.jinja_pass_arg = _PassArg.environment # type: ignore - return f - - + return f + + class _PassArg(enum.Enum): context = enum.auto() eval_context = enum.auto() @@ -153,98 +153,98 @@ def environmentfunction(f: F) -> F: def internalcode(f: F) -> F: - """Marks the function as internally used""" - internal_code.add(f.__code__) - return f - - + """Marks the function as internally used""" + internal_code.add(f.__code__) + return f + + def is_undefined(obj: t.Any) -> bool: - """Check if the object passed is undefined. This does nothing more than - performing an instance check against :class:`Undefined` but looks nicer. - This can be used for custom filters or tests that want to react to - undefined variables. For example a custom default filter can look like - this:: - - def default(var, default=''): - if is_undefined(var): - return default - return var - """ + """Check if the object passed is undefined. This does nothing more than + performing an instance check against :class:`Undefined` but looks nicer. + This can be used for custom filters or tests that want to react to + undefined variables. For example a custom default filter can look like + this:: + + def default(var, default=''): + if is_undefined(var): + return default + return var + """ from .runtime import Undefined - return isinstance(obj, Undefined) - - + return isinstance(obj, Undefined) + + def consume(iterable: t.Iterable[t.Any]) -> None: - """Consumes an iterable without doing anything with it.""" + """Consumes an iterable without doing anything with it.""" for _ in iterable: - pass - - + pass + + def clear_caches() -> None: """Jinja keeps internal caches for environments and lexers. These are used so that Jinja doesn't have to recreate environments and lexers all - the time. Normally you don't have to care about that but if you are - measuring memory consumption you may want to clean the caches. - """ + the time. Normally you don't have to care about that but if you are + measuring memory consumption you may want to clean the caches. + """ from .environment import get_spontaneous_environment from .lexer import _lexer_cache get_spontaneous_environment.cache_clear() - _lexer_cache.clear() - - + _lexer_cache.clear() + + def import_string(import_name: str, silent: bool = False) -> t.Any: - """Imports an object based on a string. This is useful if you want to - use import paths as endpoints or something similar. An import path can - be specified either in dotted notation (``xml.sax.saxutils.escape``) - or with a colon as object delimiter (``xml.sax.saxutils:escape``). - - If the `silent` is True the return value will be `None` if the import - fails. - - :return: imported object - """ - try: + """Imports an object based on a string. This is useful if you want to + use import paths as endpoints or something similar. An import path can + be specified either in dotted notation (``xml.sax.saxutils.escape``) + or with a colon as object delimiter (``xml.sax.saxutils:escape``). + + If the `silent` is True the return value will be `None` if the import + fails. + + :return: imported object + """ + try: if ":" in import_name: module, obj = import_name.split(":", 1) elif "." in import_name: module, _, obj = import_name.rpartition(".") - else: - return __import__(import_name) - return getattr(__import__(module, None, None, [obj]), obj) - except (ImportError, AttributeError): - if not silent: - raise - - + else: + return __import__(import_name) + return getattr(__import__(module, None, None, [obj]), obj) + except (ImportError, AttributeError): + if not silent: + raise + + def open_if_exists(filename: str, mode: str = "rb") -> t.Optional[t.IO]: - """Returns a file descriptor for the filename if that file exists, + """Returns a file descriptor for the filename if that file exists, otherwise ``None``. - """ + """ if not os.path.isfile(filename): return None - + return open(filename, mode) - + def object_type_repr(obj: t.Any) -> str: - """Returns the name of the object's type. For some recognized - singletons the name of the object is returned instead. (For - example for `None` and `Ellipsis`). - """ - if obj is None: + """Returns the name of the object's type. For some recognized + singletons the name of the object is returned instead. (For + example for `None` and `Ellipsis`). + """ + if obj is None: return "None" - elif obj is Ellipsis: + elif obj is Ellipsis: return "Ellipsis" cls = type(obj) if cls.__module__ == "builtins": return f"{cls.__name__} object" - + return f"{cls.__module__}.{cls.__name__} object" - + def pformat(obj: t.Any) -> str: """Format an object using :func:`pprint.pformat`.""" @@ -252,7 +252,7 @@ def pformat(obj: t.Any) -> str: return pformat(obj) - + _http_re = re.compile( r""" ^ @@ -282,8 +282,8 @@ _http_re = re.compile( re.IGNORECASE | re.VERBOSE, ) _email_re = re.compile(r"^\S+@\w[\w.-]*\.\w+$") - - + + def urlize( text: str, trim_url_limit: t.Optional[int] = None, @@ -292,11 +292,11 @@ def urlize( extra_schemes: t.Optional[t.Iterable[str]] = None, ) -> str: """Convert URLs in text into clickable links. - + This may not recognize links in some situations. Usually, a more comprehensive formatter, such as a Markdown library, is a better choice. - + Works on ``http://``, ``https://``, ``www.``, ``mailto:``, and email addresses. Links with trailing punctuation (periods, commas, closing parentheses) and leading punctuation (opening parentheses) are @@ -321,9 +321,9 @@ def urlize( The parsing rules were updated. Recognize email addresses with or without the ``mailto:`` scheme. Validate IP addresses. Ignore parentheses and brackets in more cases. - """ + """ if trim_url_limit is not None: - + def trim_url(x: str) -> str: if len(x) > trim_url_limit: # type: ignore return f"{x[:trim_url_limit]}..." @@ -339,11 +339,11 @@ def urlize( rel_attr = f' rel="{markupsafe.escape(rel)}"' if rel else "" target_attr = f' target="{markupsafe.escape(target)}"' if target else "" - for i, word in enumerate(words): + for i, word in enumerate(words): head, middle, tail = "", word, "" match = re.match(r"^([(<]|<)+", middle) - if match: + if match: head = match.group() middle = middle[match.end() :] @@ -401,51 +401,51 @@ def urlize( middle = f'<a href="{middle}"{rel_attr}{target_attr}>{middle}</a>' words[i] = f"{head}{middle}{tail}" - + return "".join(words) - + def generate_lorem_ipsum( n: int = 5, html: bool = True, min: int = 20, max: int = 100 ) -> str: - """Generate some lorem ipsum for the template.""" + """Generate some lorem ipsum for the template.""" from .constants import LOREM_IPSUM_WORDS - words = LOREM_IPSUM_WORDS.split() - result = [] - - for _ in range(n): - next_capitalized = True - last_comma = last_fullstop = 0 - word = None - last = None - p = [] - - # each paragraph contains out of 20 to 100 words. - for idx, _ in enumerate(range(randrange(min, max))): - while True: - word = choice(words) - if word != last: - last = word - break - if next_capitalized: - word = word.capitalize() - next_capitalized = False - # add commas - if idx - randrange(3, 8) > last_comma: - last_comma = idx - last_fullstop += 2 + words = LOREM_IPSUM_WORDS.split() + result = [] + + for _ in range(n): + next_capitalized = True + last_comma = last_fullstop = 0 + word = None + last = None + p = [] + + # each paragraph contains out of 20 to 100 words. + for idx, _ in enumerate(range(randrange(min, max))): + while True: + word = choice(words) + if word != last: + last = word + break + if next_capitalized: + word = word.capitalize() + next_capitalized = False + # add commas + if idx - randrange(3, 8) > last_comma: + last_comma = idx + last_fullstop += 2 word += "," - # add end of sentences - if idx - randrange(10, 20) > last_fullstop: - last_comma = last_fullstop = idx + # add end of sentences + if idx - randrange(10, 20) > last_fullstop: + last_comma = last_fullstop = idx word += "." - next_capitalized = True - p.append(word) - - # ensure that the paragraph ends with a dot. + next_capitalized = True + p.append(word) + + # ensure that the paragraph ends with a dot. p_str = " ".join(p) - + if p_str.endswith(","): p_str = p_str[:-1] + "." elif not p_str.endswith("."): @@ -453,36 +453,36 @@ def generate_lorem_ipsum( result.append(p_str) - if not html: + if not html: return "\n\n".join(result) return markupsafe.Markup( "\n".join(f"<p>{markupsafe.escape(x)}</p>" for x in result) ) - - + + def url_quote(obj: t.Any, charset: str = "utf-8", for_qs: bool = False) -> str: """Quote a string for use in a URL using the given charset. - + :param obj: String or bytes to quote. Other types are converted to string then encoded to bytes using the given charset. :param charset: Encode text to bytes using this charset. :param for_qs: Quote "/" and use "+" for spaces. - """ + """ if not isinstance(obj, bytes): if not isinstance(obj, str): obj = str(obj) - obj = obj.encode(charset) + obj = obj.encode(charset) safe = b"" if for_qs else b"/" rv = quote_from_bytes(obj, safe) - if for_qs: + if for_qs: rv = rv.replace("%20", "+") - return rv - - + return rv + + def unicode_urlencode(obj: t.Any, charset: str = "utf-8", for_qs: bool = False) -> str: import warnings @@ -497,221 +497,221 @@ def unicode_urlencode(obj: t.Any, charset: str = "utf-8", for_qs: bool = False) @abc.MutableMapping.register class LRUCache: - """A simple LRU Cache implementation.""" - - # this is fast for small capacities (something below 1000) but doesn't - # scale. But as long as it's only used as storage for templates this - # won't do any harm. - + """A simple LRU Cache implementation.""" + + # this is fast for small capacities (something below 1000) but doesn't + # scale. But as long as it's only used as storage for templates this + # won't do any harm. + def __init__(self, capacity: int) -> None: - self.capacity = capacity + self.capacity = capacity self._mapping: t.Dict[t.Any, t.Any] = {} self._queue: "te.Deque[t.Any]" = deque() - self._postinit() - + self._postinit() + def _postinit(self) -> None: - # alias all queue methods for faster lookup - self._popleft = self._queue.popleft - self._pop = self._queue.pop - self._remove = self._queue.remove - self._wlock = Lock() - self._append = self._queue.append - + # alias all queue methods for faster lookup + self._popleft = self._queue.popleft + self._pop = self._queue.pop + self._remove = self._queue.remove + self._wlock = Lock() + self._append = self._queue.append + def __getstate__(self) -> t.Mapping[str, t.Any]: - return { + return { "capacity": self.capacity, "_mapping": self._mapping, "_queue": self._queue, - } - + } + def __setstate__(self, d: t.Mapping[str, t.Any]) -> None: - self.__dict__.update(d) - self._postinit() - + self.__dict__.update(d) + self._postinit() + def __getnewargs__(self) -> t.Tuple: - return (self.capacity,) - + return (self.capacity,) + def copy(self) -> "LRUCache": - """Return a shallow copy of the instance.""" - rv = self.__class__(self.capacity) - rv._mapping.update(self._mapping) + """Return a shallow copy of the instance.""" + rv = self.__class__(self.capacity) + rv._mapping.update(self._mapping) rv._queue.extend(self._queue) - return rv - + return rv + def get(self, key: t.Any, default: t.Any = None) -> t.Any: - """Return an item from the cache dict or `default`""" - try: - return self[key] - except KeyError: - return default - + """Return an item from the cache dict or `default`""" + try: + return self[key] + except KeyError: + return default + def setdefault(self, key: t.Any, default: t.Any = None) -> t.Any: - """Set `default` if the key is not in the cache otherwise - leave unchanged. Return the value of this key. - """ - try: + """Set `default` if the key is not in the cache otherwise + leave unchanged. Return the value of this key. + """ + try: return self[key] except KeyError: self[key] = default return default - + def clear(self) -> None: - """Clear the cache.""" + """Clear the cache.""" with self._wlock: - self._mapping.clear() - self._queue.clear() - + self._mapping.clear() + self._queue.clear() + def __contains__(self, key: t.Any) -> bool: - """Check if a key exists in this cache.""" - return key in self._mapping - + """Check if a key exists in this cache.""" + return key in self._mapping + def __len__(self) -> int: - """Return the current size of the cache.""" - return len(self._mapping) - + """Return the current size of the cache.""" + return len(self._mapping) + def __repr__(self) -> str: return f"<{type(self).__name__} {self._mapping!r}>" - + def __getitem__(self, key: t.Any) -> t.Any: - """Get an item from the cache. Moves the item up so that it has the - highest priority then. - - Raise a `KeyError` if it does not exist. - """ + """Get an item from the cache. Moves the item up so that it has the + highest priority then. + + Raise a `KeyError` if it does not exist. + """ with self._wlock: - rv = self._mapping[key] + rv = self._mapping[key] - if self._queue[-1] != key: - try: - self._remove(key) - except ValueError: - # if something removed the key from the container - # when we read, ignore the ValueError that we would - # get otherwise. - pass + if self._queue[-1] != key: + try: + self._remove(key) + except ValueError: + # if something removed the key from the container + # when we read, ignore the ValueError that we would + # get otherwise. + pass - self._append(key) - - return rv + self._append(key) + return rv + def __setitem__(self, key: t.Any, value: t.Any) -> None: - """Sets the value for an item. Moves the item up so that it - has the highest priority then. - """ + """Sets the value for an item. Moves the item up so that it + has the highest priority then. + """ with self._wlock: - if key in self._mapping: - self._remove(key) - elif len(self._mapping) == self.capacity: - del self._mapping[self._popleft()] - - self._append(key) - self._mapping[key] = value - + if key in self._mapping: + self._remove(key) + elif len(self._mapping) == self.capacity: + del self._mapping[self._popleft()] + + self._append(key) + self._mapping[key] = value + def __delitem__(self, key: t.Any) -> None: - """Remove an item from the cache dict. - Raise a `KeyError` if it does not exist. - """ + """Remove an item from the cache dict. + Raise a `KeyError` if it does not exist. + """ with self._wlock: - del self._mapping[key] - - try: - self._remove(key) - except ValueError: - pass + del self._mapping[key] + try: + self._remove(key) + except ValueError: + pass + def items(self) -> t.Iterable[t.Tuple[t.Any, t.Any]]: - """Return a list of items.""" - result = [(key, self._mapping[key]) for key in list(self._queue)] - result.reverse() - return result - + """Return a list of items.""" + result = [(key, self._mapping[key]) for key in list(self._queue)] + result.reverse() + return result + def values(self) -> t.Iterable[t.Any]: - """Return a list of all values.""" - return [x[1] for x in self.items()] - + """Return a list of all values.""" + return [x[1] for x in self.items()] + def keys(self) -> t.Iterable[t.Any]: - """Return a list of all keys ordered by most recent usage.""" - return list(self) - + """Return a list of all keys ordered by most recent usage.""" + return list(self) + def __iter__(self) -> t.Iterator[t.Any]: - return reversed(tuple(self._queue)) - + return reversed(tuple(self._queue)) + def __reversed__(self) -> t.Iterator[t.Any]: """Iterate over the keys in the cache dict, oldest items - coming first. - """ - return iter(tuple(self._queue)) - - __copy__ = copy - - + coming first. + """ + return iter(tuple(self._queue)) + + __copy__ = copy + + def select_autoescape( enabled_extensions: t.Collection[str] = ("html", "htm", "xml"), disabled_extensions: t.Collection[str] = (), default_for_string: bool = True, default: bool = False, ) -> t.Callable[[t.Optional[str]], bool]: - """Intelligently sets the initial value of autoescaping based on the - filename of the template. This is the recommended way to configure - autoescaping if you do not want to write a custom function yourself. - - If you want to enable it for all templates created from strings or - for all templates with `.html` and `.xml` extensions:: - - from jinja2 import Environment, select_autoescape - env = Environment(autoescape=select_autoescape( - enabled_extensions=('html', 'xml'), - default_for_string=True, - )) - - Example configuration to turn it on at all times except if the template - ends with `.txt`:: - - from jinja2 import Environment, select_autoescape - env = Environment(autoescape=select_autoescape( - disabled_extensions=('txt',), - default_for_string=True, - default=True, - )) - - The `enabled_extensions` is an iterable of all the extensions that - autoescaping should be enabled for. Likewise `disabled_extensions` is - a list of all templates it should be disabled for. If a template is - loaded from a string then the default from `default_for_string` is used. - If nothing matches then the initial value of autoescaping is set to the - value of `default`. - - For security reasons this function operates case insensitive. - - .. versionadded:: 2.9 - """ + """Intelligently sets the initial value of autoescaping based on the + filename of the template. This is the recommended way to configure + autoescaping if you do not want to write a custom function yourself. + + If you want to enable it for all templates created from strings or + for all templates with `.html` and `.xml` extensions:: + + from jinja2 import Environment, select_autoescape + env = Environment(autoescape=select_autoescape( + enabled_extensions=('html', 'xml'), + default_for_string=True, + )) + + Example configuration to turn it on at all times except if the template + ends with `.txt`:: + + from jinja2 import Environment, select_autoescape + env = Environment(autoescape=select_autoescape( + disabled_extensions=('txt',), + default_for_string=True, + default=True, + )) + + The `enabled_extensions` is an iterable of all the extensions that + autoescaping should be enabled for. Likewise `disabled_extensions` is + a list of all templates it should be disabled for. If a template is + loaded from a string then the default from `default_for_string` is used. + If nothing matches then the initial value of autoescaping is set to the + value of `default`. + + For security reasons this function operates case insensitive. + + .. versionadded:: 2.9 + """ enabled_patterns = tuple(f".{x.lstrip('.').lower()}" for x in enabled_extensions) disabled_patterns = tuple(f".{x.lstrip('.').lower()}" for x in disabled_extensions) def autoescape(template_name: t.Optional[str]) -> bool: - if template_name is None: - return default_for_string - template_name = template_name.lower() - if template_name.endswith(enabled_patterns): - return True - if template_name.endswith(disabled_patterns): - return False - return default - - return autoescape - - + if template_name is None: + return default_for_string + template_name = template_name.lower() + if template_name.endswith(enabled_patterns): + return True + if template_name.endswith(disabled_patterns): + return False + return default + + return autoescape + + def htmlsafe_json_dumps( obj: t.Any, dumps: t.Optional[t.Callable[..., str]] = None, **kwargs: t.Any ) -> markupsafe.Markup: """Serialize an object to a string of JSON with :func:`json.dumps`, then replace HTML-unsafe characters with Unicode escapes and mark the result safe with :class:`~markupsafe.Markup`. - + This is available in templates as the ``|tojson`` filter. - + The following characters are escaped: ``<``, ``>``, ``&``, ``'``. - + The returned string is safe to render in HTML documents and ``<script>`` tags. The exception is in HTML attributes that are double quoted; either use single quotes or the ``|forceescape`` @@ -728,7 +728,7 @@ def htmlsafe_json_dumps( The ``dumper`` parameter is renamed to ``dumps``. .. versionadded:: 2.9 - """ + """ if dumps is None: dumps = json.dumps @@ -739,12 +739,12 @@ def htmlsafe_json_dumps( .replace("&", "\\u0026") .replace("'", "\\u0027") ) - - + + class Cycler: """Cycle through values by yield them one at a time, then restarting once the end is reached. Available as ``cycler`` in templates. - + Similar to ``loop.cycle``, but can be used outside loops or across multiple loops. For example, render a list of folders and files in a list, alternating giving them "odd" and "even" classes. @@ -768,71 +768,71 @@ class Cycler: """ def __init__(self, *items: t.Any) -> None: - if not items: + if not items: raise RuntimeError("at least one item has to be provided") - self.items = items + self.items = items self.pos = 0 - + def reset(self) -> None: """Resets the current item to the first item.""" - self.pos = 0 - - @property + self.pos = 0 + + @property def current(self) -> t.Any: """Return the current item. Equivalent to the item that will be returned next time :meth:`next` is called. """ - return self.items[self.pos] - + return self.items[self.pos] + def next(self) -> t.Any: """Return the current item, then advance :attr:`current` to the next item. """ - rv = self.current - self.pos = (self.pos + 1) % len(self.items) - return rv - - __next__ = next - - + rv = self.current + self.pos = (self.pos + 1) % len(self.items) + return rv + + __next__ = next + + class Joiner: - """A joining helper for templates.""" - + """A joining helper for templates.""" + def __init__(self, sep: str = ", ") -> None: - self.sep = sep - self.used = False - + self.sep = sep + self.used = False + def __call__(self) -> str: - if not self.used: - self.used = True + if not self.used: + self.used = True return "" - return self.sep - - + return self.sep + + class Namespace: - """A namespace object that can hold arbitrary attributes. It may be + """A namespace object that can hold arbitrary attributes. It may be initialized from a dictionary or with keyword arguments.""" - + def __init__(*args: t.Any, **kwargs: t.Any) -> None: # noqa: B902 - self, args = args[0], args[1:] - self.__attrs = dict(*args, **kwargs) - + self, args = args[0], args[1:] + self.__attrs = dict(*args, **kwargs) + def __getattribute__(self, name: str) -> t.Any: # __class__ is needed for the awaitable check in async mode if name in {"_Namespace__attrs", "__class__"}: - return object.__getattribute__(self, name) - try: - return self.__attrs[name] - except KeyError: + return object.__getattribute__(self, name) + try: + return self.__attrs[name] + except KeyError: raise AttributeError(name) from None - + def __setitem__(self, name: str, value: t.Any) -> None: - self.__attrs[name] = value - + self.__attrs[name] = value + def __repr__(self) -> str: return f"<Namespace {self.__attrs!r}>" - - + + class Markup(markupsafe.Markup): def __new__(cls, base="", encoding=None, errors="strict"): # type: ignore warnings.warn( @@ -842,8 +842,8 @@ class Markup(markupsafe.Markup): stacklevel=2, ) return super().__new__(cls, base, encoding, errors) - - + + def escape(s: t.Any) -> str: warnings.warn( "'jinja2.escape' is deprecated and will be removed in Jinja" |
