Update Jinja2 to v2.11.3 and MarkupSafe to v1.1.1 (#27264)
This commit is contained in:

committed by
GitHub

parent
12da0a9a69
commit
70d5d234db
4
lib/spack/external/__init__.py
vendored
4
lib/spack/external/__init__.py
vendored
@@ -44,7 +44,7 @@
|
||||
|
||||
* Homepage: https://pypi.python.org/pypi/Jinja2
|
||||
* Usage: A modern and designer-friendly templating language for Python.
|
||||
* Version: 2.10
|
||||
* Version: 2.11.3 (last version supporting Python 2.7)
|
||||
|
||||
jsonschema
|
||||
----------
|
||||
@@ -62,7 +62,7 @@
|
||||
|
||||
* Homepage: https://pypi.python.org/pypi/MarkupSafe
|
||||
* Usage: Implements a XML/HTML/XHTML Markup safe string for Python.
|
||||
* Version: 1.0
|
||||
* Version: 1.1.1 (last version supporting Python 2.7)
|
||||
|
||||
py
|
||||
--
|
||||
|
28
lib/spack/external/jinja2/LICENSE.rst
vendored
Normal file
28
lib/spack/external/jinja2/LICENSE.rst
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
Copyright 2007 Pallets
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
119
lib/spack/external/jinja2/__init__.py
vendored
119
lib/spack/external/jinja2/__init__.py
vendored
@@ -1,83 +1,44 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Jinja is a template engine written in pure Python. It provides a
|
||||
non-XML syntax that supports inline expressions and an optional
|
||||
sandboxed environment.
|
||||
"""
|
||||
jinja2
|
||||
~~~~~~
|
||||
from markupsafe import escape
|
||||
from markupsafe import Markup
|
||||
|
||||
Jinja2 is a template engine written in pure Python. It provides a
|
||||
Django inspired non-XML syntax but supports inline expressions and
|
||||
an optional sandboxed environment.
|
||||
from .bccache import BytecodeCache
|
||||
from .bccache import FileSystemBytecodeCache
|
||||
from .bccache import MemcachedBytecodeCache
|
||||
from .environment import Environment
|
||||
from .environment import Template
|
||||
from .exceptions import TemplateAssertionError
|
||||
from .exceptions import TemplateError
|
||||
from .exceptions import TemplateNotFound
|
||||
from .exceptions import TemplateRuntimeError
|
||||
from .exceptions import TemplatesNotFound
|
||||
from .exceptions import TemplateSyntaxError
|
||||
from .exceptions import UndefinedError
|
||||
from .filters import contextfilter
|
||||
from .filters import environmentfilter
|
||||
from .filters import evalcontextfilter
|
||||
from .loaders import BaseLoader
|
||||
from .loaders import ChoiceLoader
|
||||
from .loaders import DictLoader
|
||||
from .loaders import FileSystemLoader
|
||||
from .loaders import FunctionLoader
|
||||
from .loaders import ModuleLoader
|
||||
from .loaders import PackageLoader
|
||||
from .loaders import PrefixLoader
|
||||
from .runtime import ChainableUndefined
|
||||
from .runtime import DebugUndefined
|
||||
from .runtime import make_logging_undefined
|
||||
from .runtime import StrictUndefined
|
||||
from .runtime import Undefined
|
||||
from .utils import clear_caches
|
||||
from .utils import contextfunction
|
||||
from .utils import environmentfunction
|
||||
from .utils import evalcontextfunction
|
||||
from .utils import is_undefined
|
||||
from .utils import select_autoescape
|
||||
|
||||
Nutshell
|
||||
--------
|
||||
|
||||
Here a small example of a Jinja2 template::
|
||||
|
||||
{% extends 'base.html' %}
|
||||
{% block title %}Memberlist{% endblock %}
|
||||
{% block content %}
|
||||
<ul>
|
||||
{% for user in users %}
|
||||
<li><a href="{{ user.url }}">{{ user.username }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
:copyright: (c) 2017 by the Jinja Team.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
__docformat__ = 'restructuredtext en'
|
||||
__version__ = '2.10'
|
||||
|
||||
# high level interface
|
||||
from jinja2.environment import Environment, Template
|
||||
|
||||
# loaders
|
||||
from jinja2.loaders import BaseLoader, FileSystemLoader, PackageLoader, \
|
||||
DictLoader, FunctionLoader, PrefixLoader, ChoiceLoader, \
|
||||
ModuleLoader
|
||||
|
||||
# bytecode caches
|
||||
from jinja2.bccache import BytecodeCache, FileSystemBytecodeCache, \
|
||||
MemcachedBytecodeCache
|
||||
|
||||
# undefined types
|
||||
from jinja2.runtime import Undefined, DebugUndefined, StrictUndefined, \
|
||||
make_logging_undefined
|
||||
|
||||
# exceptions
|
||||
from jinja2.exceptions import TemplateError, UndefinedError, \
|
||||
TemplateNotFound, TemplatesNotFound, TemplateSyntaxError, \
|
||||
TemplateAssertionError, TemplateRuntimeError
|
||||
|
||||
# decorators and public utilities
|
||||
from jinja2.filters import environmentfilter, contextfilter, \
|
||||
evalcontextfilter
|
||||
from jinja2.utils import Markup, escape, clear_caches, \
|
||||
environmentfunction, evalcontextfunction, contextfunction, \
|
||||
is_undefined, select_autoescape
|
||||
|
||||
__all__ = [
|
||||
'Environment', 'Template', 'BaseLoader', 'FileSystemLoader',
|
||||
'PackageLoader', 'DictLoader', 'FunctionLoader', 'PrefixLoader',
|
||||
'ChoiceLoader', 'BytecodeCache', 'FileSystemBytecodeCache',
|
||||
'MemcachedBytecodeCache', 'Undefined', 'DebugUndefined',
|
||||
'StrictUndefined', 'TemplateError', 'UndefinedError', 'TemplateNotFound',
|
||||
'TemplatesNotFound', 'TemplateSyntaxError', 'TemplateAssertionError',
|
||||
'TemplateRuntimeError',
|
||||
'ModuleLoader', 'environmentfilter', 'contextfilter', 'Markup', 'escape',
|
||||
'environmentfunction', 'contextfunction', 'clear_caches', 'is_undefined',
|
||||
'evalcontextfilter', 'evalcontextfunction', 'make_logging_undefined',
|
||||
'select_autoescape',
|
||||
]
|
||||
|
||||
|
||||
def _patch_async():
|
||||
from jinja2.utils import have_async_gen
|
||||
if have_async_gen:
|
||||
from jinja2.asyncsupport import patch_all
|
||||
patch_all()
|
||||
|
||||
|
||||
_patch_async()
|
||||
del _patch_async
|
||||
__version__ = "2.11.3"
|
||||
|
67
lib/spack/external/jinja2/_compat.py
vendored
67
lib/spack/external/jinja2/_compat.py
vendored
@@ -1,22 +1,12 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
jinja2._compat
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Some py2/py3 compatibility support based on a stripped down
|
||||
version of six so we don't have to depend on a specific version
|
||||
of it.
|
||||
|
||||
:copyright: Copyright 2013 by the Jinja team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
# flake8: noqa
|
||||
import marshal
|
||||
import sys
|
||||
|
||||
PY2 = sys.version_info[0] == 2
|
||||
PYPY = hasattr(sys, 'pypy_translation_info')
|
||||
PYPY = hasattr(sys, "pypy_translation_info")
|
||||
_identity = lambda x: x
|
||||
|
||||
|
||||
if not PY2:
|
||||
unichr = chr
|
||||
range_type = range
|
||||
@@ -30,6 +20,7 @@
|
||||
|
||||
import pickle
|
||||
from io import BytesIO, StringIO
|
||||
|
||||
NativeStringIO = StringIO
|
||||
|
||||
def reraise(tp, value, tb=None):
|
||||
@@ -46,6 +37,9 @@ def reraise(tp, value, tb=None):
|
||||
implements_to_string = _identity
|
||||
encode_filename = _identity
|
||||
|
||||
marshal_dump = marshal.dump
|
||||
marshal_load = marshal.load
|
||||
|
||||
else:
|
||||
unichr = unichr
|
||||
text_type = unicode
|
||||
@@ -59,11 +53,13 @@ def reraise(tp, value, tb=None):
|
||||
|
||||
import cPickle as pickle
|
||||
from cStringIO import StringIO as BytesIO, StringIO
|
||||
|
||||
NativeStringIO = BytesIO
|
||||
|
||||
exec('def reraise(tp, value, tb=None):\n raise tp, value, tb')
|
||||
exec("def reraise(tp, value, tb=None):\n raise tp, value, tb")
|
||||
|
||||
from itertools import imap, izip, ifilter
|
||||
|
||||
intern = intern
|
||||
|
||||
def implements_iterator(cls):
|
||||
@@ -73,14 +69,25 @@ def implements_iterator(cls):
|
||||
|
||||
def implements_to_string(cls):
|
||||
cls.__unicode__ = cls.__str__
|
||||
cls.__str__ = lambda x: x.__unicode__().encode('utf-8')
|
||||
cls.__str__ = lambda x: x.__unicode__().encode("utf-8")
|
||||
return cls
|
||||
|
||||
def encode_filename(filename):
|
||||
if isinstance(filename, unicode):
|
||||
return filename.encode('utf-8')
|
||||
return filename.encode("utf-8")
|
||||
return filename
|
||||
|
||||
def marshal_dump(code, f):
|
||||
if isinstance(f, file):
|
||||
marshal.dump(code, f)
|
||||
else:
|
||||
f.write(marshal.dumps(code))
|
||||
|
||||
def marshal_load(f):
|
||||
if isinstance(f, file):
|
||||
return marshal.load(f)
|
||||
return marshal.loads(f.read())
|
||||
|
||||
|
||||
def with_metaclass(meta, *bases):
|
||||
"""Create a base class with a metaclass."""
|
||||
@@ -90,10 +97,36 @@ def with_metaclass(meta, *bases):
|
||||
class metaclass(type):
|
||||
def __new__(cls, name, this_bases, d):
|
||||
return meta(name, bases, d)
|
||||
return type.__new__(metaclass, 'temporary_class', (), {})
|
||||
|
||||
return type.__new__(metaclass, "temporary_class", (), {})
|
||||
|
||||
|
||||
try:
|
||||
from urllib.parse import quote_from_bytes as url_quote
|
||||
except ImportError:
|
||||
from urllib import quote as url_quote
|
||||
|
||||
|
||||
try:
|
||||
from collections import abc
|
||||
except ImportError:
|
||||
import collections as abc
|
||||
|
||||
|
||||
try:
|
||||
from os import fspath
|
||||
except ImportError:
|
||||
try:
|
||||
from pathlib import PurePath
|
||||
except ImportError:
|
||||
PurePath = None
|
||||
|
||||
def fspath(path):
|
||||
if hasattr(path, "__fspath__"):
|
||||
return path.__fspath__()
|
||||
|
||||
# Python 3.5 doesn't have __fspath__ yet, use str.
|
||||
if PurePath is not None and isinstance(path, PurePath):
|
||||
return str(path)
|
||||
|
||||
return path
|
||||
|
6
lib/spack/external/jinja2/_identifier.py
vendored
6
lib/spack/external/jinja2/_identifier.py
vendored
@@ -1,2 +1,6 @@
|
||||
import re
|
||||
|
||||
# generated by scripts/generate_identifier_pattern.py
|
||||
pattern = '·̀-ͯ·҃-֑҇-ׇֽֿׁׂׅׄؐ-ًؚ-ٰٟۖ-ۜ۟-۪ۤۧۨ-ܑۭܰ-݊ަ-ް߫-߳ࠖ-࠙ࠛ-ࠣࠥ-ࠧࠩ-࡙࠭-࡛ࣔ-ࣣ࣡-ःऺ-़ा-ॏ॑-ॗॢॣঁ-ঃ়া-ৄেৈো-্ৗৢৣਁ-ਃ਼ਾ-ੂੇੈੋ-੍ੑੰੱੵઁ-ઃ઼ા-ૅે-ૉો-્ૢૣଁ-ଃ଼ା-ୄେୈୋ-୍ୖୗୢୣஂா-ூெ-ைொ-்ௗఀ-ఃా-ౄె-ైొ-్ౕౖౢౣಁ-ಃ಼ಾ-ೄೆ-ೈೊ-್ೕೖೢೣഁ-ഃാ-ൄെ-ൈൊ-്ൗൢൣංඃ්ා-ුූෘ-ෟෲෳัิ-ฺ็-๎ັິ-ູົຼ່-ໍ༹༘༙༵༷༾༿ཱ-྄྆྇ྍ-ྗྙ-ྼ࿆ါ-ှၖ-ၙၞ-ၠၢ-ၤၧ-ၭၱ-ၴႂ-ႍႏႚ-ႝ፝-፟ᜒ-᜔ᜲ-᜴ᝒᝓᝲᝳ឴-៓៝᠋-᠍ᢅᢆᢩᤠ-ᤫᤰ-᤻ᨗ-ᨛᩕ-ᩞ᩠-᩿᩼᪰-᪽ᬀ-ᬄ᬴-᭄᭫-᭳ᮀ-ᮂᮡ-ᮭ᯦-᯳ᰤ-᰷᳐-᳔᳒-᳨᳭ᳲ-᳴᳸᳹᷀-᷵᷻-᷿‿⁀⁔⃐-⃥⃜⃡-⃰℘℮⳯-⵿⳱ⷠ-〪ⷿ-゙゚〯꙯ꙴ-꙽ꚞꚟ꛰꛱ꠂ꠆ꠋꠣ-ꠧꢀꢁꢴ-ꣅ꣠-꣱ꤦ-꤭ꥇ-꥓ꦀ-ꦃ꦳-꧀ꧥꨩ-ꨶꩃꩌꩍꩻ-ꩽꪰꪲ-ꪴꪷꪸꪾ꪿꫁ꫫ-ꫯꫵ꫶ꯣ-ꯪ꯬꯭ﬞ︀-️︠-︯︳︴﹍-﹏_𐇽𐋠𐍶-𐍺𐨁-𐨃𐨅𐨆𐨌-𐨏𐨸-𐨿𐨺𐫦𐫥𑀀-𑀂𑀸-𑁆𑁿-𑂂𑂰-𑂺𑄀-𑄂𑄧-𑅳𑄴𑆀-𑆂𑆳-𑇊𑇀-𑇌𑈬-𑈷𑈾𑋟-𑋪𑌀-𑌃𑌼𑌾-𑍄𑍇𑍈𑍋-𑍍𑍗𑍢𑍣𑍦-𑍬𑍰-𑍴𑐵-𑑆𑒰-𑓃𑖯-𑖵𑖸-𑗀𑗜𑗝𑘰-𑙀𑚫-𑚷𑜝-𑜫𑰯-𑰶𑰸-𑰿𑲒-𑲧𑲩-𑲶𖫰-𖫴𖬰-𖬶𖽑-𖽾𖾏-𖾒𛲝𛲞𝅥-𝅩𝅭-𝅲𝅻-𝆂𝆅-𝆋𝆪-𝆭𝉂-𝉄𝨀-𝨶𝨻-𝩬𝩵𝪄𝪛-𝪟𝪡-𝪯𞀀-𞀆𞀈-𞀘𞀛-𞀡𞀣𞀤𞀦-𞣐𞀪-𞣖𞥄-𞥊󠄀-󠇯'
|
||||
pattern = re.compile(
|
||||
r"[\w·̀-ͯ·҃-֑҇-ׇֽֿׁׂׅׄؐ-ًؚ-ٰٟۖ-ۜ۟-۪ۤۧۨ-ܑۭܰ-݊ަ-ް߫-߳ࠖ-࠙ࠛ-ࠣࠥ-ࠧࠩ-࡙࠭-࡛ࣔ-ࣣ࣡-ःऺ-़ा-ॏ॑-ॗॢॣঁ-ঃ়া-ৄেৈো-্ৗৢৣਁ-ਃ਼ਾ-ੂੇੈੋ-੍ੑੰੱੵઁ-ઃ઼ા-ૅે-ૉો-્ૢૣଁ-ଃ଼ା-ୄେୈୋ-୍ୖୗୢୣஂா-ூெ-ைொ-்ௗఀ-ఃా-ౄె-ైొ-్ౕౖౢౣಁ-ಃ಼ಾ-ೄೆ-ೈೊ-್ೕೖೢೣഁ-ഃാ-ൄെ-ൈൊ-്ൗൢൣංඃ්ා-ුූෘ-ෟෲෳัิ-ฺ็-๎ັິ-ູົຼ່-ໍ༹༘༙༵༷༾༿ཱ-྄྆྇ྍ-ྗྙ-ྼ࿆ါ-ှၖ-ၙၞ-ၠၢ-ၤၧ-ၭၱ-ၴႂ-ႍႏႚ-ႝ፝-፟ᜒ-᜔ᜲ-᜴ᝒᝓᝲᝳ឴-៓៝᠋-᠍ᢅᢆᢩᤠ-ᤫᤰ-᤻ᨗ-ᨛᩕ-ᩞ᩠-᩿᩼᪰-᪽ᬀ-ᬄ᬴-᭄᭫-᭳ᮀ-ᮂᮡ-ᮭ᯦-᯳ᰤ-᰷᳐-᳔᳒-᳨᳭ᳲ-᳴᳸᳹᷀-᷵᷻-᷿‿⁀⁔⃐-⃥⃜⃡-⃰℘℮⳯-⵿⳱ⷠ-〪ⷿ-゙゚〯꙯ꙴ-꙽ꚞꚟ꛰꛱ꠂ꠆ꠋꠣ-ꠧꢀꢁꢴ-ꣅ꣠-꣱ꤦ-꤭ꥇ-꥓ꦀ-ꦃ꦳-꧀ꧥꨩ-ꨶꩃꩌꩍꩻ-ꩽꪰꪲ-ꪴꪷꪸꪾ꪿꫁ꫫ-ꫯꫵ꫶ꯣ-ꯪ꯬꯭ﬞ︀-️︠-︯︳︴﹍-﹏_𐇽𐋠𐍶-𐍺𐨁-𐨃𐨅𐨆𐨌-𐨏𐨸-𐨿𐨺𐫦𐫥𑀀-𑀂𑀸-𑁆𑁿-𑂂𑂰-𑂺𑄀-𑄂𑄧-𑅳𑄴𑆀-𑆂𑆳-𑇊𑇀-𑇌𑈬-𑈷𑈾𑋟-𑋪𑌀-𑌃𑌼𑌾-𑍄𑍇𑍈𑍋-𑍍𑍗𑍢𑍣𑍦-𑍬𑍰-𑍴𑐵-𑑆𑒰-𑓃𑖯-𑖵𑖸-𑗀𑗜𑗝𑘰-𑙀𑚫-𑚷𑜝-𑜫𑰯-𑰶𑰸-𑰿𑲒-𑲧𑲩-𑲶𖫰-𖫴𖬰-𖬶𖽑-𖽾𖾏-𖾒𛲝𛲞𝅥-𝅩𝅭-𝅲𝅻-𝆂𝆅-𝆋𝆪-𝆭𝉂-𝉄𝨀-𝨶𝨻-𝩬𝩵𝪄𝪛-𝪟𝪡-𝪯𞀀-𞀆𞀈-𞀘𞀛-𞀡𞀣𞀤𞀦-𞣐𞀪-𞣖𞥄-𞥊󠄀-󠇯]+" # noqa: B950
|
||||
)
|
||||
|
70
lib/spack/external/jinja2/asyncfilters.py
vendored
70
lib/spack/external/jinja2/asyncfilters.py
vendored
@@ -1,12 +1,13 @@
|
||||
from functools import wraps
|
||||
|
||||
from jinja2.asyncsupport import auto_aiter
|
||||
from jinja2 import filters
|
||||
from . import filters
|
||||
from .asyncsupport import auto_aiter
|
||||
from .asyncsupport import auto_await
|
||||
|
||||
|
||||
async def auto_to_seq(value):
|
||||
seq = []
|
||||
if hasattr(value, '__aiter__'):
|
||||
if hasattr(value, "__aiter__"):
|
||||
async for item in value:
|
||||
seq.append(item)
|
||||
else:
|
||||
@@ -16,8 +17,7 @@ async def auto_to_seq(value):
|
||||
|
||||
|
||||
async def async_select_or_reject(args, kwargs, modfunc, lookup_attr):
|
||||
seq, func = filters.prepare_select_or_reject(
|
||||
args, kwargs, modfunc, lookup_attr)
|
||||
seq, func = filters.prepare_select_or_reject(args, kwargs, modfunc, lookup_attr)
|
||||
if seq:
|
||||
async for item in auto_aiter(seq):
|
||||
if func(item):
|
||||
@@ -26,14 +26,19 @@ async def async_select_or_reject(args, kwargs, modfunc, lookup_attr):
|
||||
|
||||
def dualfilter(normal_filter, async_filter):
|
||||
wrap_evalctx = False
|
||||
if getattr(normal_filter, 'environmentfilter', False):
|
||||
is_async = lambda args: args[0].is_async
|
||||
if getattr(normal_filter, "environmentfilter", False) is True:
|
||||
|
||||
def is_async(args):
|
||||
return args[0].is_async
|
||||
|
||||
wrap_evalctx = False
|
||||
else:
|
||||
if not getattr(normal_filter, 'evalcontextfilter', False) and \
|
||||
not getattr(normal_filter, 'contextfilter', False):
|
||||
wrap_evalctx = True
|
||||
is_async = lambda args: args[0].environment.is_async
|
||||
has_evalctxfilter = getattr(normal_filter, "evalcontextfilter", False) is True
|
||||
has_ctxfilter = getattr(normal_filter, "contextfilter", False) is True
|
||||
wrap_evalctx = not has_evalctxfilter and not has_ctxfilter
|
||||
|
||||
def is_async(args):
|
||||
return args[0].environment.is_async
|
||||
|
||||
@wraps(normal_filter)
|
||||
def wrapper(*args, **kwargs):
|
||||
@@ -55,6 +60,7 @@ def wrapper(*args, **kwargs):
|
||||
def asyncfiltervariant(original):
|
||||
def decorator(f):
|
||||
return dualfilter(original, f)
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
@@ -63,19 +69,22 @@ async def do_first(environment, seq):
|
||||
try:
|
||||
return await auto_aiter(seq).__anext__()
|
||||
except StopAsyncIteration:
|
||||
return environment.undefined('No first item, sequence was empty.')
|
||||
return environment.undefined("No first item, sequence was empty.")
|
||||
|
||||
|
||||
@asyncfiltervariant(filters.do_groupby)
|
||||
async def do_groupby(environment, value, attribute):
|
||||
expr = filters.make_attrgetter(environment, attribute)
|
||||
return [filters._GroupTuple(key, await auto_to_seq(values))
|
||||
for key, values in filters.groupby(sorted(
|
||||
await auto_to_seq(value), key=expr), expr)]
|
||||
return [
|
||||
filters._GroupTuple(key, await auto_to_seq(values))
|
||||
for key, values in filters.groupby(
|
||||
sorted(await auto_to_seq(value), key=expr), expr
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
@asyncfiltervariant(filters.do_join)
|
||||
async def do_join(eval_ctx, value, d=u'', attribute=None):
|
||||
async def do_join(eval_ctx, value, d=u"", attribute=None):
|
||||
return filters.do_join(eval_ctx, await auto_to_seq(value), d, attribute)
|
||||
|
||||
|
||||
@@ -109,7 +118,7 @@ async def do_map(*args, **kwargs):
|
||||
seq, func = filters.prepare_map(args, kwargs)
|
||||
if seq:
|
||||
async for item in auto_aiter(seq):
|
||||
yield func(item)
|
||||
yield await auto_await(func(item))
|
||||
|
||||
|
||||
@asyncfiltervariant(filters.do_sum)
|
||||
@@ -118,7 +127,10 @@ async def do_sum(environment, iterable, attribute=None, start=0):
|
||||
if attribute is not None:
|
||||
func = filters.make_attrgetter(environment, attribute)
|
||||
else:
|
||||
func = lambda x: x
|
||||
|
||||
def func(x):
|
||||
return x
|
||||
|
||||
async for item in auto_aiter(iterable):
|
||||
rv += func(item)
|
||||
return rv
|
||||
@@ -130,17 +142,17 @@ async def do_slice(value, slices, fill_with=None):
|
||||
|
||||
|
||||
ASYNC_FILTERS = {
|
||||
'first': do_first,
|
||||
'groupby': do_groupby,
|
||||
'join': do_join,
|
||||
'list': do_list,
|
||||
"first": do_first,
|
||||
"groupby": do_groupby,
|
||||
"join": do_join,
|
||||
"list": do_list,
|
||||
# we intentionally do not support do_last because that would be
|
||||
# ridiculous
|
||||
'reject': do_reject,
|
||||
'rejectattr': do_rejectattr,
|
||||
'map': do_map,
|
||||
'select': do_select,
|
||||
'selectattr': do_selectattr,
|
||||
'sum': do_sum,
|
||||
'slice': do_slice,
|
||||
"reject": do_reject,
|
||||
"rejectattr": do_rejectattr,
|
||||
"map": do_map,
|
||||
"select": do_select,
|
||||
"selectattr": do_selectattr,
|
||||
"sum": do_sum,
|
||||
"slice": do_slice,
|
||||
}
|
||||
|
186
lib/spack/external/jinja2/asyncsupport.py
vendored
186
lib/spack/external/jinja2/asyncsupport.py
vendored
@@ -1,29 +1,27 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""The code for async support. Importing this patches Jinja on supported
|
||||
Python versions.
|
||||
"""
|
||||
jinja2.asyncsupport
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Has all the code for async support which is implemented as a patch
|
||||
for supported Python versions.
|
||||
|
||||
:copyright: (c) 2017 by the Jinja Team.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
import sys
|
||||
import asyncio
|
||||
import inspect
|
||||
from functools import update_wrapper
|
||||
|
||||
from jinja2.utils import concat, internalcode, Markup
|
||||
from jinja2.environment import TemplateModule
|
||||
from jinja2.runtime import LoopContextBase, _last_iteration
|
||||
from markupsafe import Markup
|
||||
|
||||
from .environment import TemplateModule
|
||||
from .runtime import LoopContext
|
||||
from .utils import concat
|
||||
from .utils import internalcode
|
||||
from .utils import missing
|
||||
|
||||
|
||||
async def concat_async(async_gen):
|
||||
rv = []
|
||||
|
||||
async def collect():
|
||||
async for event in async_gen:
|
||||
rv.append(event)
|
||||
|
||||
await collect()
|
||||
return concat(rv)
|
||||
|
||||
@@ -34,10 +32,7 @@ async def generate_async(self, *args, **kwargs):
|
||||
async for event in self.root_render_func(self.new_context(vars)):
|
||||
yield event
|
||||
except Exception:
|
||||
exc_info = sys.exc_info()
|
||||
else:
|
||||
return
|
||||
yield self.environment.handle_exception(exc_info, True)
|
||||
yield self.environment.handle_exception()
|
||||
|
||||
|
||||
def wrap_generate_func(original_generate):
|
||||
@@ -48,17 +43,18 @@ def _convert_generator(self, loop, args, kwargs):
|
||||
yield loop.run_until_complete(async_gen.__anext__())
|
||||
except StopAsyncIteration:
|
||||
pass
|
||||
|
||||
def generate(self, *args, **kwargs):
|
||||
if not self.environment.is_async:
|
||||
return original_generate(self, *args, **kwargs)
|
||||
return _convert_generator(self, asyncio.get_event_loop(), args, kwargs)
|
||||
|
||||
return update_wrapper(generate, original_generate)
|
||||
|
||||
|
||||
async def render_async(self, *args, **kwargs):
|
||||
if not self.environment.is_async:
|
||||
raise RuntimeError('The environment was not created with async mode '
|
||||
'enabled.')
|
||||
raise RuntimeError("The environment was not created with async mode enabled.")
|
||||
|
||||
vars = dict(*args, **kwargs)
|
||||
ctx = self.new_context(vars)
|
||||
@@ -66,8 +62,7 @@ async def render_async(self, *args, **kwargs):
|
||||
try:
|
||||
return await concat_async(self.root_render_func(ctx))
|
||||
except Exception:
|
||||
exc_info = sys.exc_info()
|
||||
return self.environment.handle_exception(exc_info, True)
|
||||
return self.environment.handle_exception()
|
||||
|
||||
|
||||
def wrap_render_func(original_render):
|
||||
@@ -76,6 +71,7 @@ def render(self, *args, **kwargs):
|
||||
return original_render(self, *args, **kwargs)
|
||||
loop = asyncio.get_event_loop()
|
||||
return loop.run_until_complete(self.render_async(*args, **kwargs))
|
||||
|
||||
return update_wrapper(render, original_render)
|
||||
|
||||
|
||||
@@ -109,6 +105,7 @@ def _invoke(self, arguments, autoescape):
|
||||
if not self._environment.is_async:
|
||||
return original_invoke(self, arguments, autoescape)
|
||||
return async_invoke(self, arguments, autoescape)
|
||||
|
||||
return update_wrapper(_invoke, original_invoke)
|
||||
|
||||
|
||||
@@ -124,9 +121,9 @@ def wrap_default_module(original_default_module):
|
||||
@internalcode
|
||||
def _get_default_module(self):
|
||||
if self.environment.is_async:
|
||||
raise RuntimeError('Template module attribute is unavailable '
|
||||
'in async mode')
|
||||
raise RuntimeError("Template module attribute is unavailable in async mode")
|
||||
return original_default_module(self)
|
||||
|
||||
return _get_default_module
|
||||
|
||||
|
||||
@@ -139,30 +136,30 @@ async def make_module_async(self, vars=None, shared=False, locals=None):
|
||||
|
||||
|
||||
def patch_template():
|
||||
from jinja2 import Template
|
||||
from . import Template
|
||||
|
||||
Template.generate = wrap_generate_func(Template.generate)
|
||||
Template.generate_async = update_wrapper(
|
||||
generate_async, Template.generate_async)
|
||||
Template.render_async = update_wrapper(
|
||||
render_async, Template.render_async)
|
||||
Template.generate_async = update_wrapper(generate_async, Template.generate_async)
|
||||
Template.render_async = update_wrapper(render_async, Template.render_async)
|
||||
Template.render = wrap_render_func(Template.render)
|
||||
Template._get_default_module = wrap_default_module(
|
||||
Template._get_default_module)
|
||||
Template._get_default_module = wrap_default_module(Template._get_default_module)
|
||||
Template._get_default_module_async = get_default_module_async
|
||||
Template.make_module_async = update_wrapper(
|
||||
make_module_async, Template.make_module_async)
|
||||
make_module_async, Template.make_module_async
|
||||
)
|
||||
|
||||
|
||||
def patch_runtime():
|
||||
from jinja2.runtime import BlockReference, Macro
|
||||
BlockReference.__call__ = wrap_block_reference_call(
|
||||
BlockReference.__call__)
|
||||
from .runtime import BlockReference, Macro
|
||||
|
||||
BlockReference.__call__ = wrap_block_reference_call(BlockReference.__call__)
|
||||
Macro._invoke = wrap_macro_invoke(Macro._invoke)
|
||||
|
||||
|
||||
def patch_filters():
|
||||
from jinja2.filters import FILTERS
|
||||
from jinja2.asyncfilters import ASYNC_FILTERS
|
||||
from .filters import FILTERS
|
||||
from .asyncfilters import ASYNC_FILTERS
|
||||
|
||||
FILTERS.update(ASYNC_FILTERS)
|
||||
|
||||
|
||||
@@ -179,7 +176,7 @@ async def auto_await(value):
|
||||
|
||||
|
||||
async def auto_aiter(iterable):
|
||||
if hasattr(iterable, '__aiter__'):
|
||||
if hasattr(iterable, "__aiter__"):
|
||||
async for item in iterable:
|
||||
yield item
|
||||
return
|
||||
@@ -187,70 +184,81 @@ async def auto_aiter(iterable):
|
||||
yield item
|
||||
|
||||
|
||||
class AsyncLoopContext(LoopContextBase):
|
||||
|
||||
def __init__(self, async_iterator, undefined, after, length, recurse=None,
|
||||
depth0=0):
|
||||
LoopContextBase.__init__(self, undefined, recurse, depth0)
|
||||
self._async_iterator = async_iterator
|
||||
self._after = after
|
||||
self._length = length
|
||||
class AsyncLoopContext(LoopContext):
|
||||
_to_iterator = staticmethod(auto_aiter)
|
||||
|
||||
@property
|
||||
def length(self):
|
||||
if self._length is None:
|
||||
raise TypeError('Loop length for some iterators cannot be '
|
||||
'lazily calculated in async mode')
|
||||
async def length(self):
|
||||
if self._length is not None:
|
||||
return self._length
|
||||
|
||||
try:
|
||||
self._length = len(self._iterable)
|
||||
except TypeError:
|
||||
iterable = [x async for x in self._iterator]
|
||||
self._iterator = self._to_iterator(iterable)
|
||||
self._length = len(iterable) + self.index + (self._after is not missing)
|
||||
|
||||
return self._length
|
||||
|
||||
def __aiter__(self):
|
||||
return AsyncLoopContextIterator(self)
|
||||
@property
|
||||
async def revindex0(self):
|
||||
return await self.length - self.index
|
||||
|
||||
@property
|
||||
async def revindex(self):
|
||||
return await self.length - self.index0
|
||||
|
||||
class AsyncLoopContextIterator(object):
|
||||
__slots__ = ('context',)
|
||||
async def _peek_next(self):
|
||||
if self._after is not missing:
|
||||
return self._after
|
||||
|
||||
def __init__(self, context):
|
||||
self.context = context
|
||||
try:
|
||||
self._after = await self._iterator.__anext__()
|
||||
except StopAsyncIteration:
|
||||
self._after = missing
|
||||
|
||||
return self._after
|
||||
|
||||
@property
|
||||
async def last(self):
|
||||
return await self._peek_next() is missing
|
||||
|
||||
@property
|
||||
async def nextitem(self):
|
||||
rv = await self._peek_next()
|
||||
|
||||
if rv is missing:
|
||||
return self._undefined("there is no next item")
|
||||
|
||||
return rv
|
||||
|
||||
def __aiter__(self):
|
||||
return self
|
||||
|
||||
async def __anext__(self):
|
||||
ctx = self.context
|
||||
ctx.index0 += 1
|
||||
if ctx._after is _last_iteration:
|
||||
raise StopAsyncIteration()
|
||||
ctx._before = ctx._current
|
||||
ctx._current = ctx._after
|
||||
try:
|
||||
ctx._after = await ctx._async_iterator.__anext__()
|
||||
except StopAsyncIteration:
|
||||
ctx._after = _last_iteration
|
||||
return ctx._current, ctx
|
||||
if self._after is not missing:
|
||||
rv = self._after
|
||||
self._after = missing
|
||||
else:
|
||||
rv = await self._iterator.__anext__()
|
||||
|
||||
self.index0 += 1
|
||||
self._before = self._current
|
||||
self._current = rv
|
||||
return rv, self
|
||||
|
||||
|
||||
async def make_async_loop_context(iterable, undefined, recurse=None, depth0=0):
|
||||
# Length is more complicated and less efficient in async mode. The
|
||||
# reason for this is that we cannot know if length will be used
|
||||
# upfront but because length is a property we cannot lazily execute it
|
||||
# later. This means that we need to buffer it up and measure :(
|
||||
#
|
||||
# We however only do this for actual iterators, not for async
|
||||
# iterators as blocking here does not seem like the best idea in the
|
||||
# world.
|
||||
try:
|
||||
length = len(iterable)
|
||||
except (TypeError, AttributeError):
|
||||
if not hasattr(iterable, '__aiter__'):
|
||||
iterable = tuple(iterable)
|
||||
length = len(iterable)
|
||||
else:
|
||||
length = None
|
||||
async_iterator = auto_aiter(iterable)
|
||||
try:
|
||||
after = await async_iterator.__anext__()
|
||||
except StopAsyncIteration:
|
||||
after = _last_iteration
|
||||
return AsyncLoopContext(async_iterator, undefined, after, length, recurse,
|
||||
depth0)
|
||||
import warnings
|
||||
|
||||
warnings.warn(
|
||||
"This template must be recompiled with at least Jinja 2.11, or"
|
||||
" it will fail in 3.0.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
return AsyncLoopContext(iterable, undefined, recurse, depth0)
|
||||
|
||||
|
||||
patch_all()
|
||||
|
142
lib/spack/external/jinja2/bccache.py
vendored
142
lib/spack/external/jinja2/bccache.py
vendored
@@ -1,60 +1,37 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""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.
|
||||
"""
|
||||
jinja2.bccache
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
This module implements the bytecode cache system Jinja is optionally
|
||||
using. This is useful if you have very complex template situations and
|
||||
the compiliation of all those templates slow down your application too
|
||||
much.
|
||||
|
||||
Situations where this is useful are often forking web applications that
|
||||
are initialized on the first request.
|
||||
|
||||
:copyright: (c) 2017 by the Jinja Team.
|
||||
:license: BSD.
|
||||
"""
|
||||
from os import path, listdir
|
||||
import os
|
||||
import sys
|
||||
import stat
|
||||
import errno
|
||||
import marshal
|
||||
import tempfile
|
||||
import fnmatch
|
||||
import os
|
||||
import stat
|
||||
import sys
|
||||
import tempfile
|
||||
from hashlib import sha1
|
||||
from jinja2.utils import open_if_exists
|
||||
from jinja2._compat import BytesIO, pickle, PY2, text_type
|
||||
from os import listdir
|
||||
from os import path
|
||||
|
||||
from ._compat import BytesIO
|
||||
from ._compat import marshal_dump
|
||||
from ._compat import marshal_load
|
||||
from ._compat import pickle
|
||||
from ._compat import text_type
|
||||
from .utils import open_if_exists
|
||||
|
||||
# marshal works better on 3.x, one hack less required
|
||||
if not PY2:
|
||||
marshal_dump = marshal.dump
|
||||
marshal_load = marshal.load
|
||||
else:
|
||||
|
||||
def marshal_dump(code, f):
|
||||
if isinstance(f, file):
|
||||
marshal.dump(code, f)
|
||||
else:
|
||||
f.write(marshal.dumps(code))
|
||||
|
||||
def marshal_load(f):
|
||||
if isinstance(f, file):
|
||||
return marshal.load(f)
|
||||
return marshal.loads(f.read())
|
||||
|
||||
|
||||
bc_version = 3
|
||||
|
||||
# magic version used to only change with new jinja versions. With 2.6
|
||||
# we change this to also take Python version changes into account. The
|
||||
# reason for this is that Python tends to segfault if fed earlier bytecode
|
||||
# versions because someone thought it would be a good idea to reuse opcodes
|
||||
# or make Python incompatible with earlier versions.
|
||||
bc_magic = 'j2'.encode('ascii') + \
|
||||
pickle.dumps(bc_version, 2) + \
|
||||
pickle.dumps((sys.version_info[0] << 24) | sys.version_info[1])
|
||||
bc_version = 4
|
||||
# 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(object):
|
||||
@@ -98,7 +75,7 @@ def load_bytecode(self, f):
|
||||
def write_bytecode(self, f):
|
||||
"""Dump the bytecode into the file or file like object passed."""
|
||||
if self.code is None:
|
||||
raise TypeError('can\'t write empty bucket')
|
||||
raise TypeError("can't write empty bucket")
|
||||
f.write(bc_magic)
|
||||
pickle.dump(self.checksum, f, 2)
|
||||
marshal_dump(self.code, f)
|
||||
@@ -140,7 +117,7 @@ def dump_bytecode(self, bucket):
|
||||
bucket.write_bytecode(f)
|
||||
|
||||
A more advanced version of a filesystem based bytecode cache is part of
|
||||
Jinja2.
|
||||
Jinja.
|
||||
"""
|
||||
|
||||
def load_bytecode(self, bucket):
|
||||
@@ -158,24 +135,24 @@ def dump_bytecode(self, bucket):
|
||||
raise NotImplementedError()
|
||||
|
||||
def clear(self):
|
||||
"""Clears the cache. This method is not used by Jinja2 but should be
|
||||
"""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, filename=None):
|
||||
"""Returns the unique hash key for this template name."""
|
||||
hash = sha1(name.encode('utf-8'))
|
||||
hash = sha1(name.encode("utf-8"))
|
||||
if filename is not None:
|
||||
filename = '|' + filename
|
||||
filename = "|" + filename
|
||||
if isinstance(filename, text_type):
|
||||
filename = filename.encode('utf-8')
|
||||
filename = filename.encode("utf-8")
|
||||
hash.update(filename)
|
||||
return hash.hexdigest()
|
||||
|
||||
def get_source_checksum(self, source):
|
||||
"""Returns a checksum for the source."""
|
||||
return sha1(source.encode('utf-8')).hexdigest()
|
||||
return sha1(source.encode("utf-8")).hexdigest()
|
||||
|
||||
def get_bucket(self, environment, name, filename, source):
|
||||
"""Return a cache bucket for the given template. All arguments are
|
||||
@@ -210,7 +187,7 @@ class FileSystemBytecodeCache(BytecodeCache):
|
||||
This bytecode cache supports clearing of the cache using the clear method.
|
||||
"""
|
||||
|
||||
def __init__(self, directory=None, pattern='__jinja2_%s.cache'):
|
||||
def __init__(self, directory=None, pattern="__jinja2_%s.cache"):
|
||||
if directory is None:
|
||||
directory = self._get_default_cache_dir()
|
||||
self.directory = directory
|
||||
@@ -218,19 +195,21 @@ def __init__(self, directory=None, pattern='__jinja2_%s.cache'):
|
||||
|
||||
def _get_default_cache_dir(self):
|
||||
def _unsafe_dir():
|
||||
raise RuntimeError('Cannot determine safe temp directory. You '
|
||||
'need to explicitly provide one.')
|
||||
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':
|
||||
if os.name == "nt":
|
||||
return tmpdir
|
||||
if not hasattr(os, 'getuid'):
|
||||
if not hasattr(os, "getuid"):
|
||||
_unsafe_dir()
|
||||
|
||||
dirname = '_jinja2-cache-%d' % os.getuid()
|
||||
dirname = "_jinja2-cache-%d" % os.getuid()
|
||||
actual_dir = os.path.join(tmpdir, dirname)
|
||||
|
||||
try:
|
||||
@@ -241,18 +220,22 @@ def _unsafe_dir():
|
||||
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:
|
||||
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:
|
||||
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
|
||||
@@ -261,7 +244,7 @@ def _get_cache_filename(self, bucket):
|
||||
return path.join(self.directory, self.pattern % bucket.key)
|
||||
|
||||
def load_bytecode(self, bucket):
|
||||
f = open_if_exists(self._get_cache_filename(bucket), 'rb')
|
||||
f = open_if_exists(self._get_cache_filename(bucket), "rb")
|
||||
if f is not None:
|
||||
try:
|
||||
bucket.load_bytecode(f)
|
||||
@@ -269,7 +252,7 @@ def load_bytecode(self, bucket):
|
||||
f.close()
|
||||
|
||||
def dump_bytecode(self, bucket):
|
||||
f = open(self._get_cache_filename(bucket), 'wb')
|
||||
f = open(self._get_cache_filename(bucket), "wb")
|
||||
try:
|
||||
bucket.write_bytecode(f)
|
||||
finally:
|
||||
@@ -280,7 +263,8 @@ def clear(self):
|
||||
# write access on the file system and the function does not exist
|
||||
# normally.
|
||||
from os import remove
|
||||
files = fnmatch.filter(listdir(self.directory), self.pattern % '*')
|
||||
|
||||
files = fnmatch.filter(listdir(self.directory), self.pattern % "*")
|
||||
for filename in files:
|
||||
try:
|
||||
remove(path.join(self.directory, filename))
|
||||
@@ -296,9 +280,8 @@ class MemcachedBytecodeCache(BytecodeCache):
|
||||
|
||||
Libraries compatible with this class:
|
||||
|
||||
- `werkzeug <http://werkzeug.pocoo.org/>`_.contrib.cache
|
||||
- `python-memcached <https://www.tummy.com/Community/software/python-memcached/>`_
|
||||
- `cmemcache <http://gijsbert.org/cmemcache/>`_
|
||||
- `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 unicode. You can however pass
|
||||
@@ -334,8 +317,13 @@ class MemcachedBytecodeCache(BytecodeCache):
|
||||
`ignore_memcache_errors` parameter.
|
||||
"""
|
||||
|
||||
def __init__(self, client, prefix='jinja2/bytecode/', timeout=None,
|
||||
ignore_memcache_errors=True):
|
||||
def __init__(
|
||||
self,
|
||||
client,
|
||||
prefix="jinja2/bytecode/",
|
||||
timeout=None,
|
||||
ignore_memcache_errors=True,
|
||||
):
|
||||
self.client = client
|
||||
self.prefix = prefix
|
||||
self.timeout = timeout
|
||||
|
1248
lib/spack/external/jinja2/compiler.py
vendored
1248
lib/spack/external/jinja2/compiler.py
vendored
File diff suppressed because it is too large
Load Diff
15
lib/spack/external/jinja2/constants.py
vendored
15
lib/spack/external/jinja2/constants.py
vendored
@@ -1,17 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
jinja.constants
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Various constants.
|
||||
|
||||
:copyright: (c) 2017 by the Jinja Team.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
|
||||
#: list of lorem ipsum words used by the lipsum() helper function
|
||||
LOREM_IPSUM_WORDS = u'''\
|
||||
LOREM_IPSUM_WORDS = u"""\
|
||||
a ac accumsan ad adipiscing aenean aliquam aliquet amet ante aptent arcu at
|
||||
auctor augue bibendum blandit class commodo condimentum congue consectetuer
|
||||
consequat conubia convallis cras cubilia cum curabitur curae cursus dapibus
|
||||
@@ -29,4 +18,4 @@
|
||||
sociis sociosqu sodales sollicitudin suscipit suspendisse taciti tellus tempor
|
||||
tempus tincidunt torquent tortor tristique turpis ullamcorper ultrices
|
||||
ultricies urna ut varius vehicula vel velit venenatis vestibulum vitae vivamus
|
||||
viverra volutpat vulputate'''
|
||||
viverra volutpat vulputate"""
|
||||
|
528
lib/spack/external/jinja2/debug.py
vendored
528
lib/spack/external/jinja2/debug.py
vendored
@@ -1,372 +1,268 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
jinja2.debug
|
||||
~~~~~~~~~~~~
|
||||
|
||||
Implements the debug interface for Jinja. This module does some pretty
|
||||
ugly stuff with the Python traceback system in order to achieve tracebacks
|
||||
with correct line numbers, locals and contents.
|
||||
|
||||
:copyright: (c) 2017 by the Jinja Team.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
import sys
|
||||
import traceback
|
||||
from types import TracebackType, CodeType
|
||||
from jinja2.utils import missing, internal_code
|
||||
from jinja2.exceptions import TemplateSyntaxError
|
||||
from jinja2._compat import iteritems, reraise, PY2
|
||||
from types import CodeType
|
||||
|
||||
# on pypy we can take advantage of transparent proxies
|
||||
try:
|
||||
from __pypy__ import tproxy
|
||||
except ImportError:
|
||||
tproxy = None
|
||||
from . import TemplateSyntaxError
|
||||
from ._compat import PYPY
|
||||
from .utils import internal_code
|
||||
from .utils import missing
|
||||
|
||||
|
||||
# how does the raise helper look like?
|
||||
try:
|
||||
exec("raise TypeError, 'foo'")
|
||||
except SyntaxError:
|
||||
raise_helper = 'raise __jinja_exception__[1]'
|
||||
except TypeError:
|
||||
raise_helper = 'raise __jinja_exception__[0], __jinja_exception__[1]'
|
||||
def rewrite_traceback_stack(source=None):
|
||||
"""Rewrite the current exception to replace any tracebacks from
|
||||
within compiled template code with tracebacks that look like they
|
||||
came from the template source.
|
||||
|
||||
This must be called within an ``except`` block.
|
||||
|
||||
class TracebackFrameProxy(object):
|
||||
"""Proxies a traceback frame."""
|
||||
|
||||
def __init__(self, tb):
|
||||
self.tb = tb
|
||||
self._tb_next = None
|
||||
|
||||
@property
|
||||
def tb_next(self):
|
||||
return self._tb_next
|
||||
|
||||
def set_next(self, next):
|
||||
if tb_set_next is not None:
|
||||
try:
|
||||
tb_set_next(self.tb, next and next.tb or None)
|
||||
except Exception:
|
||||
# this function can fail due to all the hackery it does
|
||||
# on various python implementations. We just catch errors
|
||||
# down and ignore them if necessary.
|
||||
pass
|
||||
self._tb_next = next
|
||||
|
||||
@property
|
||||
def is_jinja_frame(self):
|
||||
return '__jinja_template__' in self.tb.tb_frame.f_globals
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(self.tb, name)
|
||||
|
||||
|
||||
def make_frame_proxy(frame):
|
||||
proxy = TracebackFrameProxy(frame)
|
||||
if tproxy is None:
|
||||
return proxy
|
||||
def operation_handler(operation, *args, **kwargs):
|
||||
if operation in ('__getattribute__', '__getattr__'):
|
||||
return getattr(proxy, args[0])
|
||||
elif operation == '__setattr__':
|
||||
proxy.__setattr__(*args, **kwargs)
|
||||
else:
|
||||
return getattr(proxy, operation)(*args, **kwargs)
|
||||
return tproxy(TracebackType, operation_handler)
|
||||
|
||||
|
||||
class ProcessedTraceback(object):
|
||||
"""Holds a Jinja preprocessed traceback for printing or reraising."""
|
||||
|
||||
def __init__(self, exc_type, exc_value, frames):
|
||||
assert frames, 'no frames for this traceback?'
|
||||
self.exc_type = exc_type
|
||||
self.exc_value = exc_value
|
||||
self.frames = frames
|
||||
|
||||
# newly concatenate the frames (which are proxies)
|
||||
prev_tb = None
|
||||
for tb in self.frames:
|
||||
if prev_tb is not None:
|
||||
prev_tb.set_next(tb)
|
||||
prev_tb = tb
|
||||
prev_tb.set_next(None)
|
||||
|
||||
def render_as_text(self, limit=None):
|
||||
"""Return a string with the traceback."""
|
||||
lines = traceback.format_exception(self.exc_type, self.exc_value,
|
||||
self.frames[0], limit=limit)
|
||||
return ''.join(lines).rstrip()
|
||||
|
||||
def render_as_html(self, full=False):
|
||||
"""Return a unicode string with the traceback as rendered HTML."""
|
||||
from jinja2.debugrenderer import render_traceback
|
||||
return u'%s\n\n<!--\n%s\n-->' % (
|
||||
render_traceback(self, full=full),
|
||||
self.render_as_text().decode('utf-8', 'replace')
|
||||
)
|
||||
|
||||
@property
|
||||
def is_template_syntax_error(self):
|
||||
"""`True` if this is a template syntax error."""
|
||||
return isinstance(self.exc_value, TemplateSyntaxError)
|
||||
|
||||
@property
|
||||
def exc_info(self):
|
||||
"""Exception info tuple with a proxy around the frame objects."""
|
||||
return self.exc_type, self.exc_value, self.frames[0]
|
||||
|
||||
@property
|
||||
def standard_exc_info(self):
|
||||
"""Standard python exc_info for re-raising"""
|
||||
tb = self.frames[0]
|
||||
# the frame will be an actual traceback (or transparent proxy) if
|
||||
# we are on pypy or a python implementation with support for tproxy
|
||||
if type(tb) is not TracebackType:
|
||||
tb = tb.tb
|
||||
return self.exc_type, self.exc_value, tb
|
||||
|
||||
|
||||
def make_traceback(exc_info, source_hint=None):
|
||||
"""Creates a processed traceback object from the exc_info."""
|
||||
exc_type, exc_value, tb = exc_info
|
||||
if isinstance(exc_value, TemplateSyntaxError):
|
||||
exc_info = translate_syntax_error(exc_value, source_hint)
|
||||
initial_skip = 0
|
||||
else:
|
||||
initial_skip = 1
|
||||
return translate_exception(exc_info, initial_skip)
|
||||
|
||||
|
||||
def translate_syntax_error(error, source=None):
|
||||
"""Rewrites a syntax error to please traceback systems."""
|
||||
error.source = source
|
||||
error.translated = True
|
||||
exc_info = (error.__class__, error, None)
|
||||
filename = error.filename
|
||||
if filename is None:
|
||||
filename = '<unknown>'
|
||||
return fake_exc_info(exc_info, filename, error.lineno)
|
||||
|
||||
|
||||
def translate_exception(exc_info, initial_skip=0):
|
||||
"""If passed an exc_info it will automatically rewrite the exceptions
|
||||
all the way down to the correct line numbers and frames.
|
||||
:param exc_info: A :meth:`sys.exc_info` tuple. If not provided,
|
||||
the current ``exc_info`` is used.
|
||||
:param source: For ``TemplateSyntaxError``, the original source if
|
||||
known.
|
||||
:return: A :meth:`sys.exc_info` tuple that can be re-raised.
|
||||
"""
|
||||
tb = exc_info[2]
|
||||
frames = []
|
||||
exc_type, exc_value, tb = sys.exc_info()
|
||||
|
||||
# skip some internal frames if wanted
|
||||
for x in range(initial_skip):
|
||||
if tb is not None:
|
||||
tb = tb.tb_next
|
||||
initial_tb = tb
|
||||
if isinstance(exc_value, TemplateSyntaxError) and not exc_value.translated:
|
||||
exc_value.translated = True
|
||||
exc_value.source = source
|
||||
|
||||
try:
|
||||
# Remove the old traceback on Python 3, otherwise the frames
|
||||
# from the compiler still show up.
|
||||
exc_value.with_traceback(None)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
# Outside of runtime, so the frame isn't executing template
|
||||
# code, but it still needs to point at the template.
|
||||
tb = fake_traceback(
|
||||
exc_value, None, exc_value.filename or "<unknown>", exc_value.lineno
|
||||
)
|
||||
else:
|
||||
# Skip the frame for the render function.
|
||||
tb = tb.tb_next
|
||||
|
||||
stack = []
|
||||
|
||||
# Build the stack of traceback object, replacing any in template
|
||||
# code with the source file and line information.
|
||||
while tb is not None:
|
||||
# skip frames decorated with @internalcode. These are internal
|
||||
# calls we can't avoid and that are useless in template debugging
|
||||
# output.
|
||||
# Skip frames decorated with @internalcode. These are internal
|
||||
# calls that aren't useful in template debugging output.
|
||||
if tb.tb_frame.f_code in internal_code:
|
||||
tb = tb.tb_next
|
||||
continue
|
||||
|
||||
# save a reference to the next frame if we override the current
|
||||
# one with a faked one.
|
||||
next = tb.tb_next
|
||||
template = tb.tb_frame.f_globals.get("__jinja_template__")
|
||||
|
||||
# fake template exceptions
|
||||
template = tb.tb_frame.f_globals.get('__jinja_template__')
|
||||
if template is not None:
|
||||
lineno = template.get_corresponding_lineno(tb.tb_lineno)
|
||||
tb = fake_exc_info(exc_info[:2] + (tb,), template.filename,
|
||||
lineno)[2]
|
||||
fake_tb = fake_traceback(exc_value, tb, template.filename, lineno)
|
||||
stack.append(fake_tb)
|
||||
else:
|
||||
stack.append(tb)
|
||||
|
||||
frames.append(make_frame_proxy(tb))
|
||||
tb = next
|
||||
tb = tb.tb_next
|
||||
|
||||
# if we don't have any exceptions in the frames left, we have to
|
||||
# reraise it unchanged.
|
||||
# XXX: can we backup here? when could this happen?
|
||||
if not frames:
|
||||
reraise(exc_info[0], exc_info[1], exc_info[2])
|
||||
tb_next = None
|
||||
|
||||
return ProcessedTraceback(exc_info[0], exc_info[1], frames)
|
||||
# Assign tb_next in reverse to avoid circular references.
|
||||
for tb in reversed(stack):
|
||||
tb_next = tb_set_next(tb, tb_next)
|
||||
|
||||
return exc_type, exc_value, tb_next
|
||||
|
||||
|
||||
def get_jinja_locals(real_locals):
|
||||
ctx = real_locals.get('context')
|
||||
if ctx:
|
||||
locals = ctx.get_all().copy()
|
||||
def fake_traceback(exc_value, tb, filename, lineno):
|
||||
"""Produce a new traceback object that looks like it came from the
|
||||
template source instead of the compiled code. The filename, line
|
||||
number, and location name will point to the template, and the local
|
||||
variables will be the current template context.
|
||||
|
||||
:param exc_value: The original exception to be re-raised to create
|
||||
the new traceback.
|
||||
:param tb: The original traceback to get the local variables and
|
||||
code info from.
|
||||
:param filename: The template filename.
|
||||
:param lineno: The line number in the template source.
|
||||
"""
|
||||
if tb is not None:
|
||||
# Replace the real locals with the context that would be
|
||||
# available at that point in the template.
|
||||
locals = get_template_locals(tb.tb_frame.f_locals)
|
||||
locals.pop("__jinja_exception__", None)
|
||||
else:
|
||||
locals = {}
|
||||
|
||||
globals = {
|
||||
"__name__": filename,
|
||||
"__file__": filename,
|
||||
"__jinja_exception__": exc_value,
|
||||
}
|
||||
# Raise an exception at the correct line number.
|
||||
code = compile("\n" * (lineno - 1) + "raise __jinja_exception__", filename, "exec")
|
||||
|
||||
# Build a new code object that points to the template file and
|
||||
# replaces the location with a block name.
|
||||
try:
|
||||
location = "template"
|
||||
|
||||
if tb is not None:
|
||||
function = tb.tb_frame.f_code.co_name
|
||||
|
||||
if function == "root":
|
||||
location = "top-level template code"
|
||||
elif function.startswith("block_"):
|
||||
location = 'block "%s"' % function[6:]
|
||||
|
||||
# Collect arguments for the new code object. CodeType only
|
||||
# accepts positional arguments, and arguments were inserted in
|
||||
# new Python versions.
|
||||
code_args = []
|
||||
|
||||
for attr in (
|
||||
"argcount",
|
||||
"posonlyargcount", # Python 3.8
|
||||
"kwonlyargcount", # Python 3
|
||||
"nlocals",
|
||||
"stacksize",
|
||||
"flags",
|
||||
"code", # codestring
|
||||
"consts", # constants
|
||||
"names",
|
||||
"varnames",
|
||||
("filename", filename),
|
||||
("name", location),
|
||||
"firstlineno",
|
||||
"lnotab",
|
||||
"freevars",
|
||||
"cellvars",
|
||||
):
|
||||
if isinstance(attr, tuple):
|
||||
# Replace with given value.
|
||||
code_args.append(attr[1])
|
||||
continue
|
||||
|
||||
try:
|
||||
# Copy original value if it exists.
|
||||
code_args.append(getattr(code, "co_" + attr))
|
||||
except AttributeError:
|
||||
# Some arguments were added later.
|
||||
continue
|
||||
|
||||
code = CodeType(*code_args)
|
||||
except Exception:
|
||||
# Some environments such as Google App Engine don't support
|
||||
# modifying code objects.
|
||||
pass
|
||||
|
||||
# Execute the new code, which is guaranteed to raise, and return
|
||||
# the new traceback without this frame.
|
||||
try:
|
||||
exec(code, globals, locals)
|
||||
except BaseException:
|
||||
return sys.exc_info()[2].tb_next
|
||||
|
||||
|
||||
def get_template_locals(real_locals):
|
||||
"""Based on the runtime locals, get the context that would be
|
||||
available at that point in the template.
|
||||
"""
|
||||
# Start with the current template context.
|
||||
ctx = real_locals.get("context")
|
||||
|
||||
if ctx:
|
||||
data = ctx.get_all().copy()
|
||||
else:
|
||||
data = {}
|
||||
|
||||
# Might be in a derived context that only sets local variables
|
||||
# rather than pushing a context. Local variables follow the scheme
|
||||
# l_depth_name. Find the highest-depth local that has a value for
|
||||
# each name.
|
||||
local_overrides = {}
|
||||
|
||||
for name, value in iteritems(real_locals):
|
||||
if not name.startswith('l_') or value is missing:
|
||||
for name, value in real_locals.items():
|
||||
if not name.startswith("l_") or value is missing:
|
||||
# Not a template variable, or no longer relevant.
|
||||
continue
|
||||
|
||||
try:
|
||||
_, depth, name = name.split('_', 2)
|
||||
_, depth, name = name.split("_", 2)
|
||||
depth = int(depth)
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
cur_depth = local_overrides.get(name, (-1,))[0]
|
||||
|
||||
if cur_depth < depth:
|
||||
local_overrides[name] = (depth, value)
|
||||
|
||||
for name, (_, value) in iteritems(local_overrides):
|
||||
# Modify the context with any derived context.
|
||||
for name, (_, value) in local_overrides.items():
|
||||
if value is missing:
|
||||
locals.pop(name, None)
|
||||
data.pop(name, None)
|
||||
else:
|
||||
locals[name] = value
|
||||
data[name] = value
|
||||
|
||||
return locals
|
||||
return data
|
||||
|
||||
|
||||
def fake_exc_info(exc_info, filename, lineno):
|
||||
"""Helper for `translate_exception`."""
|
||||
exc_type, exc_value, tb = exc_info
|
||||
if sys.version_info >= (3, 7):
|
||||
# tb_next is directly assignable as of Python 3.7
|
||||
def tb_set_next(tb, tb_next):
|
||||
tb.tb_next = tb_next
|
||||
return tb
|
||||
|
||||
# figure the real context out
|
||||
if tb is not None:
|
||||
locals = get_jinja_locals(tb.tb_frame.f_locals)
|
||||
|
||||
# if there is a local called __jinja_exception__, we get
|
||||
# rid of it to not break the debug functionality.
|
||||
locals.pop('__jinja_exception__', None)
|
||||
elif PYPY:
|
||||
# PyPy might have special support, and won't work with ctypes.
|
||||
try:
|
||||
import tputil
|
||||
except ImportError:
|
||||
# Without tproxy support, use the original traceback.
|
||||
def tb_set_next(tb, tb_next):
|
||||
return tb
|
||||
|
||||
else:
|
||||
locals = {}
|
||||
# With tproxy support, create a proxy around the traceback that
|
||||
# returns the new tb_next.
|
||||
def tb_set_next(tb, tb_next):
|
||||
def controller(op):
|
||||
if op.opname == "__getattribute__" and op.args[0] == "tb_next":
|
||||
return tb_next
|
||||
|
||||
# assamble fake globals we need
|
||||
globals = {
|
||||
'__name__': filename,
|
||||
'__file__': filename,
|
||||
'__jinja_exception__': exc_info[:2],
|
||||
return op.delegate()
|
||||
|
||||
# we don't want to keep the reference to the template around
|
||||
# to not cause circular dependencies, but we mark it as Jinja
|
||||
# frame for the ProcessedTraceback
|
||||
'__jinja_template__': None
|
||||
}
|
||||
|
||||
# and fake the exception
|
||||
code = compile('\n' * (lineno - 1) + raise_helper, filename, 'exec')
|
||||
|
||||
# if it's possible, change the name of the code. This won't work
|
||||
# on some python environments such as google appengine
|
||||
try:
|
||||
if tb is None:
|
||||
location = 'template'
|
||||
else:
|
||||
function = tb.tb_frame.f_code.co_name
|
||||
if function == 'root':
|
||||
location = 'top-level template code'
|
||||
elif function.startswith('block_'):
|
||||
location = 'block "%s"' % function[6:]
|
||||
else:
|
||||
location = 'template'
|
||||
|
||||
if PY2:
|
||||
code = CodeType(0, code.co_nlocals, code.co_stacksize,
|
||||
code.co_flags, code.co_code, code.co_consts,
|
||||
code.co_names, code.co_varnames, filename,
|
||||
location, code.co_firstlineno,
|
||||
code.co_lnotab, (), ())
|
||||
else:
|
||||
code = CodeType(0, code.co_kwonlyargcount,
|
||||
code.co_nlocals, code.co_stacksize,
|
||||
code.co_flags, code.co_code, code.co_consts,
|
||||
code.co_names, code.co_varnames, filename,
|
||||
location, code.co_firstlineno,
|
||||
code.co_lnotab, (), ())
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
# execute the code and catch the new traceback
|
||||
try:
|
||||
exec(code, globals, locals)
|
||||
except:
|
||||
exc_info = sys.exc_info()
|
||||
new_tb = exc_info[2].tb_next
|
||||
|
||||
# return without this frame
|
||||
return exc_info[:2] + (new_tb,)
|
||||
return tputil.make_proxy(controller, obj=tb)
|
||||
|
||||
|
||||
def _init_ugly_crap():
|
||||
"""This function implements a few ugly things so that we can patch the
|
||||
traceback objects. The function returned allows resetting `tb_next` on
|
||||
any python traceback object. Do not attempt to use this on non cpython
|
||||
interpreters
|
||||
"""
|
||||
else:
|
||||
# Use ctypes to assign tb_next at the C level since it's read-only
|
||||
# from Python.
|
||||
import ctypes
|
||||
from types import TracebackType
|
||||
|
||||
if PY2:
|
||||
# figure out size of _Py_ssize_t for Python 2:
|
||||
if hasattr(ctypes.pythonapi, 'Py_InitModule4_64'):
|
||||
_Py_ssize_t = ctypes.c_int64
|
||||
else:
|
||||
_Py_ssize_t = ctypes.c_int
|
||||
else:
|
||||
# platform ssize_t on Python 3
|
||||
_Py_ssize_t = ctypes.c_ssize_t
|
||||
|
||||
# regular python
|
||||
class _PyObject(ctypes.Structure):
|
||||
pass
|
||||
_PyObject._fields_ = [
|
||||
('ob_refcnt', _Py_ssize_t),
|
||||
('ob_type', ctypes.POINTER(_PyObject))
|
||||
]
|
||||
|
||||
# python with trace
|
||||
if hasattr(sys, 'getobjects'):
|
||||
class _PyObject(ctypes.Structure):
|
||||
pass
|
||||
_PyObject._fields_ = [
|
||||
('_ob_next', ctypes.POINTER(_PyObject)),
|
||||
('_ob_prev', ctypes.POINTER(_PyObject)),
|
||||
('ob_refcnt', _Py_ssize_t),
|
||||
('ob_type', ctypes.POINTER(_PyObject))
|
||||
class _CTraceback(ctypes.Structure):
|
||||
_fields_ = [
|
||||
# Extra PyObject slots when compiled with Py_TRACE_REFS.
|
||||
("PyObject_HEAD", ctypes.c_byte * object().__sizeof__()),
|
||||
# Only care about tb_next as an object, not a traceback.
|
||||
("tb_next", ctypes.py_object),
|
||||
]
|
||||
|
||||
class _Traceback(_PyObject):
|
||||
pass
|
||||
_Traceback._fields_ = [
|
||||
('tb_next', ctypes.POINTER(_Traceback)),
|
||||
('tb_frame', ctypes.POINTER(_PyObject)),
|
||||
('tb_lasti', ctypes.c_int),
|
||||
('tb_lineno', ctypes.c_int)
|
||||
]
|
||||
def tb_set_next(tb, tb_next):
|
||||
c_tb = _CTraceback.from_address(id(tb))
|
||||
|
||||
def tb_set_next(tb, next):
|
||||
"""Set the tb_next attribute of a traceback object."""
|
||||
if not (isinstance(tb, TracebackType) and
|
||||
(next is None or isinstance(next, TracebackType))):
|
||||
raise TypeError('tb_set_next arguments must be traceback objects')
|
||||
obj = _Traceback.from_address(id(tb))
|
||||
# Clear out the old tb_next.
|
||||
if tb.tb_next is not None:
|
||||
old = _Traceback.from_address(id(tb.tb_next))
|
||||
old.ob_refcnt -= 1
|
||||
if next is None:
|
||||
obj.tb_next = ctypes.POINTER(_Traceback)()
|
||||
else:
|
||||
next = _Traceback.from_address(id(next))
|
||||
next.ob_refcnt += 1
|
||||
obj.tb_next = ctypes.pointer(next)
|
||||
c_tb_next = ctypes.py_object(tb.tb_next)
|
||||
c_tb.tb_next = ctypes.py_object()
|
||||
ctypes.pythonapi.Py_DecRef(c_tb_next)
|
||||
|
||||
return tb_set_next
|
||||
# Assign the new tb_next.
|
||||
if tb_next is not None:
|
||||
c_tb_next = ctypes.py_object(tb_next)
|
||||
ctypes.pythonapi.Py_IncRef(c_tb_next)
|
||||
c_tb.tb_next = c_tb_next
|
||||
|
||||
|
||||
# try to get a tb_set_next implementation if we don't have transparent
|
||||
# proxies.
|
||||
tb_set_next = None
|
||||
if tproxy is None:
|
||||
try:
|
||||
tb_set_next = _init_ugly_crap()
|
||||
except:
|
||||
pass
|
||||
del _init_ugly_crap
|
||||
return tb
|
||||
|
70
lib/spack/external/jinja2/defaults.py
vendored
70
lib/spack/external/jinja2/defaults.py
vendored
@@ -1,56 +1,44 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
jinja2.defaults
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Jinja default filters and tags.
|
||||
|
||||
:copyright: (c) 2017 by the Jinja Team.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
from jinja2._compat import range_type
|
||||
from jinja2.utils import generate_lorem_ipsum, Cycler, Joiner, Namespace
|
||||
|
||||
from ._compat import range_type
|
||||
from .filters import FILTERS as DEFAULT_FILTERS # noqa: F401
|
||||
from .tests import TESTS as DEFAULT_TESTS # noqa: F401
|
||||
from .utils import Cycler
|
||||
from .utils import generate_lorem_ipsum
|
||||
from .utils import Joiner
|
||||
from .utils import Namespace
|
||||
|
||||
# defaults for the parser / lexer
|
||||
BLOCK_START_STRING = '{%'
|
||||
BLOCK_END_STRING = '%}'
|
||||
VARIABLE_START_STRING = '{{'
|
||||
VARIABLE_END_STRING = '}}'
|
||||
COMMENT_START_STRING = '{#'
|
||||
COMMENT_END_STRING = '#}'
|
||||
BLOCK_START_STRING = "{%"
|
||||
BLOCK_END_STRING = "%}"
|
||||
VARIABLE_START_STRING = "{{"
|
||||
VARIABLE_END_STRING = "}}"
|
||||
COMMENT_START_STRING = "{#"
|
||||
COMMENT_END_STRING = "#}"
|
||||
LINE_STATEMENT_PREFIX = None
|
||||
LINE_COMMENT_PREFIX = None
|
||||
TRIM_BLOCKS = False
|
||||
LSTRIP_BLOCKS = False
|
||||
NEWLINE_SEQUENCE = '\n'
|
||||
NEWLINE_SEQUENCE = "\n"
|
||||
KEEP_TRAILING_NEWLINE = False
|
||||
|
||||
|
||||
# default filters, tests and namespace
|
||||
from jinja2.filters import FILTERS as DEFAULT_FILTERS
|
||||
from jinja2.tests import TESTS as DEFAULT_TESTS
|
||||
DEFAULT_NAMESPACE = {
|
||||
'range': range_type,
|
||||
'dict': dict,
|
||||
'lipsum': generate_lorem_ipsum,
|
||||
'cycler': Cycler,
|
||||
'joiner': Joiner,
|
||||
'namespace': Namespace
|
||||
}
|
||||
|
||||
DEFAULT_NAMESPACE = {
|
||||
"range": range_type,
|
||||
"dict": dict,
|
||||
"lipsum": generate_lorem_ipsum,
|
||||
"cycler": Cycler,
|
||||
"joiner": Joiner,
|
||||
"namespace": Namespace,
|
||||
}
|
||||
|
||||
# default policies
|
||||
DEFAULT_POLICIES = {
|
||||
'compiler.ascii_str': True,
|
||||
'urlize.rel': 'noopener',
|
||||
'urlize.target': None,
|
||||
'truncate.leeway': 5,
|
||||
'json.dumps_function': None,
|
||||
'json.dumps_kwargs': {'sort_keys': True},
|
||||
'ext.i18n.trimmed': False,
|
||||
"compiler.ascii_str": True,
|
||||
"urlize.rel": "noopener",
|
||||
"urlize.target": None,
|
||||
"truncate.leeway": 5,
|
||||
"json.dumps_function": None,
|
||||
"json.dumps_kwargs": {"sort_keys": True},
|
||||
"ext.i18n.trimmed": False,
|
||||
}
|
||||
|
||||
|
||||
# export all constants
|
||||
__all__ = tuple(x for x in locals().keys() if x.isupper())
|
||||
|
590
lib/spack/external/jinja2/environment.py
vendored
590
lib/spack/external/jinja2/environment.py
vendored
@@ -1,60 +1,83 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
jinja2.environment
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Provides a class that holds runtime and parsing time options.
|
||||
|
||||
:copyright: (c) 2017 by the Jinja Team.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""Classes for managing templates and their runtime and compile time
|
||||
options.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import weakref
|
||||
from functools import reduce, partial
|
||||
from jinja2 import nodes
|
||||
from jinja2.defaults import BLOCK_START_STRING, \
|
||||
BLOCK_END_STRING, VARIABLE_START_STRING, VARIABLE_END_STRING, \
|
||||
COMMENT_START_STRING, COMMENT_END_STRING, LINE_STATEMENT_PREFIX, \
|
||||
LINE_COMMENT_PREFIX, TRIM_BLOCKS, NEWLINE_SEQUENCE, \
|
||||
DEFAULT_FILTERS, DEFAULT_TESTS, DEFAULT_NAMESPACE, \
|
||||
DEFAULT_POLICIES, KEEP_TRAILING_NEWLINE, LSTRIP_BLOCKS
|
||||
from jinja2.lexer import get_lexer, TokenStream
|
||||
from jinja2.parser import Parser
|
||||
from jinja2.nodes import EvalContext
|
||||
from jinja2.compiler import generate, CodeGenerator
|
||||
from jinja2.runtime import Undefined, new_context, Context
|
||||
from jinja2.exceptions import TemplateSyntaxError, TemplateNotFound, \
|
||||
TemplatesNotFound, TemplateRuntimeError
|
||||
from jinja2.utils import import_string, LRUCache, Markup, missing, \
|
||||
concat, consume, internalcode, have_async_gen
|
||||
from jinja2._compat import imap, ifilter, string_types, iteritems, \
|
||||
text_type, reraise, implements_iterator, implements_to_string, \
|
||||
encode_filename, PY2, PYPY
|
||||
from functools import partial
|
||||
from functools import reduce
|
||||
|
||||
from markupsafe import Markup
|
||||
|
||||
from . import nodes
|
||||
from ._compat import encode_filename
|
||||
from ._compat import implements_iterator
|
||||
from ._compat import implements_to_string
|
||||
from ._compat import iteritems
|
||||
from ._compat import PY2
|
||||
from ._compat import PYPY
|
||||
from ._compat import reraise
|
||||
from ._compat import string_types
|
||||
from ._compat import text_type
|
||||
from .compiler import CodeGenerator
|
||||
from .compiler import generate
|
||||
from .defaults import BLOCK_END_STRING
|
||||
from .defaults import BLOCK_START_STRING
|
||||
from .defaults import COMMENT_END_STRING
|
||||
from .defaults import COMMENT_START_STRING
|
||||
from .defaults import DEFAULT_FILTERS
|
||||
from .defaults import DEFAULT_NAMESPACE
|
||||
from .defaults import DEFAULT_POLICIES
|
||||
from .defaults import DEFAULT_TESTS
|
||||
from .defaults import KEEP_TRAILING_NEWLINE
|
||||
from .defaults import LINE_COMMENT_PREFIX
|
||||
from .defaults import LINE_STATEMENT_PREFIX
|
||||
from .defaults import LSTRIP_BLOCKS
|
||||
from .defaults import NEWLINE_SEQUENCE
|
||||
from .defaults import TRIM_BLOCKS
|
||||
from .defaults import VARIABLE_END_STRING
|
||||
from .defaults import VARIABLE_START_STRING
|
||||
from .exceptions import TemplateNotFound
|
||||
from .exceptions import TemplateRuntimeError
|
||||
from .exceptions import TemplatesNotFound
|
||||
from .exceptions import TemplateSyntaxError
|
||||
from .exceptions import UndefinedError
|
||||
from .lexer import get_lexer
|
||||
from .lexer import TokenStream
|
||||
from .nodes import EvalContext
|
||||
from .parser import Parser
|
||||
from .runtime import Context
|
||||
from .runtime import new_context
|
||||
from .runtime import Undefined
|
||||
from .utils import concat
|
||||
from .utils import consume
|
||||
from .utils import have_async_gen
|
||||
from .utils import import_string
|
||||
from .utils import internalcode
|
||||
from .utils import LRUCache
|
||||
from .utils import missing
|
||||
|
||||
# for direct template usage we have up to ten living environments
|
||||
_spontaneous_environments = LRUCache(10)
|
||||
|
||||
# the function to create jinja traceback objects. This is dynamically
|
||||
# imported on the first exception in the exception handler.
|
||||
_make_traceback = None
|
||||
|
||||
def get_spontaneous_environment(cls, *args):
|
||||
"""Return a new spontaneous environment. A spontaneous environment
|
||||
is used for templates created directly rather than through an
|
||||
existing environment.
|
||||
|
||||
def get_spontaneous_environment(*args):
|
||||
"""Return a new spontaneous environment. A spontaneous environment is an
|
||||
unnamed and unaccessible (in theory) environment that is used for
|
||||
templates generated from a string and not from the file system.
|
||||
:param cls: Environment class to create.
|
||||
:param args: Positional arguments passed to environment.
|
||||
"""
|
||||
key = (cls, args)
|
||||
|
||||
try:
|
||||
env = _spontaneous_environments.get(args)
|
||||
except TypeError:
|
||||
return Environment(*args)
|
||||
if env is not None:
|
||||
return _spontaneous_environments[key]
|
||||
except KeyError:
|
||||
_spontaneous_environments[key] = env = cls(*args)
|
||||
env.shared = True
|
||||
return env
|
||||
_spontaneous_environments[args] = env = Environment(*args)
|
||||
env.shared = True
|
||||
return env
|
||||
|
||||
|
||||
def create_cache(size):
|
||||
@@ -93,20 +116,25 @@ def fail_for_missing_callable(string, name):
|
||||
try:
|
||||
name._fail_with_undefined_error()
|
||||
except Exception as e:
|
||||
msg = '%s (%s; did you forget to quote the callable name?)' % (msg, e)
|
||||
msg = "%s (%s; did you forget to quote the callable name?)" % (msg, e)
|
||||
raise TemplateRuntimeError(msg)
|
||||
|
||||
|
||||
def _environment_sanity_check(environment):
|
||||
"""Perform a sanity check on the environment."""
|
||||
assert issubclass(environment.undefined, Undefined), 'undefined must ' \
|
||||
'be a subclass of undefined because filters depend on it.'
|
||||
assert environment.block_start_string != \
|
||||
environment.variable_start_string != \
|
||||
environment.comment_start_string, 'block, variable and comment ' \
|
||||
'start strings must be different'
|
||||
assert environment.newline_sequence in ('\r', '\r\n', '\n'), \
|
||||
'newline_sequence set to unknown line ending string.'
|
||||
assert issubclass(
|
||||
environment.undefined, Undefined
|
||||
), "undefined must be a subclass of undefined because filters depend on it."
|
||||
assert (
|
||||
environment.block_start_string
|
||||
!= environment.variable_start_string
|
||||
!= environment.comment_start_string
|
||||
), "block, variable and comment start strings must be different"
|
||||
assert environment.newline_sequence in (
|
||||
"\r",
|
||||
"\r\n",
|
||||
"\n",
|
||||
), "newline_sequence set to unknown line ending string."
|
||||
return environment
|
||||
|
||||
|
||||
@@ -191,7 +219,7 @@ class Environment(object):
|
||||
`autoescape`
|
||||
If set to ``True`` the XML/HTML autoescaping feature is enabled by
|
||||
default. For more details about autoescaping see
|
||||
:class:`~jinja2.utils.Markup`. As of Jinja 2.4 this can also
|
||||
:class:`~markupsafe.Markup`. As of Jinja 2.4 this can also
|
||||
be a callable that is passed the template name and has to
|
||||
return ``True`` or ``False`` depending on autoescape should be
|
||||
enabled by default.
|
||||
@@ -249,10 +277,6 @@ class Environment(object):
|
||||
#: must not be modified
|
||||
shared = False
|
||||
|
||||
#: these are currently EXPERIMENTAL undocumented features.
|
||||
exception_handler = None
|
||||
exception_formatter = None
|
||||
|
||||
#: the class that is used for code generation. See
|
||||
#: :class:`~jinja2.compiler.CodeGenerator` for more information.
|
||||
code_generator_class = CodeGenerator
|
||||
@@ -261,29 +285,31 @@ class Environment(object):
|
||||
#: :class:`~jinja2.runtime.Context` for more information.
|
||||
context_class = Context
|
||||
|
||||
def __init__(self,
|
||||
block_start_string=BLOCK_START_STRING,
|
||||
block_end_string=BLOCK_END_STRING,
|
||||
variable_start_string=VARIABLE_START_STRING,
|
||||
variable_end_string=VARIABLE_END_STRING,
|
||||
comment_start_string=COMMENT_START_STRING,
|
||||
comment_end_string=COMMENT_END_STRING,
|
||||
line_statement_prefix=LINE_STATEMENT_PREFIX,
|
||||
line_comment_prefix=LINE_COMMENT_PREFIX,
|
||||
trim_blocks=TRIM_BLOCKS,
|
||||
lstrip_blocks=LSTRIP_BLOCKS,
|
||||
newline_sequence=NEWLINE_SEQUENCE,
|
||||
keep_trailing_newline=KEEP_TRAILING_NEWLINE,
|
||||
extensions=(),
|
||||
optimized=True,
|
||||
undefined=Undefined,
|
||||
finalize=None,
|
||||
autoescape=False,
|
||||
loader=None,
|
||||
cache_size=400,
|
||||
auto_reload=True,
|
||||
bytecode_cache=None,
|
||||
enable_async=False):
|
||||
def __init__(
|
||||
self,
|
||||
block_start_string=BLOCK_START_STRING,
|
||||
block_end_string=BLOCK_END_STRING,
|
||||
variable_start_string=VARIABLE_START_STRING,
|
||||
variable_end_string=VARIABLE_END_STRING,
|
||||
comment_start_string=COMMENT_START_STRING,
|
||||
comment_end_string=COMMENT_END_STRING,
|
||||
line_statement_prefix=LINE_STATEMENT_PREFIX,
|
||||
line_comment_prefix=LINE_COMMENT_PREFIX,
|
||||
trim_blocks=TRIM_BLOCKS,
|
||||
lstrip_blocks=LSTRIP_BLOCKS,
|
||||
newline_sequence=NEWLINE_SEQUENCE,
|
||||
keep_trailing_newline=KEEP_TRAILING_NEWLINE,
|
||||
extensions=(),
|
||||
optimized=True,
|
||||
undefined=Undefined,
|
||||
finalize=None,
|
||||
autoescape=False,
|
||||
loader=None,
|
||||
cache_size=400,
|
||||
auto_reload=True,
|
||||
bytecode_cache=None,
|
||||
enable_async=False,
|
||||
):
|
||||
# !!Important notice!!
|
||||
# The constructor accepts quite a few arguments that should be
|
||||
# passed by keyword rather than position. However it's important to
|
||||
@@ -334,6 +360,9 @@ def __init__(self,
|
||||
|
||||
self.enable_async = enable_async
|
||||
self.is_async = self.enable_async and have_async_gen
|
||||
if self.is_async:
|
||||
# runs patch_all() to enable async support
|
||||
from . import asyncsupport # noqa: F401
|
||||
|
||||
_environment_sanity_check(self)
|
||||
|
||||
@@ -353,15 +382,28 @@ def extend(self, **attributes):
|
||||
if not hasattr(self, key):
|
||||
setattr(self, key, value)
|
||||
|
||||
def overlay(self, block_start_string=missing, block_end_string=missing,
|
||||
variable_start_string=missing, variable_end_string=missing,
|
||||
comment_start_string=missing, comment_end_string=missing,
|
||||
line_statement_prefix=missing, line_comment_prefix=missing,
|
||||
trim_blocks=missing, lstrip_blocks=missing,
|
||||
extensions=missing, optimized=missing,
|
||||
undefined=missing, finalize=missing, autoescape=missing,
|
||||
loader=missing, cache_size=missing, auto_reload=missing,
|
||||
bytecode_cache=missing):
|
||||
def overlay(
|
||||
self,
|
||||
block_start_string=missing,
|
||||
block_end_string=missing,
|
||||
variable_start_string=missing,
|
||||
variable_end_string=missing,
|
||||
comment_start_string=missing,
|
||||
comment_end_string=missing,
|
||||
line_statement_prefix=missing,
|
||||
line_comment_prefix=missing,
|
||||
trim_blocks=missing,
|
||||
lstrip_blocks=missing,
|
||||
extensions=missing,
|
||||
optimized=missing,
|
||||
undefined=missing,
|
||||
finalize=missing,
|
||||
autoescape=missing,
|
||||
loader=missing,
|
||||
cache_size=missing,
|
||||
auto_reload=missing,
|
||||
bytecode_cache=missing,
|
||||
):
|
||||
"""Create a new overlay environment that shares all the data with the
|
||||
current environment except for cache and the overridden attributes.
|
||||
Extensions cannot be removed for an overlayed environment. An overlayed
|
||||
@@ -374,7 +416,7 @@ def overlay(self, block_start_string=missing, block_end_string=missing,
|
||||
through.
|
||||
"""
|
||||
args = dict(locals())
|
||||
del args['self'], args['cache_size'], args['extensions']
|
||||
del args["self"], args["cache_size"], args["extensions"]
|
||||
|
||||
rv = object.__new__(self.__class__)
|
||||
rv.__dict__.update(self.__dict__)
|
||||
@@ -402,8 +444,7 @@ def overlay(self, block_start_string=missing, block_end_string=missing,
|
||||
|
||||
def iter_extensions(self):
|
||||
"""Iterates over the extensions by priority."""
|
||||
return iter(sorted(self.extensions.values(),
|
||||
key=lambda x: x.priority))
|
||||
return iter(sorted(self.extensions.values(), key=lambda x: x.priority))
|
||||
|
||||
def getitem(self, obj, argument):
|
||||
"""Get an item or attribute of an object but prefer the item."""
|
||||
@@ -435,8 +476,9 @@ def getattr(self, obj, attribute):
|
||||
except (TypeError, LookupError, AttributeError):
|
||||
return self.undefined(obj=obj, name=attribute)
|
||||
|
||||
def call_filter(self, name, value, args=None, kwargs=None,
|
||||
context=None, eval_ctx=None):
|
||||
def call_filter(
|
||||
self, name, value, args=None, kwargs=None, context=None, eval_ctx=None
|
||||
):
|
||||
"""Invokes a filter on a value the same way the compiler does it.
|
||||
|
||||
Note that on Python 3 this might return a coroutine in case the
|
||||
@@ -448,21 +490,22 @@ def call_filter(self, name, value, args=None, kwargs=None,
|
||||
"""
|
||||
func = self.filters.get(name)
|
||||
if func is None:
|
||||
fail_for_missing_callable('no filter named %r', name)
|
||||
fail_for_missing_callable("no filter named %r", name)
|
||||
args = [value] + list(args or ())
|
||||
if getattr(func, 'contextfilter', False):
|
||||
if getattr(func, "contextfilter", False) is True:
|
||||
if context is None:
|
||||
raise TemplateRuntimeError('Attempted to invoke context '
|
||||
'filter without context')
|
||||
raise TemplateRuntimeError(
|
||||
"Attempted to invoke context filter without context"
|
||||
)
|
||||
args.insert(0, context)
|
||||
elif getattr(func, 'evalcontextfilter', False):
|
||||
elif getattr(func, "evalcontextfilter", False) is True:
|
||||
if eval_ctx is None:
|
||||
if context is not None:
|
||||
eval_ctx = context.eval_ctx
|
||||
else:
|
||||
eval_ctx = EvalContext(self)
|
||||
args.insert(0, eval_ctx)
|
||||
elif getattr(func, 'environmentfilter', False):
|
||||
elif getattr(func, "environmentfilter", False) is True:
|
||||
args.insert(0, self)
|
||||
return func(*args, **(kwargs or {}))
|
||||
|
||||
@@ -473,7 +516,7 @@ def call_test(self, name, value, args=None, kwargs=None):
|
||||
"""
|
||||
func = self.tests.get(name)
|
||||
if func is None:
|
||||
fail_for_missing_callable('no test named %r', name)
|
||||
fail_for_missing_callable("no test named %r", name)
|
||||
return func(value, *(args or ()), **(kwargs or {}))
|
||||
|
||||
@internalcode
|
||||
@@ -483,14 +526,13 @@ def parse(self, source, name=None, filename=None):
|
||||
executable source- or bytecode. This is useful for debugging or to
|
||||
extract information from templates.
|
||||
|
||||
If you are :ref:`developing Jinja2 extensions <writing-extensions>`
|
||||
If you are :ref:`developing Jinja extensions <writing-extensions>`
|
||||
this gives you a good overview of the node tree generated.
|
||||
"""
|
||||
try:
|
||||
return self._parse(source, name, filename)
|
||||
except TemplateSyntaxError:
|
||||
exc_info = sys.exc_info()
|
||||
self.handle_exception(exc_info, source_hint=source)
|
||||
self.handle_exception(source=source)
|
||||
|
||||
def _parse(self, source, name, filename):
|
||||
"""Internal parsing function used by `parse` and `compile`."""
|
||||
@@ -510,16 +552,18 @@ def lex(self, source, name=None, filename=None):
|
||||
try:
|
||||
return self.lexer.tokeniter(source, name, filename)
|
||||
except TemplateSyntaxError:
|
||||
exc_info = sys.exc_info()
|
||||
self.handle_exception(exc_info, source_hint=source)
|
||||
self.handle_exception(source=source)
|
||||
|
||||
def preprocess(self, source, name=None, filename=None):
|
||||
"""Preprocesses the source with all extensions. This is automatically
|
||||
called for all parsing and compiling methods but *not* for :meth:`lex`
|
||||
because there you usually only want the actual source tokenized.
|
||||
"""
|
||||
return reduce(lambda s, e: e.preprocess(s, name, filename),
|
||||
self.iter_extensions(), text_type(source))
|
||||
return reduce(
|
||||
lambda s, e: e.preprocess(s, name, filename),
|
||||
self.iter_extensions(),
|
||||
text_type(source),
|
||||
)
|
||||
|
||||
def _tokenize(self, source, name, filename=None, state=None):
|
||||
"""Called by the parser to do the preprocessing and filtering
|
||||
@@ -539,8 +583,14 @@ def _generate(self, source, name, filename, defer_init=False):
|
||||
|
||||
.. versionadded:: 2.5
|
||||
"""
|
||||
return generate(source, self, name, filename, defer_init=defer_init,
|
||||
optimized=self.optimized)
|
||||
return generate(
|
||||
source,
|
||||
self,
|
||||
name,
|
||||
filename,
|
||||
defer_init=defer_init,
|
||||
optimized=self.optimized,
|
||||
)
|
||||
|
||||
def _compile(self, source, filename):
|
||||
"""Internal hook that can be overridden to hook a different compile
|
||||
@@ -548,11 +598,10 @@ def _compile(self, source, filename):
|
||||
|
||||
.. versionadded:: 2.5
|
||||
"""
|
||||
return compile(source, filename, 'exec')
|
||||
return compile(source, filename, "exec")
|
||||
|
||||
@internalcode
|
||||
def compile(self, source, name=None, filename=None, raw=False,
|
||||
defer_init=False):
|
||||
def compile(self, source, name=None, filename=None, raw=False, defer_init=False):
|
||||
"""Compile a node or template source code. The `name` parameter is
|
||||
the load name of the template after it was joined using
|
||||
:meth:`join_path` if necessary, not the filename on the file system.
|
||||
@@ -577,18 +626,16 @@ def compile(self, source, name=None, filename=None, raw=False,
|
||||
if isinstance(source, string_types):
|
||||
source_hint = source
|
||||
source = self._parse(source, name, filename)
|
||||
source = self._generate(source, name, filename,
|
||||
defer_init=defer_init)
|
||||
source = self._generate(source, name, filename, defer_init=defer_init)
|
||||
if raw:
|
||||
return source
|
||||
if filename is None:
|
||||
filename = '<template>'
|
||||
filename = "<template>"
|
||||
else:
|
||||
filename = encode_filename(filename)
|
||||
return self._compile(source, filename)
|
||||
except TemplateSyntaxError:
|
||||
exc_info = sys.exc_info()
|
||||
self.handle_exception(exc_info, source_hint=source_hint)
|
||||
self.handle_exception(source=source_hint)
|
||||
|
||||
def compile_expression(self, source, undefined_to_none=True):
|
||||
"""A handy helper method that returns a callable that accepts keyword
|
||||
@@ -618,26 +665,32 @@ def compile_expression(self, source, undefined_to_none=True):
|
||||
|
||||
.. versionadded:: 2.1
|
||||
"""
|
||||
parser = Parser(self, source, state='variable')
|
||||
exc_info = None
|
||||
parser = Parser(self, source, state="variable")
|
||||
try:
|
||||
expr = parser.parse_expression()
|
||||
if not parser.stream.eos:
|
||||
raise TemplateSyntaxError('chunk after expression',
|
||||
parser.stream.current.lineno,
|
||||
None, None)
|
||||
raise TemplateSyntaxError(
|
||||
"chunk after expression", parser.stream.current.lineno, None, None
|
||||
)
|
||||
expr.set_environment(self)
|
||||
except TemplateSyntaxError:
|
||||
exc_info = sys.exc_info()
|
||||
if exc_info is not None:
|
||||
self.handle_exception(exc_info, source_hint=source)
|
||||
body = [nodes.Assign(nodes.Name('result', 'store'), expr, lineno=1)]
|
||||
if sys.exc_info() is not None:
|
||||
self.handle_exception(source=source)
|
||||
|
||||
body = [nodes.Assign(nodes.Name("result", "store"), expr, lineno=1)]
|
||||
template = self.from_string(nodes.Template(body, lineno=1))
|
||||
return TemplateExpression(template, undefined_to_none)
|
||||
|
||||
def compile_templates(self, target, extensions=None, filter_func=None,
|
||||
zip='deflated', log_function=None,
|
||||
ignore_errors=True, py_compile=False):
|
||||
def compile_templates(
|
||||
self,
|
||||
target,
|
||||
extensions=None,
|
||||
filter_func=None,
|
||||
zip="deflated",
|
||||
log_function=None,
|
||||
ignore_errors=True,
|
||||
py_compile=False,
|
||||
):
|
||||
"""Finds all the templates the loader can find, compiles them
|
||||
and stores them in `target`. If `zip` is `None`, instead of in a
|
||||
zipfile, the templates will be stored in a directory.
|
||||
@@ -660,42 +713,52 @@ def compile_templates(self, target, extensions=None, filter_func=None,
|
||||
|
||||
.. versionadded:: 2.4
|
||||
"""
|
||||
from jinja2.loaders import ModuleLoader
|
||||
from .loaders import ModuleLoader
|
||||
|
||||
if log_function is None:
|
||||
log_function = lambda x: None
|
||||
|
||||
def log_function(x):
|
||||
pass
|
||||
|
||||
if py_compile:
|
||||
if not PY2 or PYPY:
|
||||
from warnings import warn
|
||||
warn(Warning('py_compile has no effect on pypy or Python 3'))
|
||||
import warnings
|
||||
|
||||
warnings.warn(
|
||||
"'py_compile=True' has no effect on PyPy or Python"
|
||||
" 3 and will be removed in version 3.0",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
py_compile = False
|
||||
else:
|
||||
import imp
|
||||
import marshal
|
||||
py_header = imp.get_magic() + \
|
||||
u'\xff\xff\xff\xff'.encode('iso-8859-15')
|
||||
|
||||
py_header = imp.get_magic() + u"\xff\xff\xff\xff".encode("iso-8859-15")
|
||||
|
||||
# Python 3.3 added a source filesize to the header
|
||||
if sys.version_info >= (3, 3):
|
||||
py_header += u'\x00\x00\x00\x00'.encode('iso-8859-15')
|
||||
py_header += u"\x00\x00\x00\x00".encode("iso-8859-15")
|
||||
|
||||
def write_file(filename, data, mode):
|
||||
def write_file(filename, data):
|
||||
if zip:
|
||||
info = ZipInfo(filename)
|
||||
info.external_attr = 0o755 << 16
|
||||
zip_file.writestr(info, data)
|
||||
else:
|
||||
f = open(os.path.join(target, filename), mode)
|
||||
try:
|
||||
if isinstance(data, text_type):
|
||||
data = data.encode("utf8")
|
||||
|
||||
with open(os.path.join(target, filename), "wb") as f:
|
||||
f.write(data)
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
if zip is not None:
|
||||
from zipfile import ZipFile, ZipInfo, ZIP_DEFLATED, ZIP_STORED
|
||||
zip_file = ZipFile(target, 'w', dict(deflated=ZIP_DEFLATED,
|
||||
stored=ZIP_STORED)[zip])
|
||||
|
||||
zip_file = ZipFile(
|
||||
target, "w", dict(deflated=ZIP_DEFLATED, stored=ZIP_STORED)[zip]
|
||||
)
|
||||
log_function('Compiling into Zip archive "%s"' % target)
|
||||
else:
|
||||
if not os.path.isdir(target):
|
||||
@@ -717,18 +780,16 @@ def write_file(filename, data, mode):
|
||||
|
||||
if py_compile:
|
||||
c = self._compile(code, encode_filename(filename))
|
||||
write_file(filename + 'c', py_header +
|
||||
marshal.dumps(c), 'wb')
|
||||
log_function('Byte-compiled "%s" as %s' %
|
||||
(name, filename + 'c'))
|
||||
write_file(filename + "c", py_header + marshal.dumps(c))
|
||||
log_function('Byte-compiled "%s" as %s' % (name, filename + "c"))
|
||||
else:
|
||||
write_file(filename, code, 'w')
|
||||
write_file(filename, code)
|
||||
log_function('Compiled "%s" as %s' % (name, filename))
|
||||
finally:
|
||||
if zip:
|
||||
zip_file.close()
|
||||
|
||||
log_function('Finished compiling templates')
|
||||
log_function("Finished compiling templates")
|
||||
|
||||
def list_templates(self, extensions=None, filter_func=None):
|
||||
"""Returns a list of templates for this environment. This requires
|
||||
@@ -746,38 +807,29 @@ def list_templates(self, extensions=None, filter_func=None):
|
||||
|
||||
.. versionadded:: 2.4
|
||||
"""
|
||||
x = self.loader.list_templates()
|
||||
names = self.loader.list_templates()
|
||||
|
||||
if extensions is not None:
|
||||
if filter_func is not None:
|
||||
raise TypeError('either extensions or filter_func '
|
||||
'can be passed, but not both')
|
||||
filter_func = lambda x: '.' in x and \
|
||||
x.rsplit('.', 1)[1] in extensions
|
||||
if filter_func is not None:
|
||||
x = list(ifilter(filter_func, x))
|
||||
return x
|
||||
raise TypeError(
|
||||
"either extensions or filter_func can be passed, but not both"
|
||||
)
|
||||
|
||||
def handle_exception(self, exc_info=None, rendered=False, source_hint=None):
|
||||
def filter_func(x):
|
||||
return "." in x and x.rsplit(".", 1)[1] in extensions
|
||||
|
||||
if filter_func is not None:
|
||||
names = [name for name in names if filter_func(name)]
|
||||
|
||||
return names
|
||||
|
||||
def handle_exception(self, source=None):
|
||||
"""Exception handling helper. This is used internally to either raise
|
||||
rewritten exceptions or return a rendered traceback for the template.
|
||||
"""
|
||||
global _make_traceback
|
||||
if exc_info is None:
|
||||
exc_info = sys.exc_info()
|
||||
from .debug import rewrite_traceback_stack
|
||||
|
||||
# the debugging module is imported when it's used for the first time.
|
||||
# we're doing a lot of stuff there and for applications that do not
|
||||
# get any exceptions in template rendering there is no need to load
|
||||
# all of that.
|
||||
if _make_traceback is None:
|
||||
from jinja2.debug import make_traceback as _make_traceback
|
||||
traceback = _make_traceback(exc_info, source_hint)
|
||||
if rendered and self.exception_formatter is not None:
|
||||
return self.exception_formatter(traceback)
|
||||
if self.exception_handler is not None:
|
||||
self.exception_handler(traceback)
|
||||
exc_type, exc_value, tb = traceback.standard_exc_info
|
||||
reraise(exc_type, exc_value, tb)
|
||||
reraise(*rewrite_traceback_stack(source=source))
|
||||
|
||||
def join_path(self, template, parent):
|
||||
"""Join a template with the parent. By default all the lookups are
|
||||
@@ -794,12 +846,13 @@ def join_path(self, template, parent):
|
||||
@internalcode
|
||||
def _load_template(self, name, globals):
|
||||
if self.loader is None:
|
||||
raise TypeError('no loader for this environment specified')
|
||||
raise TypeError("no loader for this environment specified")
|
||||
cache_key = (weakref.ref(self.loader), name)
|
||||
if self.cache is not None:
|
||||
template = self.cache.get(cache_key)
|
||||
if template is not None and (not self.auto_reload or
|
||||
template.is_up_to_date):
|
||||
if template is not None and (
|
||||
not self.auto_reload or template.is_up_to_date
|
||||
):
|
||||
return template
|
||||
template = self.loader.load(self, name, globals)
|
||||
if self.cache is not None:
|
||||
@@ -835,15 +888,24 @@ def select_template(self, names, parent=None, globals=None):
|
||||
before it fails. If it cannot find any of the templates, it will
|
||||
raise a :exc:`TemplatesNotFound` exception.
|
||||
|
||||
.. versionadded:: 2.3
|
||||
.. versionchanged:: 2.11
|
||||
If names is :class:`Undefined`, an :exc:`UndefinedError` is
|
||||
raised instead. If no templates were found and names
|
||||
contains :class:`Undefined`, the message is more helpful.
|
||||
|
||||
.. versionchanged:: 2.4
|
||||
If `names` contains a :class:`Template` object it is returned
|
||||
from the function unchanged.
|
||||
|
||||
.. versionadded:: 2.3
|
||||
"""
|
||||
if isinstance(names, Undefined):
|
||||
names._fail_with_undefined_error()
|
||||
|
||||
if not names:
|
||||
raise TemplatesNotFound(message=u'Tried to select from an empty list '
|
||||
u'of templates.')
|
||||
raise TemplatesNotFound(
|
||||
message=u"Tried to select from an empty list " u"of templates."
|
||||
)
|
||||
globals = self.make_globals(globals)
|
||||
for name in names:
|
||||
if isinstance(name, Template):
|
||||
@@ -852,20 +914,19 @@ def select_template(self, names, parent=None, globals=None):
|
||||
name = self.join_path(name, parent)
|
||||
try:
|
||||
return self._load_template(name, globals)
|
||||
except TemplateNotFound:
|
||||
except (TemplateNotFound, UndefinedError):
|
||||
pass
|
||||
raise TemplatesNotFound(names)
|
||||
|
||||
@internalcode
|
||||
def get_or_select_template(self, template_name_or_list,
|
||||
parent=None, globals=None):
|
||||
def get_or_select_template(self, template_name_or_list, parent=None, globals=None):
|
||||
"""Does a typecheck and dispatches to :meth:`select_template`
|
||||
if an iterable of template names is given, otherwise to
|
||||
:meth:`get_template`.
|
||||
|
||||
.. versionadded:: 2.3
|
||||
"""
|
||||
if isinstance(template_name_or_list, string_types):
|
||||
if isinstance(template_name_or_list, (string_types, Undefined)):
|
||||
return self.get_template(template_name_or_list, parent, globals)
|
||||
elif isinstance(template_name_or_list, Template):
|
||||
return template_name_or_list
|
||||
@@ -916,32 +977,57 @@ class Template(object):
|
||||
StopIteration
|
||||
"""
|
||||
|
||||
def __new__(cls, source,
|
||||
block_start_string=BLOCK_START_STRING,
|
||||
block_end_string=BLOCK_END_STRING,
|
||||
variable_start_string=VARIABLE_START_STRING,
|
||||
variable_end_string=VARIABLE_END_STRING,
|
||||
comment_start_string=COMMENT_START_STRING,
|
||||
comment_end_string=COMMENT_END_STRING,
|
||||
line_statement_prefix=LINE_STATEMENT_PREFIX,
|
||||
line_comment_prefix=LINE_COMMENT_PREFIX,
|
||||
trim_blocks=TRIM_BLOCKS,
|
||||
lstrip_blocks=LSTRIP_BLOCKS,
|
||||
newline_sequence=NEWLINE_SEQUENCE,
|
||||
keep_trailing_newline=KEEP_TRAILING_NEWLINE,
|
||||
extensions=(),
|
||||
optimized=True,
|
||||
undefined=Undefined,
|
||||
finalize=None,
|
||||
autoescape=False,
|
||||
enable_async=False):
|
||||
#: Type of environment to create when creating a template directly
|
||||
#: rather than through an existing environment.
|
||||
environment_class = Environment
|
||||
|
||||
def __new__(
|
||||
cls,
|
||||
source,
|
||||
block_start_string=BLOCK_START_STRING,
|
||||
block_end_string=BLOCK_END_STRING,
|
||||
variable_start_string=VARIABLE_START_STRING,
|
||||
variable_end_string=VARIABLE_END_STRING,
|
||||
comment_start_string=COMMENT_START_STRING,
|
||||
comment_end_string=COMMENT_END_STRING,
|
||||
line_statement_prefix=LINE_STATEMENT_PREFIX,
|
||||
line_comment_prefix=LINE_COMMENT_PREFIX,
|
||||
trim_blocks=TRIM_BLOCKS,
|
||||
lstrip_blocks=LSTRIP_BLOCKS,
|
||||
newline_sequence=NEWLINE_SEQUENCE,
|
||||
keep_trailing_newline=KEEP_TRAILING_NEWLINE,
|
||||
extensions=(),
|
||||
optimized=True,
|
||||
undefined=Undefined,
|
||||
finalize=None,
|
||||
autoescape=False,
|
||||
enable_async=False,
|
||||
):
|
||||
env = get_spontaneous_environment(
|
||||
block_start_string, block_end_string, variable_start_string,
|
||||
variable_end_string, comment_start_string, comment_end_string,
|
||||
line_statement_prefix, line_comment_prefix, trim_blocks,
|
||||
lstrip_blocks, newline_sequence, keep_trailing_newline,
|
||||
frozenset(extensions), optimized, undefined, finalize, autoescape,
|
||||
None, 0, False, None, enable_async)
|
||||
cls.environment_class,
|
||||
block_start_string,
|
||||
block_end_string,
|
||||
variable_start_string,
|
||||
variable_end_string,
|
||||
comment_start_string,
|
||||
comment_end_string,
|
||||
line_statement_prefix,
|
||||
line_comment_prefix,
|
||||
trim_blocks,
|
||||
lstrip_blocks,
|
||||
newline_sequence,
|
||||
keep_trailing_newline,
|
||||
frozenset(extensions),
|
||||
optimized,
|
||||
undefined,
|
||||
finalize,
|
||||
autoescape,
|
||||
None,
|
||||
0,
|
||||
False,
|
||||
None,
|
||||
enable_async,
|
||||
)
|
||||
return env.from_string(source, template_class=cls)
|
||||
|
||||
@classmethod
|
||||
@@ -949,10 +1035,7 @@ def from_code(cls, environment, code, globals, uptodate=None):
|
||||
"""Creates a template object from compiled code and the globals. This
|
||||
is used by the loaders and environment to create a template object.
|
||||
"""
|
||||
namespace = {
|
||||
'environment': environment,
|
||||
'__file__': code.co_filename
|
||||
}
|
||||
namespace = {"environment": environment, "__file__": code.co_filename}
|
||||
exec(code, namespace)
|
||||
rv = cls._from_namespace(environment, namespace, globals)
|
||||
rv._uptodate = uptodate
|
||||
@@ -972,21 +1055,21 @@ def _from_namespace(cls, environment, namespace, globals):
|
||||
t = object.__new__(cls)
|
||||
t.environment = environment
|
||||
t.globals = globals
|
||||
t.name = namespace['name']
|
||||
t.filename = namespace['__file__']
|
||||
t.blocks = namespace['blocks']
|
||||
t.name = namespace["name"]
|
||||
t.filename = namespace["__file__"]
|
||||
t.blocks = namespace["blocks"]
|
||||
|
||||
# render function and module
|
||||
t.root_render_func = namespace['root']
|
||||
t.root_render_func = namespace["root"]
|
||||
t._module = None
|
||||
|
||||
# debug and loader helpers
|
||||
t._debug_info = namespace['debug_info']
|
||||
t._debug_info = namespace["debug_info"]
|
||||
t._uptodate = None
|
||||
|
||||
# store the reference
|
||||
namespace['environment'] = environment
|
||||
namespace['__jinja_template__'] = t
|
||||
namespace["environment"] = environment
|
||||
namespace["__jinja_template__"] = t
|
||||
|
||||
return t
|
||||
|
||||
@@ -1004,8 +1087,7 @@ def render(self, *args, **kwargs):
|
||||
try:
|
||||
return concat(self.root_render_func(self.new_context(vars)))
|
||||
except Exception:
|
||||
exc_info = sys.exc_info()
|
||||
return self.environment.handle_exception(exc_info, True)
|
||||
self.environment.handle_exception()
|
||||
|
||||
def render_async(self, *args, **kwargs):
|
||||
"""This works similar to :meth:`render` but returns a coroutine
|
||||
@@ -1017,8 +1099,9 @@ def render_async(self, *args, **kwargs):
|
||||
await template.render_async(knights='that say nih; asynchronously')
|
||||
"""
|
||||
# see asyncsupport for the actual implementation
|
||||
raise NotImplementedError('This feature is not available for this '
|
||||
'version of Python')
|
||||
raise NotImplementedError(
|
||||
"This feature is not available for this version of Python"
|
||||
)
|
||||
|
||||
def stream(self, *args, **kwargs):
|
||||
"""Works exactly like :meth:`generate` but returns a
|
||||
@@ -1039,29 +1122,28 @@ def generate(self, *args, **kwargs):
|
||||
for event in self.root_render_func(self.new_context(vars)):
|
||||
yield event
|
||||
except Exception:
|
||||
exc_info = sys.exc_info()
|
||||
else:
|
||||
return
|
||||
yield self.environment.handle_exception(exc_info, True)
|
||||
yield self.environment.handle_exception()
|
||||
|
||||
def generate_async(self, *args, **kwargs):
|
||||
"""An async version of :meth:`generate`. Works very similarly but
|
||||
returns an async iterator instead.
|
||||
"""
|
||||
# see asyncsupport for the actual implementation
|
||||
raise NotImplementedError('This feature is not available for this '
|
||||
'version of Python')
|
||||
raise NotImplementedError(
|
||||
"This feature is not available for this version of Python"
|
||||
)
|
||||
|
||||
def new_context(self, vars=None, shared=False, locals=None):
|
||||
"""Create a new :class:`Context` for this template. The vars
|
||||
provided will be passed to the template. Per default the globals
|
||||
are added to the context. If shared is set to `True` the data
|
||||
is passed as it to the context without adding the globals.
|
||||
is passed as is to the context without adding the globals.
|
||||
|
||||
`locals` can be a dict of local variables for internal usage.
|
||||
"""
|
||||
return new_context(self.environment, self.name, self.blocks,
|
||||
vars, shared, self.globals, locals)
|
||||
return new_context(
|
||||
self.environment, self.name, self.blocks, vars, shared, self.globals, locals
|
||||
)
|
||||
|
||||
def make_module(self, vars=None, shared=False, locals=None):
|
||||
"""This method works like the :attr:`module` attribute when called
|
||||
@@ -1074,13 +1156,14 @@ def make_module(self, vars=None, shared=False, locals=None):
|
||||
|
||||
def make_module_async(self, vars=None, shared=False, locals=None):
|
||||
"""As template module creation can invoke template code for
|
||||
asynchronous exections this method must be used instead of the
|
||||
asynchronous executions this method must be used instead of the
|
||||
normal :meth:`make_module` one. Likewise the module attribute
|
||||
becomes unavailable in async mode.
|
||||
"""
|
||||
# see asyncsupport for the actual implementation
|
||||
raise NotImplementedError('This feature is not available for this '
|
||||
'version of Python')
|
||||
raise NotImplementedError(
|
||||
"This feature is not available for this version of Python"
|
||||
)
|
||||
|
||||
@internalcode
|
||||
def _get_default_module(self):
|
||||
@@ -1124,15 +1207,16 @@ def is_up_to_date(self):
|
||||
@property
|
||||
def debug_info(self):
|
||||
"""The debug info mapping."""
|
||||
return [tuple(imap(int, x.split('='))) for x in
|
||||
self._debug_info.split('&')]
|
||||
if self._debug_info:
|
||||
return [tuple(map(int, x.split("="))) for x in self._debug_info.split("&")]
|
||||
return []
|
||||
|
||||
def __repr__(self):
|
||||
if self.name is None:
|
||||
name = 'memory:%x' % id(self)
|
||||
name = "memory:%x" % id(self)
|
||||
else:
|
||||
name = repr(self.name)
|
||||
return '<%s %s>' % (self.__class__.__name__, name)
|
||||
return "<%s %s>" % (self.__class__.__name__, name)
|
||||
|
||||
|
||||
@implements_to_string
|
||||
@@ -1145,10 +1229,12 @@ class TemplateModule(object):
|
||||
def __init__(self, template, context, body_stream=None):
|
||||
if body_stream is None:
|
||||
if context.environment.is_async:
|
||||
raise RuntimeError('Async mode requires a body stream '
|
||||
'to be passed to a template module. Use '
|
||||
'the async methods of the API you are '
|
||||
'using.')
|
||||
raise RuntimeError(
|
||||
"Async mode requires a body stream "
|
||||
"to be passed to a template module. Use "
|
||||
"the async methods of the API you are "
|
||||
"using."
|
||||
)
|
||||
body_stream = list(template.root_render_func(context))
|
||||
self._body_stream = body_stream
|
||||
self.__dict__.update(context.get_exported())
|
||||
@@ -1162,10 +1248,10 @@ def __str__(self):
|
||||
|
||||
def __repr__(self):
|
||||
if self.__name__ is None:
|
||||
name = 'memory:%x' % id(self)
|
||||
name = "memory:%x" % id(self)
|
||||
else:
|
||||
name = repr(self.__name__)
|
||||
return '<%s %s>' % (self.__class__.__name__, name)
|
||||
return "<%s %s>" % (self.__class__.__name__, name)
|
||||
|
||||
|
||||
class TemplateExpression(object):
|
||||
@@ -1181,7 +1267,7 @@ def __init__(self, template, undefined_to_none):
|
||||
def __call__(self, *args, **kwargs):
|
||||
context = self._template.new_context(dict(*args, **kwargs))
|
||||
consume(self._template.root_render_func(context))
|
||||
rv = context.vars['result']
|
||||
rv = context.vars["result"]
|
||||
if self._undefined_to_none and isinstance(rv, Undefined):
|
||||
rv = None
|
||||
return rv
|
||||
@@ -1203,7 +1289,7 @@ def __init__(self, gen):
|
||||
self._gen = gen
|
||||
self.disable_buffering()
|
||||
|
||||
def dump(self, fp, encoding=None, errors='strict'):
|
||||
def dump(self, fp, encoding=None, errors="strict"):
|
||||
"""Dump the complete stream into a file or file-like object.
|
||||
Per default unicode strings are written, if you want to encode
|
||||
before writing specify an `encoding`.
|
||||
@@ -1215,15 +1301,15 @@ def dump(self, fp, encoding=None, errors='strict'):
|
||||
close = False
|
||||
if isinstance(fp, string_types):
|
||||
if encoding is None:
|
||||
encoding = 'utf-8'
|
||||
fp = open(fp, 'wb')
|
||||
encoding = "utf-8"
|
||||
fp = open(fp, "wb")
|
||||
close = True
|
||||
try:
|
||||
if encoding is not None:
|
||||
iterable = (x.encode(encoding, errors) for x in self)
|
||||
else:
|
||||
iterable = self
|
||||
if hasattr(fp, 'writelines'):
|
||||
if hasattr(fp, "writelines"):
|
||||
fp.writelines(iterable)
|
||||
else:
|
||||
for item in iterable:
|
||||
@@ -1259,7 +1345,7 @@ def _buffered_generator(self, size):
|
||||
def enable_buffering(self, size=5):
|
||||
"""Enable buffering. Buffer `size` items before yielding them."""
|
||||
if size <= 1:
|
||||
raise ValueError('buffer size too small')
|
||||
raise ValueError("buffer size too small")
|
||||
|
||||
self.buffered = True
|
||||
self._next = partial(next, self._buffered_generator(size))
|
||||
|
73
lib/spack/external/jinja2/exceptions.py
vendored
73
lib/spack/external/jinja2/exceptions.py
vendored
@@ -1,23 +1,18 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
jinja2.exceptions
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Jinja exceptions.
|
||||
|
||||
:copyright: (c) 2017 by the Jinja Team.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
from jinja2._compat import imap, text_type, PY2, implements_to_string
|
||||
from ._compat import imap
|
||||
from ._compat import implements_to_string
|
||||
from ._compat import PY2
|
||||
from ._compat import text_type
|
||||
|
||||
|
||||
class TemplateError(Exception):
|
||||
"""Baseclass for all template errors."""
|
||||
|
||||
if PY2:
|
||||
|
||||
def __init__(self, message=None):
|
||||
if message is not None:
|
||||
message = text_type(message).encode('utf-8')
|
||||
message = text_type(message).encode("utf-8")
|
||||
Exception.__init__(self, message)
|
||||
|
||||
@property
|
||||
@@ -25,11 +20,13 @@ def message(self):
|
||||
if self.args:
|
||||
message = self.args[0]
|
||||
if message is not None:
|
||||
return message.decode('utf-8', 'replace')
|
||||
return message.decode("utf-8", "replace")
|
||||
|
||||
def __unicode__(self):
|
||||
return self.message or u''
|
||||
return self.message or u""
|
||||
|
||||
else:
|
||||
|
||||
def __init__(self, message=None):
|
||||
Exception.__init__(self, message)
|
||||
|
||||
@@ -43,16 +40,28 @@ def message(self):
|
||||
|
||||
@implements_to_string
|
||||
class TemplateNotFound(IOError, LookupError, TemplateError):
|
||||
"""Raised if a template does not exist."""
|
||||
"""Raised if a template does not exist.
|
||||
|
||||
.. versionchanged:: 2.11
|
||||
If the given name is :class:`Undefined` and no message was
|
||||
provided, an :exc:`UndefinedError` is raised.
|
||||
"""
|
||||
|
||||
# looks weird, but removes the warning descriptor that just
|
||||
# bogusly warns us about message being deprecated
|
||||
message = None
|
||||
|
||||
def __init__(self, name, message=None):
|
||||
IOError.__init__(self)
|
||||
IOError.__init__(self, name)
|
||||
|
||||
if message is None:
|
||||
from .runtime import Undefined
|
||||
|
||||
if isinstance(name, Undefined):
|
||||
name._fail_with_undefined_error()
|
||||
|
||||
message = name
|
||||
|
||||
self.message = message
|
||||
self.name = name
|
||||
self.templates = [name]
|
||||
@@ -66,13 +75,28 @@ class TemplatesNotFound(TemplateNotFound):
|
||||
are selected. This is a subclass of :class:`TemplateNotFound`
|
||||
exception, so just catching the base exception will catch both.
|
||||
|
||||
.. versionchanged:: 2.11
|
||||
If a name in the list of names is :class:`Undefined`, a message
|
||||
about it being undefined is shown rather than the empty string.
|
||||
|
||||
.. versionadded:: 2.2
|
||||
"""
|
||||
|
||||
def __init__(self, names=(), message=None):
|
||||
if message is None:
|
||||
message = u'none of the templates given were found: ' + \
|
||||
u', '.join(imap(text_type, names))
|
||||
from .runtime import Undefined
|
||||
|
||||
parts = []
|
||||
|
||||
for name in names:
|
||||
if isinstance(name, Undefined):
|
||||
parts.append(name._undefined_message)
|
||||
else:
|
||||
parts.append(name)
|
||||
|
||||
message = u"none of the templates given were found: " + u", ".join(
|
||||
imap(text_type, parts)
|
||||
)
|
||||
TemplateNotFound.__init__(self, names and names[-1] or None, message)
|
||||
self.templates = list(names)
|
||||
|
||||
@@ -98,11 +122,11 @@ def __str__(self):
|
||||
return self.message
|
||||
|
||||
# otherwise attach some stuff
|
||||
location = 'line %d' % self.lineno
|
||||
location = "line %d" % self.lineno
|
||||
name = self.filename or self.name
|
||||
if name:
|
||||
location = 'File "%s", %s' % (name, location)
|
||||
lines = [self.message, ' ' + location]
|
||||
lines = [self.message, " " + location]
|
||||
|
||||
# if the source is set, add the line to the output
|
||||
if self.source is not None:
|
||||
@@ -111,9 +135,16 @@ def __str__(self):
|
||||
except IndexError:
|
||||
line = None
|
||||
if line:
|
||||
lines.append(' ' + line.strip())
|
||||
lines.append(" " + line.strip())
|
||||
|
||||
return u'\n'.join(lines)
|
||||
return u"\n".join(lines)
|
||||
|
||||
def __reduce__(self):
|
||||
# https://bugs.python.org/issue1692335 Exceptions that take
|
||||
# multiple required arguments have problems with pickling.
|
||||
# Without this, raises TypeError: __init__() missing 1 required
|
||||
# positional argument: 'lineno'
|
||||
return self.__class__, (self.message, self.lineno, self.name, self.filename)
|
||||
|
||||
|
||||
class TemplateAssertionError(TemplateSyntaxError):
|
||||
|
383
lib/spack/external/jinja2/ext.py
vendored
383
lib/spack/external/jinja2/ext.py
vendored
@@ -1,42 +1,49 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
jinja2.ext
|
||||
~~~~~~~~~~
|
||||
|
||||
Jinja extensions allow to add custom tags similar to the way django custom
|
||||
tags work. By default two example extensions exist: an i18n and a cache
|
||||
extension.
|
||||
|
||||
:copyright: (c) 2017 by the Jinja Team.
|
||||
:license: BSD.
|
||||
"""
|
||||
"""Extension API for adding custom tags and behavior."""
|
||||
import pprint
|
||||
import re
|
||||
from sys import version_info
|
||||
|
||||
from jinja2 import nodes
|
||||
from jinja2.defaults import BLOCK_START_STRING, \
|
||||
BLOCK_END_STRING, VARIABLE_START_STRING, VARIABLE_END_STRING, \
|
||||
COMMENT_START_STRING, COMMENT_END_STRING, LINE_STATEMENT_PREFIX, \
|
||||
LINE_COMMENT_PREFIX, TRIM_BLOCKS, NEWLINE_SEQUENCE, \
|
||||
KEEP_TRAILING_NEWLINE, LSTRIP_BLOCKS
|
||||
from jinja2.environment import Environment
|
||||
from jinja2.runtime import concat
|
||||
from jinja2.exceptions import TemplateAssertionError, TemplateSyntaxError
|
||||
from jinja2.utils import contextfunction, import_string, Markup
|
||||
from jinja2._compat import with_metaclass, string_types, iteritems
|
||||
from markupsafe import Markup
|
||||
|
||||
from . import nodes
|
||||
from ._compat import iteritems
|
||||
from ._compat import string_types
|
||||
from ._compat import with_metaclass
|
||||
from .defaults import BLOCK_END_STRING
|
||||
from .defaults import BLOCK_START_STRING
|
||||
from .defaults import COMMENT_END_STRING
|
||||
from .defaults import COMMENT_START_STRING
|
||||
from .defaults import KEEP_TRAILING_NEWLINE
|
||||
from .defaults import LINE_COMMENT_PREFIX
|
||||
from .defaults import LINE_STATEMENT_PREFIX
|
||||
from .defaults import LSTRIP_BLOCKS
|
||||
from .defaults import NEWLINE_SEQUENCE
|
||||
from .defaults import TRIM_BLOCKS
|
||||
from .defaults import VARIABLE_END_STRING
|
||||
from .defaults import VARIABLE_START_STRING
|
||||
from .environment import Environment
|
||||
from .exceptions import TemplateAssertionError
|
||||
from .exceptions import TemplateSyntaxError
|
||||
from .nodes import ContextReference
|
||||
from .runtime import concat
|
||||
from .utils import contextfunction
|
||||
from .utils import import_string
|
||||
|
||||
# the only real useful gettext functions for a Jinja template. Note
|
||||
# that ugettext must be assigned to gettext as Jinja doesn't support
|
||||
# non unicode strings.
|
||||
GETTEXT_FUNCTIONS = ('_', 'gettext', 'ngettext')
|
||||
GETTEXT_FUNCTIONS = ("_", "gettext", "ngettext")
|
||||
|
||||
_ws_re = re.compile(r"\s*\n\s*")
|
||||
|
||||
|
||||
class ExtensionRegistry(type):
|
||||
"""Gives the extension an unique identifier."""
|
||||
|
||||
def __new__(cls, name, bases, d):
|
||||
rv = type.__new__(cls, name, bases, d)
|
||||
rv.identifier = rv.__module__ + '.' + rv.__name__
|
||||
def __new__(mcs, name, bases, d):
|
||||
rv = type.__new__(mcs, name, bases, d)
|
||||
rv.identifier = rv.__module__ + "." + rv.__name__
|
||||
return rv
|
||||
|
||||
|
||||
@@ -91,10 +98,6 @@ def filter_stream(self, stream):
|
||||
to filter tokens returned. This method has to return an iterable of
|
||||
:class:`~jinja2.lexer.Token`\\s, but it doesn't have to return a
|
||||
:class:`~jinja2.lexer.TokenStream`.
|
||||
|
||||
In the `ext` folder of the Jinja2 source distribution there is a file
|
||||
called `inlinegettext.py` which implements a filter that utilizes this
|
||||
method.
|
||||
"""
|
||||
return stream
|
||||
|
||||
@@ -116,8 +119,9 @@ def attr(self, name, lineno=None):
|
||||
"""
|
||||
return nodes.ExtensionAttribute(self.identifier, name, lineno=lineno)
|
||||
|
||||
def call_method(self, name, args=None, kwargs=None, dyn_args=None,
|
||||
dyn_kwargs=None, lineno=None):
|
||||
def call_method(
|
||||
self, name, args=None, kwargs=None, dyn_args=None, dyn_kwargs=None, lineno=None
|
||||
):
|
||||
"""Call a method of the extension. This is a shortcut for
|
||||
:meth:`attr` + :class:`jinja2.nodes.Call`.
|
||||
"""
|
||||
@@ -125,13 +129,19 @@ def call_method(self, name, args=None, kwargs=None, dyn_args=None,
|
||||
args = []
|
||||
if kwargs is None:
|
||||
kwargs = []
|
||||
return nodes.Call(self.attr(name, lineno=lineno), args, kwargs,
|
||||
dyn_args, dyn_kwargs, lineno=lineno)
|
||||
return nodes.Call(
|
||||
self.attr(name, lineno=lineno),
|
||||
args,
|
||||
kwargs,
|
||||
dyn_args,
|
||||
dyn_kwargs,
|
||||
lineno=lineno,
|
||||
)
|
||||
|
||||
|
||||
@contextfunction
|
||||
def _gettext_alias(__context, *args, **kwargs):
|
||||
return __context.call(__context.resolve('gettext'), *args, **kwargs)
|
||||
return __context.call(__context.resolve("gettext"), *args, **kwargs)
|
||||
|
||||
|
||||
def _make_new_gettext(func):
|
||||
@@ -140,24 +150,31 @@ def gettext(__context, __string, **variables):
|
||||
rv = __context.call(func, __string)
|
||||
if __context.eval_ctx.autoescape:
|
||||
rv = Markup(rv)
|
||||
# Always treat as a format string, even if there are no
|
||||
# variables. This makes translation strings more consistent
|
||||
# and predictable. This requires escaping
|
||||
return rv % variables
|
||||
|
||||
return gettext
|
||||
|
||||
|
||||
def _make_new_ngettext(func):
|
||||
@contextfunction
|
||||
def ngettext(__context, __singular, __plural, __num, **variables):
|
||||
variables.setdefault('num', __num)
|
||||
variables.setdefault("num", __num)
|
||||
rv = __context.call(func, __singular, __plural, __num)
|
||||
if __context.eval_ctx.autoescape:
|
||||
rv = Markup(rv)
|
||||
# Always treat as a format string, see gettext comment above.
|
||||
return rv % variables
|
||||
|
||||
return ngettext
|
||||
|
||||
|
||||
class InternationalizationExtension(Extension):
|
||||
"""This extension adds gettext support to Jinja2."""
|
||||
tags = set(['trans'])
|
||||
"""This extension adds gettext support to Jinja."""
|
||||
|
||||
tags = {"trans"}
|
||||
|
||||
# TODO: the i18n extension is currently reevaluating values in a few
|
||||
# situations. Take this example:
|
||||
@@ -168,30 +185,28 @@ class InternationalizationExtension(Extension):
|
||||
|
||||
def __init__(self, environment):
|
||||
Extension.__init__(self, environment)
|
||||
environment.globals['_'] = _gettext_alias
|
||||
environment.globals["_"] = _gettext_alias
|
||||
environment.extend(
|
||||
install_gettext_translations=self._install,
|
||||
install_null_translations=self._install_null,
|
||||
install_gettext_callables=self._install_callables,
|
||||
uninstall_gettext_translations=self._uninstall,
|
||||
extract_translations=self._extract,
|
||||
newstyle_gettext=False
|
||||
newstyle_gettext=False,
|
||||
)
|
||||
|
||||
def _install(self, translations, newstyle=None):
|
||||
gettext = getattr(translations, 'ugettext', None)
|
||||
gettext = getattr(translations, "ugettext", None)
|
||||
if gettext is None:
|
||||
gettext = translations.gettext
|
||||
ngettext = getattr(translations, 'ungettext', None)
|
||||
ngettext = getattr(translations, "ungettext", None)
|
||||
if ngettext is None:
|
||||
ngettext = translations.ngettext
|
||||
self._install_callables(gettext, ngettext, newstyle)
|
||||
|
||||
def _install_null(self, newstyle=None):
|
||||
self._install_callables(
|
||||
lambda x: x,
|
||||
lambda s, p, n: (n != 1 and (p,) or (s,))[0],
|
||||
newstyle
|
||||
lambda x: x, lambda s, p, n: (n != 1 and (p,) or (s,))[0], newstyle
|
||||
)
|
||||
|
||||
def _install_callables(self, gettext, ngettext, newstyle=None):
|
||||
@@ -200,13 +215,10 @@ def _install_callables(self, gettext, ngettext, newstyle=None):
|
||||
if self.environment.newstyle_gettext:
|
||||
gettext = _make_new_gettext(gettext)
|
||||
ngettext = _make_new_ngettext(ngettext)
|
||||
self.environment.globals.update(
|
||||
gettext=gettext,
|
||||
ngettext=ngettext
|
||||
)
|
||||
self.environment.globals.update(gettext=gettext, ngettext=ngettext)
|
||||
|
||||
def _uninstall(self, translations):
|
||||
for key in 'gettext', 'ngettext':
|
||||
for key in "gettext", "ngettext":
|
||||
self.environment.globals.pop(key, None)
|
||||
|
||||
def _extract(self, source, gettext_functions=GETTEXT_FUNCTIONS):
|
||||
@@ -226,41 +238,44 @@ def parse(self, parser):
|
||||
plural_expr_assignment = None
|
||||
variables = {}
|
||||
trimmed = None
|
||||
while parser.stream.current.type != 'block_end':
|
||||
while parser.stream.current.type != "block_end":
|
||||
if variables:
|
||||
parser.stream.expect('comma')
|
||||
parser.stream.expect("comma")
|
||||
|
||||
# skip colon for python compatibility
|
||||
if parser.stream.skip_if('colon'):
|
||||
if parser.stream.skip_if("colon"):
|
||||
break
|
||||
|
||||
name = parser.stream.expect('name')
|
||||
name = parser.stream.expect("name")
|
||||
if name.value in variables:
|
||||
parser.fail('translatable variable %r defined twice.' %
|
||||
name.value, name.lineno,
|
||||
exc=TemplateAssertionError)
|
||||
parser.fail(
|
||||
"translatable variable %r defined twice." % name.value,
|
||||
name.lineno,
|
||||
exc=TemplateAssertionError,
|
||||
)
|
||||
|
||||
# expressions
|
||||
if parser.stream.current.type == 'assign':
|
||||
if parser.stream.current.type == "assign":
|
||||
next(parser.stream)
|
||||
variables[name.value] = var = parser.parse_expression()
|
||||
elif trimmed is None and name.value in ('trimmed', 'notrimmed'):
|
||||
trimmed = name.value == 'trimmed'
|
||||
elif trimmed is None and name.value in ("trimmed", "notrimmed"):
|
||||
trimmed = name.value == "trimmed"
|
||||
continue
|
||||
else:
|
||||
variables[name.value] = var = nodes.Name(name.value, 'load')
|
||||
variables[name.value] = var = nodes.Name(name.value, "load")
|
||||
|
||||
if plural_expr is None:
|
||||
if isinstance(var, nodes.Call):
|
||||
plural_expr = nodes.Name('_trans', 'load')
|
||||
plural_expr = nodes.Name("_trans", "load")
|
||||
variables[name.value] = plural_expr
|
||||
plural_expr_assignment = nodes.Assign(
|
||||
nodes.Name('_trans', 'store'), var)
|
||||
nodes.Name("_trans", "store"), var
|
||||
)
|
||||
else:
|
||||
plural_expr = var
|
||||
num_called_num = name.value == 'num'
|
||||
num_called_num = name.value == "num"
|
||||
|
||||
parser.stream.expect('block_end')
|
||||
parser.stream.expect("block_end")
|
||||
|
||||
plural = None
|
||||
have_plural = False
|
||||
@@ -271,22 +286,24 @@ def parse(self, parser):
|
||||
if singular_names:
|
||||
referenced.update(singular_names)
|
||||
if plural_expr is None:
|
||||
plural_expr = nodes.Name(singular_names[0], 'load')
|
||||
num_called_num = singular_names[0] == 'num'
|
||||
plural_expr = nodes.Name(singular_names[0], "load")
|
||||
num_called_num = singular_names[0] == "num"
|
||||
|
||||
# if we have a pluralize block, we parse that too
|
||||
if parser.stream.current.test('name:pluralize'):
|
||||
if parser.stream.current.test("name:pluralize"):
|
||||
have_plural = True
|
||||
next(parser.stream)
|
||||
if parser.stream.current.type != 'block_end':
|
||||
name = parser.stream.expect('name')
|
||||
if parser.stream.current.type != "block_end":
|
||||
name = parser.stream.expect("name")
|
||||
if name.value not in variables:
|
||||
parser.fail('unknown variable %r for pluralization' %
|
||||
name.value, name.lineno,
|
||||
exc=TemplateAssertionError)
|
||||
parser.fail(
|
||||
"unknown variable %r for pluralization" % name.value,
|
||||
name.lineno,
|
||||
exc=TemplateAssertionError,
|
||||
)
|
||||
plural_expr = variables[name.value]
|
||||
num_called_num = name.value == 'num'
|
||||
parser.stream.expect('block_end')
|
||||
num_called_num = name.value == "num"
|
||||
parser.stream.expect("block_end")
|
||||
plural_names, plural = self._parse_block(parser, False)
|
||||
next(parser.stream)
|
||||
referenced.update(plural_names)
|
||||
@@ -296,88 +313,97 @@ def parse(self, parser):
|
||||
# register free names as simple name expressions
|
||||
for var in referenced:
|
||||
if var not in variables:
|
||||
variables[var] = nodes.Name(var, 'load')
|
||||
variables[var] = nodes.Name(var, "load")
|
||||
|
||||
if not have_plural:
|
||||
plural_expr = None
|
||||
elif plural_expr is None:
|
||||
parser.fail('pluralize without variables', lineno)
|
||||
parser.fail("pluralize without variables", lineno)
|
||||
|
||||
if trimmed is None:
|
||||
trimmed = self.environment.policies['ext.i18n.trimmed']
|
||||
trimmed = self.environment.policies["ext.i18n.trimmed"]
|
||||
if trimmed:
|
||||
singular = self._trim_whitespace(singular)
|
||||
if plural:
|
||||
plural = self._trim_whitespace(plural)
|
||||
|
||||
node = self._make_node(singular, plural, variables, plural_expr,
|
||||
bool(referenced),
|
||||
num_called_num and have_plural)
|
||||
node = self._make_node(
|
||||
singular,
|
||||
plural,
|
||||
variables,
|
||||
plural_expr,
|
||||
bool(referenced),
|
||||
num_called_num and have_plural,
|
||||
)
|
||||
node.set_lineno(lineno)
|
||||
if plural_expr_assignment is not None:
|
||||
return [plural_expr_assignment, node]
|
||||
else:
|
||||
return node
|
||||
|
||||
def _trim_whitespace(self, string, _ws_re=re.compile(r'\s*\n\s*')):
|
||||
return _ws_re.sub(' ', string.strip())
|
||||
def _trim_whitespace(self, string, _ws_re=_ws_re):
|
||||
return _ws_re.sub(" ", string.strip())
|
||||
|
||||
def _parse_block(self, parser, allow_pluralize):
|
||||
"""Parse until the next block tag with a given name."""
|
||||
referenced = []
|
||||
buf = []
|
||||
while 1:
|
||||
if parser.stream.current.type == 'data':
|
||||
buf.append(parser.stream.current.value.replace('%', '%%'))
|
||||
if parser.stream.current.type == "data":
|
||||
buf.append(parser.stream.current.value.replace("%", "%%"))
|
||||
next(parser.stream)
|
||||
elif parser.stream.current.type == 'variable_begin':
|
||||
elif parser.stream.current.type == "variable_begin":
|
||||
next(parser.stream)
|
||||
name = parser.stream.expect('name').value
|
||||
name = parser.stream.expect("name").value
|
||||
referenced.append(name)
|
||||
buf.append('%%(%s)s' % name)
|
||||
parser.stream.expect('variable_end')
|
||||
elif parser.stream.current.type == 'block_begin':
|
||||
buf.append("%%(%s)s" % name)
|
||||
parser.stream.expect("variable_end")
|
||||
elif parser.stream.current.type == "block_begin":
|
||||
next(parser.stream)
|
||||
if parser.stream.current.test('name:endtrans'):
|
||||
if parser.stream.current.test("name:endtrans"):
|
||||
break
|
||||
elif parser.stream.current.test('name:pluralize'):
|
||||
elif parser.stream.current.test("name:pluralize"):
|
||||
if allow_pluralize:
|
||||
break
|
||||
parser.fail('a translatable section can have only one '
|
||||
'pluralize section')
|
||||
parser.fail('control structures in translatable sections are '
|
||||
'not allowed')
|
||||
parser.fail(
|
||||
"a translatable section can have only one pluralize section"
|
||||
)
|
||||
parser.fail(
|
||||
"control structures in translatable sections are not allowed"
|
||||
)
|
||||
elif parser.stream.eos:
|
||||
parser.fail('unclosed translation block')
|
||||
parser.fail("unclosed translation block")
|
||||
else:
|
||||
assert False, 'internal parser error'
|
||||
raise RuntimeError("internal parser error")
|
||||
|
||||
return referenced, concat(buf)
|
||||
|
||||
def _make_node(self, singular, plural, variables, plural_expr,
|
||||
vars_referenced, num_called_num):
|
||||
def _make_node(
|
||||
self, singular, plural, variables, plural_expr, vars_referenced, num_called_num
|
||||
):
|
||||
"""Generates a useful node from the data provided."""
|
||||
# no variables referenced? no need to escape for old style
|
||||
# gettext invocations only if there are vars.
|
||||
if not vars_referenced and not self.environment.newstyle_gettext:
|
||||
singular = singular.replace('%%', '%')
|
||||
singular = singular.replace("%%", "%")
|
||||
if plural:
|
||||
plural = plural.replace('%%', '%')
|
||||
plural = plural.replace("%%", "%")
|
||||
|
||||
# singular only:
|
||||
if plural_expr is None:
|
||||
gettext = nodes.Name('gettext', 'load')
|
||||
node = nodes.Call(gettext, [nodes.Const(singular)],
|
||||
[], None, None)
|
||||
gettext = nodes.Name("gettext", "load")
|
||||
node = nodes.Call(gettext, [nodes.Const(singular)], [], None, None)
|
||||
|
||||
# singular and plural
|
||||
else:
|
||||
ngettext = nodes.Name('ngettext', 'load')
|
||||
node = nodes.Call(ngettext, [
|
||||
nodes.Const(singular),
|
||||
nodes.Const(plural),
|
||||
plural_expr
|
||||
], [], None, None)
|
||||
ngettext = nodes.Name("ngettext", "load")
|
||||
node = nodes.Call(
|
||||
ngettext,
|
||||
[nodes.Const(singular), nodes.Const(plural), plural_expr],
|
||||
[],
|
||||
None,
|
||||
None,
|
||||
)
|
||||
|
||||
# in case newstyle gettext is used, the method is powerful
|
||||
# enough to handle the variable expansion and autoescape
|
||||
@@ -386,7 +412,7 @@ def _make_node(self, singular, plural, variables, plural_expr,
|
||||
for key, value in iteritems(variables):
|
||||
# the function adds that later anyways in case num was
|
||||
# called num, so just skip it.
|
||||
if num_called_num and key == 'num':
|
||||
if num_called_num and key == "num":
|
||||
continue
|
||||
node.kwargs.append(nodes.Keyword(key, value))
|
||||
|
||||
@@ -396,18 +422,24 @@ def _make_node(self, singular, plural, variables, plural_expr,
|
||||
# environment with autoescaping turned on
|
||||
node = nodes.MarkSafeIfAutoescape(node)
|
||||
if variables:
|
||||
node = nodes.Mod(node, nodes.Dict([
|
||||
nodes.Pair(nodes.Const(key), value)
|
||||
for key, value in variables.items()
|
||||
]))
|
||||
node = nodes.Mod(
|
||||
node,
|
||||
nodes.Dict(
|
||||
[
|
||||
nodes.Pair(nodes.Const(key), value)
|
||||
for key, value in variables.items()
|
||||
]
|
||||
),
|
||||
)
|
||||
return nodes.Output([node])
|
||||
|
||||
|
||||
class ExprStmtExtension(Extension):
|
||||
"""Adds a `do` tag to Jinja2 that works like the print statement just
|
||||
"""Adds a `do` tag to Jinja that works like the print statement just
|
||||
that it doesn't print the return value.
|
||||
"""
|
||||
tags = set(['do'])
|
||||
|
||||
tags = set(["do"])
|
||||
|
||||
def parse(self, parser):
|
||||
node = nodes.ExprStmt(lineno=next(parser.stream).lineno)
|
||||
@@ -417,11 +449,12 @@ def parse(self, parser):
|
||||
|
||||
class LoopControlExtension(Extension):
|
||||
"""Adds break and continue to the template engine."""
|
||||
tags = set(['break', 'continue'])
|
||||
|
||||
tags = set(["break", "continue"])
|
||||
|
||||
def parse(self, parser):
|
||||
token = next(parser.stream)
|
||||
if token.value == 'break':
|
||||
if token.value == "break":
|
||||
return nodes.Break(lineno=token.lineno)
|
||||
return nodes.Continue(lineno=token.lineno)
|
||||
|
||||
@@ -434,8 +467,50 @@ class AutoEscapeExtension(Extension):
|
||||
pass
|
||||
|
||||
|
||||
def extract_from_ast(node, gettext_functions=GETTEXT_FUNCTIONS,
|
||||
babel_style=True):
|
||||
class DebugExtension(Extension):
|
||||
"""A ``{% debug %}`` tag that dumps the available variables,
|
||||
filters, and tests.
|
||||
|
||||
.. code-block:: html+jinja
|
||||
|
||||
<pre>{% debug %}</pre>
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
{'context': {'cycler': <class 'jinja2.utils.Cycler'>,
|
||||
...,
|
||||
'namespace': <class 'jinja2.utils.Namespace'>},
|
||||
'filters': ['abs', 'attr', 'batch', 'capitalize', 'center', 'count', 'd',
|
||||
..., 'urlencode', 'urlize', 'wordcount', 'wordwrap', 'xmlattr'],
|
||||
'tests': ['!=', '<', '<=', '==', '>', '>=', 'callable', 'defined',
|
||||
..., 'odd', 'sameas', 'sequence', 'string', 'undefined', 'upper']}
|
||||
|
||||
.. versionadded:: 2.11.0
|
||||
"""
|
||||
|
||||
tags = {"debug"}
|
||||
|
||||
def parse(self, parser):
|
||||
lineno = parser.stream.expect("name:debug").lineno
|
||||
context = ContextReference()
|
||||
result = self.call_method("_render", [context], lineno=lineno)
|
||||
return nodes.Output([result], lineno=lineno)
|
||||
|
||||
def _render(self, context):
|
||||
result = {
|
||||
"context": context.get_all(),
|
||||
"filters": sorted(self.environment.filters.keys()),
|
||||
"tests": sorted(self.environment.tests.keys()),
|
||||
}
|
||||
|
||||
# Set the depth since the intent is to show the top few names.
|
||||
if version_info[:2] >= (3, 4):
|
||||
return pprint.pformat(result, depth=3, compact=True)
|
||||
else:
|
||||
return pprint.pformat(result, depth=3)
|
||||
|
||||
|
||||
def extract_from_ast(node, gettext_functions=GETTEXT_FUNCTIONS, babel_style=True):
|
||||
"""Extract localizable strings from the given template node. Per
|
||||
default this function returns matches in babel style that means non string
|
||||
parameters as well as keyword arguments are returned as `None`. This
|
||||
@@ -471,19 +546,20 @@ def extract_from_ast(node, gettext_functions=GETTEXT_FUNCTIONS,
|
||||
extraction interface or extract comments yourself.
|
||||
"""
|
||||
for node in node.find_all(nodes.Call):
|
||||
if not isinstance(node.node, nodes.Name) or \
|
||||
node.node.name not in gettext_functions:
|
||||
if (
|
||||
not isinstance(node.node, nodes.Name)
|
||||
or node.node.name not in gettext_functions
|
||||
):
|
||||
continue
|
||||
|
||||
strings = []
|
||||
for arg in node.args:
|
||||
if isinstance(arg, nodes.Const) and \
|
||||
isinstance(arg.value, string_types):
|
||||
if isinstance(arg, nodes.Const) and isinstance(arg.value, string_types):
|
||||
strings.append(arg.value)
|
||||
else:
|
||||
strings.append(None)
|
||||
|
||||
for arg in node.kwargs:
|
||||
for _ in node.kwargs:
|
||||
strings.append(None)
|
||||
if node.dyn_args is not None:
|
||||
strings.append(None)
|
||||
@@ -517,9 +593,10 @@ def __init__(self, tokens, comment_tags):
|
||||
|
||||
def find_backwards(self, offset):
|
||||
try:
|
||||
for _, token_type, token_value in \
|
||||
reversed(self.tokens[self.offset:offset]):
|
||||
if token_type in ('comment', 'linecomment'):
|
||||
for _, token_type, token_value in reversed(
|
||||
self.tokens[self.offset : offset]
|
||||
):
|
||||
if token_type in ("comment", "linecomment"):
|
||||
try:
|
||||
prefix, comment = token_value.split(None, 1)
|
||||
except ValueError:
|
||||
@@ -533,7 +610,7 @@ def find_backwards(self, offset):
|
||||
def find_comments(self, lineno):
|
||||
if not self.comment_tags or self.last_lineno > lineno:
|
||||
return []
|
||||
for idx, (token_lineno, _, _) in enumerate(self.tokens[self.offset:]):
|
||||
for idx, (token_lineno, _, _) in enumerate(self.tokens[self.offset :]):
|
||||
if token_lineno > lineno:
|
||||
return self.find_backwards(self.offset + idx)
|
||||
return self.find_backwards(len(self.tokens))
|
||||
@@ -545,7 +622,7 @@ def babel_extract(fileobj, keywords, comment_tags, options):
|
||||
.. versionchanged:: 2.3
|
||||
Basic support for translation comments was added. If `comment_tags`
|
||||
is now set to a list of keywords for extraction, the extractor will
|
||||
try to find the best preceeding comment that begins with one of the
|
||||
try to find the best preceding comment that begins with one of the
|
||||
keywords. For best results, make sure to not have more than one
|
||||
gettext call in one line of code and the matching comment in the
|
||||
same line or the line before.
|
||||
@@ -568,7 +645,7 @@ def babel_extract(fileobj, keywords, comment_tags, options):
|
||||
(comments will be empty currently)
|
||||
"""
|
||||
extensions = set()
|
||||
for extension in options.get('extensions', '').split(','):
|
||||
for extension in options.get("extensions", "").split(","):
|
||||
extension = extension.strip()
|
||||
if not extension:
|
||||
continue
|
||||
@@ -577,38 +654,37 @@ def babel_extract(fileobj, keywords, comment_tags, options):
|
||||
extensions.add(InternationalizationExtension)
|
||||
|
||||
def getbool(options, key, default=False):
|
||||
return options.get(key, str(default)).lower() in \
|
||||
('1', 'on', 'yes', 'true')
|
||||
return options.get(key, str(default)).lower() in ("1", "on", "yes", "true")
|
||||
|
||||
silent = getbool(options, 'silent', True)
|
||||
silent = getbool(options, "silent", True)
|
||||
environment = Environment(
|
||||
options.get('block_start_string', BLOCK_START_STRING),
|
||||
options.get('block_end_string', BLOCK_END_STRING),
|
||||
options.get('variable_start_string', VARIABLE_START_STRING),
|
||||
options.get('variable_end_string', VARIABLE_END_STRING),
|
||||
options.get('comment_start_string', COMMENT_START_STRING),
|
||||
options.get('comment_end_string', COMMENT_END_STRING),
|
||||
options.get('line_statement_prefix') or LINE_STATEMENT_PREFIX,
|
||||
options.get('line_comment_prefix') or LINE_COMMENT_PREFIX,
|
||||
getbool(options, 'trim_blocks', TRIM_BLOCKS),
|
||||
getbool(options, 'lstrip_blocks', LSTRIP_BLOCKS),
|
||||
options.get("block_start_string", BLOCK_START_STRING),
|
||||
options.get("block_end_string", BLOCK_END_STRING),
|
||||
options.get("variable_start_string", VARIABLE_START_STRING),
|
||||
options.get("variable_end_string", VARIABLE_END_STRING),
|
||||
options.get("comment_start_string", COMMENT_START_STRING),
|
||||
options.get("comment_end_string", COMMENT_END_STRING),
|
||||
options.get("line_statement_prefix") or LINE_STATEMENT_PREFIX,
|
||||
options.get("line_comment_prefix") or LINE_COMMENT_PREFIX,
|
||||
getbool(options, "trim_blocks", TRIM_BLOCKS),
|
||||
getbool(options, "lstrip_blocks", LSTRIP_BLOCKS),
|
||||
NEWLINE_SEQUENCE,
|
||||
getbool(options, 'keep_trailing_newline', KEEP_TRAILING_NEWLINE),
|
||||
getbool(options, "keep_trailing_newline", KEEP_TRAILING_NEWLINE),
|
||||
frozenset(extensions),
|
||||
cache_size=0,
|
||||
auto_reload=False
|
||||
auto_reload=False,
|
||||
)
|
||||
|
||||
if getbool(options, 'trimmed'):
|
||||
environment.policies['ext.i18n.trimmed'] = True
|
||||
if getbool(options, 'newstyle_gettext'):
|
||||
if getbool(options, "trimmed"):
|
||||
environment.policies["ext.i18n.trimmed"] = True
|
||||
if getbool(options, "newstyle_gettext"):
|
||||
environment.newstyle_gettext = True
|
||||
|
||||
source = fileobj.read().decode(options.get('encoding', 'utf-8'))
|
||||
source = fileobj.read().decode(options.get("encoding", "utf-8"))
|
||||
try:
|
||||
node = environment.parse(source)
|
||||
tokens = list(environment.lex(environment.preprocess(source)))
|
||||
except TemplateSyntaxError as e:
|
||||
except TemplateSyntaxError:
|
||||
if not silent:
|
||||
raise
|
||||
# skip templates with syntax errors
|
||||
@@ -625,3 +701,4 @@ def getbool(options, key, default=False):
|
||||
loopcontrols = LoopControlExtension
|
||||
with_ = WithExtension
|
||||
autoescape = AutoEscapeExtension
|
||||
debug = DebugExtension
|
||||
|
718
lib/spack/external/jinja2/filters.py
vendored
718
lib/spack/external/jinja2/filters.py
vendored
File diff suppressed because it is too large
Load Diff
58
lib/spack/external/jinja2/idtracking.py
vendored
58
lib/spack/external/jinja2/idtracking.py
vendored
@@ -1,11 +1,10 @@
|
||||
from jinja2.visitor import NodeVisitor
|
||||
from jinja2._compat import iteritems
|
||||
from ._compat import iteritems
|
||||
from .visitor import NodeVisitor
|
||||
|
||||
|
||||
VAR_LOAD_PARAMETER = 'param'
|
||||
VAR_LOAD_RESOLVE = 'resolve'
|
||||
VAR_LOAD_ALIAS = 'alias'
|
||||
VAR_LOAD_UNDEFINED = 'undefined'
|
||||
VAR_LOAD_PARAMETER = "param"
|
||||
VAR_LOAD_RESOLVE = "resolve"
|
||||
VAR_LOAD_ALIAS = "alias"
|
||||
VAR_LOAD_UNDEFINED = "undefined"
|
||||
|
||||
|
||||
def find_symbols(nodes, parent_symbols=None):
|
||||
@@ -23,7 +22,6 @@ def symbols_for_node(node, parent_symbols=None):
|
||||
|
||||
|
||||
class Symbols(object):
|
||||
|
||||
def __init__(self, parent=None, level=None):
|
||||
if level is None:
|
||||
if parent is None:
|
||||
@@ -41,7 +39,7 @@ def analyze_node(self, node, **kwargs):
|
||||
visitor.visit(node, **kwargs)
|
||||
|
||||
def _define_ref(self, name, load=None):
|
||||
ident = 'l_%d_%s' % (self.level, name)
|
||||
ident = "l_%d_%s" % (self.level, name)
|
||||
self.refs[name] = ident
|
||||
if load is not None:
|
||||
self.loads[ident] = load
|
||||
@@ -62,8 +60,10 @@ def find_ref(self, name):
|
||||
def ref(self, name):
|
||||
rv = self.find_ref(name)
|
||||
if rv is None:
|
||||
raise AssertionError('Tried to resolve a name to a reference that '
|
||||
'was unknown to the frame (%r)' % name)
|
||||
raise AssertionError(
|
||||
"Tried to resolve a name to a reference that "
|
||||
"was unknown to the frame (%r)" % name
|
||||
)
|
||||
return rv
|
||||
|
||||
def copy(self):
|
||||
@@ -118,7 +118,7 @@ def branch_update(self, branch_symbols):
|
||||
if branch_count == len(branch_symbols):
|
||||
continue
|
||||
target = self.find_ref(name)
|
||||
assert target is not None, 'should not happen'
|
||||
assert target is not None, "should not happen"
|
||||
|
||||
if self.parent is not None:
|
||||
outer_target = self.parent.find_ref(name)
|
||||
@@ -149,7 +149,6 @@ def dump_param_targets(self):
|
||||
|
||||
|
||||
class RootVisitor(NodeVisitor):
|
||||
|
||||
def __init__(self, symbols):
|
||||
self.sym_visitor = FrameSymbolVisitor(symbols)
|
||||
|
||||
@@ -157,35 +156,39 @@ def _simple_visit(self, node, **kwargs):
|
||||
for child in node.iter_child_nodes():
|
||||
self.sym_visitor.visit(child)
|
||||
|
||||
visit_Template = visit_Block = visit_Macro = visit_FilterBlock = \
|
||||
visit_Scope = visit_If = visit_ScopedEvalContextModifier = \
|
||||
_simple_visit
|
||||
visit_Template = (
|
||||
visit_Block
|
||||
) = (
|
||||
visit_Macro
|
||||
) = (
|
||||
visit_FilterBlock
|
||||
) = visit_Scope = visit_If = visit_ScopedEvalContextModifier = _simple_visit
|
||||
|
||||
def visit_AssignBlock(self, node, **kwargs):
|
||||
for child in node.body:
|
||||
self.sym_visitor.visit(child)
|
||||
|
||||
def visit_CallBlock(self, node, **kwargs):
|
||||
for child in node.iter_child_nodes(exclude=('call',)):
|
||||
for child in node.iter_child_nodes(exclude=("call",)):
|
||||
self.sym_visitor.visit(child)
|
||||
|
||||
def visit_OverlayScope(self, node, **kwargs):
|
||||
for child in node.body:
|
||||
self.sym_visitor.visit(child)
|
||||
|
||||
def visit_For(self, node, for_branch='body', **kwargs):
|
||||
if for_branch == 'body':
|
||||
def visit_For(self, node, for_branch="body", **kwargs):
|
||||
if for_branch == "body":
|
||||
self.sym_visitor.visit(node.target, store_as_param=True)
|
||||
branch = node.body
|
||||
elif for_branch == 'else':
|
||||
elif for_branch == "else":
|
||||
branch = node.else_
|
||||
elif for_branch == 'test':
|
||||
elif for_branch == "test":
|
||||
self.sym_visitor.visit(node.target, store_as_param=True)
|
||||
if node.test is not None:
|
||||
self.sym_visitor.visit(node.test)
|
||||
return
|
||||
else:
|
||||
raise RuntimeError('Unknown for branch')
|
||||
raise RuntimeError("Unknown for branch")
|
||||
for item in branch or ():
|
||||
self.sym_visitor.visit(item)
|
||||
|
||||
@@ -196,8 +199,9 @@ def visit_With(self, node, **kwargs):
|
||||
self.sym_visitor.visit(child)
|
||||
|
||||
def generic_visit(self, node, *args, **kwargs):
|
||||
raise NotImplementedError('Cannot find symbols for %r' %
|
||||
node.__class__.__name__)
|
||||
raise NotImplementedError(
|
||||
"Cannot find symbols for %r" % node.__class__.__name__
|
||||
)
|
||||
|
||||
|
||||
class FrameSymbolVisitor(NodeVisitor):
|
||||
@@ -208,11 +212,11 @@ def __init__(self, symbols):
|
||||
|
||||
def visit_Name(self, node, store_as_param=False, **kwargs):
|
||||
"""All assignments to names go through this function."""
|
||||
if store_as_param or node.ctx == 'param':
|
||||
if store_as_param or node.ctx == "param":
|
||||
self.symbols.declare_parameter(node.name)
|
||||
elif node.ctx == 'store':
|
||||
elif node.ctx == "store":
|
||||
self.symbols.store(node.name)
|
||||
elif node.ctx == 'load':
|
||||
elif node.ctx == "load":
|
||||
self.symbols.load(node.name)
|
||||
|
||||
def visit_NSRef(self, node, **kwargs):
|
||||
|
771
lib/spack/external/jinja2/lexer.py
vendored
771
lib/spack/external/jinja2/lexer.py
vendored
File diff suppressed because it is too large
Load Diff
137
lib/spack/external/jinja2/loaders.py
vendored
137
lib/spack/external/jinja2/loaders.py
vendored
@@ -1,22 +1,21 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
jinja2.loaders
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Jinja loader classes.
|
||||
|
||||
:copyright: (c) 2017 by the Jinja Team.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""API and implementations for loading templates from different data
|
||||
sources.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import weakref
|
||||
from types import ModuleType
|
||||
from os import path
|
||||
from hashlib import sha1
|
||||
from jinja2.exceptions import TemplateNotFound
|
||||
from jinja2.utils import open_if_exists, internalcode
|
||||
from jinja2._compat import string_types, iteritems
|
||||
from os import path
|
||||
from types import ModuleType
|
||||
|
||||
from ._compat import abc
|
||||
from ._compat import fspath
|
||||
from ._compat import iteritems
|
||||
from ._compat import string_types
|
||||
from .exceptions import TemplateNotFound
|
||||
from .utils import internalcode
|
||||
from .utils import open_if_exists
|
||||
|
||||
|
||||
def split_template_path(template):
|
||||
@@ -24,12 +23,14 @@ def split_template_path(template):
|
||||
'..' in the path it will raise a `TemplateNotFound` error.
|
||||
"""
|
||||
pieces = []
|
||||
for piece in template.split('/'):
|
||||
if path.sep in piece \
|
||||
or (path.altsep and path.altsep in piece) or \
|
||||
piece == path.pardir:
|
||||
for piece in template.split("/"):
|
||||
if (
|
||||
path.sep in piece
|
||||
or (path.altsep and path.altsep in piece)
|
||||
or piece == path.pardir
|
||||
):
|
||||
raise TemplateNotFound(template)
|
||||
elif piece and piece != '.':
|
||||
elif piece and piece != ".":
|
||||
pieces.append(piece)
|
||||
return pieces
|
||||
|
||||
@@ -86,15 +87,16 @@ def get_source(self, environment, template):
|
||||
the template will be reloaded.
|
||||
"""
|
||||
if not self.has_source_access:
|
||||
raise RuntimeError('%s cannot provide access to the source' %
|
||||
self.__class__.__name__)
|
||||
raise RuntimeError(
|
||||
"%s cannot provide access to the source" % self.__class__.__name__
|
||||
)
|
||||
raise TemplateNotFound(template)
|
||||
|
||||
def list_templates(self):
|
||||
"""Iterates over all templates. If the loader does not support that
|
||||
it should raise a :exc:`TypeError` which is the default behavior.
|
||||
"""
|
||||
raise TypeError('this loader cannot iterate over all templates')
|
||||
raise TypeError("this loader cannot iterate over all templates")
|
||||
|
||||
@internalcode
|
||||
def load(self, environment, name, globals=None):
|
||||
@@ -131,8 +133,9 @@ def load(self, environment, name, globals=None):
|
||||
bucket.code = code
|
||||
bcc.set_bucket(bucket)
|
||||
|
||||
return environment.template_class.from_code(environment, code,
|
||||
globals, uptodate)
|
||||
return environment.template_class.from_code(
|
||||
environment, code, globals, uptodate
|
||||
)
|
||||
|
||||
|
||||
class FileSystemLoader(BaseLoader):
|
||||
@@ -153,14 +156,20 @@ class FileSystemLoader(BaseLoader):
|
||||
|
||||
>>> loader = FileSystemLoader('/path/to/templates', followlinks=True)
|
||||
|
||||
.. versionchanged:: 2.8+
|
||||
The *followlinks* parameter was added.
|
||||
.. versionchanged:: 2.8
|
||||
The ``followlinks`` parameter was added.
|
||||
"""
|
||||
|
||||
def __init__(self, searchpath, encoding='utf-8', followlinks=False):
|
||||
if isinstance(searchpath, string_types):
|
||||
def __init__(self, searchpath, encoding="utf-8", followlinks=False):
|
||||
if not isinstance(searchpath, abc.Iterable) or isinstance(
|
||||
searchpath, string_types
|
||||
):
|
||||
searchpath = [searchpath]
|
||||
self.searchpath = list(searchpath)
|
||||
|
||||
# In Python 3.5, os.path.join doesn't support Path. This can be
|
||||
# simplified to list(searchpath) when Python 3.5 is dropped.
|
||||
self.searchpath = [fspath(p) for p in searchpath]
|
||||
|
||||
self.encoding = encoding
|
||||
self.followlinks = followlinks
|
||||
|
||||
@@ -183,6 +192,7 @@ def uptodate():
|
||||
return path.getmtime(filename) == mtime
|
||||
except OSError:
|
||||
return False
|
||||
|
||||
return contents, filename, uptodate
|
||||
raise TemplateNotFound(template)
|
||||
|
||||
@@ -190,12 +200,14 @@ def list_templates(self):
|
||||
found = set()
|
||||
for searchpath in self.searchpath:
|
||||
walk_dir = os.walk(searchpath, followlinks=self.followlinks)
|
||||
for dirpath, dirnames, filenames in walk_dir:
|
||||
for dirpath, _, filenames in walk_dir:
|
||||
for filename in filenames:
|
||||
template = os.path.join(dirpath, filename) \
|
||||
[len(searchpath):].strip(os.path.sep) \
|
||||
.replace(os.path.sep, '/')
|
||||
if template[:2] == './':
|
||||
template = (
|
||||
os.path.join(dirpath, filename)[len(searchpath) :]
|
||||
.strip(os.path.sep)
|
||||
.replace(os.path.sep, "/")
|
||||
)
|
||||
if template[:2] == "./":
|
||||
template = template[2:]
|
||||
if template not in found:
|
||||
found.add(template)
|
||||
@@ -217,10 +229,11 @@ class PackageLoader(BaseLoader):
|
||||
from the file system and not a zip file.
|
||||
"""
|
||||
|
||||
def __init__(self, package_name, package_path='templates',
|
||||
encoding='utf-8'):
|
||||
from pkg_resources import DefaultProvider, ResourceManager, \
|
||||
get_provider
|
||||
def __init__(self, package_name, package_path="templates", encoding="utf-8"):
|
||||
from pkg_resources import DefaultProvider
|
||||
from pkg_resources import get_provider
|
||||
from pkg_resources import ResourceManager
|
||||
|
||||
provider = get_provider(package_name)
|
||||
self.encoding = encoding
|
||||
self.manager = ResourceManager()
|
||||
@@ -230,14 +243,17 @@ def __init__(self, package_name, package_path='templates',
|
||||
|
||||
def get_source(self, environment, template):
|
||||
pieces = split_template_path(template)
|
||||
p = '/'.join((self.package_path,) + tuple(pieces))
|
||||
p = "/".join((self.package_path,) + tuple(pieces))
|
||||
|
||||
if not self.provider.has_resource(p):
|
||||
raise TemplateNotFound(template)
|
||||
|
||||
filename = uptodate = None
|
||||
|
||||
if self.filesystem_bound:
|
||||
filename = self.provider.get_resource_filename(self.manager, p)
|
||||
mtime = path.getmtime(filename)
|
||||
|
||||
def uptodate():
|
||||
try:
|
||||
return path.getmtime(filename) == mtime
|
||||
@@ -249,19 +265,24 @@ def uptodate():
|
||||
|
||||
def list_templates(self):
|
||||
path = self.package_path
|
||||
if path[:2] == './':
|
||||
|
||||
if path[:2] == "./":
|
||||
path = path[2:]
|
||||
elif path == '.':
|
||||
path = ''
|
||||
elif path == ".":
|
||||
path = ""
|
||||
|
||||
offset = len(path)
|
||||
results = []
|
||||
|
||||
def _walk(path):
|
||||
for filename in self.provider.resource_listdir(path):
|
||||
fullname = path + '/' + filename
|
||||
fullname = path + "/" + filename
|
||||
|
||||
if self.provider.resource_isdir(fullname):
|
||||
_walk(fullname)
|
||||
else:
|
||||
results.append(fullname[offset:].lstrip('/'))
|
||||
results.append(fullname[offset:].lstrip("/"))
|
||||
|
||||
_walk(path)
|
||||
results.sort()
|
||||
return results
|
||||
@@ -334,7 +355,7 @@ class PrefixLoader(BaseLoader):
|
||||
by loading ``'app2/index.html'`` the file from the second.
|
||||
"""
|
||||
|
||||
def __init__(self, mapping, delimiter='/'):
|
||||
def __init__(self, mapping, delimiter="/"):
|
||||
self.mapping = mapping
|
||||
self.delimiter = delimiter
|
||||
|
||||
@@ -434,19 +455,20 @@ class ModuleLoader(BaseLoader):
|
||||
has_source_access = False
|
||||
|
||||
def __init__(self, path):
|
||||
package_name = '_jinja2_module_templates_%x' % id(self)
|
||||
package_name = "_jinja2_module_templates_%x" % id(self)
|
||||
|
||||
# create a fake module that looks for the templates in the
|
||||
# path given.
|
||||
mod = _TemplateModule(package_name)
|
||||
if isinstance(path, string_types):
|
||||
path = [path]
|
||||
else:
|
||||
path = list(path)
|
||||
mod.__path__ = path
|
||||
|
||||
sys.modules[package_name] = weakref.proxy(mod,
|
||||
lambda x: sys.modules.pop(package_name, None))
|
||||
if not isinstance(path, abc.Iterable) or isinstance(path, string_types):
|
||||
path = [path]
|
||||
|
||||
mod.__path__ = [fspath(p) for p in path]
|
||||
|
||||
sys.modules[package_name] = weakref.proxy(
|
||||
mod, lambda x: sys.modules.pop(package_name, None)
|
||||
)
|
||||
|
||||
# the only strong reference, the sys.modules entry is weak
|
||||
# so that the garbage collector can remove it once the
|
||||
@@ -456,20 +478,20 @@ def __init__(self, path):
|
||||
|
||||
@staticmethod
|
||||
def get_template_key(name):
|
||||
return 'tmpl_' + sha1(name.encode('utf-8')).hexdigest()
|
||||
return "tmpl_" + sha1(name.encode("utf-8")).hexdigest()
|
||||
|
||||
@staticmethod
|
||||
def get_module_filename(name):
|
||||
return ModuleLoader.get_template_key(name) + '.py'
|
||||
return ModuleLoader.get_template_key(name) + ".py"
|
||||
|
||||
@internalcode
|
||||
def load(self, environment, name, globals=None):
|
||||
key = self.get_template_key(name)
|
||||
module = '%s.%s' % (self.package_name, key)
|
||||
module = "%s.%s" % (self.package_name, key)
|
||||
mod = getattr(self.module, module, None)
|
||||
if mod is None:
|
||||
try:
|
||||
mod = __import__(module, None, None, ['root'])
|
||||
mod = __import__(module, None, None, ["root"])
|
||||
except ImportError:
|
||||
raise TemplateNotFound(name)
|
||||
|
||||
@@ -478,4 +500,5 @@ def load(self, environment, name, globals=None):
|
||||
sys.modules.pop(module, None)
|
||||
|
||||
return environment.template_class.from_module_dict(
|
||||
environment, mod.__dict__, globals)
|
||||
environment, mod.__dict__, globals
|
||||
)
|
||||
|
33
lib/spack/external/jinja2/meta.py
vendored
33
lib/spack/external/jinja2/meta.py
vendored
@@ -1,25 +1,18 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Functions that expose information about templates that might be
|
||||
interesting for introspection.
|
||||
"""
|
||||
jinja2.meta
|
||||
~~~~~~~~~~~
|
||||
|
||||
This module implements various functions that exposes information about
|
||||
templates that might be interesting for various kinds of applications.
|
||||
|
||||
:copyright: (c) 2017 by the Jinja Team, see AUTHORS for more details.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
from jinja2 import nodes
|
||||
from jinja2.compiler import CodeGenerator
|
||||
from jinja2._compat import string_types, iteritems
|
||||
from . import nodes
|
||||
from ._compat import iteritems
|
||||
from ._compat import string_types
|
||||
from .compiler import CodeGenerator
|
||||
|
||||
|
||||
class TrackingCodeGenerator(CodeGenerator):
|
||||
"""We abuse the code generator for introspection."""
|
||||
|
||||
def __init__(self, environment):
|
||||
CodeGenerator.__init__(self, environment, '<introspection>',
|
||||
'<introspection>')
|
||||
CodeGenerator.__init__(self, environment, "<introspection>", "<introspection>")
|
||||
self.undeclared_identifiers = set()
|
||||
|
||||
def write(self, x):
|
||||
@@ -29,7 +22,7 @@ def enter_frame(self, frame):
|
||||
"""Remember all undeclared identifiers."""
|
||||
CodeGenerator.enter_frame(self, frame)
|
||||
for _, (action, param) in iteritems(frame.symbols.loads):
|
||||
if action == 'resolve':
|
||||
if action == "resolve" and param not in self.environment.globals:
|
||||
self.undeclared_identifiers.add(param)
|
||||
|
||||
|
||||
@@ -72,8 +65,9 @@ def find_referenced_templates(ast):
|
||||
This function is useful for dependency tracking. For example if you want
|
||||
to rebuild parts of the website after a layout template has changed.
|
||||
"""
|
||||
for node in ast.find_all((nodes.Extends, nodes.FromImport, nodes.Import,
|
||||
nodes.Include)):
|
||||
for node in ast.find_all(
|
||||
(nodes.Extends, nodes.FromImport, nodes.Import, nodes.Include)
|
||||
):
|
||||
if not isinstance(node.template, nodes.Const):
|
||||
# a tuple with some non consts in there
|
||||
if isinstance(node.template, (nodes.Tuple, nodes.List)):
|
||||
@@ -96,8 +90,9 @@ def find_referenced_templates(ast):
|
||||
# a tuple or list (latter *should* not happen) made of consts,
|
||||
# yield the consts that are strings. We could warn here for
|
||||
# non string values
|
||||
elif isinstance(node, nodes.Include) and \
|
||||
isinstance(node.template.value, (tuple, list)):
|
||||
elif isinstance(node, nodes.Include) and isinstance(
|
||||
node.template.value, (tuple, list)
|
||||
):
|
||||
for template_name in node.template.value:
|
||||
if isinstance(template_name, string_types):
|
||||
yield template_name
|
||||
|
248
lib/spack/external/jinja2/nativetypes.py
vendored
248
lib/spack/external/jinja2/nativetypes.py
vendored
@@ -1,19 +1,23 @@
|
||||
import sys
|
||||
from ast import literal_eval
|
||||
from itertools import islice, chain
|
||||
from jinja2 import nodes
|
||||
from jinja2._compat import text_type
|
||||
from jinja2.compiler import CodeGenerator, has_safe_repr
|
||||
from jinja2.environment import Environment, Template
|
||||
from jinja2.utils import concat, escape
|
||||
from itertools import chain
|
||||
from itertools import islice
|
||||
|
||||
from . import nodes
|
||||
from ._compat import text_type
|
||||
from .compiler import CodeGenerator
|
||||
from .compiler import has_safe_repr
|
||||
from .environment import Environment
|
||||
from .environment import Template
|
||||
|
||||
|
||||
def native_concat(nodes):
|
||||
"""Return a native Python type from the list of compiled nodes. If the
|
||||
result is a single node, its value is returned. Otherwise, the nodes are
|
||||
concatenated as strings. If the result can be parsed with
|
||||
:func:`ast.literal_eval`, the parsed value is returned. Otherwise, the
|
||||
string is returned.
|
||||
"""Return a native Python type from the list of compiled nodes. If
|
||||
the result is a single node, its value is returned. Otherwise, the
|
||||
nodes are concatenated as strings. If the result can be parsed with
|
||||
:func:`ast.literal_eval`, the parsed value is returned. Otherwise,
|
||||
the string is returned.
|
||||
|
||||
:param nodes: Iterable of nodes to concatenate.
|
||||
"""
|
||||
head = list(islice(nodes, 2))
|
||||
|
||||
@@ -21,200 +25,70 @@ def native_concat(nodes):
|
||||
return None
|
||||
|
||||
if len(head) == 1:
|
||||
out = head[0]
|
||||
raw = head[0]
|
||||
else:
|
||||
out = u''.join([text_type(v) for v in chain(head, nodes)])
|
||||
raw = u"".join([text_type(v) for v in chain(head, nodes)])
|
||||
|
||||
try:
|
||||
return literal_eval(out)
|
||||
return literal_eval(raw)
|
||||
except (ValueError, SyntaxError, MemoryError):
|
||||
return out
|
||||
return raw
|
||||
|
||||
|
||||
class NativeCodeGenerator(CodeGenerator):
|
||||
"""A code generator which avoids injecting ``to_string()`` calls around the
|
||||
internal code Jinja uses to render templates.
|
||||
"""A code generator which renders Python types by not adding
|
||||
``to_string()`` around output nodes.
|
||||
"""
|
||||
|
||||
def visit_Output(self, node, frame):
|
||||
"""Same as :meth:`CodeGenerator.visit_Output`, but do not call
|
||||
``to_string`` on output nodes in generated code.
|
||||
"""
|
||||
if self.has_known_extends and frame.require_output_check:
|
||||
return
|
||||
@staticmethod
|
||||
def _default_finalize(value):
|
||||
return value
|
||||
|
||||
finalize = self.environment.finalize
|
||||
finalize_context = getattr(finalize, 'contextfunction', False)
|
||||
finalize_eval = getattr(finalize, 'evalcontextfunction', False)
|
||||
finalize_env = getattr(finalize, 'environmentfunction', False)
|
||||
def _output_const_repr(self, group):
|
||||
return repr(u"".join([text_type(v) for v in group]))
|
||||
|
||||
if finalize is not None:
|
||||
if finalize_context or finalize_eval:
|
||||
const_finalize = None
|
||||
elif finalize_env:
|
||||
def const_finalize(x):
|
||||
return finalize(self.environment, x)
|
||||
else:
|
||||
const_finalize = finalize
|
||||
else:
|
||||
def const_finalize(x):
|
||||
return x
|
||||
def _output_child_to_const(self, node, frame, finalize):
|
||||
const = node.as_const(frame.eval_ctx)
|
||||
|
||||
# If we are inside a frame that requires output checking, we do so.
|
||||
outdent_later = False
|
||||
if not has_safe_repr(const):
|
||||
raise nodes.Impossible()
|
||||
|
||||
if frame.require_output_check:
|
||||
self.writeline('if parent_template is None:')
|
||||
self.indent()
|
||||
outdent_later = True
|
||||
if isinstance(node, nodes.TemplateData):
|
||||
return const
|
||||
|
||||
# Try to evaluate as many chunks as possible into a static string at
|
||||
# compile time.
|
||||
body = []
|
||||
return finalize.const(const)
|
||||
|
||||
for child in node.nodes:
|
||||
try:
|
||||
if const_finalize is None:
|
||||
raise nodes.Impossible()
|
||||
def _output_child_pre(self, node, frame, finalize):
|
||||
if finalize.src is not None:
|
||||
self.write(finalize.src)
|
||||
|
||||
const = child.as_const(frame.eval_ctx)
|
||||
if not has_safe_repr(const):
|
||||
raise nodes.Impossible()
|
||||
except nodes.Impossible:
|
||||
body.append(child)
|
||||
continue
|
||||
|
||||
# the frame can't be volatile here, because otherwise the as_const
|
||||
# function would raise an Impossible exception at that point
|
||||
try:
|
||||
if frame.eval_ctx.autoescape:
|
||||
if hasattr(const, '__html__'):
|
||||
const = const.__html__()
|
||||
else:
|
||||
const = escape(const)
|
||||
|
||||
const = const_finalize(const)
|
||||
except Exception:
|
||||
# if something goes wrong here we evaluate the node at runtime
|
||||
# for easier debugging
|
||||
body.append(child)
|
||||
continue
|
||||
|
||||
if body and isinstance(body[-1], list):
|
||||
body[-1].append(const)
|
||||
else:
|
||||
body.append([const])
|
||||
|
||||
# if we have less than 3 nodes or a buffer we yield or extend/append
|
||||
if len(body) < 3 or frame.buffer is not None:
|
||||
if frame.buffer is not None:
|
||||
# for one item we append, for more we extend
|
||||
if len(body) == 1:
|
||||
self.writeline('%s.append(' % frame.buffer)
|
||||
else:
|
||||
self.writeline('%s.extend((' % frame.buffer)
|
||||
|
||||
self.indent()
|
||||
|
||||
for item in body:
|
||||
if isinstance(item, list):
|
||||
val = repr(native_concat(item))
|
||||
|
||||
if frame.buffer is None:
|
||||
self.writeline('yield ' + val)
|
||||
else:
|
||||
self.writeline(val + ',')
|
||||
else:
|
||||
if frame.buffer is None:
|
||||
self.writeline('yield ', item)
|
||||
else:
|
||||
self.newline(item)
|
||||
|
||||
close = 0
|
||||
|
||||
if finalize is not None:
|
||||
self.write('environment.finalize(')
|
||||
|
||||
if finalize_context:
|
||||
self.write('context, ')
|
||||
|
||||
close += 1
|
||||
|
||||
self.visit(item, frame)
|
||||
|
||||
if close > 0:
|
||||
self.write(')' * close)
|
||||
|
||||
if frame.buffer is not None:
|
||||
self.write(',')
|
||||
|
||||
if frame.buffer is not None:
|
||||
# close the open parentheses
|
||||
self.outdent()
|
||||
self.writeline(len(body) == 1 and ')' or '))')
|
||||
|
||||
# otherwise we create a format string as this is faster in that case
|
||||
else:
|
||||
format = []
|
||||
arguments = []
|
||||
|
||||
for item in body:
|
||||
if isinstance(item, list):
|
||||
format.append(native_concat(item).replace('%', '%%'))
|
||||
else:
|
||||
format.append('%s')
|
||||
arguments.append(item)
|
||||
|
||||
self.writeline('yield ')
|
||||
self.write(repr(concat(format)) + ' % (')
|
||||
self.indent()
|
||||
|
||||
for argument in arguments:
|
||||
self.newline(argument)
|
||||
close = 0
|
||||
|
||||
if finalize is not None:
|
||||
self.write('environment.finalize(')
|
||||
|
||||
if finalize_context:
|
||||
self.write('context, ')
|
||||
elif finalize_eval:
|
||||
self.write('context.eval_ctx, ')
|
||||
elif finalize_env:
|
||||
self.write('environment, ')
|
||||
|
||||
close += 1
|
||||
|
||||
self.visit(argument, frame)
|
||||
self.write(')' * close + ', ')
|
||||
|
||||
self.outdent()
|
||||
self.writeline(')')
|
||||
|
||||
if outdent_later:
|
||||
self.outdent()
|
||||
|
||||
|
||||
class NativeTemplate(Template):
|
||||
def render(self, *args, **kwargs):
|
||||
"""Render the template to produce a native Python type. If the result
|
||||
is a single node, its value is returned. Otherwise, the nodes are
|
||||
concatenated as strings. If the result can be parsed with
|
||||
:func:`ast.literal_eval`, the parsed value is returned. Otherwise, the
|
||||
string is returned.
|
||||
"""
|
||||
vars = dict(*args, **kwargs)
|
||||
|
||||
try:
|
||||
return native_concat(self.root_render_func(self.new_context(vars)))
|
||||
except Exception:
|
||||
exc_info = sys.exc_info()
|
||||
|
||||
return self.environment.handle_exception(exc_info, True)
|
||||
def _output_child_post(self, node, frame, finalize):
|
||||
if finalize.src is not None:
|
||||
self.write(")")
|
||||
|
||||
|
||||
class NativeEnvironment(Environment):
|
||||
"""An environment that renders templates to native Python types."""
|
||||
|
||||
code_generator_class = NativeCodeGenerator
|
||||
template_class = NativeTemplate
|
||||
|
||||
|
||||
class NativeTemplate(Template):
|
||||
environment_class = NativeEnvironment
|
||||
|
||||
def render(self, *args, **kwargs):
|
||||
"""Render the template to produce a native Python type. If the
|
||||
result is a single node, its value is returned. Otherwise, the
|
||||
nodes are concatenated as strings. If the result can be parsed
|
||||
with :func:`ast.literal_eval`, the parsed value is returned.
|
||||
Otherwise, the string is returned.
|
||||
"""
|
||||
vars = dict(*args, **kwargs)
|
||||
|
||||
try:
|
||||
return native_concat(self.root_render_func(self.new_context(vars)))
|
||||
except Exception:
|
||||
return self.environment.handle_exception()
|
||||
|
||||
|
||||
NativeEnvironment.template_class = NativeTemplate
|
||||
|
427
lib/spack/external/jinja2/nodes.py
vendored
427
lib/spack/external/jinja2/nodes.py
vendored
@@ -1,54 +1,39 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""AST nodes generated by the parser for the compiler. Also provides
|
||||
some node tree helper functions used by the parser and compiler in order
|
||||
to normalize nodes.
|
||||
"""
|
||||
jinja2.nodes
|
||||
~~~~~~~~~~~~
|
||||
|
||||
This module implements additional nodes derived from the ast base node.
|
||||
|
||||
It also provides some node tree helper functions like `in_lineno` and
|
||||
`get_nodes` used by the parser and translator in order to normalize
|
||||
python and jinja nodes.
|
||||
|
||||
:copyright: (c) 2017 by the Jinja Team.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
import types
|
||||
import operator
|
||||
|
||||
from collections import deque
|
||||
from jinja2.utils import Markup
|
||||
from jinja2._compat import izip, with_metaclass, text_type, PY2
|
||||
|
||||
from markupsafe import Markup
|
||||
|
||||
#: the types we support for context functions
|
||||
_context_function_types = (types.FunctionType, types.MethodType)
|
||||
|
||||
from ._compat import izip
|
||||
from ._compat import PY2
|
||||
from ._compat import text_type
|
||||
from ._compat import with_metaclass
|
||||
|
||||
_binop_to_func = {
|
||||
'*': operator.mul,
|
||||
'/': operator.truediv,
|
||||
'//': operator.floordiv,
|
||||
'**': operator.pow,
|
||||
'%': operator.mod,
|
||||
'+': operator.add,
|
||||
'-': operator.sub
|
||||
"*": operator.mul,
|
||||
"/": operator.truediv,
|
||||
"//": operator.floordiv,
|
||||
"**": operator.pow,
|
||||
"%": operator.mod,
|
||||
"+": operator.add,
|
||||
"-": operator.sub,
|
||||
}
|
||||
|
||||
_uaop_to_func = {
|
||||
'not': operator.not_,
|
||||
'+': operator.pos,
|
||||
'-': operator.neg
|
||||
}
|
||||
_uaop_to_func = {"not": operator.not_, "+": operator.pos, "-": operator.neg}
|
||||
|
||||
_cmpop_to_func = {
|
||||
'eq': operator.eq,
|
||||
'ne': operator.ne,
|
||||
'gt': operator.gt,
|
||||
'gteq': operator.ge,
|
||||
'lt': operator.lt,
|
||||
'lteq': operator.le,
|
||||
'in': lambda a, b: a in b,
|
||||
'notin': lambda a, b: a not in b
|
||||
"eq": operator.eq,
|
||||
"ne": operator.ne,
|
||||
"gt": operator.gt,
|
||||
"gteq": operator.ge,
|
||||
"lt": operator.lt,
|
||||
"lteq": operator.le,
|
||||
"in": lambda a, b: a in b,
|
||||
"notin": lambda a, b: a not in b,
|
||||
}
|
||||
|
||||
|
||||
@@ -61,16 +46,16 @@ class NodeType(type):
|
||||
inheritance. fields and attributes from the parent class are
|
||||
automatically forwarded to the child."""
|
||||
|
||||
def __new__(cls, name, bases, d):
|
||||
for attr in 'fields', 'attributes':
|
||||
def __new__(mcs, name, bases, d):
|
||||
for attr in "fields", "attributes":
|
||||
storage = []
|
||||
storage.extend(getattr(bases[0], attr, ()))
|
||||
storage.extend(d.get(attr, ()))
|
||||
assert len(bases) == 1, 'multiple inheritance not allowed'
|
||||
assert len(storage) == len(set(storage)), 'layout conflict'
|
||||
assert len(bases) == 1, "multiple inheritance not allowed"
|
||||
assert len(storage) == len(set(storage)), "layout conflict"
|
||||
d[attr] = tuple(storage)
|
||||
d.setdefault('abstract', False)
|
||||
return type.__new__(cls, name, bases, d)
|
||||
d.setdefault("abstract", False)
|
||||
return type.__new__(mcs, name, bases, d)
|
||||
|
||||
|
||||
class EvalContext(object):
|
||||
@@ -97,15 +82,17 @@ def revert(self, old):
|
||||
def get_eval_context(node, ctx):
|
||||
if ctx is None:
|
||||
if node.environment is None:
|
||||
raise RuntimeError('if no eval context is passed, the '
|
||||
'node must have an attached '
|
||||
'environment.')
|
||||
raise RuntimeError(
|
||||
"if no eval context is passed, the "
|
||||
"node must have an attached "
|
||||
"environment."
|
||||
)
|
||||
return EvalContext(node.environment)
|
||||
return ctx
|
||||
|
||||
|
||||
class Node(with_metaclass(NodeType, object)):
|
||||
"""Baseclass for all Jinja2 nodes. There are a number of nodes available
|
||||
"""Baseclass for all Jinja nodes. There are a number of nodes available
|
||||
of different types. There are four major types:
|
||||
|
||||
- :class:`Stmt`: statements
|
||||
@@ -120,30 +107,32 @@ class Node(with_metaclass(NodeType, object)):
|
||||
The `environment` attribute is set at the end of the parsing process for
|
||||
all nodes automatically.
|
||||
"""
|
||||
|
||||
fields = ()
|
||||
attributes = ('lineno', 'environment')
|
||||
attributes = ("lineno", "environment")
|
||||
abstract = True
|
||||
|
||||
def __init__(self, *fields, **attributes):
|
||||
if self.abstract:
|
||||
raise TypeError('abstract nodes are not instanciable')
|
||||
raise TypeError("abstract nodes are not instantiable")
|
||||
if fields:
|
||||
if len(fields) != len(self.fields):
|
||||
if not self.fields:
|
||||
raise TypeError('%r takes 0 arguments' %
|
||||
self.__class__.__name__)
|
||||
raise TypeError('%r takes 0 or %d argument%s' % (
|
||||
self.__class__.__name__,
|
||||
len(self.fields),
|
||||
len(self.fields) != 1 and 's' or ''
|
||||
))
|
||||
raise TypeError("%r takes 0 arguments" % self.__class__.__name__)
|
||||
raise TypeError(
|
||||
"%r takes 0 or %d argument%s"
|
||||
% (
|
||||
self.__class__.__name__,
|
||||
len(self.fields),
|
||||
len(self.fields) != 1 and "s" or "",
|
||||
)
|
||||
)
|
||||
for name, arg in izip(self.fields, fields):
|
||||
setattr(self, name, arg)
|
||||
for attr in self.attributes:
|
||||
setattr(self, attr, attributes.pop(attr, None))
|
||||
if attributes:
|
||||
raise TypeError('unknown attribute %r' %
|
||||
next(iter(attributes)))
|
||||
raise TypeError("unknown attribute %r" % next(iter(attributes)))
|
||||
|
||||
def iter_fields(self, exclude=None, only=None):
|
||||
"""This method iterates over all fields that are defined and yields
|
||||
@@ -153,9 +142,11 @@ def iter_fields(self, exclude=None, only=None):
|
||||
should be sets or tuples of field names.
|
||||
"""
|
||||
for name in self.fields:
|
||||
if (exclude is only is None) or \
|
||||
(exclude is not None and name not in exclude) or \
|
||||
(only is not None and name in only):
|
||||
if (
|
||||
(exclude is only is None)
|
||||
or (exclude is not None and name not in exclude)
|
||||
or (only is not None and name in only)
|
||||
):
|
||||
try:
|
||||
yield name, getattr(self, name)
|
||||
except AttributeError:
|
||||
@@ -166,7 +157,7 @@ def iter_child_nodes(self, exclude=None, only=None):
|
||||
over all fields and yields the values of they are nodes. If the value
|
||||
of a field is a list all the nodes in that list are returned.
|
||||
"""
|
||||
for field, item in self.iter_fields(exclude, only):
|
||||
for _, item in self.iter_fields(exclude, only):
|
||||
if isinstance(item, list):
|
||||
for n in item:
|
||||
if isinstance(n, Node):
|
||||
@@ -200,7 +191,7 @@ def set_ctx(self, ctx):
|
||||
todo = deque([self])
|
||||
while todo:
|
||||
node = todo.popleft()
|
||||
if 'ctx' in node.fields:
|
||||
if "ctx" in node.fields:
|
||||
node.ctx = ctx
|
||||
todo.extend(node.iter_child_nodes())
|
||||
return self
|
||||
@@ -210,7 +201,7 @@ def set_lineno(self, lineno, override=False):
|
||||
todo = deque([self])
|
||||
while todo:
|
||||
node = todo.popleft()
|
||||
if 'lineno' in node.attributes:
|
||||
if "lineno" in node.attributes:
|
||||
if node.lineno is None or override:
|
||||
node.lineno = lineno
|
||||
todo.extend(node.iter_child_nodes())
|
||||
@@ -226,8 +217,9 @@ def set_environment(self, environment):
|
||||
return self
|
||||
|
||||
def __eq__(self, other):
|
||||
return type(self) is type(other) and \
|
||||
tuple(self.iter_fields()) == tuple(other.iter_fields())
|
||||
return type(self) is type(other) and tuple(self.iter_fields()) == tuple(
|
||||
other.iter_fields()
|
||||
)
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self.__eq__(other)
|
||||
@@ -236,10 +228,9 @@ def __ne__(self, other):
|
||||
__hash__ = object.__hash__
|
||||
|
||||
def __repr__(self):
|
||||
return '%s(%s)' % (
|
||||
return "%s(%s)" % (
|
||||
self.__class__.__name__,
|
||||
', '.join('%s=%r' % (arg, getattr(self, arg, None)) for
|
||||
arg in self.fields)
|
||||
", ".join("%s=%r" % (arg, getattr(self, arg, None)) for arg in self.fields),
|
||||
)
|
||||
|
||||
def dump(self):
|
||||
@@ -248,37 +239,39 @@ def _dump(node):
|
||||
buf.append(repr(node))
|
||||
return
|
||||
|
||||
buf.append('nodes.%s(' % node.__class__.__name__)
|
||||
buf.append("nodes.%s(" % node.__class__.__name__)
|
||||
if not node.fields:
|
||||
buf.append(')')
|
||||
buf.append(")")
|
||||
return
|
||||
for idx, field in enumerate(node.fields):
|
||||
if idx:
|
||||
buf.append(', ')
|
||||
buf.append(", ")
|
||||
value = getattr(node, field)
|
||||
if isinstance(value, list):
|
||||
buf.append('[')
|
||||
buf.append("[")
|
||||
for idx, item in enumerate(value):
|
||||
if idx:
|
||||
buf.append(', ')
|
||||
buf.append(", ")
|
||||
_dump(item)
|
||||
buf.append(']')
|
||||
buf.append("]")
|
||||
else:
|
||||
_dump(value)
|
||||
buf.append(')')
|
||||
buf.append(")")
|
||||
|
||||
buf = []
|
||||
_dump(self)
|
||||
return ''.join(buf)
|
||||
|
||||
return "".join(buf)
|
||||
|
||||
|
||||
class Stmt(Node):
|
||||
"""Base node for all statements."""
|
||||
|
||||
abstract = True
|
||||
|
||||
|
||||
class Helper(Node):
|
||||
"""Nodes that exist in a specific context only."""
|
||||
|
||||
abstract = True
|
||||
|
||||
|
||||
@@ -286,19 +279,22 @@ class Template(Node):
|
||||
"""Node that represents a template. This must be the outermost node that
|
||||
is passed to the compiler.
|
||||
"""
|
||||
fields = ('body',)
|
||||
|
||||
fields = ("body",)
|
||||
|
||||
|
||||
class Output(Stmt):
|
||||
"""A node that holds multiple expressions which are then printed out.
|
||||
This is used both for the `print` statement and the regular template data.
|
||||
"""
|
||||
fields = ('nodes',)
|
||||
|
||||
fields = ("nodes",)
|
||||
|
||||
|
||||
class Extends(Stmt):
|
||||
"""Represents an extends statement."""
|
||||
fields = ('template',)
|
||||
|
||||
fields = ("template",)
|
||||
|
||||
|
||||
class For(Stmt):
|
||||
@@ -309,12 +305,14 @@ class For(Stmt):
|
||||
|
||||
For filtered nodes an expression can be stored as `test`, otherwise `None`.
|
||||
"""
|
||||
fields = ('target', 'iter', 'body', 'else_', 'test', 'recursive')
|
||||
|
||||
fields = ("target", "iter", "body", "else_", "test", "recursive")
|
||||
|
||||
|
||||
class If(Stmt):
|
||||
"""If `test` is true, `body` is rendered, else `else_`."""
|
||||
fields = ('test', 'body', 'elif_', 'else_')
|
||||
|
||||
fields = ("test", "body", "elif_", "else_")
|
||||
|
||||
|
||||
class Macro(Stmt):
|
||||
@@ -322,19 +320,22 @@ class Macro(Stmt):
|
||||
arguments and `defaults` a list of defaults if there are any. `body` is
|
||||
a list of nodes for the macro body.
|
||||
"""
|
||||
fields = ('name', 'args', 'defaults', 'body')
|
||||
|
||||
fields = ("name", "args", "defaults", "body")
|
||||
|
||||
|
||||
class CallBlock(Stmt):
|
||||
"""Like a macro without a name but a call instead. `call` is called with
|
||||
the unnamed macro as `caller` argument this node holds.
|
||||
"""
|
||||
fields = ('call', 'args', 'defaults', 'body')
|
||||
|
||||
fields = ("call", "args", "defaults", "body")
|
||||
|
||||
|
||||
class FilterBlock(Stmt):
|
||||
"""Node for filter sections."""
|
||||
fields = ('body', 'filter')
|
||||
|
||||
fields = ("body", "filter")
|
||||
|
||||
|
||||
class With(Stmt):
|
||||
@@ -343,22 +344,26 @@ class With(Stmt):
|
||||
|
||||
.. versionadded:: 2.9.3
|
||||
"""
|
||||
fields = ('targets', 'values', 'body')
|
||||
|
||||
fields = ("targets", "values", "body")
|
||||
|
||||
|
||||
class Block(Stmt):
|
||||
"""A node that represents a block."""
|
||||
fields = ('name', 'body', 'scoped')
|
||||
|
||||
fields = ("name", "body", "scoped")
|
||||
|
||||
|
||||
class Include(Stmt):
|
||||
"""A node that represents the include tag."""
|
||||
fields = ('template', 'with_context', 'ignore_missing')
|
||||
|
||||
fields = ("template", "with_context", "ignore_missing")
|
||||
|
||||
|
||||
class Import(Stmt):
|
||||
"""A node that represents the import tag."""
|
||||
fields = ('template', 'target', 'with_context')
|
||||
|
||||
fields = ("template", "target", "with_context")
|
||||
|
||||
|
||||
class FromImport(Stmt):
|
||||
@@ -372,26 +377,31 @@ class FromImport(Stmt):
|
||||
|
||||
The list of names may contain tuples if aliases are wanted.
|
||||
"""
|
||||
fields = ('template', 'names', 'with_context')
|
||||
|
||||
fields = ("template", "names", "with_context")
|
||||
|
||||
|
||||
class ExprStmt(Stmt):
|
||||
"""A statement that evaluates an expression and discards the result."""
|
||||
fields = ('node',)
|
||||
|
||||
fields = ("node",)
|
||||
|
||||
|
||||
class Assign(Stmt):
|
||||
"""Assigns an expression to a target."""
|
||||
fields = ('target', 'node')
|
||||
|
||||
fields = ("target", "node")
|
||||
|
||||
|
||||
class AssignBlock(Stmt):
|
||||
"""Assigns a block to a target."""
|
||||
fields = ('target', 'filter', 'body')
|
||||
|
||||
fields = ("target", "filter", "body")
|
||||
|
||||
|
||||
class Expr(Node):
|
||||
"""Baseclass for all expressions."""
|
||||
|
||||
abstract = True
|
||||
|
||||
def as_const(self, eval_ctx=None):
|
||||
@@ -414,15 +424,18 @@ def can_assign(self):
|
||||
|
||||
class BinExpr(Expr):
|
||||
"""Baseclass for all binary expressions."""
|
||||
fields = ('left', 'right')
|
||||
|
||||
fields = ("left", "right")
|
||||
operator = None
|
||||
abstract = True
|
||||
|
||||
def as_const(self, eval_ctx=None):
|
||||
eval_ctx = get_eval_context(self, eval_ctx)
|
||||
# intercepted operators cannot be folded at compile time
|
||||
if self.environment.sandboxed and \
|
||||
self.operator in self.environment.intercepted_binops:
|
||||
if (
|
||||
self.environment.sandboxed
|
||||
and self.operator in self.environment.intercepted_binops
|
||||
):
|
||||
raise Impossible()
|
||||
f = _binop_to_func[self.operator]
|
||||
try:
|
||||
@@ -433,15 +446,18 @@ def as_const(self, eval_ctx=None):
|
||||
|
||||
class UnaryExpr(Expr):
|
||||
"""Baseclass for all unary expressions."""
|
||||
fields = ('node',)
|
||||
|
||||
fields = ("node",)
|
||||
operator = None
|
||||
abstract = True
|
||||
|
||||
def as_const(self, eval_ctx=None):
|
||||
eval_ctx = get_eval_context(self, eval_ctx)
|
||||
# intercepted operators cannot be folded at compile time
|
||||
if self.environment.sandboxed and \
|
||||
self.operator in self.environment.intercepted_unops:
|
||||
if (
|
||||
self.environment.sandboxed
|
||||
and self.operator in self.environment.intercepted_unops
|
||||
):
|
||||
raise Impossible()
|
||||
f = _uaop_to_func[self.operator]
|
||||
try:
|
||||
@@ -458,16 +474,17 @@ class Name(Expr):
|
||||
- `load`: load that name
|
||||
- `param`: like `store` but if the name was defined as function parameter.
|
||||
"""
|
||||
fields = ('name', 'ctx')
|
||||
|
||||
fields = ("name", "ctx")
|
||||
|
||||
def can_assign(self):
|
||||
return self.name not in ('true', 'false', 'none',
|
||||
'True', 'False', 'None')
|
||||
return self.name not in ("true", "false", "none", "True", "False", "None")
|
||||
|
||||
|
||||
class NSRef(Expr):
|
||||
"""Reference to a namespace value assignment"""
|
||||
fields = ('name', 'attr')
|
||||
|
||||
fields = ("name", "attr")
|
||||
|
||||
def can_assign(self):
|
||||
# We don't need any special checks here; NSRef assignments have a
|
||||
@@ -479,6 +496,7 @@ def can_assign(self):
|
||||
|
||||
class Literal(Expr):
|
||||
"""Baseclass for literals."""
|
||||
|
||||
abstract = True
|
||||
|
||||
|
||||
@@ -488,14 +506,18 @@ class Const(Literal):
|
||||
complex values such as lists too. Only constants with a safe
|
||||
representation (objects where ``eval(repr(x)) == x`` is true).
|
||||
"""
|
||||
fields = ('value',)
|
||||
|
||||
fields = ("value",)
|
||||
|
||||
def as_const(self, eval_ctx=None):
|
||||
rv = self.value
|
||||
if PY2 and type(rv) is text_type and \
|
||||
self.environment.policies['compiler.ascii_str']:
|
||||
if (
|
||||
PY2
|
||||
and type(rv) is text_type
|
||||
and self.environment.policies["compiler.ascii_str"]
|
||||
):
|
||||
try:
|
||||
rv = rv.encode('ascii')
|
||||
rv = rv.encode("ascii")
|
||||
except UnicodeError:
|
||||
pass
|
||||
return rv
|
||||
@@ -507,6 +529,7 @@ def from_untrusted(cls, value, lineno=None, environment=None):
|
||||
an `Impossible` exception.
|
||||
"""
|
||||
from .compiler import has_safe_repr
|
||||
|
||||
if not has_safe_repr(value):
|
||||
raise Impossible()
|
||||
return cls(value, lineno=lineno, environment=environment)
|
||||
@@ -514,7 +537,8 @@ def from_untrusted(cls, value, lineno=None, environment=None):
|
||||
|
||||
class TemplateData(Literal):
|
||||
"""A constant template string."""
|
||||
fields = ('data',)
|
||||
|
||||
fields = ("data",)
|
||||
|
||||
def as_const(self, eval_ctx=None):
|
||||
eval_ctx = get_eval_context(self, eval_ctx)
|
||||
@@ -530,7 +554,8 @@ class Tuple(Literal):
|
||||
for subscripts. Like for :class:`Name` `ctx` specifies if the tuple
|
||||
is used for loading the names or storing.
|
||||
"""
|
||||
fields = ('items', 'ctx')
|
||||
|
||||
fields = ("items", "ctx")
|
||||
|
||||
def as_const(self, eval_ctx=None):
|
||||
eval_ctx = get_eval_context(self, eval_ctx)
|
||||
@@ -545,7 +570,8 @@ def can_assign(self):
|
||||
|
||||
class List(Literal):
|
||||
"""Any list literal such as ``[1, 2, 3]``"""
|
||||
fields = ('items',)
|
||||
|
||||
fields = ("items",)
|
||||
|
||||
def as_const(self, eval_ctx=None):
|
||||
eval_ctx = get_eval_context(self, eval_ctx)
|
||||
@@ -556,7 +582,8 @@ class Dict(Literal):
|
||||
"""Any dict literal such as ``{1: 2, 3: 4}``. The items must be a list of
|
||||
:class:`Pair` nodes.
|
||||
"""
|
||||
fields = ('items',)
|
||||
|
||||
fields = ("items",)
|
||||
|
||||
def as_const(self, eval_ctx=None):
|
||||
eval_ctx = get_eval_context(self, eval_ctx)
|
||||
@@ -565,7 +592,8 @@ def as_const(self, eval_ctx=None):
|
||||
|
||||
class Pair(Helper):
|
||||
"""A key, value pair for dicts."""
|
||||
fields = ('key', 'value')
|
||||
|
||||
fields = ("key", "value")
|
||||
|
||||
def as_const(self, eval_ctx=None):
|
||||
eval_ctx = get_eval_context(self, eval_ctx)
|
||||
@@ -574,7 +602,8 @@ def as_const(self, eval_ctx=None):
|
||||
|
||||
class Keyword(Helper):
|
||||
"""A key, value pair for keyword arguments where key is a string."""
|
||||
fields = ('key', 'value')
|
||||
|
||||
fields = ("key", "value")
|
||||
|
||||
def as_const(self, eval_ctx=None):
|
||||
eval_ctx = get_eval_context(self, eval_ctx)
|
||||
@@ -585,7 +614,8 @@ class CondExpr(Expr):
|
||||
"""A conditional expression (inline if expression). (``{{
|
||||
foo if bar else baz }}``)
|
||||
"""
|
||||
fields = ('test', 'expr1', 'expr2')
|
||||
|
||||
fields = ("test", "expr1", "expr2")
|
||||
|
||||
def as_const(self, eval_ctx=None):
|
||||
eval_ctx = get_eval_context(self, eval_ctx)
|
||||
@@ -626,7 +656,7 @@ class Filter(Expr):
|
||||
filtered. Buffers are created by macros and filter blocks.
|
||||
"""
|
||||
|
||||
fields = ('node', 'name', 'args', 'kwargs', 'dyn_args', 'dyn_kwargs')
|
||||
fields = ("node", "name", "args", "kwargs", "dyn_args", "dyn_kwargs")
|
||||
|
||||
def as_const(self, eval_ctx=None):
|
||||
eval_ctx = get_eval_context(self, eval_ctx)
|
||||
@@ -636,28 +666,27 @@ def as_const(self, eval_ctx=None):
|
||||
|
||||
# we have to be careful here because we call filter_ below.
|
||||
# if this variable would be called filter, 2to3 would wrap the
|
||||
# call in a list beause it is assuming we are talking about the
|
||||
# call in a list because it is assuming we are talking about the
|
||||
# builtin filter function here which no longer returns a list in
|
||||
# python 3. because of that, do not rename filter_ to filter!
|
||||
filter_ = self.environment.filters.get(self.name)
|
||||
|
||||
if filter_ is None or getattr(filter_, 'contextfilter', False):
|
||||
if filter_ is None or getattr(filter_, "contextfilter", False) is True:
|
||||
raise Impossible()
|
||||
|
||||
# We cannot constant handle async filters, so we need to make sure
|
||||
# to not go down this path.
|
||||
if (
|
||||
eval_ctx.environment.is_async
|
||||
and getattr(filter_, 'asyncfiltervariant', False)
|
||||
if eval_ctx.environment.is_async and getattr(
|
||||
filter_, "asyncfiltervariant", False
|
||||
):
|
||||
raise Impossible()
|
||||
|
||||
args, kwargs = args_as_const(self, eval_ctx)
|
||||
args.insert(0, self.node.as_const(eval_ctx))
|
||||
|
||||
if getattr(filter_, 'evalcontextfilter', False):
|
||||
if getattr(filter_, "evalcontextfilter", False) is True:
|
||||
args.insert(0, eval_ctx)
|
||||
elif getattr(filter_, 'environmentfilter', False):
|
||||
elif getattr(filter_, "environmentfilter", False) is True:
|
||||
args.insert(0, self.environment)
|
||||
|
||||
try:
|
||||
@@ -671,7 +700,7 @@ class Test(Expr):
|
||||
rest of the fields are the same as for :class:`Call`.
|
||||
"""
|
||||
|
||||
fields = ('node', 'name', 'args', 'kwargs', 'dyn_args', 'dyn_kwargs')
|
||||
fields = ("node", "name", "args", "kwargs", "dyn_args", "dyn_kwargs")
|
||||
|
||||
def as_const(self, eval_ctx=None):
|
||||
test = self.environment.tests.get(self.name)
|
||||
@@ -696,20 +725,23 @@ class Call(Expr):
|
||||
node for dynamic positional (``*args``) or keyword (``**kwargs``)
|
||||
arguments.
|
||||
"""
|
||||
fields = ('node', 'args', 'kwargs', 'dyn_args', 'dyn_kwargs')
|
||||
|
||||
fields = ("node", "args", "kwargs", "dyn_args", "dyn_kwargs")
|
||||
|
||||
|
||||
class Getitem(Expr):
|
||||
"""Get an attribute or item from an expression and prefer the item."""
|
||||
fields = ('node', 'arg', 'ctx')
|
||||
|
||||
fields = ("node", "arg", "ctx")
|
||||
|
||||
def as_const(self, eval_ctx=None):
|
||||
eval_ctx = get_eval_context(self, eval_ctx)
|
||||
if self.ctx != 'load':
|
||||
if self.ctx != "load":
|
||||
raise Impossible()
|
||||
try:
|
||||
return self.environment.getitem(self.node.as_const(eval_ctx),
|
||||
self.arg.as_const(eval_ctx))
|
||||
return self.environment.getitem(
|
||||
self.node.as_const(eval_ctx), self.arg.as_const(eval_ctx)
|
||||
)
|
||||
except Exception:
|
||||
raise Impossible()
|
||||
|
||||
@@ -721,15 +753,15 @@ class Getattr(Expr):
|
||||
"""Get an attribute or item from an expression that is a ascii-only
|
||||
bytestring and prefer the attribute.
|
||||
"""
|
||||
fields = ('node', 'attr', 'ctx')
|
||||
|
||||
fields = ("node", "attr", "ctx")
|
||||
|
||||
def as_const(self, eval_ctx=None):
|
||||
if self.ctx != 'load':
|
||||
if self.ctx != "load":
|
||||
raise Impossible()
|
||||
try:
|
||||
eval_ctx = get_eval_context(self, eval_ctx)
|
||||
return self.environment.getattr(self.node.as_const(eval_ctx),
|
||||
self.attr)
|
||||
return self.environment.getattr(self.node.as_const(eval_ctx), self.attr)
|
||||
except Exception:
|
||||
raise Impossible()
|
||||
|
||||
@@ -741,14 +773,17 @@ class Slice(Expr):
|
||||
"""Represents a slice object. This must only be used as argument for
|
||||
:class:`Subscript`.
|
||||
"""
|
||||
fields = ('start', 'stop', 'step')
|
||||
|
||||
fields = ("start", "stop", "step")
|
||||
|
||||
def as_const(self, eval_ctx=None):
|
||||
eval_ctx = get_eval_context(self, eval_ctx)
|
||||
|
||||
def const(obj):
|
||||
if obj is None:
|
||||
return None
|
||||
return obj.as_const(eval_ctx)
|
||||
|
||||
return slice(const(self.start), const(self.stop), const(self.step))
|
||||
|
||||
|
||||
@@ -756,82 +791,103 @@ class Concat(Expr):
|
||||
"""Concatenates the list of expressions provided after converting them to
|
||||
unicode.
|
||||
"""
|
||||
fields = ('nodes',)
|
||||
|
||||
fields = ("nodes",)
|
||||
|
||||
def as_const(self, eval_ctx=None):
|
||||
eval_ctx = get_eval_context(self, eval_ctx)
|
||||
return ''.join(text_type(x.as_const(eval_ctx)) for x in self.nodes)
|
||||
return "".join(text_type(x.as_const(eval_ctx)) for x in self.nodes)
|
||||
|
||||
|
||||
class Compare(Expr):
|
||||
"""Compares an expression with some other expressions. `ops` must be a
|
||||
list of :class:`Operand`\\s.
|
||||
"""
|
||||
fields = ('expr', 'ops')
|
||||
|
||||
fields = ("expr", "ops")
|
||||
|
||||
def as_const(self, eval_ctx=None):
|
||||
eval_ctx = get_eval_context(self, eval_ctx)
|
||||
result = value = self.expr.as_const(eval_ctx)
|
||||
|
||||
try:
|
||||
for op in self.ops:
|
||||
new_value = op.expr.as_const(eval_ctx)
|
||||
result = _cmpop_to_func[op.op](value, new_value)
|
||||
|
||||
if not result:
|
||||
return False
|
||||
|
||||
value = new_value
|
||||
except Exception:
|
||||
raise Impossible()
|
||||
|
||||
return result
|
||||
|
||||
|
||||
class Operand(Helper):
|
||||
"""Holds an operator and an expression."""
|
||||
fields = ('op', 'expr')
|
||||
|
||||
fields = ("op", "expr")
|
||||
|
||||
|
||||
if __debug__:
|
||||
Operand.__doc__ += '\nThe following operators are available: ' + \
|
||||
', '.join(sorted('``%s``' % x for x in set(_binop_to_func) |
|
||||
set(_uaop_to_func) | set(_cmpop_to_func)))
|
||||
Operand.__doc__ += "\nThe following operators are available: " + ", ".join(
|
||||
sorted(
|
||||
"``%s``" % x
|
||||
for x in set(_binop_to_func) | set(_uaop_to_func) | set(_cmpop_to_func)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class Mul(BinExpr):
|
||||
"""Multiplies the left with the right node."""
|
||||
operator = '*'
|
||||
|
||||
operator = "*"
|
||||
|
||||
|
||||
class Div(BinExpr):
|
||||
"""Divides the left by the right node."""
|
||||
operator = '/'
|
||||
|
||||
operator = "/"
|
||||
|
||||
|
||||
class FloorDiv(BinExpr):
|
||||
"""Divides the left by the right node and truncates conver the
|
||||
result into an integer by truncating.
|
||||
"""
|
||||
operator = '//'
|
||||
|
||||
operator = "//"
|
||||
|
||||
|
||||
class Add(BinExpr):
|
||||
"""Add the left to the right node."""
|
||||
operator = '+'
|
||||
|
||||
operator = "+"
|
||||
|
||||
|
||||
class Sub(BinExpr):
|
||||
"""Subtract the right from the left node."""
|
||||
operator = '-'
|
||||
|
||||
operator = "-"
|
||||
|
||||
|
||||
class Mod(BinExpr):
|
||||
"""Left modulo right."""
|
||||
operator = '%'
|
||||
|
||||
operator = "%"
|
||||
|
||||
|
||||
class Pow(BinExpr):
|
||||
"""Left to the power of right."""
|
||||
operator = '**'
|
||||
|
||||
operator = "**"
|
||||
|
||||
|
||||
class And(BinExpr):
|
||||
"""Short circuited AND."""
|
||||
operator = 'and'
|
||||
|
||||
operator = "and"
|
||||
|
||||
def as_const(self, eval_ctx=None):
|
||||
eval_ctx = get_eval_context(self, eval_ctx)
|
||||
@@ -840,7 +896,8 @@ def as_const(self, eval_ctx=None):
|
||||
|
||||
class Or(BinExpr):
|
||||
"""Short circuited OR."""
|
||||
operator = 'or'
|
||||
|
||||
operator = "or"
|
||||
|
||||
def as_const(self, eval_ctx=None):
|
||||
eval_ctx = get_eval_context(self, eval_ctx)
|
||||
@@ -849,17 +906,20 @@ def as_const(self, eval_ctx=None):
|
||||
|
||||
class Not(UnaryExpr):
|
||||
"""Negate the expression."""
|
||||
operator = 'not'
|
||||
|
||||
operator = "not"
|
||||
|
||||
|
||||
class Neg(UnaryExpr):
|
||||
"""Make the expression negative."""
|
||||
operator = '-'
|
||||
|
||||
operator = "-"
|
||||
|
||||
|
||||
class Pos(UnaryExpr):
|
||||
"""Make the expression positive (noop for most expressions)"""
|
||||
operator = '+'
|
||||
|
||||
operator = "+"
|
||||
|
||||
|
||||
# Helpers for extensions
|
||||
@@ -869,7 +929,8 @@ class EnvironmentAttribute(Expr):
|
||||
"""Loads an attribute from the environment object. This is useful for
|
||||
extensions that want to call a callback stored on the environment.
|
||||
"""
|
||||
fields = ('name',)
|
||||
|
||||
fields = ("name",)
|
||||
|
||||
|
||||
class ExtensionAttribute(Expr):
|
||||
@@ -879,7 +940,8 @@ class ExtensionAttribute(Expr):
|
||||
This node is usually constructed by calling the
|
||||
:meth:`~jinja2.ext.Extension.attr` method on an extension.
|
||||
"""
|
||||
fields = ('identifier', 'name')
|
||||
|
||||
fields = ("identifier", "name")
|
||||
|
||||
|
||||
class ImportedName(Expr):
|
||||
@@ -888,7 +950,8 @@ class ImportedName(Expr):
|
||||
function from the cgi module on evaluation. Imports are optimized by the
|
||||
compiler so there is no need to assign them to local variables.
|
||||
"""
|
||||
fields = ('importname',)
|
||||
|
||||
fields = ("importname",)
|
||||
|
||||
|
||||
class InternalName(Expr):
|
||||
@@ -898,16 +961,20 @@ class InternalName(Expr):
|
||||
a new identifier for you. This identifier is not available from the
|
||||
template and is not threated specially by the compiler.
|
||||
"""
|
||||
fields = ('name',)
|
||||
|
||||
fields = ("name",)
|
||||
|
||||
def __init__(self):
|
||||
raise TypeError('Can\'t create internal names. Use the '
|
||||
'`free_identifier` method on a parser.')
|
||||
raise TypeError(
|
||||
"Can't create internal names. Use the "
|
||||
"`free_identifier` method on a parser."
|
||||
)
|
||||
|
||||
|
||||
class MarkSafe(Expr):
|
||||
"""Mark the wrapped expression as safe (wrap it as `Markup`)."""
|
||||
fields = ('expr',)
|
||||
|
||||
fields = ("expr",)
|
||||
|
||||
def as_const(self, eval_ctx=None):
|
||||
eval_ctx = get_eval_context(self, eval_ctx)
|
||||
@@ -920,7 +987,8 @@ class MarkSafeIfAutoescape(Expr):
|
||||
|
||||
.. versionadded:: 2.5
|
||||
"""
|
||||
fields = ('expr',)
|
||||
|
||||
fields = ("expr",)
|
||||
|
||||
def as_const(self, eval_ctx=None):
|
||||
eval_ctx = get_eval_context(self, eval_ctx)
|
||||
@@ -942,6 +1010,20 @@ class ContextReference(Expr):
|
||||
|
||||
Assign(Name('foo', ctx='store'),
|
||||
Getattr(ContextReference(), 'name'))
|
||||
|
||||
This is basically equivalent to using the
|
||||
:func:`~jinja2.contextfunction` decorator when using the
|
||||
high-level API, which causes a reference to the context to be passed
|
||||
as the first argument to a function.
|
||||
"""
|
||||
|
||||
|
||||
class DerivedContextReference(Expr):
|
||||
"""Return the current template context including locals. Behaves
|
||||
exactly like :class:`ContextReference`, but includes local
|
||||
variables, such as from a ``for`` loop.
|
||||
|
||||
.. versionadded:: 2.11
|
||||
"""
|
||||
|
||||
|
||||
@@ -955,7 +1037,8 @@ class Break(Stmt):
|
||||
|
||||
class Scope(Stmt):
|
||||
"""An artificial scope."""
|
||||
fields = ('body',)
|
||||
|
||||
fields = ("body",)
|
||||
|
||||
|
||||
class OverlayScope(Stmt):
|
||||
@@ -971,7 +1054,8 @@ class OverlayScope(Stmt):
|
||||
|
||||
.. versionadded:: 2.10
|
||||
"""
|
||||
fields = ('context', 'body')
|
||||
|
||||
fields = ("context", "body")
|
||||
|
||||
|
||||
class EvalContextModifier(Stmt):
|
||||
@@ -982,7 +1066,8 @@ class EvalContextModifier(Stmt):
|
||||
|
||||
EvalContextModifier(options=[Keyword('autoescape', Const(True))])
|
||||
"""
|
||||
fields = ('options',)
|
||||
|
||||
fields = ("options",)
|
||||
|
||||
|
||||
class ScopedEvalContextModifier(EvalContextModifier):
|
||||
@@ -990,10 +1075,14 @@ class ScopedEvalContextModifier(EvalContextModifier):
|
||||
:class:`EvalContextModifier` but will only modify the
|
||||
:class:`~jinja2.nodes.EvalContext` for nodes in the :attr:`body`.
|
||||
"""
|
||||
fields = ('body',)
|
||||
|
||||
fields = ("body",)
|
||||
|
||||
|
||||
# make sure nobody creates custom nodes
|
||||
def _failing_new(*args, **kwargs):
|
||||
raise TypeError('can\'t create custom node types')
|
||||
NodeType.__new__ = staticmethod(_failing_new); del _failing_new
|
||||
raise TypeError("can't create custom node types")
|
||||
|
||||
|
||||
NodeType.__new__ = staticmethod(_failing_new)
|
||||
del _failing_new
|
||||
|
58
lib/spack/external/jinja2/optimizer.py
vendored
58
lib/spack/external/jinja2/optimizer.py
vendored
@@ -1,23 +1,15 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""The optimizer tries to constant fold expressions and modify the AST
|
||||
in place so that it should be faster to evaluate.
|
||||
|
||||
Because the AST does not contain all the scoping information and the
|
||||
compiler has to find that out, we cannot do all the optimizations we
|
||||
want. For example, loop unrolling doesn't work because unrolled loops
|
||||
would have a different scope. The solution would be a second syntax tree
|
||||
that stored the scoping rules.
|
||||
"""
|
||||
jinja2.optimizer
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
The jinja optimizer is currently trying to constant fold a few expressions
|
||||
and modify the AST in place so that it should be easier to evaluate it.
|
||||
|
||||
Because the AST does not contain all the scoping information and the
|
||||
compiler has to find that out, we cannot do all the optimizations we
|
||||
want. For example loop unrolling doesn't work because unrolled loops would
|
||||
have a different scoping.
|
||||
|
||||
The solution would be a second syntax tree that has the scoping rules stored.
|
||||
|
||||
:copyright: (c) 2017 by the Jinja Team.
|
||||
:license: BSD.
|
||||
"""
|
||||
from jinja2 import nodes
|
||||
from jinja2.visitor import NodeTransformer
|
||||
from . import nodes
|
||||
from .visitor import NodeTransformer
|
||||
|
||||
|
||||
def optimize(node, environment):
|
||||
@@ -28,22 +20,22 @@ def optimize(node, environment):
|
||||
|
||||
|
||||
class Optimizer(NodeTransformer):
|
||||
|
||||
def __init__(self, environment):
|
||||
self.environment = environment
|
||||
|
||||
def fold(self, node, eval_ctx=None):
|
||||
"""Do constant folding."""
|
||||
node = self.generic_visit(node)
|
||||
try:
|
||||
return nodes.Const.from_untrusted(node.as_const(eval_ctx),
|
||||
lineno=node.lineno,
|
||||
environment=self.environment)
|
||||
except nodes.Impossible:
|
||||
return node
|
||||
def generic_visit(self, node, *args, **kwargs):
|
||||
node = super(Optimizer, self).generic_visit(node, *args, **kwargs)
|
||||
|
||||
visit_Add = visit_Sub = visit_Mul = visit_Div = visit_FloorDiv = \
|
||||
visit_Pow = visit_Mod = visit_And = visit_Or = visit_Pos = visit_Neg = \
|
||||
visit_Not = visit_Compare = visit_Getitem = visit_Getattr = visit_Call = \
|
||||
visit_Filter = visit_Test = visit_CondExpr = fold
|
||||
del fold
|
||||
# Do constant folding. Some other nodes besides Expr have
|
||||
# as_const, but folding them causes errors later on.
|
||||
if isinstance(node, nodes.Expr):
|
||||
try:
|
||||
return nodes.Const.from_untrusted(
|
||||
node.as_const(args[0] if args else None),
|
||||
lineno=node.lineno,
|
||||
environment=self.environment,
|
||||
)
|
||||
except nodes.Impossible:
|
||||
pass
|
||||
|
||||
return node
|
||||
|
564
lib/spack/external/jinja2/parser.py
vendored
564
lib/spack/external/jinja2/parser.py
vendored
File diff suppressed because it is too large
Load Diff
716
lib/spack/external/jinja2/runtime.py
vendored
716
lib/spack/external/jinja2/runtime.py
vendored
File diff suppressed because it is too large
Load Diff
237
lib/spack/external/jinja2/sandbox.py
vendored
237
lib/spack/external/jinja2/sandbox.py
vendored
@@ -1,76 +1,66 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""A sandbox layer that ensures unsafe operations cannot be performed.
|
||||
Useful when the template itself comes from an untrusted source.
|
||||
"""
|
||||
jinja2.sandbox
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Adds a sandbox layer to Jinja as it was the default behavior in the old
|
||||
Jinja 1 releases. This sandbox is slightly different from Jinja 1 as the
|
||||
default behavior is easier to use.
|
||||
|
||||
The behavior can be changed by subclassing the environment.
|
||||
|
||||
:copyright: (c) 2017 by the Jinja Team.
|
||||
:license: BSD.
|
||||
"""
|
||||
import types
|
||||
import operator
|
||||
import sys
|
||||
from jinja2.environment import Environment
|
||||
from jinja2.exceptions import SecurityError
|
||||
from jinja2._compat import string_types, PY2
|
||||
from jinja2.utils import Markup
|
||||
|
||||
from markupsafe import EscapeFormatter
|
||||
import types
|
||||
import warnings
|
||||
from collections import deque
|
||||
from string import Formatter
|
||||
|
||||
if sys.version_info >= (3, 3):
|
||||
from collections.abc import Mapping
|
||||
else:
|
||||
from collections import Mapping
|
||||
from markupsafe import EscapeFormatter
|
||||
from markupsafe import Markup
|
||||
|
||||
from ._compat import abc
|
||||
from ._compat import PY2
|
||||
from ._compat import range_type
|
||||
from ._compat import string_types
|
||||
from .environment import Environment
|
||||
from .exceptions import SecurityError
|
||||
|
||||
#: maximum number of items a range may produce
|
||||
MAX_RANGE = 100000
|
||||
|
||||
#: attributes of function objects that are considered unsafe.
|
||||
if PY2:
|
||||
UNSAFE_FUNCTION_ATTRIBUTES = set(['func_closure', 'func_code', 'func_dict',
|
||||
'func_defaults', 'func_globals'])
|
||||
UNSAFE_FUNCTION_ATTRIBUTES = {
|
||||
"func_closure",
|
||||
"func_code",
|
||||
"func_dict",
|
||||
"func_defaults",
|
||||
"func_globals",
|
||||
}
|
||||
else:
|
||||
# On versions > python 2 the special attributes on functions are gone,
|
||||
# but they remain on methods and generators for whatever reason.
|
||||
UNSAFE_FUNCTION_ATTRIBUTES = set()
|
||||
|
||||
|
||||
#: unsafe method attributes. function attributes are unsafe for methods too
|
||||
UNSAFE_METHOD_ATTRIBUTES = set(['im_class', 'im_func', 'im_self'])
|
||||
UNSAFE_METHOD_ATTRIBUTES = {"im_class", "im_func", "im_self"}
|
||||
|
||||
#: unsafe generator attirbutes.
|
||||
UNSAFE_GENERATOR_ATTRIBUTES = set(['gi_frame', 'gi_code'])
|
||||
#: unsafe generator attributes.
|
||||
UNSAFE_GENERATOR_ATTRIBUTES = {"gi_frame", "gi_code"}
|
||||
|
||||
#: unsafe attributes on coroutines
|
||||
UNSAFE_COROUTINE_ATTRIBUTES = set(['cr_frame', 'cr_code'])
|
||||
UNSAFE_COROUTINE_ATTRIBUTES = {"cr_frame", "cr_code"}
|
||||
|
||||
#: unsafe attributes on async generators
|
||||
UNSAFE_ASYNC_GENERATOR_ATTRIBUTES = set(['ag_code', 'ag_frame'])
|
||||
|
||||
import warnings
|
||||
UNSAFE_ASYNC_GENERATOR_ATTRIBUTES = {"ag_code", "ag_frame"}
|
||||
|
||||
# make sure we don't warn in python 2.6 about stuff we don't care about
|
||||
warnings.filterwarnings('ignore', 'the sets module', DeprecationWarning,
|
||||
module='jinja2.sandbox')
|
||||
|
||||
from collections import deque
|
||||
warnings.filterwarnings(
|
||||
"ignore", "the sets module", DeprecationWarning, module=__name__
|
||||
)
|
||||
|
||||
_mutable_set_types = (set,)
|
||||
_mutable_mapping_types = (dict,)
|
||||
_mutable_sequence_types = (list,)
|
||||
|
||||
|
||||
# on python 2.x we can register the user collection types
|
||||
try:
|
||||
from UserDict import UserDict, DictMixin
|
||||
from UserList import UserList
|
||||
|
||||
_mutable_mapping_types += (UserDict, DictMixin)
|
||||
_mutable_set_types += (UserList,)
|
||||
except ImportError:
|
||||
@@ -79,39 +69,60 @@
|
||||
# if sets is still available, register the mutable set from there as well
|
||||
try:
|
||||
from sets import Set
|
||||
|
||||
_mutable_set_types += (Set,)
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
#: register Python 2.6 abstract base classes
|
||||
if sys.version_info >= (3, 3):
|
||||
from collections.abc import MutableSet, MutableMapping, MutableSequence
|
||||
else:
|
||||
from collections import MutableSet, MutableMapping, MutableSequence
|
||||
_mutable_set_types += (MutableSet,)
|
||||
_mutable_mapping_types += (MutableMapping,)
|
||||
_mutable_sequence_types += (MutableSequence,)
|
||||
|
||||
_mutable_set_types += (abc.MutableSet,)
|
||||
_mutable_mapping_types += (abc.MutableMapping,)
|
||||
_mutable_sequence_types += (abc.MutableSequence,)
|
||||
|
||||
_mutable_spec = (
|
||||
(_mutable_set_types, frozenset([
|
||||
'add', 'clear', 'difference_update', 'discard', 'pop', 'remove',
|
||||
'symmetric_difference_update', 'update'
|
||||
])),
|
||||
(_mutable_mapping_types, frozenset([
|
||||
'clear', 'pop', 'popitem', 'setdefault', 'update'
|
||||
])),
|
||||
(_mutable_sequence_types, frozenset([
|
||||
'append', 'reverse', 'insert', 'sort', 'extend', 'remove'
|
||||
])),
|
||||
(deque, frozenset([
|
||||
'append', 'appendleft', 'clear', 'extend', 'extendleft', 'pop',
|
||||
'popleft', 'remove', 'rotate'
|
||||
]))
|
||||
(
|
||||
_mutable_set_types,
|
||||
frozenset(
|
||||
[
|
||||
"add",
|
||||
"clear",
|
||||
"difference_update",
|
||||
"discard",
|
||||
"pop",
|
||||
"remove",
|
||||
"symmetric_difference_update",
|
||||
"update",
|
||||
]
|
||||
),
|
||||
),
|
||||
(
|
||||
_mutable_mapping_types,
|
||||
frozenset(["clear", "pop", "popitem", "setdefault", "update"]),
|
||||
),
|
||||
(
|
||||
_mutable_sequence_types,
|
||||
frozenset(["append", "reverse", "insert", "sort", "extend", "remove"]),
|
||||
),
|
||||
(
|
||||
deque,
|
||||
frozenset(
|
||||
[
|
||||
"append",
|
||||
"appendleft",
|
||||
"clear",
|
||||
"extend",
|
||||
"extendleft",
|
||||
"pop",
|
||||
"popleft",
|
||||
"remove",
|
||||
"rotate",
|
||||
]
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class _MagicFormatMapping(Mapping):
|
||||
class _MagicFormatMapping(abc.Mapping):
|
||||
"""This class implements a dummy wrapper to fix a bug in the Python
|
||||
standard library for string formatting.
|
||||
|
||||
@@ -125,7 +136,7 @@ def __init__(self, args, kwargs):
|
||||
self._last_index = 0
|
||||
|
||||
def __getitem__(self, key):
|
||||
if key == '':
|
||||
if key == "":
|
||||
idx = self._last_index
|
||||
self._last_index += 1
|
||||
try:
|
||||
@@ -143,9 +154,9 @@ def __len__(self):
|
||||
|
||||
|
||||
def inspect_format_method(callable):
|
||||
if not isinstance(callable, (types.MethodType,
|
||||
types.BuiltinMethodType)) or \
|
||||
callable.__name__ != 'format':
|
||||
if not isinstance(
|
||||
callable, (types.MethodType, types.BuiltinMethodType)
|
||||
) or callable.__name__ not in ("format", "format_map"):
|
||||
return None
|
||||
obj = callable.__self__
|
||||
if isinstance(obj, string_types):
|
||||
@@ -156,10 +167,14 @@ def safe_range(*args):
|
||||
"""A range that can't generate ranges with a length of more than
|
||||
MAX_RANGE items.
|
||||
"""
|
||||
rng = range(*args)
|
||||
rng = range_type(*args)
|
||||
|
||||
if len(rng) > MAX_RANGE:
|
||||
raise OverflowError('range too big, maximum size for range is %d' %
|
||||
MAX_RANGE)
|
||||
raise OverflowError(
|
||||
"Range too big. The sandbox blocks ranges larger than"
|
||||
" MAX_RANGE (%d)." % MAX_RANGE
|
||||
)
|
||||
|
||||
return rng
|
||||
|
||||
|
||||
@@ -192,24 +207,25 @@ def is_internal_attribute(obj, attr):
|
||||
if attr in UNSAFE_FUNCTION_ATTRIBUTES:
|
||||
return True
|
||||
elif isinstance(obj, types.MethodType):
|
||||
if attr in UNSAFE_FUNCTION_ATTRIBUTES or \
|
||||
attr in UNSAFE_METHOD_ATTRIBUTES:
|
||||
if attr in UNSAFE_FUNCTION_ATTRIBUTES or attr in UNSAFE_METHOD_ATTRIBUTES:
|
||||
return True
|
||||
elif isinstance(obj, type):
|
||||
if attr == 'mro':
|
||||
if attr == "mro":
|
||||
return True
|
||||
elif isinstance(obj, (types.CodeType, types.TracebackType, types.FrameType)):
|
||||
return True
|
||||
elif isinstance(obj, types.GeneratorType):
|
||||
if attr in UNSAFE_GENERATOR_ATTRIBUTES:
|
||||
return True
|
||||
elif hasattr(types, 'CoroutineType') and isinstance(obj, types.CoroutineType):
|
||||
elif hasattr(types, "CoroutineType") and isinstance(obj, types.CoroutineType):
|
||||
if attr in UNSAFE_COROUTINE_ATTRIBUTES:
|
||||
return True
|
||||
elif hasattr(types, 'AsyncGeneratorType') and isinstance(obj, types.AsyncGeneratorType):
|
||||
elif hasattr(types, "AsyncGeneratorType") and isinstance(
|
||||
obj, types.AsyncGeneratorType
|
||||
):
|
||||
if attr in UNSAFE_ASYNC_GENERATOR_ATTRIBUTES:
|
||||
return True
|
||||
return attr.startswith('__')
|
||||
return attr.startswith("__")
|
||||
|
||||
|
||||
def modifies_known_mutable(obj, attr):
|
||||
@@ -250,28 +266,26 @@ class SandboxedEnvironment(Environment):
|
||||
raised. However also other exceptions may occur during the rendering so
|
||||
the caller has to ensure that all exceptions are caught.
|
||||
"""
|
||||
|
||||
sandboxed = True
|
||||
|
||||
#: default callback table for the binary operators. A copy of this is
|
||||
#: available on each instance of a sandboxed environment as
|
||||
#: :attr:`binop_table`
|
||||
default_binop_table = {
|
||||
'+': operator.add,
|
||||
'-': operator.sub,
|
||||
'*': operator.mul,
|
||||
'/': operator.truediv,
|
||||
'//': operator.floordiv,
|
||||
'**': operator.pow,
|
||||
'%': operator.mod
|
||||
"+": operator.add,
|
||||
"-": operator.sub,
|
||||
"*": operator.mul,
|
||||
"/": operator.truediv,
|
||||
"//": operator.floordiv,
|
||||
"**": operator.pow,
|
||||
"%": operator.mod,
|
||||
}
|
||||
|
||||
#: default callback table for the unary operators. A copy of this is
|
||||
#: available on each instance of a sandboxed environment as
|
||||
#: :attr:`unop_table`
|
||||
default_unop_table = {
|
||||
'+': operator.pos,
|
||||
'-': operator.neg
|
||||
}
|
||||
default_unop_table = {"+": operator.pos, "-": operator.neg}
|
||||
|
||||
#: a set of binary operators that should be intercepted. Each operator
|
||||
#: that is added to this set (empty by default) is delegated to the
|
||||
@@ -307,7 +321,7 @@ class SandboxedEnvironment(Environment):
|
||||
def intercept_unop(self, operator):
|
||||
"""Called during template compilation with the name of a unary
|
||||
operator to check if it should be intercepted at runtime. If this
|
||||
method returns `True`, :meth:`call_unop` is excuted for this unary
|
||||
method returns `True`, :meth:`call_unop` is executed for this unary
|
||||
operator. The default implementation of :meth:`call_unop` will use
|
||||
the :attr:`unop_table` dictionary to perform the operator with the
|
||||
same logic as the builtin one.
|
||||
@@ -321,10 +335,9 @@ def intercept_unop(self, operator):
|
||||
"""
|
||||
return False
|
||||
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
Environment.__init__(self, *args, **kwargs)
|
||||
self.globals['range'] = safe_range
|
||||
self.globals["range"] = safe_range
|
||||
self.binop_table = self.default_binop_table.copy()
|
||||
self.unop_table = self.default_unop_table.copy()
|
||||
|
||||
@@ -335,7 +348,7 @@ def is_safe_attribute(self, obj, attr, value):
|
||||
special attributes of internal python objects as returned by the
|
||||
:func:`is_internal_attribute` function.
|
||||
"""
|
||||
return not (attr.startswith('_') or is_internal_attribute(obj, attr))
|
||||
return not (attr.startswith("_") or is_internal_attribute(obj, attr))
|
||||
|
||||
def is_safe_callable(self, obj):
|
||||
"""Check if an object is safely callable. Per default a function is
|
||||
@@ -343,8 +356,9 @@ def is_safe_callable(self, obj):
|
||||
True. Override this method to alter the behavior, but this won't
|
||||
affect the `unsafe` decorator from this module.
|
||||
"""
|
||||
return not (getattr(obj, 'unsafe_callable', False) or
|
||||
getattr(obj, 'alters_data', False))
|
||||
return not (
|
||||
getattr(obj, "unsafe_callable", False) or getattr(obj, "alters_data", False)
|
||||
)
|
||||
|
||||
def call_binop(self, context, operator, left, right):
|
||||
"""For intercepted binary operator calls (:meth:`intercepted_binops`)
|
||||
@@ -404,13 +418,15 @@ def getattr(self, obj, attribute):
|
||||
|
||||
def unsafe_undefined(self, obj, attribute):
|
||||
"""Return an undefined object for unsafe attributes."""
|
||||
return self.undefined('access to attribute %r of %r '
|
||||
'object is unsafe.' % (
|
||||
attribute,
|
||||
obj.__class__.__name__
|
||||
), name=attribute, obj=obj, exc=SecurityError)
|
||||
return self.undefined(
|
||||
"access to attribute %r of %r "
|
||||
"object is unsafe." % (attribute, obj.__class__.__name__),
|
||||
name=attribute,
|
||||
obj=obj,
|
||||
exc=SecurityError,
|
||||
)
|
||||
|
||||
def format_string(self, s, args, kwargs):
|
||||
def format_string(self, s, args, kwargs, format_func=None):
|
||||
"""If a format call is detected, then this is routed through this
|
||||
method so that our safety sandbox can be used for it.
|
||||
"""
|
||||
@@ -418,20 +434,31 @@ def format_string(self, s, args, kwargs):
|
||||
formatter = SandboxedEscapeFormatter(self, s.escape)
|
||||
else:
|
||||
formatter = SandboxedFormatter(self)
|
||||
|
||||
if format_func is not None and format_func.__name__ == "format_map":
|
||||
if len(args) != 1 or kwargs:
|
||||
raise TypeError(
|
||||
"format_map() takes exactly one argument %d given"
|
||||
% (len(args) + (kwargs is not None))
|
||||
)
|
||||
|
||||
kwargs = args[0]
|
||||
args = None
|
||||
|
||||
kwargs = _MagicFormatMapping(args, kwargs)
|
||||
rv = formatter.vformat(s, args, kwargs)
|
||||
return type(s)(rv)
|
||||
|
||||
def call(__self, __context, __obj, *args, **kwargs):
|
||||
def call(__self, __context, __obj, *args, **kwargs): # noqa: B902
|
||||
"""Call an object from sandboxed code."""
|
||||
fmt = inspect_format_method(__obj)
|
||||
if fmt is not None:
|
||||
return __self.format_string(fmt, args, kwargs)
|
||||
return __self.format_string(fmt, args, kwargs, __obj)
|
||||
|
||||
# the double prefixes are to avoid double keyword argument
|
||||
# errors when proxying the call.
|
||||
if not __self.is_safe_callable(__obj):
|
||||
raise SecurityError('%r is not safely callable' % (__obj,))
|
||||
raise SecurityError("%r is not safely callable" % (__obj,))
|
||||
return __context.call(__obj, *args, **kwargs)
|
||||
|
||||
|
||||
@@ -447,16 +474,16 @@ def is_safe_attribute(self, obj, attr, value):
|
||||
return not modifies_known_mutable(obj, attr)
|
||||
|
||||
|
||||
# This really is not a public API apparenlty.
|
||||
# This really is not a public API apparently.
|
||||
try:
|
||||
from _string import formatter_field_name_split
|
||||
except ImportError:
|
||||
|
||||
def formatter_field_name_split(field_name):
|
||||
return field_name._formatter_field_name_split()
|
||||
|
||||
|
||||
class SandboxedFormatterMixin(object):
|
||||
|
||||
def __init__(self, env):
|
||||
self._env = env
|
||||
|
||||
@@ -470,14 +497,14 @@ def get_field(self, field_name, args, kwargs):
|
||||
obj = self._env.getitem(obj, i)
|
||||
return obj, first
|
||||
|
||||
class SandboxedFormatter(SandboxedFormatterMixin, Formatter):
|
||||
|
||||
class SandboxedFormatter(SandboxedFormatterMixin, Formatter):
|
||||
def __init__(self, env):
|
||||
SandboxedFormatterMixin.__init__(self, env)
|
||||
Formatter.__init__(self)
|
||||
|
||||
class SandboxedEscapeFormatter(SandboxedFormatterMixin, EscapeFormatter):
|
||||
|
||||
class SandboxedEscapeFormatter(SandboxedFormatterMixin, EscapeFormatter):
|
||||
def __init__(self, env, escape):
|
||||
SandboxedFormatterMixin.__init__(self, env)
|
||||
EscapeFormatter.__init__(self, escape)
|
||||
|
145
lib/spack/external/jinja2/tests.py
vendored
145
lib/spack/external/jinja2/tests.py
vendored
@@ -1,29 +1,17 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
jinja2.tests
|
||||
~~~~~~~~~~~~
|
||||
|
||||
Jinja test functions. Used with the "is" operator.
|
||||
|
||||
:copyright: (c) 2017 by the Jinja Team.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
"""Built-in template tests used with the ``is`` operator."""
|
||||
import decimal
|
||||
import operator
|
||||
import re
|
||||
import sys
|
||||
from jinja2.runtime import Undefined
|
||||
from jinja2._compat import text_type, string_types, integer_types
|
||||
import decimal
|
||||
|
||||
if sys.version_info >= (3, 3):
|
||||
from collections.abc import Mapping
|
||||
else:
|
||||
from collections import Mapping
|
||||
from ._compat import abc
|
||||
from ._compat import integer_types
|
||||
from ._compat import string_types
|
||||
from ._compat import text_type
|
||||
from .runtime import Undefined
|
||||
|
||||
number_re = re.compile(r'^-?\d+(\.\d+)?$')
|
||||
number_re = re.compile(r"^-?\d+(\.\d+)?$")
|
||||
regex_type = type(number_re)
|
||||
|
||||
|
||||
test_callable = callable
|
||||
|
||||
|
||||
@@ -69,6 +57,48 @@ def test_none(value):
|
||||
return value is None
|
||||
|
||||
|
||||
def test_boolean(value):
|
||||
"""Return true if the object is a boolean value.
|
||||
|
||||
.. versionadded:: 2.11
|
||||
"""
|
||||
return value is True or value is False
|
||||
|
||||
|
||||
def test_false(value):
|
||||
"""Return true if the object is False.
|
||||
|
||||
.. versionadded:: 2.11
|
||||
"""
|
||||
return value is False
|
||||
|
||||
|
||||
def test_true(value):
|
||||
"""Return true if the object is True.
|
||||
|
||||
.. versionadded:: 2.11
|
||||
"""
|
||||
return value is True
|
||||
|
||||
|
||||
# NOTE: The existing 'number' test matches booleans and floats
|
||||
def test_integer(value):
|
||||
"""Return true if the object is an integer.
|
||||
|
||||
.. versionadded:: 2.11
|
||||
"""
|
||||
return isinstance(value, integer_types) and value is not True and value is not False
|
||||
|
||||
|
||||
# NOTE: The existing 'number' test matches booleans and integers
|
||||
def test_float(value):
|
||||
"""Return true if the object is a float.
|
||||
|
||||
.. versionadded:: 2.11
|
||||
"""
|
||||
return isinstance(value, float)
|
||||
|
||||
|
||||
def test_lower(value):
|
||||
"""Return true if the variable is lowercased."""
|
||||
return text_type(value).islower()
|
||||
@@ -89,7 +119,7 @@ def test_mapping(value):
|
||||
|
||||
.. versionadded:: 2.6
|
||||
"""
|
||||
return isinstance(value, Mapping)
|
||||
return isinstance(value, abc.Mapping)
|
||||
|
||||
|
||||
def test_number(value):
|
||||
@@ -104,7 +134,7 @@ def test_sequence(value):
|
||||
try:
|
||||
len(value)
|
||||
value.__getitem__
|
||||
except:
|
||||
except Exception:
|
||||
return False
|
||||
return True
|
||||
|
||||
@@ -133,7 +163,7 @@ def test_iterable(value):
|
||||
|
||||
def test_escaped(value):
|
||||
"""Check if the value is escaped."""
|
||||
return hasattr(value, '__html__')
|
||||
return hasattr(value, "__html__")
|
||||
|
||||
|
||||
def test_in(value, seq):
|
||||
@@ -145,36 +175,41 @@ def test_in(value, seq):
|
||||
|
||||
|
||||
TESTS = {
|
||||
'odd': test_odd,
|
||||
'even': test_even,
|
||||
'divisibleby': test_divisibleby,
|
||||
'defined': test_defined,
|
||||
'undefined': test_undefined,
|
||||
'none': test_none,
|
||||
'lower': test_lower,
|
||||
'upper': test_upper,
|
||||
'string': test_string,
|
||||
'mapping': test_mapping,
|
||||
'number': test_number,
|
||||
'sequence': test_sequence,
|
||||
'iterable': test_iterable,
|
||||
'callable': test_callable,
|
||||
'sameas': test_sameas,
|
||||
'escaped': test_escaped,
|
||||
'in': test_in,
|
||||
'==': operator.eq,
|
||||
'eq': operator.eq,
|
||||
'equalto': operator.eq,
|
||||
'!=': operator.ne,
|
||||
'ne': operator.ne,
|
||||
'>': operator.gt,
|
||||
'gt': operator.gt,
|
||||
'greaterthan': operator.gt,
|
||||
'ge': operator.ge,
|
||||
'>=': operator.ge,
|
||||
'<': operator.lt,
|
||||
'lt': operator.lt,
|
||||
'lessthan': operator.lt,
|
||||
'<=': operator.le,
|
||||
'le': operator.le,
|
||||
"odd": test_odd,
|
||||
"even": test_even,
|
||||
"divisibleby": test_divisibleby,
|
||||
"defined": test_defined,
|
||||
"undefined": test_undefined,
|
||||
"none": test_none,
|
||||
"boolean": test_boolean,
|
||||
"false": test_false,
|
||||
"true": test_true,
|
||||
"integer": test_integer,
|
||||
"float": test_float,
|
||||
"lower": test_lower,
|
||||
"upper": test_upper,
|
||||
"string": test_string,
|
||||
"mapping": test_mapping,
|
||||
"number": test_number,
|
||||
"sequence": test_sequence,
|
||||
"iterable": test_iterable,
|
||||
"callable": test_callable,
|
||||
"sameas": test_sameas,
|
||||
"escaped": test_escaped,
|
||||
"in": test_in,
|
||||
"==": operator.eq,
|
||||
"eq": operator.eq,
|
||||
"equalto": operator.eq,
|
||||
"!=": operator.ne,
|
||||
"ne": operator.ne,
|
||||
">": operator.gt,
|
||||
"gt": operator.gt,
|
||||
"greaterthan": operator.gt,
|
||||
"ge": operator.ge,
|
||||
">=": operator.ge,
|
||||
"<": operator.lt,
|
||||
"lt": operator.lt,
|
||||
"lessthan": operator.lt,
|
||||
"<=": operator.le,
|
||||
"le": operator.le,
|
||||
}
|
||||
|
410
lib/spack/external/jinja2/utils.py
vendored
410
lib/spack/external/jinja2/utils.py
vendored
@@ -1,44 +1,32 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
jinja2.utils
|
||||
~~~~~~~~~~~~
|
||||
|
||||
Utility functions.
|
||||
|
||||
:copyright: (c) 2017 by the Jinja Team.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
import re
|
||||
import json
|
||||
import errno
|
||||
import os
|
||||
import re
|
||||
import warnings
|
||||
from collections import deque
|
||||
from random import choice
|
||||
from random import randrange
|
||||
from string import ascii_letters as _letters
|
||||
from string import digits as _digits
|
||||
from threading import Lock
|
||||
from jinja2._compat import text_type, string_types, implements_iterator, \
|
||||
url_quote
|
||||
|
||||
from markupsafe import escape
|
||||
from markupsafe import Markup
|
||||
|
||||
_word_split_re = re.compile(r'(\s+)')
|
||||
_punctuation_re = re.compile(
|
||||
'^(?P<lead>(?:%s)*)(?P<middle>.*?)(?P<trail>(?:%s)*)$' % (
|
||||
'|'.join(map(re.escape, ('(', '<', '<'))),
|
||||
'|'.join(map(re.escape, ('.', ',', ')', '>', '\n', '>')))
|
||||
)
|
||||
)
|
||||
_simple_email_re = re.compile(r'^\S+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+$')
|
||||
_striptags_re = re.compile(r'(<!--.*?-->|<[^>]*>)')
|
||||
_entity_re = re.compile(r'&([^;]+);')
|
||||
_letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||
_digits = '0123456789'
|
||||
from ._compat import abc
|
||||
from ._compat import string_types
|
||||
from ._compat import text_type
|
||||
from ._compat import url_quote
|
||||
|
||||
# special singleton representing missing values for the runtime
|
||||
missing = type('MissingType', (), {'__repr__': lambda x: 'missing'})()
|
||||
missing = type("MissingType", (), {"__repr__": lambda x: "missing"})()
|
||||
|
||||
# internal code
|
||||
internal_code = set()
|
||||
|
||||
concat = u''.join
|
||||
concat = u"".join
|
||||
|
||||
_slash_escape = '\\/' not in json.dumps('/')
|
||||
_slash_escape = "\\/" not in json.dumps("/")
|
||||
|
||||
|
||||
def contextfunction(f):
|
||||
@@ -98,24 +86,26 @@ def default(var, default=''):
|
||||
return default
|
||||
return var
|
||||
"""
|
||||
from jinja2.runtime import Undefined
|
||||
from .runtime import Undefined
|
||||
|
||||
return isinstance(obj, Undefined)
|
||||
|
||||
|
||||
def consume(iterable):
|
||||
"""Consumes an iterable without doing anything with it."""
|
||||
for event in iterable:
|
||||
for _ in iterable:
|
||||
pass
|
||||
|
||||
|
||||
def clear_caches():
|
||||
"""Jinja2 keeps internal caches for environments and lexers. These are
|
||||
used so that Jinja2 doesn't have to recreate environments and lexers all
|
||||
"""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.
|
||||
"""
|
||||
from jinja2.environment import _spontaneous_environments
|
||||
from jinja2.lexer import _lexer_cache
|
||||
from .environment import _spontaneous_environments
|
||||
from .lexer import _lexer_cache
|
||||
|
||||
_spontaneous_environments.clear()
|
||||
_lexer_cache.clear()
|
||||
|
||||
@@ -132,12 +122,10 @@ def import_string(import_name, silent=False):
|
||||
:return: imported object
|
||||
"""
|
||||
try:
|
||||
if ':' in import_name:
|
||||
module, obj = import_name.split(':', 1)
|
||||
elif '.' in import_name:
|
||||
items = import_name.split('.')
|
||||
module = '.'.join(items[:-1])
|
||||
obj = items[-1]
|
||||
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)
|
||||
@@ -146,15 +134,14 @@ def import_string(import_name, silent=False):
|
||||
raise
|
||||
|
||||
|
||||
def open_if_exists(filename, mode='rb'):
|
||||
def open_if_exists(filename, mode="rb"):
|
||||
"""Returns a file descriptor for the filename if that file exists,
|
||||
otherwise `None`.
|
||||
otherwise ``None``.
|
||||
"""
|
||||
try:
|
||||
return open(filename, mode)
|
||||
except IOError as e:
|
||||
if e.errno not in (errno.ENOENT, errno.EISDIR, errno.EINVAL):
|
||||
raise
|
||||
if not os.path.isfile(filename):
|
||||
return None
|
||||
|
||||
return open(filename, mode)
|
||||
|
||||
|
||||
def object_type_repr(obj):
|
||||
@@ -163,15 +150,19 @@ def object_type_repr(obj):
|
||||
example for `None` and `Ellipsis`).
|
||||
"""
|
||||
if obj is None:
|
||||
return 'None'
|
||||
return "None"
|
||||
elif obj is Ellipsis:
|
||||
return 'Ellipsis'
|
||||
return "Ellipsis"
|
||||
|
||||
cls = type(obj)
|
||||
|
||||
# __builtin__ in 2.x, builtins in 3.x
|
||||
if obj.__class__.__module__ in ('__builtin__', 'builtins'):
|
||||
name = obj.__class__.__name__
|
||||
if cls.__module__ in ("__builtin__", "builtins"):
|
||||
name = cls.__name__
|
||||
else:
|
||||
name = obj.__class__.__module__ + '.' + obj.__class__.__name__
|
||||
return '%s object' % name
|
||||
name = cls.__module__ + "." + cls.__name__
|
||||
|
||||
return "%s object" % name
|
||||
|
||||
|
||||
def pformat(obj, verbose=False):
|
||||
@@ -180,9 +171,11 @@ def pformat(obj, verbose=False):
|
||||
"""
|
||||
try:
|
||||
from pretty import pretty
|
||||
|
||||
return pretty(obj, verbose=verbose)
|
||||
except ImportError:
|
||||
from pprint import pformat
|
||||
|
||||
return pformat(obj)
|
||||
|
||||
|
||||
@@ -200,45 +193,77 @@ def urlize(text, trim_url_limit=None, rel=None, target=None):
|
||||
|
||||
If target is not None, a target attribute will be added to the link.
|
||||
"""
|
||||
trim_url = lambda x, limit=trim_url_limit: limit is not None \
|
||||
and (x[:limit] + (len(x) >=limit and '...'
|
||||
or '')) or x
|
||||
words = _word_split_re.split(text_type(escape(text)))
|
||||
rel_attr = rel and ' rel="%s"' % text_type(escape(rel)) or ''
|
||||
target_attr = target and ' target="%s"' % escape(target) or ''
|
||||
trim_url = (
|
||||
lambda x, limit=trim_url_limit: limit is not None
|
||||
and (x[:limit] + (len(x) >= limit and "..." or ""))
|
||||
or x
|
||||
)
|
||||
words = re.split(r"(\s+)", text_type(escape(text)))
|
||||
rel_attr = rel and ' rel="%s"' % text_type(escape(rel)) or ""
|
||||
target_attr = target and ' target="%s"' % escape(target) or ""
|
||||
|
||||
for i, word in enumerate(words):
|
||||
match = _punctuation_re.match(word)
|
||||
head, middle, tail = "", word, ""
|
||||
match = re.match(r"^([(<]|<)+", middle)
|
||||
|
||||
if match:
|
||||
lead, middle, trail = match.groups()
|
||||
if middle.startswith('www.') or (
|
||||
'@' not in middle and
|
||||
not middle.startswith('http://') and
|
||||
not middle.startswith('https://') and
|
||||
len(middle) > 0 and
|
||||
middle[0] in _letters + _digits and (
|
||||
middle.endswith('.org') or
|
||||
middle.endswith('.net') or
|
||||
middle.endswith('.com')
|
||||
)):
|
||||
middle = '<a href="http://%s"%s%s>%s</a>' % (middle,
|
||||
rel_attr, target_attr, trim_url(middle))
|
||||
if middle.startswith('http://') or \
|
||||
middle.startswith('https://'):
|
||||
middle = '<a href="%s"%s%s>%s</a>' % (middle,
|
||||
rel_attr, target_attr, trim_url(middle))
|
||||
if '@' in middle and not middle.startswith('www.') and \
|
||||
not ':' in middle and _simple_email_re.match(middle):
|
||||
middle = '<a href="mailto:%s">%s</a>' % (middle, middle)
|
||||
if lead + middle + trail != word:
|
||||
words[i] = lead + middle + trail
|
||||
return u''.join(words)
|
||||
head = match.group()
|
||||
middle = middle[match.end() :]
|
||||
|
||||
# Unlike lead, which is anchored to the start of the string,
|
||||
# need to check that the string ends with any of the characters
|
||||
# before trying to match all of them, to avoid backtracking.
|
||||
if middle.endswith((")", ">", ".", ",", "\n", ">")):
|
||||
match = re.search(r"([)>.,\n]|>)+$", middle)
|
||||
|
||||
if match:
|
||||
tail = match.group()
|
||||
middle = middle[: match.start()]
|
||||
|
||||
if middle.startswith("www.") or (
|
||||
"@" not in middle
|
||||
and not middle.startswith("http://")
|
||||
and not middle.startswith("https://")
|
||||
and len(middle) > 0
|
||||
and middle[0] in _letters + _digits
|
||||
and (
|
||||
middle.endswith(".org")
|
||||
or middle.endswith(".net")
|
||||
or middle.endswith(".com")
|
||||
)
|
||||
):
|
||||
middle = '<a href="http://%s"%s%s>%s</a>' % (
|
||||
middle,
|
||||
rel_attr,
|
||||
target_attr,
|
||||
trim_url(middle),
|
||||
)
|
||||
|
||||
if middle.startswith("http://") or middle.startswith("https://"):
|
||||
middle = '<a href="%s"%s%s>%s</a>' % (
|
||||
middle,
|
||||
rel_attr,
|
||||
target_attr,
|
||||
trim_url(middle),
|
||||
)
|
||||
|
||||
if (
|
||||
"@" in middle
|
||||
and not middle.startswith("www.")
|
||||
and ":" not in middle
|
||||
and re.match(r"^\S+@\w[\w.-]*\.\w+$", middle)
|
||||
):
|
||||
middle = '<a href="mailto:%s">%s</a>' % (middle, middle)
|
||||
|
||||
words[i] = head + middle + tail
|
||||
|
||||
return u"".join(words)
|
||||
|
||||
|
||||
def generate_lorem_ipsum(n=5, html=True, min=20, max=100):
|
||||
"""Generate some lorem ipsum for the template."""
|
||||
from jinja2.constants import LOREM_IPSUM_WORDS
|
||||
from random import choice, randrange
|
||||
from .constants import LOREM_IPSUM_WORDS
|
||||
|
||||
words = LOREM_IPSUM_WORDS.split()
|
||||
result = []
|
||||
|
||||
@@ -263,43 +288,53 @@ def generate_lorem_ipsum(n=5, html=True, min=20, max=100):
|
||||
if idx - randrange(3, 8) > last_comma:
|
||||
last_comma = idx
|
||||
last_fullstop += 2
|
||||
word += ','
|
||||
word += ","
|
||||
# add end of sentences
|
||||
if idx - randrange(10, 20) > last_fullstop:
|
||||
last_comma = last_fullstop = idx
|
||||
word += '.'
|
||||
word += "."
|
||||
next_capitalized = True
|
||||
p.append(word)
|
||||
|
||||
# ensure that the paragraph ends with a dot.
|
||||
p = u' '.join(p)
|
||||
if p.endswith(','):
|
||||
p = p[:-1] + '.'
|
||||
elif not p.endswith('.'):
|
||||
p += '.'
|
||||
p = u" ".join(p)
|
||||
if p.endswith(","):
|
||||
p = p[:-1] + "."
|
||||
elif not p.endswith("."):
|
||||
p += "."
|
||||
result.append(p)
|
||||
|
||||
if not html:
|
||||
return u'\n\n'.join(result)
|
||||
return Markup(u'\n'.join(u'<p>%s</p>' % escape(x) for x in result))
|
||||
return u"\n\n".join(result)
|
||||
return Markup(u"\n".join(u"<p>%s</p>" % escape(x) for x in result))
|
||||
|
||||
|
||||
def unicode_urlencode(obj, charset='utf-8', for_qs=False):
|
||||
"""URL escapes a single bytestring or unicode string with the
|
||||
given charset if applicable to URL safe quoting under all rules
|
||||
that need to be considered under all supported Python versions.
|
||||
def unicode_urlencode(obj, charset="utf-8", for_qs=False):
|
||||
"""Quote a string for use in a URL using the given charset.
|
||||
|
||||
If non strings are provided they are converted to their unicode
|
||||
representation first.
|
||||
This function is misnamed, it is a wrapper around
|
||||
:func:`urllib.parse.quote`.
|
||||
|
||||
: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, string_types):
|
||||
obj = text_type(obj)
|
||||
|
||||
if isinstance(obj, text_type):
|
||||
obj = obj.encode(charset)
|
||||
safe = not for_qs and b'/' or b''
|
||||
rv = text_type(url_quote(obj, safe))
|
||||
|
||||
safe = b"" if for_qs else b"/"
|
||||
rv = url_quote(obj, safe)
|
||||
|
||||
if not isinstance(rv, text_type):
|
||||
rv = rv.decode("utf-8")
|
||||
|
||||
if for_qs:
|
||||
rv = rv.replace('%20', '+')
|
||||
rv = rv.replace("%20", "+")
|
||||
|
||||
return rv
|
||||
|
||||
|
||||
@@ -326,9 +361,9 @@ def _postinit(self):
|
||||
|
||||
def __getstate__(self):
|
||||
return {
|
||||
'capacity': self.capacity,
|
||||
'_mapping': self._mapping,
|
||||
'_queue': self._queue
|
||||
"capacity": self.capacity,
|
||||
"_mapping": self._mapping,
|
||||
"_queue": self._queue,
|
||||
}
|
||||
|
||||
def __setstate__(self, d):
|
||||
@@ -342,7 +377,7 @@ def copy(self):
|
||||
"""Return a shallow copy of the instance."""
|
||||
rv = self.__class__(self.capacity)
|
||||
rv._mapping.update(self._mapping)
|
||||
rv._queue = deque(self._queue)
|
||||
rv._queue.extend(self._queue)
|
||||
return rv
|
||||
|
||||
def get(self, key, default=None):
|
||||
@@ -356,15 +391,11 @@ def setdefault(self, key, default=None):
|
||||
"""Set `default` if the key is not in the cache otherwise
|
||||
leave unchanged. Return the value of this key.
|
||||
"""
|
||||
self._wlock.acquire()
|
||||
try:
|
||||
try:
|
||||
return self[key]
|
||||
except KeyError:
|
||||
self[key] = default
|
||||
return default
|
||||
finally:
|
||||
self._wlock.release()
|
||||
return self[key]
|
||||
except KeyError:
|
||||
self[key] = default
|
||||
return default
|
||||
|
||||
def clear(self):
|
||||
"""Clear the cache."""
|
||||
@@ -384,10 +415,7 @@ def __len__(self):
|
||||
return len(self._mapping)
|
||||
|
||||
def __repr__(self):
|
||||
return '<%s %r>' % (
|
||||
self.__class__.__name__,
|
||||
self._mapping
|
||||
)
|
||||
return "<%s %r>" % (self.__class__.__name__, self._mapping)
|
||||
|
||||
def __getitem__(self, key):
|
||||
"""Get an item from the cache. Moves the item up so that it has the
|
||||
@@ -436,7 +464,6 @@ def __delitem__(self, key):
|
||||
try:
|
||||
self._remove(key)
|
||||
except ValueError:
|
||||
# __getitem__ is not locked, it might happen
|
||||
pass
|
||||
finally:
|
||||
self._wlock.release()
|
||||
@@ -449,6 +476,12 @@ def items(self):
|
||||
|
||||
def iteritems(self):
|
||||
"""Iterate over all items."""
|
||||
warnings.warn(
|
||||
"'iteritems()' will be removed in version 3.0. Use"
|
||||
" 'iter(cache.items())' instead.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
return iter(self.items())
|
||||
|
||||
def values(self):
|
||||
@@ -457,6 +490,22 @@ def values(self):
|
||||
|
||||
def itervalue(self):
|
||||
"""Iterate over all values."""
|
||||
warnings.warn(
|
||||
"'itervalue()' will be removed in version 3.0. Use"
|
||||
" 'iter(cache.values())' instead.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
return iter(self.values())
|
||||
|
||||
def itervalues(self):
|
||||
"""Iterate over all values."""
|
||||
warnings.warn(
|
||||
"'itervalues()' will be removed in version 3.0. Use"
|
||||
" 'iter(cache.values())' instead.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
return iter(self.values())
|
||||
|
||||
def keys(self):
|
||||
@@ -467,12 +516,19 @@ def iterkeys(self):
|
||||
"""Iterate over all keys in the cache dict, ordered by
|
||||
the most recent usage.
|
||||
"""
|
||||
warnings.warn(
|
||||
"'iterkeys()' will be removed in version 3.0. Use"
|
||||
" 'iter(cache.keys())' instead.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
return iter(self)
|
||||
|
||||
def __iter__(self):
|
||||
return reversed(tuple(self._queue))
|
||||
|
||||
__iter__ = iterkeys
|
||||
|
||||
def __reversed__(self):
|
||||
"""Iterate over the values in the cache dict, oldest items
|
||||
"""Iterate over the keys in the cache dict, oldest items
|
||||
coming first.
|
||||
"""
|
||||
return iter(tuple(self._queue))
|
||||
@@ -480,22 +536,15 @@ def __reversed__(self):
|
||||
__copy__ = copy
|
||||
|
||||
|
||||
# register the LRU cache as mutable mapping if possible
|
||||
try:
|
||||
from collections.abc import MutableMapping
|
||||
MutableMapping.register(LRUCache)
|
||||
except ImportError:
|
||||
try:
|
||||
from collections import MutableMapping
|
||||
MutableMapping.register(LRUCache)
|
||||
except ImportError:
|
||||
pass
|
||||
abc.MutableMapping.register(LRUCache)
|
||||
|
||||
|
||||
def select_autoescape(enabled_extensions=('html', 'htm', 'xml'),
|
||||
disabled_extensions=(),
|
||||
default_for_string=True,
|
||||
default=False):
|
||||
def select_autoescape(
|
||||
enabled_extensions=("html", "htm", "xml"),
|
||||
disabled_extensions=(),
|
||||
default_for_string=True,
|
||||
default=False,
|
||||
):
|
||||
"""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.
|
||||
@@ -530,10 +579,9 @@ def select_autoescape(enabled_extensions=('html', 'htm', 'xml'),
|
||||
|
||||
.. versionadded:: 2.9
|
||||
"""
|
||||
enabled_patterns = tuple('.' + x.lstrip('.').lower()
|
||||
for x in enabled_extensions)
|
||||
disabled_patterns = tuple('.' + x.lstrip('.').lower()
|
||||
for x in disabled_extensions)
|
||||
enabled_patterns = tuple("." + x.lstrip(".").lower() for x in enabled_extensions)
|
||||
disabled_patterns = tuple("." + x.lstrip(".").lower() for x in disabled_extensions)
|
||||
|
||||
def autoescape(template_name):
|
||||
if template_name is None:
|
||||
return default_for_string
|
||||
@@ -543,6 +591,7 @@ def autoescape(template_name):
|
||||
if template_name.endswith(disabled_patterns):
|
||||
return False
|
||||
return default
|
||||
|
||||
return autoescape
|
||||
|
||||
|
||||
@@ -566,35 +615,63 @@ def htmlsafe_json_dumps(obj, dumper=None, **kwargs):
|
||||
"""
|
||||
if dumper is None:
|
||||
dumper = json.dumps
|
||||
rv = dumper(obj, **kwargs) \
|
||||
.replace(u'<', u'\\u003c') \
|
||||
.replace(u'>', u'\\u003e') \
|
||||
.replace(u'&', u'\\u0026') \
|
||||
.replace(u"'", u'\\u0027')
|
||||
rv = (
|
||||
dumper(obj, **kwargs)
|
||||
.replace(u"<", u"\\u003c")
|
||||
.replace(u">", u"\\u003e")
|
||||
.replace(u"&", u"\\u0026")
|
||||
.replace(u"'", u"\\u0027")
|
||||
)
|
||||
return Markup(rv)
|
||||
|
||||
|
||||
@implements_iterator
|
||||
class Cycler(object):
|
||||
"""A cycle helper for templates."""
|
||||
"""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.
|
||||
|
||||
.. code-block:: html+jinja
|
||||
|
||||
{% set row_class = cycler("odd", "even") %}
|
||||
<ul class="browser">
|
||||
{% for folder in folders %}
|
||||
<li class="folder {{ row_class.next() }}">{{ folder }}
|
||||
{% endfor %}
|
||||
{% for file in files %}
|
||||
<li class="file {{ row_class.next() }}">{{ file }}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
:param items: Each positional argument will be yielded in the order
|
||||
given for each cycle.
|
||||
|
||||
.. versionadded:: 2.1
|
||||
"""
|
||||
|
||||
def __init__(self, *items):
|
||||
if not items:
|
||||
raise RuntimeError('at least one item has to be provided')
|
||||
raise RuntimeError("at least one item has to be provided")
|
||||
self.items = items
|
||||
self.reset()
|
||||
self.pos = 0
|
||||
|
||||
def reset(self):
|
||||
"""Resets the cycle."""
|
||||
"""Resets the current item to the first item."""
|
||||
self.pos = 0
|
||||
|
||||
@property
|
||||
def current(self):
|
||||
"""Returns the current item."""
|
||||
"""Return the current item. Equivalent to the item that will be
|
||||
returned next time :meth:`next` is called.
|
||||
"""
|
||||
return self.items[self.pos]
|
||||
|
||||
def next(self):
|
||||
"""Goes one item ahead and returns it."""
|
||||
"""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
|
||||
@@ -605,27 +682,28 @@ def next(self):
|
||||
class Joiner(object):
|
||||
"""A joining helper for templates."""
|
||||
|
||||
def __init__(self, sep=u', '):
|
||||
def __init__(self, sep=u", "):
|
||||
self.sep = sep
|
||||
self.used = False
|
||||
|
||||
def __call__(self):
|
||||
if not self.used:
|
||||
self.used = True
|
||||
return u''
|
||||
return u""
|
||||
return self.sep
|
||||
|
||||
|
||||
class Namespace(object):
|
||||
"""A namespace object that can hold arbitrary attributes. It may be
|
||||
initialized from a dictionary or with keyword argments."""
|
||||
initialized from a dictionary or with keyword arguments."""
|
||||
|
||||
def __init__(*args, **kwargs):
|
||||
def __init__(*args, **kwargs): # noqa: B902
|
||||
self, args = args[0], args[1:]
|
||||
self.__attrs = dict(*args, **kwargs)
|
||||
|
||||
def __getattribute__(self, name):
|
||||
if name == '_Namespace__attrs':
|
||||
# __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]
|
||||
@@ -636,16 +714,24 @@ def __setitem__(self, name, value):
|
||||
self.__attrs[name] = value
|
||||
|
||||
def __repr__(self):
|
||||
return '<Namespace %r>' % self.__attrs
|
||||
return "<Namespace %r>" % self.__attrs
|
||||
|
||||
|
||||
# does this python version support async for in and async generators?
|
||||
try:
|
||||
exec('async def _():\n async for _ in ():\n yield _')
|
||||
exec("async def _():\n async for _ in ():\n yield _")
|
||||
have_async_gen = True
|
||||
except SyntaxError:
|
||||
have_async_gen = False
|
||||
|
||||
|
||||
# Imported here because that's where it was in the past
|
||||
from markupsafe import Markup, escape, soft_unicode
|
||||
def soft_unicode(s):
|
||||
from markupsafe import soft_unicode
|
||||
|
||||
warnings.warn(
|
||||
"'jinja2.utils.soft_unicode' will be removed in version 3.0."
|
||||
" Use 'markupsafe.soft_unicode' instead.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
return soft_unicode(s)
|
||||
|
14
lib/spack/external/jinja2/visitor.py
vendored
14
lib/spack/external/jinja2/visitor.py
vendored
@@ -1,14 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""API for traversing the AST nodes. Implemented by the compiler and
|
||||
meta introspection.
|
||||
"""
|
||||
jinja2.visitor
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
This module implements a visitor for the nodes.
|
||||
|
||||
:copyright: (c) 2017 by the Jinja Team.
|
||||
:license: BSD.
|
||||
"""
|
||||
from jinja2.nodes import Node
|
||||
from .nodes import Node
|
||||
|
||||
|
||||
class NodeVisitor(object):
|
||||
@@ -28,7 +22,7 @@ def get_visitor(self, node):
|
||||
exists for this node. In that case the generic visit function is
|
||||
used instead.
|
||||
"""
|
||||
method = 'visit_' + node.__class__.__name__
|
||||
method = "visit_" + node.__class__.__name__
|
||||
return getattr(self, method, None)
|
||||
|
||||
def visit(self, node, *args, **kwargs):
|
||||
|
13
lib/spack/external/markupsafe/AUTHORS
vendored
13
lib/spack/external/markupsafe/AUTHORS
vendored
@@ -1,13 +0,0 @@
|
||||
MarkupSafe is written and maintained by Armin Ronacher and
|
||||
various contributors:
|
||||
|
||||
Development Lead
|
||||
````````````````
|
||||
|
||||
- Armin Ronacher <armin.ronacher@active-4.com>
|
||||
|
||||
Patches and Suggestions
|
||||
```````````````````````
|
||||
|
||||
- Georg Brandl
|
||||
- Mickaël Guérin
|
33
lib/spack/external/markupsafe/LICENSE
vendored
33
lib/spack/external/markupsafe/LICENSE
vendored
@@ -1,33 +0,0 @@
|
||||
Copyright (c) 2010 by Armin Ronacher and contributors. See AUTHORS
|
||||
for more details.
|
||||
|
||||
Some rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms of the software as well
|
||||
as documentation, with or without modification, are permitted provided
|
||||
that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
|
||||
* The names of the contributors may not be used to endorse or
|
||||
promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
|
||||
NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE AND DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGE.
|
28
lib/spack/external/markupsafe/LICENSE.rst
vendored
Normal file
28
lib/spack/external/markupsafe/LICENSE.rst
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
Copyright 2010 Pallets
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
150
lib/spack/external/markupsafe/README.rst
vendored
150
lib/spack/external/markupsafe/README.rst
vendored
@@ -1,113 +1,69 @@
|
||||
MarkupSafe
|
||||
==========
|
||||
|
||||
Implements a unicode subclass that supports HTML strings:
|
||||
MarkupSafe implements a text object that escapes characters so it is
|
||||
safe to use in HTML and XML. Characters that have special meanings are
|
||||
replaced so that they display as the actual characters. This mitigates
|
||||
injection attacks, meaning untrusted user input can safely be displayed
|
||||
on a page.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
Installing
|
||||
----------
|
||||
|
||||
Install and update using `pip`_:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
pip install -U MarkupSafe
|
||||
|
||||
.. _pip: https://pip.pypa.io/en/stable/quickstart/
|
||||
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> from markupsafe import Markup, escape
|
||||
>>> escape("<script>alert(document.cookie);</script>")
|
||||
>>> # escape replaces special characters and wraps in Markup
|
||||
>>> escape('<script>alert(document.cookie);</script>')
|
||||
Markup(u'<script>alert(document.cookie);</script>')
|
||||
>>> tmpl = Markup("<em>%s</em>")
|
||||
>>> tmpl % "Peter > Lustig"
|
||||
Markup(u'<em>Peter > Lustig</em>')
|
||||
>>> # wrap in Markup to mark text "safe" and prevent escaping
|
||||
>>> Markup('<strong>Hello</strong>')
|
||||
Markup('<strong>hello</strong>')
|
||||
>>> escape(Markup('<strong>Hello</strong>'))
|
||||
Markup('<strong>hello</strong>')
|
||||
>>> # Markup is a text subclass (str on Python 3, unicode on Python 2)
|
||||
>>> # methods and operators escape their arguments
|
||||
>>> template = Markup("Hello <em>%s</em>")
|
||||
>>> template % '"World"'
|
||||
Markup('Hello <em>"World"</em>')
|
||||
|
||||
If you want to make an object unicode that is not yet unicode
|
||||
but don't want to lose the taint information, you can use the
|
||||
``soft_unicode`` function. (On Python 3 you can also use ``soft_str`` which
|
||||
is a different name for the same function).
|
||||
|
||||
.. code-block:: python
|
||||
Donate
|
||||
------
|
||||
|
||||
>>> from markupsafe import soft_unicode
|
||||
>>> soft_unicode(42)
|
||||
u'42'
|
||||
>>> soft_unicode(Markup('foo'))
|
||||
Markup(u'foo')
|
||||
The Pallets organization develops and supports MarkupSafe and other
|
||||
libraries that use it. In order to grow the community of contributors
|
||||
and users, and allow the maintainers to devote more time to the
|
||||
projects, `please donate today`_.
|
||||
|
||||
HTML Representations
|
||||
--------------------
|
||||
.. _please donate today: https://palletsprojects.com/donate
|
||||
|
||||
Objects can customize their HTML markup equivalent by overriding
|
||||
the ``__html__`` function:
|
||||
|
||||
.. code-block:: python
|
||||
Links
|
||||
-----
|
||||
|
||||
>>> class Foo(object):
|
||||
... def __html__(self):
|
||||
... return '<strong>Nice</strong>'
|
||||
...
|
||||
>>> escape(Foo())
|
||||
Markup(u'<strong>Nice</strong>')
|
||||
>>> Markup(Foo())
|
||||
Markup(u'<strong>Nice</strong>')
|
||||
* Website: https://palletsprojects.com/p/markupsafe/
|
||||
* Documentation: https://markupsafe.palletsprojects.com/
|
||||
* License: `BSD-3-Clause <https://github.com/pallets/markupsafe/blob/master/LICENSE.rst>`_
|
||||
* Releases: https://pypi.org/project/MarkupSafe/
|
||||
* Code: https://github.com/pallets/markupsafe
|
||||
* Issue tracker: https://github.com/pallets/markupsafe/issues
|
||||
* Test status:
|
||||
|
||||
Silent Escapes
|
||||
--------------
|
||||
* Linux, Mac: https://travis-ci.org/pallets/markupsafe
|
||||
* Windows: https://ci.appveyor.com/project/pallets/markupsafe
|
||||
|
||||
Since MarkupSafe 0.10 there is now also a separate escape function
|
||||
called ``escape_silent`` that returns an empty string for ``None`` for
|
||||
consistency with other systems that return empty strings for ``None``
|
||||
when escaping (for instance Pylons' webhelpers).
|
||||
|
||||
If you also want to use this for the escape method of the Markup
|
||||
object, you can create your own subclass that does that:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from markupsafe import Markup, escape_silent as escape
|
||||
|
||||
class SilentMarkup(Markup):
|
||||
__slots__ = ()
|
||||
|
||||
@classmethod
|
||||
def escape(cls, s):
|
||||
return cls(escape(s))
|
||||
|
||||
New-Style String Formatting
|
||||
---------------------------
|
||||
|
||||
Starting with MarkupSafe 0.21 new style string formats from Python 2.6 and
|
||||
3.x are now fully supported. Previously the escape behavior of those
|
||||
functions was spotty at best. The new implementations operates under the
|
||||
following algorithm:
|
||||
|
||||
1. if an object has an ``__html_format__`` method it is called as
|
||||
replacement for ``__format__`` with the format specifier. It either
|
||||
has to return a string or markup object.
|
||||
2. if an object has an ``__html__`` method it is called.
|
||||
3. otherwise the default format system of Python kicks in and the result
|
||||
is HTML escaped.
|
||||
|
||||
Here is how you can implement your own formatting:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class User(object):
|
||||
|
||||
def __init__(self, id, username):
|
||||
self.id = id
|
||||
self.username = username
|
||||
|
||||
def __html_format__(self, format_spec):
|
||||
if format_spec == 'link':
|
||||
return Markup('<a href="/user/{0}">{1}</a>').format(
|
||||
self.id,
|
||||
self.__html__(),
|
||||
)
|
||||
elif format_spec:
|
||||
raise ValueError('Invalid format spec')
|
||||
return self.__html__()
|
||||
|
||||
def __html__(self):
|
||||
return Markup('<span class=user>{0}</span>').format(self.username)
|
||||
|
||||
And to format that user:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> user = User(1, 'foo')
|
||||
>>> Markup('<p>User: {0:link}').format(user)
|
||||
Markup(u'<p>User: <a href="/user/1"><span class=user>foo</span></a>')
|
||||
|
||||
Markupsafe supports Python 2.6, 2.7 and Python 3.3 and higher.
|
||||
* Test coverage: https://codecov.io/gh/pallets/markupsafe
|
||||
|
263
lib/spack/external/markupsafe/__init__.py
vendored
263
lib/spack/external/markupsafe/__init__.py
vendored
@@ -1,80 +1,74 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
markupsafe
|
||||
~~~~~~~~~~
|
||||
markupsafe
|
||||
~~~~~~~~~~
|
||||
|
||||
Implements a Markup string.
|
||||
Implements an escape function and a Markup string to replace HTML
|
||||
special characters with safe representations.
|
||||
|
||||
:copyright: (c) 2010 by Armin Ronacher.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
:copyright: 2010 Pallets
|
||||
:license: BSD-3-Clause
|
||||
"""
|
||||
import re
|
||||
import string
|
||||
import sys
|
||||
from markupsafe._compat import text_type, string_types, int_types, \
|
||||
unichr, iteritems, PY2
|
||||
|
||||
if sys.version_info >= (3, 3):
|
||||
from collections.abc import Mapping
|
||||
else:
|
||||
from collections import Mapping
|
||||
from ._compat import int_types
|
||||
from ._compat import iteritems
|
||||
from ._compat import Mapping
|
||||
from ._compat import PY2
|
||||
from ._compat import string_types
|
||||
from ._compat import text_type
|
||||
from ._compat import unichr
|
||||
|
||||
__version__ = "1.0"
|
||||
__version__ = "1.1.1"
|
||||
|
||||
__all__ = ['Markup', 'soft_unicode', 'escape', 'escape_silent']
|
||||
__all__ = ["Markup", "soft_unicode", "escape", "escape_silent"]
|
||||
|
||||
|
||||
_striptags_re = re.compile(r'(<!--.*?-->|<[^>]*>)')
|
||||
_entity_re = re.compile(r'&([^& ;]+);')
|
||||
_striptags_re = re.compile(r"(<!--.*?-->|<[^>]*>)")
|
||||
_entity_re = re.compile(r"&([^& ;]+);")
|
||||
|
||||
|
||||
class Markup(text_type):
|
||||
r"""Marks a string as being safe for inclusion in HTML/XML output without
|
||||
needing to be escaped. This implements the `__html__` interface a couple
|
||||
of frameworks and web applications use. :class:`Markup` is a direct
|
||||
subclass of `unicode` and provides all the methods of `unicode` just that
|
||||
it escapes arguments passed and always returns `Markup`.
|
||||
"""A string that is ready to be safely inserted into an HTML or XML
|
||||
document, either because it was escaped or because it was marked
|
||||
safe.
|
||||
|
||||
The `escape` function returns markup objects so that double escaping can't
|
||||
happen.
|
||||
Passing an object to the constructor converts it to text and wraps
|
||||
it to mark it safe without escaping. To escape the text, use the
|
||||
:meth:`escape` class method instead.
|
||||
|
||||
The constructor of the :class:`Markup` class can be used for three
|
||||
different things: When passed an unicode object it's assumed to be safe,
|
||||
when passed an object with an HTML representation (has an `__html__`
|
||||
method) that representation is used, otherwise the object passed is
|
||||
converted into a unicode string and then assumed to be safe:
|
||||
>>> Markup('Hello, <em>World</em>!')
|
||||
Markup('Hello, <em>World</em>!')
|
||||
>>> Markup(42)
|
||||
Markup('42')
|
||||
>>> Markup.escape('Hello, <em>World</em>!')
|
||||
Markup('Hello <em>World</em>!')
|
||||
|
||||
>>> Markup("Hello <em>World</em>!")
|
||||
Markup(u'Hello <em>World</em>!')
|
||||
>>> class Foo(object):
|
||||
... def __html__(self):
|
||||
... return '<a href="#">foo</a>'
|
||||
This implements the ``__html__()`` interface that some frameworks
|
||||
use. Passing an object that implements ``__html__()`` will wrap the
|
||||
output of that method, marking it safe.
|
||||
|
||||
>>> class Foo:
|
||||
... def __html__(self):
|
||||
... return '<a href="/foo">foo</a>'
|
||||
...
|
||||
>>> Markup(Foo())
|
||||
Markup(u'<a href="#">foo</a>')
|
||||
Markup('<a href="/foo">foo</a>')
|
||||
|
||||
If you want object passed being always treated as unsafe you can use the
|
||||
:meth:`escape` classmethod to create a :class:`Markup` object:
|
||||
This is a subclass of the text type (``str`` in Python 3,
|
||||
``unicode`` in Python 2). It has the same methods as that type, but
|
||||
all methods escape their arguments and return a ``Markup`` instance.
|
||||
|
||||
>>> Markup.escape("Hello <em>World</em>!")
|
||||
Markup(u'Hello <em>World</em>!')
|
||||
|
||||
Operations on a markup string are markup aware which means that all
|
||||
arguments are passed through the :func:`escape` function:
|
||||
|
||||
>>> em = Markup("<em>%s</em>")
|
||||
>>> em % "foo & bar"
|
||||
Markup(u'<em>foo & bar</em>')
|
||||
>>> strong = Markup("<strong>%(text)s</strong>")
|
||||
>>> strong % {'text': '<blink>hacker here</blink>'}
|
||||
Markup(u'<strong><blink>hacker here</blink></strong>')
|
||||
>>> Markup("<em>Hello</em> ") + "<foo>"
|
||||
Markup(u'<em>Hello</em> <foo>')
|
||||
>>> Markup('<em>%s</em>') % 'foo & bar'
|
||||
Markup('<em>foo & bar</em>')
|
||||
>>> Markup('<em>Hello</em> ') + '<foo>'
|
||||
Markup('<em>Hello</em> <foo>')
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
def __new__(cls, base=u'', encoding=None, errors='strict'):
|
||||
if hasattr(base, '__html__'):
|
||||
def __new__(cls, base=u"", encoding=None, errors="strict"):
|
||||
if hasattr(base, "__html__"):
|
||||
base = base.__html__()
|
||||
if encoding is None:
|
||||
return text_type.__new__(cls, base)
|
||||
@@ -84,12 +78,12 @@ def __html__(self):
|
||||
return self
|
||||
|
||||
def __add__(self, other):
|
||||
if isinstance(other, string_types) or hasattr(other, '__html__'):
|
||||
if isinstance(other, string_types) or hasattr(other, "__html__"):
|
||||
return self.__class__(super(Markup, self).__add__(self.escape(other)))
|
||||
return NotImplemented
|
||||
|
||||
def __radd__(self, other):
|
||||
if hasattr(other, '__html__') or isinstance(other, string_types):
|
||||
if hasattr(other, "__html__") or isinstance(other, string_types):
|
||||
return self.escape(other).__add__(self)
|
||||
return NotImplemented
|
||||
|
||||
@@ -97,6 +91,7 @@ def __mul__(self, num):
|
||||
if isinstance(num, int_types):
|
||||
return self.__class__(text_type.__mul__(self, num))
|
||||
return NotImplemented
|
||||
|
||||
__rmul__ = __mul__
|
||||
|
||||
def __mod__(self, arg):
|
||||
@@ -107,115 +102,124 @@ def __mod__(self, arg):
|
||||
return self.__class__(text_type.__mod__(self, arg))
|
||||
|
||||
def __repr__(self):
|
||||
return '%s(%s)' % (
|
||||
self.__class__.__name__,
|
||||
text_type.__repr__(self)
|
||||
)
|
||||
return "%s(%s)" % (self.__class__.__name__, text_type.__repr__(self))
|
||||
|
||||
def join(self, seq):
|
||||
return self.__class__(text_type.join(self, map(self.escape, seq)))
|
||||
|
||||
join.__doc__ = text_type.join.__doc__
|
||||
|
||||
def split(self, *args, **kwargs):
|
||||
return list(map(self.__class__, text_type.split(self, *args, **kwargs)))
|
||||
|
||||
split.__doc__ = text_type.split.__doc__
|
||||
|
||||
def rsplit(self, *args, **kwargs):
|
||||
return list(map(self.__class__, text_type.rsplit(self, *args, **kwargs)))
|
||||
|
||||
rsplit.__doc__ = text_type.rsplit.__doc__
|
||||
|
||||
def splitlines(self, *args, **kwargs):
|
||||
return list(map(self.__class__, text_type.splitlines(
|
||||
self, *args, **kwargs)))
|
||||
return list(map(self.__class__, text_type.splitlines(self, *args, **kwargs)))
|
||||
|
||||
splitlines.__doc__ = text_type.splitlines.__doc__
|
||||
|
||||
def unescape(self):
|
||||
r"""Unescape markup again into an text_type string. This also resolves
|
||||
known HTML4 and XHTML entities:
|
||||
"""Convert escaped markup back into a text string. This replaces
|
||||
HTML entities with the characters they represent.
|
||||
|
||||
>>> Markup("Main » <em>About</em>").unescape()
|
||||
u'Main \xbb <em>About</em>'
|
||||
>>> Markup('Main » <em>About</em>').unescape()
|
||||
'Main » <em>About</em>'
|
||||
"""
|
||||
from markupsafe._constants import HTML_ENTITIES
|
||||
from ._constants import HTML_ENTITIES
|
||||
|
||||
def handle_match(m):
|
||||
name = m.group(1)
|
||||
if name in HTML_ENTITIES:
|
||||
return unichr(HTML_ENTITIES[name])
|
||||
try:
|
||||
if name[:2] in ('#x', '#X'):
|
||||
if name[:2] in ("#x", "#X"):
|
||||
return unichr(int(name[2:], 16))
|
||||
elif name.startswith('#'):
|
||||
elif name.startswith("#"):
|
||||
return unichr(int(name[1:]))
|
||||
except ValueError:
|
||||
pass
|
||||
# Don't modify unexpected input.
|
||||
return m.group()
|
||||
|
||||
return _entity_re.sub(handle_match, text_type(self))
|
||||
|
||||
def striptags(self):
|
||||
r"""Unescape markup into an text_type string and strip all tags. This
|
||||
also resolves known HTML4 and XHTML entities. Whitespace is
|
||||
normalized to one:
|
||||
""":meth:`unescape` the markup, remove tags, and normalize
|
||||
whitespace to single spaces.
|
||||
|
||||
>>> Markup("Main » <em>About</em>").striptags()
|
||||
u'Main \xbb About'
|
||||
>>> Markup('Main »\t<em>About</em>').striptags()
|
||||
'Main » About'
|
||||
"""
|
||||
stripped = u' '.join(_striptags_re.sub('', self).split())
|
||||
stripped = u" ".join(_striptags_re.sub("", self).split())
|
||||
return Markup(stripped).unescape()
|
||||
|
||||
@classmethod
|
||||
def escape(cls, s):
|
||||
"""Escape the string. Works like :func:`escape` with the difference
|
||||
that for subclasses of :class:`Markup` this function would return the
|
||||
correct subclass.
|
||||
"""Escape a string. Calls :func:`escape` and ensures that for
|
||||
subclasses the correct type is returned.
|
||||
"""
|
||||
rv = escape(s)
|
||||
if rv.__class__ is not cls:
|
||||
return cls(rv)
|
||||
return rv
|
||||
|
||||
def make_simple_escaping_wrapper(name):
|
||||
def make_simple_escaping_wrapper(name): # noqa: B902
|
||||
orig = getattr(text_type, name)
|
||||
|
||||
def func(self, *args, **kwargs):
|
||||
args = _escape_argspec(list(args), enumerate(args), self.escape)
|
||||
_escape_argspec(kwargs, iteritems(kwargs), self.escape)
|
||||
return self.__class__(orig(self, *args, **kwargs))
|
||||
|
||||
func.__name__ = orig.__name__
|
||||
func.__doc__ = orig.__doc__
|
||||
return func
|
||||
|
||||
for method in '__getitem__', 'capitalize', \
|
||||
'title', 'lower', 'upper', 'replace', 'ljust', \
|
||||
'rjust', 'lstrip', 'rstrip', 'center', 'strip', \
|
||||
'translate', 'expandtabs', 'swapcase', 'zfill':
|
||||
for method in (
|
||||
"__getitem__",
|
||||
"capitalize",
|
||||
"title",
|
||||
"lower",
|
||||
"upper",
|
||||
"replace",
|
||||
"ljust",
|
||||
"rjust",
|
||||
"lstrip",
|
||||
"rstrip",
|
||||
"center",
|
||||
"strip",
|
||||
"translate",
|
||||
"expandtabs",
|
||||
"swapcase",
|
||||
"zfill",
|
||||
):
|
||||
locals()[method] = make_simple_escaping_wrapper(method)
|
||||
|
||||
# new in python 2.5
|
||||
if hasattr(text_type, 'partition'):
|
||||
def partition(self, sep):
|
||||
return tuple(map(self.__class__,
|
||||
text_type.partition(self, self.escape(sep))))
|
||||
def rpartition(self, sep):
|
||||
return tuple(map(self.__class__,
|
||||
text_type.rpartition(self, self.escape(sep))))
|
||||
def partition(self, sep):
|
||||
return tuple(map(self.__class__, text_type.partition(self, self.escape(sep))))
|
||||
|
||||
# new in python 2.6
|
||||
if hasattr(text_type, 'format'):
|
||||
def format(*args, **kwargs):
|
||||
self, args = args[0], args[1:]
|
||||
formatter = EscapeFormatter(self.escape)
|
||||
kwargs = _MagicFormatMapping(args, kwargs)
|
||||
return self.__class__(formatter.vformat(self, args, kwargs))
|
||||
def rpartition(self, sep):
|
||||
return tuple(map(self.__class__, text_type.rpartition(self, self.escape(sep))))
|
||||
|
||||
def __html_format__(self, format_spec):
|
||||
if format_spec:
|
||||
raise ValueError('Unsupported format specification '
|
||||
'for Markup.')
|
||||
return self
|
||||
def format(self, *args, **kwargs):
|
||||
formatter = EscapeFormatter(self.escape)
|
||||
kwargs = _MagicFormatMapping(args, kwargs)
|
||||
return self.__class__(formatter.vformat(self, args, kwargs))
|
||||
|
||||
def __html_format__(self, format_spec):
|
||||
if format_spec:
|
||||
raise ValueError("Unsupported format specification " "for Markup.")
|
||||
return self
|
||||
|
||||
# not in python 3
|
||||
if hasattr(text_type, '__getslice__'):
|
||||
__getslice__ = make_simple_escaping_wrapper('__getslice__')
|
||||
if hasattr(text_type, "__getslice__"):
|
||||
__getslice__ = make_simple_escaping_wrapper("__getslice__")
|
||||
|
||||
del method, make_simple_escaping_wrapper
|
||||
|
||||
@@ -234,7 +238,7 @@ def __init__(self, args, kwargs):
|
||||
self._last_index = 0
|
||||
|
||||
def __getitem__(self, key):
|
||||
if key == '':
|
||||
if key == "":
|
||||
idx = self._last_index
|
||||
self._last_index += 1
|
||||
try:
|
||||
@@ -251,35 +255,37 @@ def __len__(self):
|
||||
return len(self._kwargs)
|
||||
|
||||
|
||||
if hasattr(text_type, 'format'):
|
||||
class EscapeFormatter(string.Formatter):
|
||||
if hasattr(text_type, "format"):
|
||||
|
||||
class EscapeFormatter(string.Formatter):
|
||||
def __init__(self, escape):
|
||||
self.escape = escape
|
||||
|
||||
def format_field(self, value, format_spec):
|
||||
if hasattr(value, '__html_format__'):
|
||||
if hasattr(value, "__html_format__"):
|
||||
rv = value.__html_format__(format_spec)
|
||||
elif hasattr(value, '__html__'):
|
||||
elif hasattr(value, "__html__"):
|
||||
if format_spec:
|
||||
raise ValueError('No format specification allowed '
|
||||
'when formatting an object with '
|
||||
'its __html__ method.')
|
||||
raise ValueError(
|
||||
"Format specifier {0} given, but {1} does not"
|
||||
" define __html_format__. A class that defines"
|
||||
" __html__ must define __html_format__ to work"
|
||||
" with format specifiers.".format(format_spec, type(value))
|
||||
)
|
||||
rv = value.__html__()
|
||||
else:
|
||||
# We need to make sure the format spec is unicode here as
|
||||
# otherwise the wrong callback methods are invoked. For
|
||||
# instance a byte string there would invoke __str__ and
|
||||
# not __unicode__.
|
||||
rv = string.Formatter.format_field(
|
||||
self, value, text_type(format_spec))
|
||||
rv = string.Formatter.format_field(self, value, text_type(format_spec))
|
||||
return text_type(self.escape(rv))
|
||||
|
||||
|
||||
def _escape_argspec(obj, iterable, escape):
|
||||
"""Helper for various string-wrapped functions."""
|
||||
for key, value in iterable:
|
||||
if hasattr(value, '__html__') or isinstance(value, string_types):
|
||||
if hasattr(value, "__html__") or isinstance(value, string_types):
|
||||
obj[key] = escape(value)
|
||||
return obj
|
||||
|
||||
@@ -291,20 +297,31 @@ def __init__(self, obj, escape):
|
||||
self.obj = obj
|
||||
self.escape = escape
|
||||
|
||||
__getitem__ = lambda s, x: _MarkupEscapeHelper(s.obj[x], s.escape)
|
||||
__unicode__ = __str__ = lambda s: text_type(s.escape(s.obj))
|
||||
__repr__ = lambda s: str(s.escape(repr(s.obj)))
|
||||
__int__ = lambda s: int(s.obj)
|
||||
__float__ = lambda s: float(s.obj)
|
||||
def __getitem__(self, item):
|
||||
return _MarkupEscapeHelper(self.obj[item], self.escape)
|
||||
|
||||
def __str__(self):
|
||||
return text_type(self.escape(self.obj))
|
||||
|
||||
__unicode__ = __str__
|
||||
|
||||
def __repr__(self):
|
||||
return str(self.escape(repr(self.obj)))
|
||||
|
||||
def __int__(self):
|
||||
return int(self.obj)
|
||||
|
||||
def __float__(self):
|
||||
return float(self.obj)
|
||||
|
||||
|
||||
# we have to import it down here as the speedups and native
|
||||
# modules imports the markup type which is define above.
|
||||
try:
|
||||
from markupsafe._speedups import escape, escape_silent, soft_unicode
|
||||
from ._speedups import escape, escape_silent, soft_unicode
|
||||
except ImportError:
|
||||
from markupsafe._native import escape, escape_silent, soft_unicode
|
||||
from ._native import escape, escape_silent, soft_unicode
|
||||
|
||||
if not PY2:
|
||||
soft_str = soft_unicode
|
||||
__all__.append('soft_str')
|
||||
__all__.append("soft_str")
|
||||
|
23
lib/spack/external/markupsafe/_compat.py
vendored
23
lib/spack/external/markupsafe/_compat.py
vendored
@@ -1,12 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
markupsafe._compat
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
markupsafe._compat
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Compatibility module for different Python versions.
|
||||
|
||||
:copyright: (c) 2013 by Armin Ronacher.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
:copyright: 2010 Pallets
|
||||
:license: BSD-3-Clause
|
||||
"""
|
||||
import sys
|
||||
|
||||
@@ -17,10 +15,19 @@
|
||||
string_types = (str,)
|
||||
unichr = chr
|
||||
int_types = (int,)
|
||||
iteritems = lambda x: iter(x.items())
|
||||
|
||||
def iteritems(x):
|
||||
return iter(x.items())
|
||||
|
||||
from collections.abc import Mapping
|
||||
|
||||
else:
|
||||
text_type = unicode
|
||||
string_types = (str, unicode)
|
||||
unichr = unichr
|
||||
int_types = (int, long)
|
||||
iteritems = lambda x: x.iteritems()
|
||||
|
||||
def iteritems(x):
|
||||
return x.iteritems()
|
||||
|
||||
from collections import Mapping
|
||||
|
517
lib/spack/external/markupsafe/_constants.py
vendored
517
lib/spack/external/markupsafe/_constants.py
vendored
@@ -1,267 +1,264 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
markupsafe._constants
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
markupsafe._constants
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Highlevel implementation of the Markup string.
|
||||
|
||||
:copyright: (c) 2010 by Armin Ronacher.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
:copyright: 2010 Pallets
|
||||
:license: BSD-3-Clause
|
||||
"""
|
||||
|
||||
|
||||
HTML_ENTITIES = {
|
||||
'AElig': 198,
|
||||
'Aacute': 193,
|
||||
'Acirc': 194,
|
||||
'Agrave': 192,
|
||||
'Alpha': 913,
|
||||
'Aring': 197,
|
||||
'Atilde': 195,
|
||||
'Auml': 196,
|
||||
'Beta': 914,
|
||||
'Ccedil': 199,
|
||||
'Chi': 935,
|
||||
'Dagger': 8225,
|
||||
'Delta': 916,
|
||||
'ETH': 208,
|
||||
'Eacute': 201,
|
||||
'Ecirc': 202,
|
||||
'Egrave': 200,
|
||||
'Epsilon': 917,
|
||||
'Eta': 919,
|
||||
'Euml': 203,
|
||||
'Gamma': 915,
|
||||
'Iacute': 205,
|
||||
'Icirc': 206,
|
||||
'Igrave': 204,
|
||||
'Iota': 921,
|
||||
'Iuml': 207,
|
||||
'Kappa': 922,
|
||||
'Lambda': 923,
|
||||
'Mu': 924,
|
||||
'Ntilde': 209,
|
||||
'Nu': 925,
|
||||
'OElig': 338,
|
||||
'Oacute': 211,
|
||||
'Ocirc': 212,
|
||||
'Ograve': 210,
|
||||
'Omega': 937,
|
||||
'Omicron': 927,
|
||||
'Oslash': 216,
|
||||
'Otilde': 213,
|
||||
'Ouml': 214,
|
||||
'Phi': 934,
|
||||
'Pi': 928,
|
||||
'Prime': 8243,
|
||||
'Psi': 936,
|
||||
'Rho': 929,
|
||||
'Scaron': 352,
|
||||
'Sigma': 931,
|
||||
'THORN': 222,
|
||||
'Tau': 932,
|
||||
'Theta': 920,
|
||||
'Uacute': 218,
|
||||
'Ucirc': 219,
|
||||
'Ugrave': 217,
|
||||
'Upsilon': 933,
|
||||
'Uuml': 220,
|
||||
'Xi': 926,
|
||||
'Yacute': 221,
|
||||
'Yuml': 376,
|
||||
'Zeta': 918,
|
||||
'aacute': 225,
|
||||
'acirc': 226,
|
||||
'acute': 180,
|
||||
'aelig': 230,
|
||||
'agrave': 224,
|
||||
'alefsym': 8501,
|
||||
'alpha': 945,
|
||||
'amp': 38,
|
||||
'and': 8743,
|
||||
'ang': 8736,
|
||||
'apos': 39,
|
||||
'aring': 229,
|
||||
'asymp': 8776,
|
||||
'atilde': 227,
|
||||
'auml': 228,
|
||||
'bdquo': 8222,
|
||||
'beta': 946,
|
||||
'brvbar': 166,
|
||||
'bull': 8226,
|
||||
'cap': 8745,
|
||||
'ccedil': 231,
|
||||
'cedil': 184,
|
||||
'cent': 162,
|
||||
'chi': 967,
|
||||
'circ': 710,
|
||||
'clubs': 9827,
|
||||
'cong': 8773,
|
||||
'copy': 169,
|
||||
'crarr': 8629,
|
||||
'cup': 8746,
|
||||
'curren': 164,
|
||||
'dArr': 8659,
|
||||
'dagger': 8224,
|
||||
'darr': 8595,
|
||||
'deg': 176,
|
||||
'delta': 948,
|
||||
'diams': 9830,
|
||||
'divide': 247,
|
||||
'eacute': 233,
|
||||
'ecirc': 234,
|
||||
'egrave': 232,
|
||||
'empty': 8709,
|
||||
'emsp': 8195,
|
||||
'ensp': 8194,
|
||||
'epsilon': 949,
|
||||
'equiv': 8801,
|
||||
'eta': 951,
|
||||
'eth': 240,
|
||||
'euml': 235,
|
||||
'euro': 8364,
|
||||
'exist': 8707,
|
||||
'fnof': 402,
|
||||
'forall': 8704,
|
||||
'frac12': 189,
|
||||
'frac14': 188,
|
||||
'frac34': 190,
|
||||
'frasl': 8260,
|
||||
'gamma': 947,
|
||||
'ge': 8805,
|
||||
'gt': 62,
|
||||
'hArr': 8660,
|
||||
'harr': 8596,
|
||||
'hearts': 9829,
|
||||
'hellip': 8230,
|
||||
'iacute': 237,
|
||||
'icirc': 238,
|
||||
'iexcl': 161,
|
||||
'igrave': 236,
|
||||
'image': 8465,
|
||||
'infin': 8734,
|
||||
'int': 8747,
|
||||
'iota': 953,
|
||||
'iquest': 191,
|
||||
'isin': 8712,
|
||||
'iuml': 239,
|
||||
'kappa': 954,
|
||||
'lArr': 8656,
|
||||
'lambda': 955,
|
||||
'lang': 9001,
|
||||
'laquo': 171,
|
||||
'larr': 8592,
|
||||
'lceil': 8968,
|
||||
'ldquo': 8220,
|
||||
'le': 8804,
|
||||
'lfloor': 8970,
|
||||
'lowast': 8727,
|
||||
'loz': 9674,
|
||||
'lrm': 8206,
|
||||
'lsaquo': 8249,
|
||||
'lsquo': 8216,
|
||||
'lt': 60,
|
||||
'macr': 175,
|
||||
'mdash': 8212,
|
||||
'micro': 181,
|
||||
'middot': 183,
|
||||
'minus': 8722,
|
||||
'mu': 956,
|
||||
'nabla': 8711,
|
||||
'nbsp': 160,
|
||||
'ndash': 8211,
|
||||
'ne': 8800,
|
||||
'ni': 8715,
|
||||
'not': 172,
|
||||
'notin': 8713,
|
||||
'nsub': 8836,
|
||||
'ntilde': 241,
|
||||
'nu': 957,
|
||||
'oacute': 243,
|
||||
'ocirc': 244,
|
||||
'oelig': 339,
|
||||
'ograve': 242,
|
||||
'oline': 8254,
|
||||
'omega': 969,
|
||||
'omicron': 959,
|
||||
'oplus': 8853,
|
||||
'or': 8744,
|
||||
'ordf': 170,
|
||||
'ordm': 186,
|
||||
'oslash': 248,
|
||||
'otilde': 245,
|
||||
'otimes': 8855,
|
||||
'ouml': 246,
|
||||
'para': 182,
|
||||
'part': 8706,
|
||||
'permil': 8240,
|
||||
'perp': 8869,
|
||||
'phi': 966,
|
||||
'pi': 960,
|
||||
'piv': 982,
|
||||
'plusmn': 177,
|
||||
'pound': 163,
|
||||
'prime': 8242,
|
||||
'prod': 8719,
|
||||
'prop': 8733,
|
||||
'psi': 968,
|
||||
'quot': 34,
|
||||
'rArr': 8658,
|
||||
'radic': 8730,
|
||||
'rang': 9002,
|
||||
'raquo': 187,
|
||||
'rarr': 8594,
|
||||
'rceil': 8969,
|
||||
'rdquo': 8221,
|
||||
'real': 8476,
|
||||
'reg': 174,
|
||||
'rfloor': 8971,
|
||||
'rho': 961,
|
||||
'rlm': 8207,
|
||||
'rsaquo': 8250,
|
||||
'rsquo': 8217,
|
||||
'sbquo': 8218,
|
||||
'scaron': 353,
|
||||
'sdot': 8901,
|
||||
'sect': 167,
|
||||
'shy': 173,
|
||||
'sigma': 963,
|
||||
'sigmaf': 962,
|
||||
'sim': 8764,
|
||||
'spades': 9824,
|
||||
'sub': 8834,
|
||||
'sube': 8838,
|
||||
'sum': 8721,
|
||||
'sup': 8835,
|
||||
'sup1': 185,
|
||||
'sup2': 178,
|
||||
'sup3': 179,
|
||||
'supe': 8839,
|
||||
'szlig': 223,
|
||||
'tau': 964,
|
||||
'there4': 8756,
|
||||
'theta': 952,
|
||||
'thetasym': 977,
|
||||
'thinsp': 8201,
|
||||
'thorn': 254,
|
||||
'tilde': 732,
|
||||
'times': 215,
|
||||
'trade': 8482,
|
||||
'uArr': 8657,
|
||||
'uacute': 250,
|
||||
'uarr': 8593,
|
||||
'ucirc': 251,
|
||||
'ugrave': 249,
|
||||
'uml': 168,
|
||||
'upsih': 978,
|
||||
'upsilon': 965,
|
||||
'uuml': 252,
|
||||
'weierp': 8472,
|
||||
'xi': 958,
|
||||
'yacute': 253,
|
||||
'yen': 165,
|
||||
'yuml': 255,
|
||||
'zeta': 950,
|
||||
'zwj': 8205,
|
||||
'zwnj': 8204
|
||||
"AElig": 198,
|
||||
"Aacute": 193,
|
||||
"Acirc": 194,
|
||||
"Agrave": 192,
|
||||
"Alpha": 913,
|
||||
"Aring": 197,
|
||||
"Atilde": 195,
|
||||
"Auml": 196,
|
||||
"Beta": 914,
|
||||
"Ccedil": 199,
|
||||
"Chi": 935,
|
||||
"Dagger": 8225,
|
||||
"Delta": 916,
|
||||
"ETH": 208,
|
||||
"Eacute": 201,
|
||||
"Ecirc": 202,
|
||||
"Egrave": 200,
|
||||
"Epsilon": 917,
|
||||
"Eta": 919,
|
||||
"Euml": 203,
|
||||
"Gamma": 915,
|
||||
"Iacute": 205,
|
||||
"Icirc": 206,
|
||||
"Igrave": 204,
|
||||
"Iota": 921,
|
||||
"Iuml": 207,
|
||||
"Kappa": 922,
|
||||
"Lambda": 923,
|
||||
"Mu": 924,
|
||||
"Ntilde": 209,
|
||||
"Nu": 925,
|
||||
"OElig": 338,
|
||||
"Oacute": 211,
|
||||
"Ocirc": 212,
|
||||
"Ograve": 210,
|
||||
"Omega": 937,
|
||||
"Omicron": 927,
|
||||
"Oslash": 216,
|
||||
"Otilde": 213,
|
||||
"Ouml": 214,
|
||||
"Phi": 934,
|
||||
"Pi": 928,
|
||||
"Prime": 8243,
|
||||
"Psi": 936,
|
||||
"Rho": 929,
|
||||
"Scaron": 352,
|
||||
"Sigma": 931,
|
||||
"THORN": 222,
|
||||
"Tau": 932,
|
||||
"Theta": 920,
|
||||
"Uacute": 218,
|
||||
"Ucirc": 219,
|
||||
"Ugrave": 217,
|
||||
"Upsilon": 933,
|
||||
"Uuml": 220,
|
||||
"Xi": 926,
|
||||
"Yacute": 221,
|
||||
"Yuml": 376,
|
||||
"Zeta": 918,
|
||||
"aacute": 225,
|
||||
"acirc": 226,
|
||||
"acute": 180,
|
||||
"aelig": 230,
|
||||
"agrave": 224,
|
||||
"alefsym": 8501,
|
||||
"alpha": 945,
|
||||
"amp": 38,
|
||||
"and": 8743,
|
||||
"ang": 8736,
|
||||
"apos": 39,
|
||||
"aring": 229,
|
||||
"asymp": 8776,
|
||||
"atilde": 227,
|
||||
"auml": 228,
|
||||
"bdquo": 8222,
|
||||
"beta": 946,
|
||||
"brvbar": 166,
|
||||
"bull": 8226,
|
||||
"cap": 8745,
|
||||
"ccedil": 231,
|
||||
"cedil": 184,
|
||||
"cent": 162,
|
||||
"chi": 967,
|
||||
"circ": 710,
|
||||
"clubs": 9827,
|
||||
"cong": 8773,
|
||||
"copy": 169,
|
||||
"crarr": 8629,
|
||||
"cup": 8746,
|
||||
"curren": 164,
|
||||
"dArr": 8659,
|
||||
"dagger": 8224,
|
||||
"darr": 8595,
|
||||
"deg": 176,
|
||||
"delta": 948,
|
||||
"diams": 9830,
|
||||
"divide": 247,
|
||||
"eacute": 233,
|
||||
"ecirc": 234,
|
||||
"egrave": 232,
|
||||
"empty": 8709,
|
||||
"emsp": 8195,
|
||||
"ensp": 8194,
|
||||
"epsilon": 949,
|
||||
"equiv": 8801,
|
||||
"eta": 951,
|
||||
"eth": 240,
|
||||
"euml": 235,
|
||||
"euro": 8364,
|
||||
"exist": 8707,
|
||||
"fnof": 402,
|
||||
"forall": 8704,
|
||||
"frac12": 189,
|
||||
"frac14": 188,
|
||||
"frac34": 190,
|
||||
"frasl": 8260,
|
||||
"gamma": 947,
|
||||
"ge": 8805,
|
||||
"gt": 62,
|
||||
"hArr": 8660,
|
||||
"harr": 8596,
|
||||
"hearts": 9829,
|
||||
"hellip": 8230,
|
||||
"iacute": 237,
|
||||
"icirc": 238,
|
||||
"iexcl": 161,
|
||||
"igrave": 236,
|
||||
"image": 8465,
|
||||
"infin": 8734,
|
||||
"int": 8747,
|
||||
"iota": 953,
|
||||
"iquest": 191,
|
||||
"isin": 8712,
|
||||
"iuml": 239,
|
||||
"kappa": 954,
|
||||
"lArr": 8656,
|
||||
"lambda": 955,
|
||||
"lang": 9001,
|
||||
"laquo": 171,
|
||||
"larr": 8592,
|
||||
"lceil": 8968,
|
||||
"ldquo": 8220,
|
||||
"le": 8804,
|
||||
"lfloor": 8970,
|
||||
"lowast": 8727,
|
||||
"loz": 9674,
|
||||
"lrm": 8206,
|
||||
"lsaquo": 8249,
|
||||
"lsquo": 8216,
|
||||
"lt": 60,
|
||||
"macr": 175,
|
||||
"mdash": 8212,
|
||||
"micro": 181,
|
||||
"middot": 183,
|
||||
"minus": 8722,
|
||||
"mu": 956,
|
||||
"nabla": 8711,
|
||||
"nbsp": 160,
|
||||
"ndash": 8211,
|
||||
"ne": 8800,
|
||||
"ni": 8715,
|
||||
"not": 172,
|
||||
"notin": 8713,
|
||||
"nsub": 8836,
|
||||
"ntilde": 241,
|
||||
"nu": 957,
|
||||
"oacute": 243,
|
||||
"ocirc": 244,
|
||||
"oelig": 339,
|
||||
"ograve": 242,
|
||||
"oline": 8254,
|
||||
"omega": 969,
|
||||
"omicron": 959,
|
||||
"oplus": 8853,
|
||||
"or": 8744,
|
||||
"ordf": 170,
|
||||
"ordm": 186,
|
||||
"oslash": 248,
|
||||
"otilde": 245,
|
||||
"otimes": 8855,
|
||||
"ouml": 246,
|
||||
"para": 182,
|
||||
"part": 8706,
|
||||
"permil": 8240,
|
||||
"perp": 8869,
|
||||
"phi": 966,
|
||||
"pi": 960,
|
||||
"piv": 982,
|
||||
"plusmn": 177,
|
||||
"pound": 163,
|
||||
"prime": 8242,
|
||||
"prod": 8719,
|
||||
"prop": 8733,
|
||||
"psi": 968,
|
||||
"quot": 34,
|
||||
"rArr": 8658,
|
||||
"radic": 8730,
|
||||
"rang": 9002,
|
||||
"raquo": 187,
|
||||
"rarr": 8594,
|
||||
"rceil": 8969,
|
||||
"rdquo": 8221,
|
||||
"real": 8476,
|
||||
"reg": 174,
|
||||
"rfloor": 8971,
|
||||
"rho": 961,
|
||||
"rlm": 8207,
|
||||
"rsaquo": 8250,
|
||||
"rsquo": 8217,
|
||||
"sbquo": 8218,
|
||||
"scaron": 353,
|
||||
"sdot": 8901,
|
||||
"sect": 167,
|
||||
"shy": 173,
|
||||
"sigma": 963,
|
||||
"sigmaf": 962,
|
||||
"sim": 8764,
|
||||
"spades": 9824,
|
||||
"sub": 8834,
|
||||
"sube": 8838,
|
||||
"sum": 8721,
|
||||
"sup": 8835,
|
||||
"sup1": 185,
|
||||
"sup2": 178,
|
||||
"sup3": 179,
|
||||
"supe": 8839,
|
||||
"szlig": 223,
|
||||
"tau": 964,
|
||||
"there4": 8756,
|
||||
"theta": 952,
|
||||
"thetasym": 977,
|
||||
"thinsp": 8201,
|
||||
"thorn": 254,
|
||||
"tilde": 732,
|
||||
"times": 215,
|
||||
"trade": 8482,
|
||||
"uArr": 8657,
|
||||
"uacute": 250,
|
||||
"uarr": 8593,
|
||||
"ucirc": 251,
|
||||
"ugrave": 249,
|
||||
"uml": 168,
|
||||
"upsih": 978,
|
||||
"upsilon": 965,
|
||||
"uuml": 252,
|
||||
"weierp": 8472,
|
||||
"xi": 958,
|
||||
"yacute": 253,
|
||||
"yen": 165,
|
||||
"yuml": 255,
|
||||
"zeta": 950,
|
||||
"zwj": 8205,
|
||||
"zwnj": 8204,
|
||||
}
|
||||
|
67
lib/spack/external/markupsafe/_native.py
vendored
67
lib/spack/external/markupsafe/_native.py
vendored
@@ -1,36 +1,49 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
markupsafe._native
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
markupsafe._native
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Native Python implementation the C module is not compiled.
|
||||
Native Python implementation used when the C module is not compiled.
|
||||
|
||||
:copyright: (c) 2010 by Armin Ronacher.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
:copyright: 2010 Pallets
|
||||
:license: BSD-3-Clause
|
||||
"""
|
||||
from markupsafe import Markup
|
||||
from markupsafe._compat import text_type
|
||||
from . import Markup
|
||||
from ._compat import text_type
|
||||
|
||||
|
||||
def escape(s):
|
||||
"""Convert the characters &, <, >, ' and " in string s to HTML-safe
|
||||
sequences. Use this if you need to display text that might contain
|
||||
such characters in HTML. Marks return value as markup string.
|
||||
"""Replace the characters ``&``, ``<``, ``>``, ``'``, and ``"`` in
|
||||
the string with HTML-safe sequences. Use this if you need to display
|
||||
text that might contain such characters in HTML.
|
||||
|
||||
If the object has an ``__html__`` method, it is called and the
|
||||
return value is assumed to already be safe for HTML.
|
||||
|
||||
:param s: An object to be converted to a string and escaped.
|
||||
:return: A :class:`Markup` string with the escaped text.
|
||||
"""
|
||||
if hasattr(s, '__html__'):
|
||||
return s.__html__()
|
||||
return Markup(text_type(s)
|
||||
.replace('&', '&')
|
||||
.replace('>', '>')
|
||||
.replace('<', '<')
|
||||
.replace("'", ''')
|
||||
.replace('"', '"')
|
||||
if hasattr(s, "__html__"):
|
||||
return Markup(s.__html__())
|
||||
return Markup(
|
||||
text_type(s)
|
||||
.replace("&", "&")
|
||||
.replace(">", ">")
|
||||
.replace("<", "<")
|
||||
.replace("'", "'")
|
||||
.replace('"', """)
|
||||
)
|
||||
|
||||
|
||||
def escape_silent(s):
|
||||
"""Like :func:`escape` but converts `None` into an empty
|
||||
markup string.
|
||||
"""Like :func:`escape` but treats ``None`` as the empty string.
|
||||
Useful with optional values, as otherwise you get the string
|
||||
``'None'`` when the value is ``None``.
|
||||
|
||||
>>> escape(None)
|
||||
Markup('None')
|
||||
>>> escape_silent(None)
|
||||
Markup('')
|
||||
"""
|
||||
if s is None:
|
||||
return Markup()
|
||||
@@ -38,8 +51,18 @@ def escape_silent(s):
|
||||
|
||||
|
||||
def soft_unicode(s):
|
||||
"""Make a string unicode if it isn't already. That way a markup
|
||||
string is not converted back to unicode.
|
||||
"""Convert an object to a string if it isn't already. This preserves
|
||||
a :class:`Markup` string rather than converting it back to a basic
|
||||
string, so it will still be marked as safe and won't be escaped
|
||||
again.
|
||||
|
||||
>>> value = escape('<User 1>')
|
||||
>>> value
|
||||
Markup('<User 1>')
|
||||
>>> escape(str(value))
|
||||
Markup('&lt;User 1&gt;')
|
||||
>>> escape(soft_unicode(value))
|
||||
Markup('<User 1>')
|
||||
"""
|
||||
if not isinstance(s, text_type):
|
||||
s = text_type(s)
|
||||
|
Reference in New Issue
Block a user