Update to the latest version of jinja2 (#6790)
This commit is contained in:
parent
4fdf08e51c
commit
d17a10c6ac
33
lib/spack/external/jinja2/AUTHORS
vendored
33
lib/spack/external/jinja2/AUTHORS
vendored
@ -1,33 +0,0 @@
|
||||
Jinja is written and maintained by the Jinja Team and various
|
||||
contributors:
|
||||
|
||||
Lead Developer:
|
||||
|
||||
- Armin Ronacher <armin.ronacher@active-4.com>
|
||||
|
||||
Developers:
|
||||
|
||||
- Christoph Hack
|
||||
- Georg Brandl
|
||||
|
||||
Contributors:
|
||||
|
||||
- Bryan McLemore
|
||||
- Mickaël Guérin <kael@crocobox.org>
|
||||
- Cameron Knight
|
||||
- Lawrence Journal-World.
|
||||
- David Cramer
|
||||
|
||||
Patches and suggestions:
|
||||
|
||||
- Ronny Pfannschmidt
|
||||
- Axel Böhm
|
||||
- Alexey Melchakov
|
||||
- Bryan McLemore
|
||||
- Clovis Fabricio (nosklo)
|
||||
- Cameron Knight
|
||||
- Peter van Dijk (Habbie)
|
||||
- Stefan Ebner
|
||||
- Rene Leonhardt
|
||||
- Thomas Waldmann
|
||||
- Cory Benfield (Lukasa)
|
31
lib/spack/external/jinja2/LICENSE
vendored
31
lib/spack/external/jinja2/LICENSE
vendored
@ -1,31 +0,0 @@
|
||||
Copyright (c) 2009 by the Jinja Team, see AUTHORS for more details.
|
||||
|
||||
Some rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, 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 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, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
51
lib/spack/external/jinja2/README.rst
vendored
51
lib/spack/external/jinja2/README.rst
vendored
@ -1,51 +0,0 @@
|
||||
Jinja2
|
||||
~~~~~~
|
||||
|
||||
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.
|
||||
|
||||
Nutshell
|
||||
--------
|
||||
|
||||
Here a small example of a Jinja template:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
{% 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 %}
|
||||
|
||||
Philosophy
|
||||
----------
|
||||
|
||||
Application logic is for the controller, but don't make the template designer's
|
||||
life difficult by restricting functionality too much.
|
||||
|
||||
For more information visit the new `Jinja2 webpage`_ and `documentation`_.
|
||||
|
||||
The `Jinja2 tip`_ is installable via ``pip`` with ``pip install
|
||||
https://github.com/pallets/jinja/zipball/master``.
|
||||
|
||||
.. _sandboxed: http://en.wikipedia.org/wiki/Sandbox_(computer_security)
|
||||
.. _Django: http://www.djangoproject.com/
|
||||
.. _Jinja2 webpage: http://jinja.pocoo.org/
|
||||
.. _documentation: http://jinja.pocoo.org/docs/
|
||||
.. _Jinja2 tip: http://jinja.pocoo.org/docs/intro/#as-a-python-egg-via-easy-install
|
||||
|
||||
Builds
|
||||
------
|
||||
|
||||
+---------------------+------------------------------------------------------------------------------+
|
||||
| ``master`` | .. image:: https://travis-ci.org/pallets/jinja.svg?branch=master |
|
||||
| | :target: https://travis-ci.org/pallets/jinja |
|
||||
+---------------------+------------------------------------------------------------------------------+
|
||||
| ``2.9-maintenance`` | .. image:: https://travis-ci.org/pallets/jinja.svg?branch=2.9-maintenance |
|
||||
| | :target: https://travis-ci.org/pallets/jinja |
|
||||
+---------------------+------------------------------------------------------------------------------+
|
5
lib/spack/external/jinja2/__init__.py
vendored
5
lib/spack/external/jinja2/__init__.py
vendored
@ -27,7 +27,7 @@
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
__docformat__ = 'restructuredtext en'
|
||||
__version__ = '2.9.6'
|
||||
__version__ = '2.10'
|
||||
|
||||
# high level interface
|
||||
from jinja2.environment import Environment, Template
|
||||
@ -48,7 +48,7 @@
|
||||
# exceptions
|
||||
from jinja2.exceptions import TemplateError, UndefinedError, \
|
||||
TemplateNotFound, TemplatesNotFound, TemplateSyntaxError, \
|
||||
TemplateAssertionError
|
||||
TemplateAssertionError, TemplateRuntimeError
|
||||
|
||||
# decorators and public utilities
|
||||
from jinja2.filters import environmentfilter, contextfilter, \
|
||||
@ -64,6 +64,7 @@
|
||||
'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',
|
||||
|
2
lib/spack/external/jinja2/_identifier.py
vendored
Normal file
2
lib/spack/external/jinja2/_identifier.py
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
# generated by scripts/generate_identifier_pattern.py
|
||||
pattern = '·̀-ͯ·҃-֑҇-ׇֽֿׁׂׅׄؐ-ًؚ-ٰٟۖ-ۜ۟-۪ۤۧۨ-ܑۭܰ-݊ަ-ް߫-߳ࠖ-࠙ࠛ-ࠣࠥ-ࠧࠩ-࡙࠭-࡛ࣔ-ࣣ࣡-ःऺ-़ा-ॏ॑-ॗॢॣঁ-ঃ়া-ৄেৈো-্ৗৢৣਁ-ਃ਼ਾ-ੂੇੈੋ-੍ੑੰੱੵઁ-ઃ઼ા-ૅે-ૉો-્ૢૣଁ-ଃ଼ା-ୄେୈୋ-୍ୖୗୢୣஂா-ூெ-ைொ-்ௗఀ-ఃా-ౄె-ైొ-్ౕౖౢౣಁ-ಃ಼ಾ-ೄೆ-ೈೊ-್ೕೖೢೣഁ-ഃാ-ൄെ-ൈൊ-്ൗൢൣංඃ්ා-ුූෘ-ෟෲෳัิ-ฺ็-๎ັິ-ູົຼ່-ໍ༹༘༙༵༷༾༿ཱ-྄྆྇ྍ-ྗྙ-ྼ࿆ါ-ှၖ-ၙၞ-ၠၢ-ၤၧ-ၭၱ-ၴႂ-ႍႏႚ-ႝ፝-፟ᜒ-᜔ᜲ-᜴ᝒᝓᝲᝳ឴-៓៝᠋-᠍ᢅᢆᢩᤠ-ᤫᤰ-᤻ᨗ-ᨛᩕ-ᩞ᩠-᩿᩼᪰-᪽ᬀ-ᬄ᬴-᭄᭫-᭳ᮀ-ᮂᮡ-ᮭ᯦-᯳ᰤ-᰷᳐-᳔᳒-᳨᳭ᳲ-᳴᳸᳹᷀-᷵᷻-᷿‿⁀⁔⃐-⃥⃜⃡-⃰℘℮⳯-⵿⳱ⷠ-〪ⷿ-゙゚〯꙯ꙴ-꙽ꚞꚟ꛰꛱ꠂ꠆ꠋꠣ-ꠧꢀꢁꢴ-ꣅ꣠-꣱ꤦ-꤭ꥇ-꥓ꦀ-ꦃ꦳-꧀ꧥꨩ-ꨶꩃꩌꩍꩻ-ꩽꪰꪲ-ꪴꪷꪸꪾ꪿꫁ꫫ-ꫯꫵ꫶ꯣ-ꯪ꯬꯭ﬞ︀-️︠-︯︳︴﹍-﹏_𐇽𐋠𐍶-𐍺𐨁-𐨃𐨅𐨆𐨌-𐨏𐨸-𐨿𐨺𐫦𐫥𑀀-𑀂𑀸-𑁆𑁿-𑂂𑂰-𑂺𑄀-𑄂𑄧-𑅳𑄴𑆀-𑆂𑆳-𑇊𑇀-𑇌𑈬-𑈷𑈾𑋟-𑋪𑌀-𑌃𑌼𑌾-𑍄𑍇𑍈𑍋-𑍍𑍗𑍢𑍣𑍦-𑍬𑍰-𑍴𑐵-𑑆𑒰-𑓃𑖯-𑖵𑖸-𑗀𑗜𑗝𑘰-𑙀𑚫-𑚷𑜝-𑜫𑰯-𑰶𑰸-𑰿𑲒-𑲧𑲩-𑲶𖫰-𖫴𖬰-𖬶𖽑-𖽾𖾏-𖾒𛲝𛲞𝅥-𝅩𝅭-𝅲𝅻-𝆂𝆅-𝆋𝆪-𝆭𝉂-𝉄𝨀-𝨶𝨻-𝩬𝩵𝪄𝪛-𝪟𝪡-𝪯𞀀-𞀆𞀈-𞀘𞀛-𞀡𞀣𞀤𞀦-𞣐𞀪-𞣖𞥄-𞥊󠄀-󠇯'
|
71
lib/spack/external/jinja2/_stringdefs.py
vendored
71
lib/spack/external/jinja2/_stringdefs.py
vendored
File diff suppressed because one or more lines are too long
14
lib/spack/external/jinja2/asyncsupport.py
vendored
14
lib/spack/external/jinja2/asyncsupport.py
vendored
@ -189,9 +189,9 @@ async def auto_aiter(iterable):
|
||||
|
||||
class AsyncLoopContext(LoopContextBase):
|
||||
|
||||
def __init__(self, async_iterator, after, length, recurse=None,
|
||||
def __init__(self, async_iterator, undefined, after, length, recurse=None,
|
||||
depth0=0):
|
||||
LoopContextBase.__init__(self, recurse, depth0)
|
||||
LoopContextBase.__init__(self, undefined, recurse, depth0)
|
||||
self._async_iterator = async_iterator
|
||||
self._after = after
|
||||
self._length = length
|
||||
@ -221,15 +221,16 @@ async def __anext__(self):
|
||||
ctx.index0 += 1
|
||||
if ctx._after is _last_iteration:
|
||||
raise StopAsyncIteration()
|
||||
next_elem = ctx._after
|
||||
ctx._before = ctx._current
|
||||
ctx._current = ctx._after
|
||||
try:
|
||||
ctx._after = await ctx._async_iterator.__anext__()
|
||||
except StopAsyncIteration:
|
||||
ctx._after = _last_iteration
|
||||
return next_elem, ctx
|
||||
return ctx._current, ctx
|
||||
|
||||
|
||||
async def make_async_loop_context(iterable, recurse=None, depth0=0):
|
||||
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
|
||||
@ -251,4 +252,5 @@ async def make_async_loop_context(iterable, recurse=None, depth0=0):
|
||||
after = await async_iterator.__anext__()
|
||||
except StopAsyncIteration:
|
||||
after = _last_iteration
|
||||
return AsyncLoopContext(async_iterator, after, length, recurse, depth0)
|
||||
return AsyncLoopContext(async_iterator, undefined, after, length, recurse,
|
||||
depth0)
|
||||
|
2
lib/spack/external/jinja2/bccache.py
vendored
2
lib/spack/external/jinja2/bccache.py
vendored
@ -297,7 +297,7 @@ class MemcachedBytecodeCache(BytecodeCache):
|
||||
Libraries compatible with this class:
|
||||
|
||||
- `werkzeug <http://werkzeug.pocoo.org/>`_.contrib.cache
|
||||
- `python-memcached <http://www.tummy.com/Community/software/python-memcached/>`_
|
||||
- `python-memcached <https://www.tummy.com/Community/software/python-memcached/>`_
|
||||
- `cmemcache <http://gijsbert.org/cmemcache/>`_
|
||||
|
||||
(Unfortunately the django cache interface is not compatible because it
|
||||
|
88
lib/spack/external/jinja2/compiler.py
vendored
88
lib/spack/external/jinja2/compiler.py
vendored
@ -130,9 +130,10 @@ def __init__(self, node):
|
||||
class Frame(object):
|
||||
"""Holds compile time information for us."""
|
||||
|
||||
def __init__(self, eval_ctx, parent=None):
|
||||
def __init__(self, eval_ctx, parent=None, level=None):
|
||||
self.eval_ctx = eval_ctx
|
||||
self.symbols = Symbols(parent and parent.symbols or None)
|
||||
self.symbols = Symbols(parent and parent.symbols or None,
|
||||
level=level)
|
||||
|
||||
# a toplevel frame is the root + soft frames such as if conditions.
|
||||
self.toplevel = False
|
||||
@ -168,8 +169,10 @@ def copy(self):
|
||||
rv.symbols = self.symbols.copy()
|
||||
return rv
|
||||
|
||||
def inner(self):
|
||||
def inner(self, isolated=False):
|
||||
"""Return an inner frame."""
|
||||
if isolated:
|
||||
return Frame(self.eval_ctx, level=self.symbols.level + 1)
|
||||
return Frame(self.eval_ctx, self)
|
||||
|
||||
def soft(self):
|
||||
@ -302,6 +305,9 @@ def __init__(self, environment, name, filename, stream=None,
|
||||
# Tracks parameter definition blocks
|
||||
self._param_def_block = []
|
||||
|
||||
# Tracks the current context.
|
||||
self._context_reference_stack = ['context']
|
||||
|
||||
# -- Various compilation helpers
|
||||
|
||||
def fail(self, msg, lineno):
|
||||
@ -472,8 +478,8 @@ def enter_frame(self, frame):
|
||||
if action == VAR_LOAD_PARAMETER:
|
||||
pass
|
||||
elif action == VAR_LOAD_RESOLVE:
|
||||
self.writeline('%s = resolve(%r)' %
|
||||
(target, param))
|
||||
self.writeline('%s = %s(%r)' %
|
||||
(target, self.get_resolve_func(), param))
|
||||
elif action == VAR_LOAD_ALIAS:
|
||||
self.writeline('%s = %s' % (target, param))
|
||||
elif action == VAR_LOAD_UNDEFINED:
|
||||
@ -625,6 +631,27 @@ def mark_parameter_stored(self, target):
|
||||
if self._param_def_block:
|
||||
self._param_def_block[-1].discard(target)
|
||||
|
||||
def push_context_reference(self, target):
|
||||
self._context_reference_stack.append(target)
|
||||
|
||||
def pop_context_reference(self):
|
||||
self._context_reference_stack.pop()
|
||||
|
||||
def get_context_ref(self):
|
||||
return self._context_reference_stack[-1]
|
||||
|
||||
def get_resolve_func(self):
|
||||
target = self._context_reference_stack[-1]
|
||||
if target == 'context':
|
||||
return 'resolve'
|
||||
return '%s.resolve' % target
|
||||
|
||||
def derive_context(self, frame):
|
||||
return '%s.derived(%s)' % (
|
||||
self.get_context_ref(),
|
||||
self.dump_local_context(frame),
|
||||
)
|
||||
|
||||
def parameter_is_undeclared(self, target):
|
||||
"""Checks if a given target is an undeclared parameter."""
|
||||
if not self._param_def_block:
|
||||
@ -793,8 +820,11 @@ def visit_Block(self, node, frame):
|
||||
self.writeline('if parent_template is None:')
|
||||
self.indent()
|
||||
level += 1
|
||||
context = node.scoped and (
|
||||
'context.derived(%s)' % self.dump_local_context(frame)) or 'context'
|
||||
|
||||
if node.scoped:
|
||||
context = self.derive_context(frame)
|
||||
else:
|
||||
context = self.get_context_ref()
|
||||
|
||||
if supports_yield_from and not self.environment.is_async and \
|
||||
frame.buffer is None:
|
||||
@ -1082,9 +1112,9 @@ def visit_For(self, node, frame):
|
||||
self.write(')')
|
||||
|
||||
if node.recursive:
|
||||
self.write(', loop_render_func, depth):')
|
||||
self.write(', undefined, loop_render_func, depth):')
|
||||
else:
|
||||
self.write(extended_loop and '):' or ':')
|
||||
self.write(extended_loop and ', undefined):' or ':')
|
||||
|
||||
self.indent()
|
||||
self.enter_frame(loop_frame)
|
||||
@ -1129,6 +1159,13 @@ def visit_If(self, node, frame):
|
||||
self.indent()
|
||||
self.blockvisit(node.body, if_frame)
|
||||
self.outdent()
|
||||
for elif_ in node.elif_:
|
||||
self.writeline('elif ', elif_)
|
||||
self.visit(elif_.test, if_frame)
|
||||
self.write(':')
|
||||
self.indent()
|
||||
self.blockvisit(elif_.body, if_frame)
|
||||
self.outdent()
|
||||
if node.else_:
|
||||
self.writeline('else:')
|
||||
self.indent()
|
||||
@ -1348,7 +1385,12 @@ def visit_AssignBlock(self, node, frame):
|
||||
self.newline(node)
|
||||
self.visit(node.target, frame)
|
||||
self.write(' = (Markup if context.eval_ctx.autoescape '
|
||||
'else identity)(concat(%s))' % block_frame.buffer)
|
||||
'else identity)(')
|
||||
if node.filter is not None:
|
||||
self.visit_Filter(node.filter, block_frame)
|
||||
else:
|
||||
self.write('concat(%s)' % block_frame.buffer)
|
||||
self.write(')')
|
||||
self.pop_assign_tracking(frame)
|
||||
self.leave_frame(block_frame)
|
||||
|
||||
@ -1373,6 +1415,18 @@ def visit_Name(self, node, frame):
|
||||
|
||||
self.write(ref)
|
||||
|
||||
def visit_NSRef(self, node, frame):
|
||||
# NSRefs can only be used to store values; since they use the normal
|
||||
# `foo.bar` notation they will be parsed as a normal attribute access
|
||||
# when used anywhere but in a `set` context
|
||||
ref = frame.symbols.ref(node.name)
|
||||
self.writeline('if not isinstance(%s, Namespace):' % ref)
|
||||
self.indent()
|
||||
self.writeline('raise TemplateRuntimeError(%r)' %
|
||||
'cannot assign attribute on non-namespace object')
|
||||
self.outdent()
|
||||
self.writeline('%s[%r]' % (ref, node.attr))
|
||||
|
||||
def visit_Const(self, node, frame):
|
||||
val = node.as_const(frame.eval_ctx)
|
||||
if isinstance(val, float):
|
||||
@ -1631,6 +1685,20 @@ def visit_Scope(self, node, frame):
|
||||
self.blockvisit(node.body, scope_frame)
|
||||
self.leave_frame(scope_frame)
|
||||
|
||||
def visit_OverlayScope(self, node, frame):
|
||||
ctx = self.temporary_identifier()
|
||||
self.writeline('%s = %s' % (ctx, self.derive_context(frame)))
|
||||
self.writeline('%s.vars = ' % ctx)
|
||||
self.visit(node.context, frame)
|
||||
self.push_context_reference(ctx)
|
||||
|
||||
scope_frame = frame.inner(isolated=True)
|
||||
scope_frame.symbols.analyze_node(node)
|
||||
self.enter_frame(scope_frame)
|
||||
self.blockvisit(node.body, scope_frame)
|
||||
self.leave_frame(scope_frame)
|
||||
self.pop_context_reference()
|
||||
|
||||
def visit_EvalContextModifier(self, node, frame):
|
||||
for keyword in node.options:
|
||||
self.writeline('context.eval_ctx.%s = ' % keyword.key)
|
||||
|
2
lib/spack/external/jinja2/debug.py
vendored
2
lib/spack/external/jinja2/debug.py
vendored
@ -198,7 +198,7 @@ def translate_exception(exc_info, initial_skip=0):
|
||||
def get_jinja_locals(real_locals):
|
||||
ctx = real_locals.get('context')
|
||||
if ctx:
|
||||
locals = ctx.get_all()
|
||||
locals = ctx.get_all().copy()
|
||||
else:
|
||||
locals = {}
|
||||
|
||||
|
6
lib/spack/external/jinja2/defaults.py
vendored
6
lib/spack/external/jinja2/defaults.py
vendored
@ -9,7 +9,7 @@
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
from jinja2._compat import range_type
|
||||
from jinja2.utils import generate_lorem_ipsum, Cycler, Joiner
|
||||
from jinja2.utils import generate_lorem_ipsum, Cycler, Joiner, Namespace
|
||||
|
||||
|
||||
# defaults for the parser / lexer
|
||||
@ -35,7 +35,8 @@
|
||||
'dict': dict,
|
||||
'lipsum': generate_lorem_ipsum,
|
||||
'cycler': Cycler,
|
||||
'joiner': Joiner
|
||||
'joiner': Joiner,
|
||||
'namespace': Namespace
|
||||
}
|
||||
|
||||
|
||||
@ -47,6 +48,7 @@
|
||||
'truncate.leeway': 5,
|
||||
'json.dumps_function': None,
|
||||
'json.dumps_kwargs': {'sort_keys': True},
|
||||
'ext.i18n.trimmed': False,
|
||||
}
|
||||
|
||||
|
||||
|
2
lib/spack/external/jinja2/environment.py
vendored
2
lib/spack/external/jinja2/environment.py
vendored
@ -809,7 +809,7 @@ def _load_template(self, name, globals):
|
||||
@internalcode
|
||||
def get_template(self, name, parent=None, globals=None):
|
||||
"""Load a template from the loader. If a loader is configured this
|
||||
method ask the loader for the template and returns a :class:`Template`.
|
||||
method asks the loader for the template and returns a :class:`Template`.
|
||||
If the `parent` parameter is not `None`, :meth:`join_path` is called
|
||||
to get the real template name before loading.
|
||||
|
||||
|
20
lib/spack/external/jinja2/ext.py
vendored
20
lib/spack/external/jinja2/ext.py
vendored
@ -10,6 +10,8 @@
|
||||
:copyright: (c) 2017 by the Jinja Team.
|
||||
:license: BSD.
|
||||
"""
|
||||
import re
|
||||
|
||||
from jinja2 import nodes
|
||||
from jinja2.defaults import BLOCK_START_STRING, \
|
||||
BLOCK_END_STRING, VARIABLE_START_STRING, VARIABLE_END_STRING, \
|
||||
@ -223,6 +225,7 @@ def parse(self, parser):
|
||||
plural_expr = None
|
||||
plural_expr_assignment = None
|
||||
variables = {}
|
||||
trimmed = None
|
||||
while parser.stream.current.type != 'block_end':
|
||||
if variables:
|
||||
parser.stream.expect('comma')
|
||||
@ -241,6 +244,9 @@ def parse(self, parser):
|
||||
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'
|
||||
continue
|
||||
else:
|
||||
variables[name.value] = var = nodes.Name(name.value, 'load')
|
||||
|
||||
@ -256,7 +262,7 @@ def parse(self, parser):
|
||||
|
||||
parser.stream.expect('block_end')
|
||||
|
||||
plural = plural_names = None
|
||||
plural = None
|
||||
have_plural = False
|
||||
referenced = set()
|
||||
|
||||
@ -297,6 +303,13 @@ def parse(self, parser):
|
||||
elif plural_expr is None:
|
||||
parser.fail('pluralize without variables', lineno)
|
||||
|
||||
if trimmed is None:
|
||||
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)
|
||||
@ -306,6 +319,9 @@ def parse(self, parser):
|
||||
else:
|
||||
return node
|
||||
|
||||
def _trim_whitespace(self, string, _ws_re=re.compile(r'\s*\n\s*')):
|
||||
return _ws_re.sub(' ', string.strip())
|
||||
|
||||
def _parse_block(self, parser, allow_pluralize):
|
||||
"""Parse until the next block tag with a given name."""
|
||||
referenced = []
|
||||
@ -583,6 +599,8 @@ def getbool(options, key, default=False):
|
||||
auto_reload=False
|
||||
)
|
||||
|
||||
if getbool(options, 'trimmed'):
|
||||
environment.policies['ext.i18n.trimmed'] = True
|
||||
if getbool(options, 'newstyle_gettext'):
|
||||
environment.newstyle_gettext = True
|
||||
|
||||
|
203
lib/spack/external/jinja2/filters.py
vendored
203
lib/spack/external/jinja2/filters.py
vendored
@ -10,9 +10,10 @@
|
||||
"""
|
||||
import re
|
||||
import math
|
||||
import random
|
||||
import warnings
|
||||
|
||||
from random import choice
|
||||
from itertools import groupby
|
||||
from itertools import groupby, chain
|
||||
from collections import namedtuple
|
||||
from jinja2.utils import Markup, escape, pformat, urlize, soft_unicode, \
|
||||
unicode_urlencode, htmlsafe_json_dumps
|
||||
@ -52,22 +53,34 @@ def environmentfilter(f):
|
||||
return f
|
||||
|
||||
|
||||
def make_attrgetter(environment, attribute):
|
||||
def ignore_case(value):
|
||||
"""For use as a postprocessor for :func:`make_attrgetter`. Converts strings
|
||||
to lowercase and returns other types as-is."""
|
||||
return value.lower() if isinstance(value, string_types) else value
|
||||
|
||||
|
||||
def make_attrgetter(environment, attribute, postprocess=None):
|
||||
"""Returns a callable that looks up the given attribute from a
|
||||
passed object with the rules of the environment. Dots are allowed
|
||||
to access attributes of attributes. Integer parts in paths are
|
||||
looked up as integers.
|
||||
"""
|
||||
if not isinstance(attribute, string_types) \
|
||||
or ('.' not in attribute and not attribute.isdigit()):
|
||||
return lambda x: environment.getitem(x, attribute)
|
||||
attribute = attribute.split('.')
|
||||
if attribute is None:
|
||||
attribute = []
|
||||
elif isinstance(attribute, string_types):
|
||||
attribute = [int(x) if x.isdigit() else x for x in attribute.split('.')]
|
||||
else:
|
||||
attribute = [attribute]
|
||||
|
||||
def attrgetter(item):
|
||||
for part in attribute:
|
||||
if part.isdigit():
|
||||
part = int(part)
|
||||
item = environment.getitem(item, part)
|
||||
|
||||
if postprocess is not None:
|
||||
item = postprocess(item)
|
||||
|
||||
return item
|
||||
|
||||
return attrgetter
|
||||
|
||||
|
||||
@ -190,7 +203,7 @@ def do_title(s):
|
||||
if item])
|
||||
|
||||
|
||||
def do_dictsort(value, case_sensitive=False, by='key'):
|
||||
def do_dictsort(value, case_sensitive=False, by='key', reverse=False):
|
||||
"""Sort a dict and yield (key, value) pairs. Because python dicts are
|
||||
unsorted you may want to use this function to order them by either
|
||||
key or value:
|
||||
@ -200,6 +213,9 @@ def do_dictsort(value, case_sensitive=False, by='key'):
|
||||
{% for item in mydict|dictsort %}
|
||||
sort the dict by key, case insensitive
|
||||
|
||||
{% for item in mydict|dictsort(reverse=true) %}
|
||||
sort the dict by key, case insensitive, reverse order
|
||||
|
||||
{% for item in mydict|dictsort(true) %}
|
||||
sort the dict by key, case sensitive
|
||||
|
||||
@ -211,20 +227,25 @@ def do_dictsort(value, case_sensitive=False, by='key'):
|
||||
elif by == 'value':
|
||||
pos = 1
|
||||
else:
|
||||
raise FilterArgumentError('You can only sort by either '
|
||||
'"key" or "value"')
|
||||
raise FilterArgumentError(
|
||||
'You can only sort by either "key" or "value"'
|
||||
)
|
||||
|
||||
def sort_func(item):
|
||||
value = item[pos]
|
||||
if isinstance(value, string_types) and not case_sensitive:
|
||||
value = value.lower()
|
||||
|
||||
if not case_sensitive:
|
||||
value = ignore_case(value)
|
||||
|
||||
return value
|
||||
|
||||
return sorted(value.items(), key=sort_func)
|
||||
return sorted(value.items(), key=sort_func, reverse=reverse)
|
||||
|
||||
|
||||
@environmentfilter
|
||||
def do_sort(environment, value, reverse=False, case_sensitive=False,
|
||||
attribute=None):
|
||||
def do_sort(
|
||||
environment, value, reverse=False, case_sensitive=False, attribute=None
|
||||
):
|
||||
"""Sort an iterable. Per default it sorts ascending, if you pass it
|
||||
true as first argument it will reverse the sorting.
|
||||
|
||||
@ -250,18 +271,85 @@ def do_sort(environment, value, reverse=False, case_sensitive=False,
|
||||
.. versionchanged:: 2.6
|
||||
The `attribute` parameter was added.
|
||||
"""
|
||||
if not case_sensitive:
|
||||
def sort_func(item):
|
||||
if isinstance(item, string_types):
|
||||
item = item.lower()
|
||||
return item
|
||||
else:
|
||||
sort_func = None
|
||||
if attribute is not None:
|
||||
getter = make_attrgetter(environment, attribute)
|
||||
def sort_func(item, processor=sort_func or (lambda x: x)):
|
||||
return processor(getter(item))
|
||||
return sorted(value, key=sort_func, reverse=reverse)
|
||||
key_func = make_attrgetter(
|
||||
environment, attribute,
|
||||
postprocess=ignore_case if not case_sensitive else None
|
||||
)
|
||||
return sorted(value, key=key_func, reverse=reverse)
|
||||
|
||||
|
||||
@environmentfilter
|
||||
def do_unique(environment, value, case_sensitive=False, attribute=None):
|
||||
"""Returns a list of unique items from the the given iterable.
|
||||
|
||||
.. sourcecode:: jinja
|
||||
|
||||
{{ ['foo', 'bar', 'foobar', 'FooBar']|unique }}
|
||||
-> ['foo', 'bar', 'foobar']
|
||||
|
||||
The unique items are yielded in the same order as their first occurrence in
|
||||
the iterable passed to the filter.
|
||||
|
||||
:param case_sensitive: Treat upper and lower case strings as distinct.
|
||||
:param attribute: Filter objects with unique values for this attribute.
|
||||
"""
|
||||
getter = make_attrgetter(
|
||||
environment, attribute,
|
||||
postprocess=ignore_case if not case_sensitive else None
|
||||
)
|
||||
seen = set()
|
||||
|
||||
for item in value:
|
||||
key = getter(item)
|
||||
|
||||
if key not in seen:
|
||||
seen.add(key)
|
||||
yield item
|
||||
|
||||
|
||||
def _min_or_max(environment, value, func, case_sensitive, attribute):
|
||||
it = iter(value)
|
||||
|
||||
try:
|
||||
first = next(it)
|
||||
except StopIteration:
|
||||
return environment.undefined('No aggregated item, sequence was empty.')
|
||||
|
||||
key_func = make_attrgetter(
|
||||
environment, attribute,
|
||||
ignore_case if not case_sensitive else None
|
||||
)
|
||||
return func(chain([first], it), key=key_func)
|
||||
|
||||
|
||||
@environmentfilter
|
||||
def do_min(environment, value, case_sensitive=False, attribute=None):
|
||||
"""Return the smallest item from the sequence.
|
||||
|
||||
.. sourcecode:: jinja
|
||||
|
||||
{{ [1, 2, 3]|min }}
|
||||
-> 1
|
||||
|
||||
:param case_sensitive: Treat upper and lower case strings as distinct.
|
||||
:param attribute: Get the object with the max value of this attribute.
|
||||
"""
|
||||
return _min_or_max(environment, value, min, case_sensitive, attribute)
|
||||
|
||||
|
||||
@environmentfilter
|
||||
def do_max(environment, value, case_sensitive=False, attribute=None):
|
||||
"""Return the largest item from the sequence.
|
||||
|
||||
.. sourcecode:: jinja
|
||||
|
||||
{{ [1, 2, 3]|max }}
|
||||
-> 3
|
||||
|
||||
:param case_sensitive: Treat upper and lower case strings as distinct.
|
||||
:param attribute: Get the object with the max value of this attribute.
|
||||
"""
|
||||
return _min_or_max(environment, value, max, case_sensitive, attribute)
|
||||
|
||||
|
||||
def do_default(value, default_value=u'', boolean=False):
|
||||
@ -359,13 +447,13 @@ def do_last(environment, seq):
|
||||
return environment.undefined('No last item, sequence was empty.')
|
||||
|
||||
|
||||
@environmentfilter
|
||||
def do_random(environment, seq):
|
||||
@contextfilter
|
||||
def do_random(context, seq):
|
||||
"""Return a random item from the sequence."""
|
||||
try:
|
||||
return choice(seq)
|
||||
return random.choice(seq)
|
||||
except IndexError:
|
||||
return environment.undefined('No random item, sequence was empty.')
|
||||
return context.environment.undefined('No random item, sequence was empty.')
|
||||
|
||||
|
||||
def do_filesizeformat(value, binary=False):
|
||||
@ -445,21 +533,44 @@ def do_urlize(eval_ctx, value, trim_url_limit=None, nofollow=False,
|
||||
return rv
|
||||
|
||||
|
||||
def do_indent(s, width=4, indentfirst=False):
|
||||
"""Return a copy of the passed string, each line indented by
|
||||
4 spaces. The first line is not indented. If you want to
|
||||
change the number of spaces or indent the first line too
|
||||
you can pass additional parameters to the filter:
|
||||
def do_indent(
|
||||
s, width=4, first=False, blank=False, indentfirst=None
|
||||
):
|
||||
"""Return a copy of the string with each line indented by 4 spaces. The
|
||||
first line and blank lines are not indented by default.
|
||||
|
||||
.. sourcecode:: jinja
|
||||
:param width: Number of spaces to indent by.
|
||||
:param first: Don't skip indenting the first line.
|
||||
:param blank: Don't skip indenting empty lines.
|
||||
|
||||
{{ mytext|indent(2, true) }}
|
||||
indent by two spaces and indent the first line too.
|
||||
.. versionchanged:: 2.10
|
||||
Blank lines are not indented by default.
|
||||
|
||||
Rename the ``indentfirst`` argument to ``first``.
|
||||
"""
|
||||
if indentfirst is not None:
|
||||
warnings.warn(DeprecationWarning(
|
||||
'The "indentfirst" argument is renamed to "first".'
|
||||
), stacklevel=2)
|
||||
first = indentfirst
|
||||
|
||||
s += u'\n' # this quirk is necessary for splitlines method
|
||||
indention = u' ' * width
|
||||
rv = (u'\n' + indention).join(s.splitlines())
|
||||
if indentfirst:
|
||||
|
||||
if blank:
|
||||
rv = (u'\n' + indention).join(s.splitlines())
|
||||
else:
|
||||
lines = s.splitlines()
|
||||
rv = lines.pop(0)
|
||||
|
||||
if lines:
|
||||
rv += u'\n' + u'\n'.join(
|
||||
indention + line if line else line for line in lines
|
||||
)
|
||||
|
||||
if first:
|
||||
rv = indention + rv
|
||||
|
||||
return rv
|
||||
|
||||
|
||||
@ -865,6 +976,9 @@ def do_select(*args, **kwargs):
|
||||
|
||||
{{ numbers|select("odd") }}
|
||||
{{ numbers|select("odd") }}
|
||||
{{ numbers|select("divisibleby", 3) }}
|
||||
{{ numbers|select("lessthan", 42) }}
|
||||
{{ strings|select("equalto", "mystring") }}
|
||||
|
||||
.. versionadded:: 2.7
|
||||
"""
|
||||
@ -1045,6 +1159,8 @@ def select_or_reject(args, kwargs, modfunc, lookup_attr):
|
||||
'list': do_list,
|
||||
'lower': do_lower,
|
||||
'map': do_map,
|
||||
'min': do_min,
|
||||
'max': do_max,
|
||||
'pprint': do_pprint,
|
||||
'random': do_random,
|
||||
'reject': do_reject,
|
||||
@ -1063,6 +1179,7 @@ def select_or_reject(args, kwargs, modfunc, lookup_attr):
|
||||
'title': do_title,
|
||||
'trim': do_trim,
|
||||
'truncate': do_truncate,
|
||||
'unique': do_unique,
|
||||
'upper': do_upper,
|
||||
'urlencode': do_urlencode,
|
||||
'urlize': do_urlize,
|
||||
|
25
lib/spack/external/jinja2/idtracking.py
vendored
25
lib/spack/external/jinja2/idtracking.py
vendored
@ -24,11 +24,13 @@ def symbols_for_node(node, parent_symbols=None):
|
||||
|
||||
class Symbols(object):
|
||||
|
||||
def __init__(self, parent=None):
|
||||
if parent is None:
|
||||
self.level = 0
|
||||
else:
|
||||
self.level = parent.level + 1
|
||||
def __init__(self, parent=None, level=None):
|
||||
if level is None:
|
||||
if parent is None:
|
||||
level = 0
|
||||
else:
|
||||
level = parent.level + 1
|
||||
self.level = level
|
||||
self.parent = parent
|
||||
self.refs = {}
|
||||
self.loads = {}
|
||||
@ -167,6 +169,10 @@ def visit_CallBlock(self, node, **kwargs):
|
||||
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':
|
||||
self.sym_visitor.visit(node.target, store_as_param=True)
|
||||
@ -209,6 +215,9 @@ def visit_Name(self, node, store_as_param=False, **kwargs):
|
||||
elif node.ctx == 'load':
|
||||
self.symbols.load(node.name)
|
||||
|
||||
def visit_NSRef(self, node, **kwargs):
|
||||
self.symbols.load(node.name)
|
||||
|
||||
def visit_If(self, node, **kwargs):
|
||||
self.visit(node.test, **kwargs)
|
||||
|
||||
@ -222,9 +231,10 @@ def inner_visit(nodes):
|
||||
return rv
|
||||
|
||||
body_symbols = inner_visit(node.body)
|
||||
elif_symbols = inner_visit(node.elif_)
|
||||
else_symbols = inner_visit(node.else_ or ())
|
||||
|
||||
self.symbols.branch_update([body_symbols, else_symbols])
|
||||
self.symbols.branch_update([body_symbols, elif_symbols, else_symbols])
|
||||
|
||||
def visit_Macro(self, node, **kwargs):
|
||||
self.symbols.store(node.name)
|
||||
@ -271,3 +281,6 @@ def visit_Scope(self, node, **kwargs):
|
||||
|
||||
def visit_Block(self, node, **kwargs):
|
||||
"""Stop visiting at blocks."""
|
||||
|
||||
def visit_OverlayScope(self, node, **kwargs):
|
||||
"""Do not visit into overlay scopes."""
|
||||
|
56
lib/spack/external/jinja2/lexer.py
vendored
56
lib/spack/external/jinja2/lexer.py
vendored
@ -15,14 +15,12 @@
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
import re
|
||||
import sys
|
||||
|
||||
from operator import itemgetter
|
||||
from collections import deque
|
||||
from operator import itemgetter
|
||||
|
||||
from jinja2._compat import implements_iterator, intern, iteritems, text_type
|
||||
from jinja2.exceptions import TemplateSyntaxError
|
||||
from jinja2.utils import LRUCache
|
||||
from jinja2._compat import iteritems, implements_iterator, text_type, intern
|
||||
|
||||
|
||||
# cache for the lexers. Exists in order to be able to have multiple
|
||||
# environments with the same lexer
|
||||
@ -34,28 +32,25 @@
|
||||
r'|"([^"\\]*(?:\\.[^"\\]*)*)")', re.S)
|
||||
integer_re = re.compile(r'\d+')
|
||||
|
||||
def _make_name_re():
|
||||
try:
|
||||
compile('föö', '<unknown>', 'eval')
|
||||
except SyntaxError:
|
||||
return re.compile(r'\b[a-zA-Z_][a-zA-Z0-9_]*\b')
|
||||
|
||||
try:
|
||||
# check if this Python supports Unicode identifiers
|
||||
compile('föö', '<unknown>', 'eval')
|
||||
except SyntaxError:
|
||||
# no Unicode support, use ASCII identifiers
|
||||
name_re = re.compile(r'[a-zA-Z_][a-zA-Z0-9_]*')
|
||||
check_ident = False
|
||||
else:
|
||||
# Unicode support, build a pattern to match valid characters, and set flag
|
||||
# to use str.isidentifier to validate during lexing
|
||||
from jinja2 import _identifier
|
||||
name_re = re.compile(r'[\w{0}]+'.format(_identifier.pattern))
|
||||
check_ident = True
|
||||
# remove the pattern from memory after building the regex
|
||||
import sys
|
||||
del sys.modules['jinja2._identifier']
|
||||
import jinja2
|
||||
from jinja2 import _stringdefs
|
||||
name_re = re.compile(r'[%s][%s]*' % (_stringdefs.xid_start,
|
||||
_stringdefs.xid_continue))
|
||||
|
||||
# Save some memory here
|
||||
sys.modules.pop('jinja2._stringdefs')
|
||||
del _stringdefs
|
||||
del jinja2._stringdefs
|
||||
|
||||
return name_re
|
||||
|
||||
# we use the unicode identifier rule if this python version is able
|
||||
# to handle unicode identifiers, otherwise the standard ASCII one.
|
||||
name_re = _make_name_re()
|
||||
del _make_name_re
|
||||
del jinja2._identifier
|
||||
del _identifier
|
||||
|
||||
float_re = re.compile(r'(?<!\.)\d+\.\d+')
|
||||
newline_re = re.compile(r'(\r\n|\r|\n)')
|
||||
@ -352,7 +347,10 @@ def skip_if(self, expr):
|
||||
return self.next_if(expr) is not None
|
||||
|
||||
def __next__(self):
|
||||
"""Go one token ahead and return the old one"""
|
||||
"""Go one token ahead and return the old one.
|
||||
|
||||
Use the built-in :func:`next` instead of calling this directly.
|
||||
"""
|
||||
rv = self.current
|
||||
if self._pushed:
|
||||
self.current = self._pushed.popleft()
|
||||
@ -577,6 +575,10 @@ def wrap(self, stream, name=None, filename=None):
|
||||
token = value
|
||||
elif token == 'name':
|
||||
value = str(value)
|
||||
if check_ident and not value.isidentifier():
|
||||
raise TemplateSyntaxError(
|
||||
'Invalid character in identifier',
|
||||
lineno, name, filename)
|
||||
elif token == 'string':
|
||||
# try to unescape string
|
||||
try:
|
||||
|
220
lib/spack/external/jinja2/nativetypes.py
vendored
Normal file
220
lib/spack/external/jinja2/nativetypes.py
vendored
Normal file
@ -0,0 +1,220 @@
|
||||
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
|
||||
|
||||
|
||||
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.
|
||||
"""
|
||||
head = list(islice(nodes, 2))
|
||||
|
||||
if not head:
|
||||
return None
|
||||
|
||||
if len(head) == 1:
|
||||
out = head[0]
|
||||
else:
|
||||
out = u''.join([text_type(v) for v in chain(head, nodes)])
|
||||
|
||||
try:
|
||||
return literal_eval(out)
|
||||
except (ValueError, SyntaxError, MemoryError):
|
||||
return out
|
||||
|
||||
|
||||
class NativeCodeGenerator(CodeGenerator):
|
||||
"""A code generator which avoids injecting ``to_string()`` calls around the
|
||||
internal code Jinja uses to render templates.
|
||||
"""
|
||||
|
||||
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
|
||||
|
||||
finalize = self.environment.finalize
|
||||
finalize_context = getattr(finalize, 'contextfunction', False)
|
||||
finalize_eval = getattr(finalize, 'evalcontextfunction', False)
|
||||
finalize_env = getattr(finalize, 'environmentfunction', False)
|
||||
|
||||
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
|
||||
|
||||
# If we are inside a frame that requires output checking, we do so.
|
||||
outdent_later = False
|
||||
|
||||
if frame.require_output_check:
|
||||
self.writeline('if parent_template is None:')
|
||||
self.indent()
|
||||
outdent_later = True
|
||||
|
||||
# Try to evaluate as many chunks as possible into a static string at
|
||||
# compile time.
|
||||
body = []
|
||||
|
||||
for child in node.nodes:
|
||||
try:
|
||||
if const_finalize is None:
|
||||
raise nodes.Impossible()
|
||||
|
||||
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)
|
||||
|
||||
|
||||
class NativeEnvironment(Environment):
|
||||
"""An environment that renders templates to native Python types."""
|
||||
|
||||
code_generator_class = NativeCodeGenerator
|
||||
template_class = NativeTemplate
|
94
lib/spack/external/jinja2/nodes.py
vendored
94
lib/spack/external/jinja2/nodes.py
vendored
@ -314,7 +314,7 @@ class For(Stmt):
|
||||
|
||||
class If(Stmt):
|
||||
"""If `test` is true, `body` is rendered, else `else_`."""
|
||||
fields = ('test', 'body', 'else_')
|
||||
fields = ('test', 'body', 'elif_', 'else_')
|
||||
|
||||
|
||||
class Macro(Stmt):
|
||||
@ -387,7 +387,7 @@ class Assign(Stmt):
|
||||
|
||||
class AssignBlock(Stmt):
|
||||
"""Assigns a block to a target."""
|
||||
fields = ('target', 'body')
|
||||
fields = ('target', 'filter', 'body')
|
||||
|
||||
|
||||
class Expr(Node):
|
||||
@ -465,6 +465,18 @@ def can_assign(self):
|
||||
'True', 'False', 'None')
|
||||
|
||||
|
||||
class NSRef(Expr):
|
||||
"""Reference to a namespace value assignment"""
|
||||
fields = ('name', 'attr')
|
||||
|
||||
def can_assign(self):
|
||||
# We don't need any special checks here; NSRef assignments have a
|
||||
# runtime check to ensure the target is a namespace object which will
|
||||
# have been checked already as it is created using a normal assignment
|
||||
# which goes through a `Name` node.
|
||||
return True
|
||||
|
||||
|
||||
class Literal(Expr):
|
||||
"""Baseclass for literals."""
|
||||
abstract = True
|
||||
@ -587,6 +599,25 @@ def as_const(self, eval_ctx=None):
|
||||
return self.expr2.as_const(eval_ctx)
|
||||
|
||||
|
||||
def args_as_const(node, eval_ctx):
|
||||
args = [x.as_const(eval_ctx) for x in node.args]
|
||||
kwargs = dict(x.as_const(eval_ctx) for x in node.kwargs)
|
||||
|
||||
if node.dyn_args is not None:
|
||||
try:
|
||||
args.extend(node.dyn_args.as_const(eval_ctx))
|
||||
except Exception:
|
||||
raise Impossible()
|
||||
|
||||
if node.dyn_kwargs is not None:
|
||||
try:
|
||||
kwargs.update(node.dyn_kwargs.as_const(eval_ctx))
|
||||
except Exception:
|
||||
raise Impossible()
|
||||
|
||||
return args, kwargs
|
||||
|
||||
|
||||
class Filter(Expr):
|
||||
"""This node applies a filter on an expression. `name` is the name of
|
||||
the filter, the rest of the fields are the same as for :class:`Call`.
|
||||
@ -594,44 +625,41 @@ class Filter(Expr):
|
||||
If the `node` of a filter is `None` the contents of the last buffer are
|
||||
filtered. Buffers are created by macros and filter blocks.
|
||||
"""
|
||||
|
||||
fields = ('node', 'name', 'args', 'kwargs', 'dyn_args', 'dyn_kwargs')
|
||||
|
||||
def as_const(self, eval_ctx=None):
|
||||
eval_ctx = get_eval_context(self, eval_ctx)
|
||||
|
||||
if eval_ctx.volatile or self.node is None:
|
||||
raise Impossible()
|
||||
|
||||
# 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
|
||||
# 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):
|
||||
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()
|
||||
|
||||
obj = self.node.as_const(eval_ctx)
|
||||
args = [obj] + [x.as_const(eval_ctx) for x in self.args]
|
||||
args, kwargs = args_as_const(self, eval_ctx)
|
||||
args.insert(0, self.node.as_const(eval_ctx))
|
||||
|
||||
if getattr(filter_, 'evalcontextfilter', False):
|
||||
args.insert(0, eval_ctx)
|
||||
elif getattr(filter_, 'environmentfilter', False):
|
||||
args.insert(0, self.environment)
|
||||
kwargs = dict(x.as_const(eval_ctx) for x in self.kwargs)
|
||||
if self.dyn_args is not None:
|
||||
try:
|
||||
args.extend(self.dyn_args.as_const(eval_ctx))
|
||||
except Exception:
|
||||
raise Impossible()
|
||||
if self.dyn_kwargs is not None:
|
||||
try:
|
||||
kwargs.update(self.dyn_kwargs.as_const(eval_ctx))
|
||||
except Exception:
|
||||
raise Impossible()
|
||||
|
||||
try:
|
||||
return filter_(*args, **kwargs)
|
||||
except Exception:
|
||||
@ -642,8 +670,24 @@ class Test(Expr):
|
||||
"""Applies a test on an expression. `name` is the name of the test, the
|
||||
rest of the fields are the same as for :class:`Call`.
|
||||
"""
|
||||
|
||||
fields = ('node', 'name', 'args', 'kwargs', 'dyn_args', 'dyn_kwargs')
|
||||
|
||||
def as_const(self, eval_ctx=None):
|
||||
test = self.environment.tests.get(self.name)
|
||||
|
||||
if test is None:
|
||||
raise Impossible()
|
||||
|
||||
eval_ctx = get_eval_context(self, eval_ctx)
|
||||
args, kwargs = args_as_const(self, eval_ctx)
|
||||
args.insert(0, self.node.as_const(eval_ctx))
|
||||
|
||||
try:
|
||||
return test(*args, **kwargs)
|
||||
except Exception:
|
||||
raise Impossible()
|
||||
|
||||
|
||||
class Call(Expr):
|
||||
"""Calls an expression. `args` is a list of arguments, `kwargs` a list
|
||||
@ -914,6 +958,22 @@ class Scope(Stmt):
|
||||
fields = ('body',)
|
||||
|
||||
|
||||
class OverlayScope(Stmt):
|
||||
"""An overlay scope for extensions. This is a largely unoptimized scope
|
||||
that however can be used to introduce completely arbitrary variables into
|
||||
a sub scope from a dictionary or dictionary like object. The `context`
|
||||
field has to evaluate to a dictionary object.
|
||||
|
||||
Example usage::
|
||||
|
||||
OverlayScope(context=self.call_method('get_context'),
|
||||
body=[...])
|
||||
|
||||
.. versionadded:: 2.10
|
||||
"""
|
||||
fields = ('context', 'body')
|
||||
|
||||
|
||||
class EvalContextModifier(Stmt):
|
||||
"""Modifies the eval context. For each option that should be modified,
|
||||
a :class:`Keyword` has to be added to the :attr:`options` list.
|
||||
|
33
lib/spack/external/jinja2/parser.py
vendored
33
lib/spack/external/jinja2/parser.py
vendored
@ -176,13 +176,14 @@ def parse_statements(self, end_tokens, drop_needle=False):
|
||||
def parse_set(self):
|
||||
"""Parse an assign statement."""
|
||||
lineno = next(self.stream).lineno
|
||||
target = self.parse_assign_target()
|
||||
target = self.parse_assign_target(with_namespace=True)
|
||||
if self.stream.skip_if('assign'):
|
||||
expr = self.parse_tuple()
|
||||
return nodes.Assign(target, expr, lineno=lineno)
|
||||
filter_node = self.parse_filter(None)
|
||||
body = self.parse_statements(('name:endset',),
|
||||
drop_needle=True)
|
||||
return nodes.AssignBlock(target, body, lineno=lineno)
|
||||
return nodes.AssignBlock(target, filter_node, body, lineno=lineno)
|
||||
|
||||
def parse_for(self):
|
||||
"""Parse a for loop."""
|
||||
@ -210,17 +211,16 @@ def parse_if(self):
|
||||
node.test = self.parse_tuple(with_condexpr=False)
|
||||
node.body = self.parse_statements(('name:elif', 'name:else',
|
||||
'name:endif'))
|
||||
node.elif_ = []
|
||||
node.else_ = []
|
||||
token = next(self.stream)
|
||||
if token.test('name:elif'):
|
||||
new_node = nodes.If(lineno=self.stream.current.lineno)
|
||||
node.else_ = [new_node]
|
||||
node = new_node
|
||||
node = nodes.If(lineno=self.stream.current.lineno)
|
||||
result.elif_.append(node)
|
||||
continue
|
||||
elif token.test('name:else'):
|
||||
node.else_ = self.parse_statements(('name:endif',),
|
||||
drop_needle=True)
|
||||
else:
|
||||
node.else_ = []
|
||||
result.else_ = self.parse_statements(('name:endif',),
|
||||
drop_needle=True)
|
||||
break
|
||||
return result
|
||||
|
||||
@ -334,10 +334,9 @@ def parse_context():
|
||||
if parse_context() or self.stream.current.type != 'comma':
|
||||
break
|
||||
else:
|
||||
break
|
||||
self.stream.expect('name')
|
||||
if not hasattr(node, 'with_context'):
|
||||
node.with_context = False
|
||||
self.stream.skip_if('comma')
|
||||
return node
|
||||
|
||||
def parse_signature(self, node):
|
||||
@ -395,15 +394,21 @@ def parse_print(self):
|
||||
return node
|
||||
|
||||
def parse_assign_target(self, with_tuple=True, name_only=False,
|
||||
extra_end_rules=None):
|
||||
extra_end_rules=None, with_namespace=False):
|
||||
"""Parse an assignment target. As Jinja2 allows assignments to
|
||||
tuples, this function can parse all allowed assignment targets. Per
|
||||
default assignments to tuples are parsed, that can be disable however
|
||||
by setting `with_tuple` to `False`. If only assignments to names are
|
||||
wanted `name_only` can be set to `True`. The `extra_end_rules`
|
||||
parameter is forwarded to the tuple parsing function.
|
||||
parameter is forwarded to the tuple parsing function. If
|
||||
`with_namespace` is enabled, a namespace assignment may be parsed.
|
||||
"""
|
||||
if name_only:
|
||||
if with_namespace and self.stream.look().type == 'dot':
|
||||
token = self.stream.expect('name')
|
||||
next(self.stream) # dot
|
||||
attr = self.stream.expect('name')
|
||||
target = nodes.NSRef(token.value, attr.value, lineno=token.lineno)
|
||||
elif name_only:
|
||||
token = self.stream.expect('name')
|
||||
target = nodes.Name(token.value, 'store', lineno=token.lineno)
|
||||
else:
|
||||
|
54
lib/spack/external/jinja2/runtime.py
vendored
54
lib/spack/external/jinja2/runtime.py
vendored
@ -15,7 +15,7 @@
|
||||
|
||||
from jinja2.nodes import EvalContext, _context_function_types
|
||||
from jinja2.utils import Markup, soft_unicode, escape, missing, concat, \
|
||||
internalcode, object_type_repr, evalcontextfunction
|
||||
internalcode, object_type_repr, evalcontextfunction, Namespace
|
||||
from jinja2.exceptions import UndefinedError, TemplateRuntimeError, \
|
||||
TemplateNotFound
|
||||
from jinja2._compat import imap, text_type, iteritems, \
|
||||
@ -27,7 +27,7 @@
|
||||
__all__ = ['LoopContext', 'TemplateReference', 'Macro', 'Markup',
|
||||
'TemplateRuntimeError', 'missing', 'concat', 'escape',
|
||||
'markup_join', 'unicode_join', 'to_string', 'identity',
|
||||
'TemplateNotFound']
|
||||
'TemplateNotFound', 'Namespace']
|
||||
|
||||
#: the name of the function that is used to convert something into
|
||||
#: a string. We can just use the text type here.
|
||||
@ -36,6 +36,7 @@
|
||||
#: the identity function. Useful for certain things in the environment
|
||||
identity = lambda x: x
|
||||
|
||||
_first_iteration = object()
|
||||
_last_iteration = object()
|
||||
|
||||
|
||||
@ -241,13 +242,14 @@ def call(__self, __obj, *args, **kwargs):
|
||||
__traceback_hide__ = True # noqa
|
||||
|
||||
# Allow callable classes to take a context
|
||||
fn = __obj.__call__
|
||||
for fn_type in ('contextfunction',
|
||||
'evalcontextfunction',
|
||||
'environmentfunction'):
|
||||
if hasattr(fn, fn_type):
|
||||
__obj = fn
|
||||
break
|
||||
if hasattr(__obj, '__call__'):
|
||||
fn = __obj.__call__
|
||||
for fn_type in ('contextfunction',
|
||||
'evalcontextfunction',
|
||||
'environmentfunction'):
|
||||
if hasattr(fn, fn_type):
|
||||
__obj = fn
|
||||
break
|
||||
|
||||
if isinstance(__obj, _context_function_types):
|
||||
if getattr(__obj, 'contextfunction', 0):
|
||||
@ -349,13 +351,17 @@ def __call__(self):
|
||||
class LoopContextBase(object):
|
||||
"""A loop context for dynamic iteration."""
|
||||
|
||||
_before = _first_iteration
|
||||
_current = _first_iteration
|
||||
_after = _last_iteration
|
||||
_length = None
|
||||
|
||||
def __init__(self, recurse=None, depth0=0):
|
||||
def __init__(self, undefined, recurse=None, depth0=0):
|
||||
self._undefined = undefined
|
||||
self._recurse = recurse
|
||||
self.index0 = -1
|
||||
self.depth0 = depth0
|
||||
self._last_checked_value = missing
|
||||
|
||||
def cycle(self, *args):
|
||||
"""Cycles among the arguments with the current loop index."""
|
||||
@ -363,6 +369,13 @@ def cycle(self, *args):
|
||||
raise TypeError('no items for cycling given')
|
||||
return args[self.index0 % len(args)]
|
||||
|
||||
def changed(self, *value):
|
||||
"""Checks whether the value has changed since the last call."""
|
||||
if self._last_checked_value != value:
|
||||
self._last_checked_value = value
|
||||
return True
|
||||
return False
|
||||
|
||||
first = property(lambda x: x.index0 == 0)
|
||||
last = property(lambda x: x._after is _last_iteration)
|
||||
index = property(lambda x: x.index0 + 1)
|
||||
@ -370,6 +383,18 @@ def cycle(self, *args):
|
||||
revindex0 = property(lambda x: x.length - x.index)
|
||||
depth = property(lambda x: x.depth0 + 1)
|
||||
|
||||
@property
|
||||
def previtem(self):
|
||||
if self._before is _first_iteration:
|
||||
return self._undefined('there is no previous item')
|
||||
return self._before
|
||||
|
||||
@property
|
||||
def nextitem(self):
|
||||
if self._after is _last_iteration:
|
||||
return self._undefined('there is no next item')
|
||||
return self._after
|
||||
|
||||
def __len__(self):
|
||||
return self.length
|
||||
|
||||
@ -395,8 +420,8 @@ def __repr__(self):
|
||||
|
||||
class LoopContext(LoopContextBase):
|
||||
|
||||
def __init__(self, iterable, recurse=None, depth0=0):
|
||||
LoopContextBase.__init__(self, recurse, depth0)
|
||||
def __init__(self, iterable, undefined, recurse=None, depth0=0):
|
||||
LoopContextBase.__init__(self, undefined, recurse, depth0)
|
||||
self._iterator = iter(iterable)
|
||||
|
||||
# try to get the length of the iterable early. This must be done
|
||||
@ -448,9 +473,10 @@ def __next__(self):
|
||||
ctx.index0 += 1
|
||||
if ctx._after is _last_iteration:
|
||||
raise StopIteration()
|
||||
next_elem = ctx._after
|
||||
ctx._before = ctx._current
|
||||
ctx._current = ctx._after
|
||||
ctx._after = ctx._safe_next()
|
||||
return next_elem, ctx
|
||||
return ctx._current, ctx
|
||||
|
||||
|
||||
class Macro(object):
|
||||
|
2
lib/spack/external/jinja2/sandbox.py
vendored
2
lib/spack/external/jinja2/sandbox.py
vendored
@ -107,7 +107,7 @@ class _MagicFormatMapping(Mapping):
|
||||
"""This class implements a dummy wrapper to fix a bug in the Python
|
||||
standard library for string formatting.
|
||||
|
||||
See http://bugs.python.org/issue13598 for information about why
|
||||
See https://bugs.python.org/issue13598 for information about why
|
||||
this is necessary.
|
||||
"""
|
||||
|
||||
|
54
lib/spack/external/jinja2/tests.py
vendored
54
lib/spack/external/jinja2/tests.py
vendored
@ -8,6 +8,7 @@
|
||||
:copyright: (c) 2017 by the Jinja Team.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
import operator
|
||||
import re
|
||||
from collections import Mapping
|
||||
from jinja2.runtime import Undefined
|
||||
@ -103,28 +104,6 @@ def test_sequence(value):
|
||||
return True
|
||||
|
||||
|
||||
def test_equalto(value, other):
|
||||
"""Check if an object has the same value as another object:
|
||||
|
||||
.. sourcecode:: jinja
|
||||
|
||||
{% if foo.expression is equalto 42 %}
|
||||
the foo attribute evaluates to the constant 42
|
||||
{% endif %}
|
||||
|
||||
This appears to be a useless test as it does exactly the same as the
|
||||
``==`` operator, but it can be useful when used together with the
|
||||
`selectattr` function:
|
||||
|
||||
.. sourcecode:: jinja
|
||||
|
||||
{{ users|selectattr("email", "equalto", "foo@bar.invalid") }}
|
||||
|
||||
.. versionadded:: 2.8
|
||||
"""
|
||||
return value == other
|
||||
|
||||
|
||||
def test_sameas(value, other):
|
||||
"""Check if an object points to the same memory address than another
|
||||
object:
|
||||
@ -152,14 +131,12 @@ def test_escaped(value):
|
||||
return hasattr(value, '__html__')
|
||||
|
||||
|
||||
def test_greaterthan(value, other):
|
||||
"""Check if value is greater than other."""
|
||||
return value > other
|
||||
def test_in(value, seq):
|
||||
"""Check if value is in seq.
|
||||
|
||||
|
||||
def test_lessthan(value, other):
|
||||
"""Check if value is less than other."""
|
||||
return value < other
|
||||
.. versionadded:: 2.10
|
||||
"""
|
||||
return value in seq
|
||||
|
||||
|
||||
TESTS = {
|
||||
@ -178,8 +155,21 @@ def test_lessthan(value, other):
|
||||
'iterable': test_iterable,
|
||||
'callable': test_callable,
|
||||
'sameas': test_sameas,
|
||||
'equalto': test_equalto,
|
||||
'escaped': test_escaped,
|
||||
'greaterthan': test_greaterthan,
|
||||
'lessthan': test_lessthan
|
||||
'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,
|
||||
}
|
||||
|
25
lib/spack/external/jinja2/utils.py
vendored
25
lib/spack/external/jinja2/utils.py
vendored
@ -567,7 +567,7 @@ def htmlsafe_json_dumps(obj, dumper=None, **kwargs):
|
||||
.replace(u'>', u'\\u003e') \
|
||||
.replace(u'&', u'\\u0026') \
|
||||
.replace(u"'", u'\\u0027')
|
||||
return rv
|
||||
return Markup(rv)
|
||||
|
||||
|
||||
@implements_iterator
|
||||
@ -612,6 +612,29 @@ def __call__(self):
|
||||
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."""
|
||||
|
||||
def __init__(*args, **kwargs):
|
||||
self, args = args[0], args[1:]
|
||||
self.__attrs = dict(*args, **kwargs)
|
||||
|
||||
def __getattribute__(self, name):
|
||||
if name == '_Namespace__attrs':
|
||||
return object.__getattribute__(self, name)
|
||||
try:
|
||||
return self.__attrs[name]
|
||||
except KeyError:
|
||||
raise AttributeError(name)
|
||||
|
||||
def __setitem__(self, name, value):
|
||||
self.__attrs[name] = value
|
||||
|
||||
def __repr__(self):
|
||||
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 _')
|
||||
|
Loading…
Reference in New Issue
Block a user