diff --git a/third_party/python/Jinja2/CHANGES.rst b/third_party/python/Jinja2/CHANGES.rst
index 51f49845c21f..9b8b24ee030c 100644
--- a/third_party/python/Jinja2/CHANGES.rst
+++ b/third_party/python/Jinja2/CHANGES.rst
@@ -1,5 +1,38 @@
.. currentmodule:: jinja2
+Version 2.11.2
+--------------
+
+Released 2020-04-13
+
+- Fix a bug that caused callable objects with ``__getattr__``, like
+ :class:`~unittest.mock.Mock` to be treated as a
+ :func:`contextfunction`. :issue:`1145`
+- Update ``wordcount`` filter to trigger :class:`Undefined` methods
+ by wrapping the input in :func:`soft_unicode`. :pr:`1160`
+- Fix a hang when displaying tracebacks on Python 32-bit.
+ :issue:`1162`
+- Showing an undefined error for an object that raises
+ ``AttributeError`` on access doesn't cause a recursion error.
+ :issue:`1177`
+- Revert changes to :class:`~loaders.PackageLoader` from 2.10 which
+ removed the dependency on setuptools and pkg_resources, and added
+ limited support for namespace packages. The changes caused issues
+ when using Pytest. Due to the difficulty in supporting Python 2 and
+ :pep:`451` simultaneously, the changes are reverted until 3.0.
+ :pr:`1182`
+- Fix line numbers in error messages when newlines are stripped.
+ :pr:`1178`
+- The special ``namespace()`` assignment object in templates works in
+ async environments. :issue:`1180`
+- Fix whitespace being removed before tags in the middle of lines when
+ ``lstrip_blocks`` is enabled. :issue:`1138`
+- :class:`~nativetypes.NativeEnvironment` doesn't evaluate
+ intermediate strings during rendering. This prevents early
+ evaluation which could change the value of an expression.
+ :issue:`1186`
+
+
Version 2.11.1
--------------
diff --git a/third_party/python/Jinja2/PKG-INFO b/third_party/python/Jinja2/PKG-INFO
index 1b71b8cb806e..87f815183184 100644
--- a/third_party/python/Jinja2/PKG-INFO
+++ b/third_party/python/Jinja2/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: Jinja2
-Version: 2.11.1
+Version: 2.11.2
Summary: A very fast and expressive template engine.
Home-page: https://palletsprojects.com/p/jinja/
Author: Armin Ronacher
diff --git a/third_party/python/Jinja2/examples/basic/cycle.py b/third_party/python/Jinja2/examples/basic/cycle.py
new file mode 100644
index 000000000000..25dcb0b090ec
--- /dev/null
+++ b/third_party/python/Jinja2/examples/basic/cycle.py
@@ -0,0 +1,18 @@
+from __future__ import print_function
+
+from jinja2 import Environment
+
+env = Environment(
+ line_statement_prefix="#", variable_start_string="${", variable_end_string="}"
+)
+print(
+ env.from_string(
+ """\
+
+# for item in range(10)
+ - ${item}
+# endfor
+
\
+"""
+ ).render()
+)
diff --git a/third_party/python/Jinja2/examples/basic/debugger.py b/third_party/python/Jinja2/examples/basic/debugger.py
new file mode 100644
index 000000000000..d3c1a60a7ae8
--- /dev/null
+++ b/third_party/python/Jinja2/examples/basic/debugger.py
@@ -0,0 +1,8 @@
+from __future__ import print_function
+
+from jinja2 import Environment
+from jinja2.loaders import FileSystemLoader
+
+env = Environment(loader=FileSystemLoader("templates"))
+tmpl = env.get_template("broken.html")
+print(tmpl.render(seq=[3, 2, 4, 5, 3, 2, 0, 2, 1]))
diff --git a/third_party/python/Jinja2/examples/basic/inheritance.py b/third_party/python/Jinja2/examples/basic/inheritance.py
new file mode 100644
index 000000000000..4a881bf8a87c
--- /dev/null
+++ b/third_party/python/Jinja2/examples/basic/inheritance.py
@@ -0,0 +1,15 @@
+from __future__ import print_function
+
+from jinja2 import Environment
+from jinja2.loaders import DictLoader
+
+env = Environment(
+ loader=DictLoader(
+ {
+ "a": "[A[{% block body %}{% endblock %}]]",
+ "b": "{% extends 'a' %}{% block body %}[B]{% endblock %}",
+ "c": "{% extends 'b' %}{% block body %}###{{ super() }}###{% endblock %}",
+ }
+ )
+)
+print(env.get_template("c").render())
diff --git a/third_party/python/Jinja2/examples/basic/templates/broken.html b/third_party/python/Jinja2/examples/basic/templates/broken.html
new file mode 100644
index 000000000000..294d5c99894d
--- /dev/null
+++ b/third_party/python/Jinja2/examples/basic/templates/broken.html
@@ -0,0 +1,6 @@
+{% from 'subbroken.html' import may_break %}
+
+{% for item in seq %}
+ - {{ may_break(item) }}
+{% endfor %}
+
diff --git a/third_party/python/Jinja2/examples/basic/templates/subbroken.html b/third_party/python/Jinja2/examples/basic/templates/subbroken.html
new file mode 100644
index 000000000000..245eb7e6e6f7
--- /dev/null
+++ b/third_party/python/Jinja2/examples/basic/templates/subbroken.html
@@ -0,0 +1,3 @@
+{% macro may_break(item) -%}
+ [{{ item / 0 }}]
+{%- endmacro %}
diff --git a/third_party/python/Jinja2/examples/basic/test.py b/third_party/python/Jinja2/examples/basic/test.py
new file mode 100644
index 000000000000..80b9d1f052b3
--- /dev/null
+++ b/third_party/python/Jinja2/examples/basic/test.py
@@ -0,0 +1,31 @@
+from __future__ import print_function
+
+from jinja2 import Environment
+from jinja2.loaders import DictLoader
+
+env = Environment(
+ loader=DictLoader(
+ {
+ "child.html": u"""\
+{% extends master_layout or 'master.html' %}
+{% include helpers = 'helpers.html' %}
+{% macro get_the_answer() %}42{% endmacro %}
+{% title = 'Hello World' %}
+{% block body %}
+ {{ get_the_answer() }}
+ {{ helpers.conspirate() }}
+{% endblock %}
+""",
+ "master.html": u"""\
+
+{{ title }}
+{% block body %}{% endblock %}
+""",
+ "helpers.html": u"""\
+{% macro conspirate() %}23{% endmacro %}
+""",
+ }
+ )
+)
+tmpl = env.get_template("child.html")
+print(tmpl.render())
diff --git a/third_party/python/Jinja2/examples/basic/test_filter_and_linestatements.py b/third_party/python/Jinja2/examples/basic/test_filter_and_linestatements.py
new file mode 100644
index 000000000000..673b67ed76ae
--- /dev/null
+++ b/third_party/python/Jinja2/examples/basic/test_filter_and_linestatements.py
@@ -0,0 +1,29 @@
+from __future__ import print_function
+
+from jinja2 import Environment
+
+env = Environment(
+ line_statement_prefix="%", variable_start_string="${", variable_end_string="}"
+)
+tmpl = env.from_string(
+ """\
+% macro foo()
+ ${caller(42)}
+% endmacro
+
+% for item in seq
+ - ${item}
+% endfor
+
+% call(var) foo()
+ [${var}]
+% endcall
+% filter escape
+
+ % for item in [1, 2, 3]
+ - ${item}
+ % endfor
+% endfilter
+"""
+)
+print(tmpl.render(seq=range(10)))
diff --git a/third_party/python/Jinja2/examples/basic/test_loop_filter.py b/third_party/python/Jinja2/examples/basic/test_loop_filter.py
new file mode 100644
index 000000000000..39be08d61c2b
--- /dev/null
+++ b/third_party/python/Jinja2/examples/basic/test_loop_filter.py
@@ -0,0 +1,15 @@
+from __future__ import print_function
+
+from jinja2 import Environment
+
+tmpl = Environment().from_string(
+ """\
+
+{%- for item in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] if item % 2 == 0 %}
+ - {{ loop.index }} / {{ loop.length }}: {{ item }}
+{%- endfor %}
+
+if condition: {{ 1 if foo else 0 }}
+"""
+)
+print(tmpl.render(foo=True))
diff --git a/third_party/python/Jinja2/examples/basic/translate.py b/third_party/python/Jinja2/examples/basic/translate.py
new file mode 100644
index 000000000000..71547f4649ca
--- /dev/null
+++ b/third_party/python/Jinja2/examples/basic/translate.py
@@ -0,0 +1,20 @@
+from __future__ import print_function
+
+from jinja2 import Environment
+
+env = Environment(extensions=["jinja2.ext.i18n"])
+env.globals["gettext"] = {"Hello %(user)s!": "Hallo %(user)s!"}.__getitem__
+env.globals["ngettext"] = lambda s, p, n: {
+ "%(count)s user": "%(count)d Benutzer",
+ "%(count)s users": "%(count)d Benutzer",
+}[n == 1 and s or p]
+print(
+ env.from_string(
+ """\
+{% trans %}Hello {{ user }}!{% endtrans %}
+{% trans count=users|count -%}
+{{ count }} user{% pluralize %}{{ count }} users
+{% endtrans %}
+"""
+ ).render(user="someone", users=[1, 2, 3])
+)
diff --git a/third_party/python/Jinja2/src/jinja2/__init__.py b/third_party/python/Jinja2/src/jinja2/__init__.py
index 7f4a1c55a837..1229ba4275a0 100644
--- a/third_party/python/Jinja2/src/jinja2/__init__.py
+++ b/third_party/python/Jinja2/src/jinja2/__init__.py
@@ -41,4 +41,4 @@ from .utils import evalcontextfunction
from .utils import is_undefined
from .utils import select_autoescape
-__version__ = "2.11.1"
+__version__ = "2.11.2"
diff --git a/third_party/python/Jinja2/src/jinja2/asyncfilters.py b/third_party/python/Jinja2/src/jinja2/asyncfilters.py
index d29f6c62d2e5..3d98dbcc00de 100644
--- a/third_party/python/Jinja2/src/jinja2/asyncfilters.py
+++ b/third_party/python/Jinja2/src/jinja2/asyncfilters.py
@@ -26,17 +26,16 @@ 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):
+ 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
+ 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
diff --git a/third_party/python/Jinja2/src/jinja2/compiler.py b/third_party/python/Jinja2/src/jinja2/compiler.py
index f450ec6e314d..63297b42c30f 100644
--- a/third_party/python/Jinja2/src/jinja2/compiler.py
+++ b/third_party/python/Jinja2/src/jinja2/compiler.py
@@ -1307,13 +1307,13 @@ class CodeGenerator(NodeVisitor):
def finalize(value):
return default(env_finalize(value))
- if getattr(env_finalize, "contextfunction", False):
+ if getattr(env_finalize, "contextfunction", False) is True:
src += "context, "
finalize = None # noqa: F811
- elif getattr(env_finalize, "evalcontextfunction", False):
+ elif getattr(env_finalize, "evalcontextfunction", False) is True:
src += "context.eval_ctx, "
finalize = None
- elif getattr(env_finalize, "environmentfunction", False):
+ elif getattr(env_finalize, "environmentfunction", False) is True:
src += "environment, "
def finalize(value):
@@ -1689,11 +1689,11 @@ class CodeGenerator(NodeVisitor):
func = self.environment.filters.get(node.name)
if func is None:
self.fail("no filter named %r" % node.name, node.lineno)
- if getattr(func, "contextfilter", False):
+ if getattr(func, "contextfilter", False) is True:
self.write("context, ")
- elif getattr(func, "evalcontextfilter", False):
+ elif getattr(func, "evalcontextfilter", False) is True:
self.write("context.eval_ctx, ")
- elif getattr(func, "environmentfilter", False):
+ elif getattr(func, "environmentfilter", False) is True:
self.write("environment, ")
# if the filter node is None we are inside a filter block
diff --git a/third_party/python/Jinja2/src/jinja2/debug.py b/third_party/python/Jinja2/src/jinja2/debug.py
index d2c5a06bf6c1..5d8aec31d05d 100644
--- a/third_party/python/Jinja2/src/jinja2/debug.py
+++ b/third_party/python/Jinja2/src/jinja2/debug.py
@@ -245,10 +245,7 @@ else:
class _CTraceback(ctypes.Structure):
_fields_ = [
# Extra PyObject slots when compiled with Py_TRACE_REFS.
- (
- "PyObject_HEAD",
- ctypes.c_byte * (32 if hasattr(sys, "getobjects") else 16),
- ),
+ ("PyObject_HEAD", ctypes.c_byte * object().__sizeof__()),
# Only care about tb_next as an object, not a traceback.
("tb_next", ctypes.py_object),
]
diff --git a/third_party/python/Jinja2/src/jinja2/environment.py b/third_party/python/Jinja2/src/jinja2/environment.py
index bf44b9deb4a1..8430390eeab4 100644
--- a/third_party/python/Jinja2/src/jinja2/environment.py
+++ b/third_party/python/Jinja2/src/jinja2/environment.py
@@ -492,20 +492,20 @@ class Environment(object):
if func is None:
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"
)
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 {}))
diff --git a/third_party/python/Jinja2/src/jinja2/filters.py b/third_party/python/Jinja2/src/jinja2/filters.py
index 1af7ac88a7c9..974156735123 100644
--- a/third_party/python/Jinja2/src/jinja2/filters.py
+++ b/third_party/python/Jinja2/src/jinja2/filters.py
@@ -761,7 +761,7 @@ def do_wordwrap(
def do_wordcount(s):
"""Count the words in that string."""
- return len(_word_re.findall(s))
+ return len(_word_re.findall(soft_unicode(s)))
def do_int(value, default=0, base=10):
diff --git a/third_party/python/Jinja2/src/jinja2/lexer.py b/third_party/python/Jinja2/src/jinja2/lexer.py
index a2b44e926b06..552356a12d1d 100644
--- a/third_party/python/Jinja2/src/jinja2/lexer.py
+++ b/third_party/python/Jinja2/src/jinja2/lexer.py
@@ -681,6 +681,8 @@ class Lexer(object):
source_length = len(source)
balancing_stack = []
lstrip_unless_re = self.lstrip_unless_re
+ newlines_stripped = 0
+ line_starting = True
while 1:
# tokenizer loop
@@ -717,7 +719,9 @@ class Lexer(object):
if strip_sign == "-":
# Strip all whitespace between the text and the tag.
- groups = (text.rstrip(),) + groups[1:]
+ stripped = text.rstrip()
+ newlines_stripped = text[len(stripped) :].count("\n")
+ groups = (stripped,) + groups[1:]
elif (
# Not marked for preserving whitespace.
strip_sign != "+"
@@ -728,11 +732,11 @@ class Lexer(object):
):
# The start of text between the last newline and the tag.
l_pos = text.rfind("\n") + 1
-
- # If there's only whitespace between the newline and the
- # tag, strip it.
- if not lstrip_unless_re.search(text, l_pos):
- groups = (text[:l_pos],) + groups[1:]
+ if l_pos > 0 or line_starting:
+ # If there's only whitespace between the newline and the
+ # tag, strip it.
+ if not lstrip_unless_re.search(text, l_pos):
+ groups = (text[:l_pos],) + groups[1:]
for idx, token in enumerate(tokens):
# failure group
@@ -758,7 +762,8 @@ class Lexer(object):
data = groups[idx]
if data or token not in ignore_if_empty:
yield lineno, token, data
- lineno += data.count("\n")
+ lineno += data.count("\n") + newlines_stripped
+ newlines_stripped = 0
# strings as token just are yielded as it.
else:
@@ -790,6 +795,8 @@ class Lexer(object):
yield lineno, tokens, data
lineno += data.count("\n")
+ line_starting = m.group()[-1:] == "\n"
+
# fetch new position into new variable so that we can check
# if there is a internal parsing error which would result
# in an infinite loop
diff --git a/third_party/python/Jinja2/src/jinja2/loaders.py b/third_party/python/Jinja2/src/jinja2/loaders.py
index ce5537a03c0e..457c4b59a721 100644
--- a/third_party/python/Jinja2/src/jinja2/loaders.py
+++ b/third_party/python/Jinja2/src/jinja2/loaders.py
@@ -3,11 +3,9 @@
sources.
"""
import os
-import pkgutil
import sys
import weakref
from hashlib import sha1
-from importlib import import_module
from os import path
from types import ModuleType
@@ -217,141 +215,75 @@ class FileSystemLoader(BaseLoader):
class PackageLoader(BaseLoader):
- """Load templates from a directory in a Python package.
+ """Load templates from python eggs or packages. It is constructed with
+ the name of the python package and the path to the templates in that
+ package::
- :param package_name: Import name of the package that contains the
- template directory.
- :param package_path: Directory within the imported package that
- contains the templates.
- :param encoding: Encoding of template files.
+ loader = PackageLoader('mypackage', 'views')
- The following example looks up templates in the ``pages`` directory
- within the ``project.ui`` package.
+ If the package path is not given, ``'templates'`` is assumed.
- .. code-block:: python
-
- loader = PackageLoader("project.ui", "pages")
-
- Only packages installed as directories (standard pip behavior) or
- zip/egg files (less common) are supported. The Python API for
- introspecting data in packages is too limited to support other
- installation methods the way this loader requires.
-
- There is limited support for :pep:`420` namespace packages. The
- template directory is assumed to only be in one namespace
- contributor. Zip files contributing to a namespace are not
- supported.
-
- .. versionchanged:: 2.11.0
- No longer uses ``setuptools`` as a dependency.
-
- .. versionchanged:: 2.11.0
- Limited PEP 420 namespace package support.
+ Per default the template encoding is ``'utf-8'`` which can be changed
+ by setting the `encoding` parameter to something else. Due to the nature
+ of eggs it's only possible to reload templates if the package was loaded
+ from the file system and not a zip file.
"""
def __init__(self, package_name, package_path="templates", encoding="utf-8"):
- if package_path == os.path.curdir:
- package_path = ""
- elif package_path[:2] == os.path.curdir + os.path.sep:
- package_path = package_path[2:]
+ from pkg_resources import DefaultProvider
+ from pkg_resources import get_provider
+ from pkg_resources import ResourceManager
- package_path = os.path.normpath(package_path).rstrip(os.path.sep)
- self.package_path = package_path
- self.package_name = package_name
+ provider = get_provider(package_name)
self.encoding = encoding
-
- # Make sure the package exists. This also makes namespace
- # packages work, otherwise get_loader returns None.
- import_module(package_name)
- self._loader = loader = pkgutil.get_loader(package_name)
-
- # Zip loader's archive attribute points at the zip.
- self._archive = getattr(loader, "archive", None)
- self._template_root = None
-
- if hasattr(loader, "get_filename"):
- # A standard directory package, or a zip package.
- self._template_root = os.path.join(
- os.path.dirname(loader.get_filename(package_name)), package_path
- )
- elif hasattr(loader, "_path"):
- # A namespace package, limited support. Find the first
- # contributor with the template directory.
- for root in loader._path:
- root = os.path.join(root, package_path)
-
- if os.path.isdir(root):
- self._template_root = root
- break
-
- if self._template_root is None:
- raise ValueError(
- "The %r package was not installed in a way that"
- " PackageLoader understands." % package_name
- )
+ self.manager = ResourceManager()
+ self.filesystem_bound = isinstance(provider, DefaultProvider)
+ self.provider = provider
+ self.package_path = package_path
def get_source(self, environment, template):
- p = os.path.join(self._template_root, *split_template_path(template))
+ pieces = split_template_path(template)
+ p = "/".join((self.package_path,) + tuple(pieces))
- if self._archive is None:
- # Package is a directory.
- if not os.path.isfile(p):
- raise TemplateNotFound(template)
+ if not self.provider.has_resource(p):
+ raise TemplateNotFound(template)
- with open(p, "rb") as f:
- source = f.read()
+ filename = uptodate = None
- mtime = os.path.getmtime(p)
+ if self.filesystem_bound:
+ filename = self.provider.get_resource_filename(self.manager, p)
+ mtime = path.getmtime(filename)
- def up_to_date():
- return os.path.isfile(p) and os.path.getmtime(p) == mtime
+ def uptodate():
+ try:
+ return path.getmtime(filename) == mtime
+ except OSError:
+ return False
- else:
- # Package is a zip file.
- try:
- source = self._loader.get_data(p)
- except OSError:
- raise TemplateNotFound(template)
-
- # Could use the zip's mtime for all template mtimes, but
- # would need to safely reload the module if it's out of
- # date, so just report it as always current.
- up_to_date = None
-
- return source.decode(self.encoding), p, up_to_date
+ source = self.provider.get_resource_string(self.manager, p)
+ return source.decode(self.encoding), filename, uptodate
def list_templates(self):
+ path = self.package_path
+
+ if path[:2] == "./":
+ path = path[2:]
+ elif path == ".":
+ path = ""
+
+ offset = len(path)
results = []
- if self._archive is None:
- # Package is a directory.
- offset = len(self._template_root)
+ def _walk(path):
+ for filename in self.provider.resource_listdir(path):
+ fullname = path + "/" + filename
- for dirpath, _, filenames in os.walk(self._template_root):
- dirpath = dirpath[offset:].lstrip(os.path.sep)
- results.extend(
- os.path.join(dirpath, name).replace(os.path.sep, "/")
- for name in filenames
- )
- else:
- if not hasattr(self._loader, "_files"):
- raise TypeError(
- "This zip import does not have the required"
- " metadata to list templates."
- )
-
- # Package is a zip file.
- prefix = (
- self._template_root[len(self._archive) :].lstrip(os.path.sep)
- + os.path.sep
- )
- offset = len(prefix)
-
- for name in self._loader._files.keys():
- # Find names under the templates directory that aren't directories.
- if name.startswith(prefix) and name[-1] != os.path.sep:
- results.append(name[offset:].replace(os.path.sep, "/"))
+ if self.provider.resource_isdir(fullname):
+ _walk(fullname)
+ else:
+ results.append(fullname[offset:].lstrip("/"))
+ _walk(path)
results.sort()
return results
diff --git a/third_party/python/Jinja2/src/jinja2/nativetypes.py b/third_party/python/Jinja2/src/jinja2/nativetypes.py
index 9866c962dcff..a9ead4e2bbf0 100644
--- a/third_party/python/Jinja2/src/jinja2/nativetypes.py
+++ b/third_party/python/Jinja2/src/jinja2/nativetypes.py
@@ -1,4 +1,3 @@
-import types
from ast import literal_eval
from itertools import chain
from itertools import islice
@@ -11,7 +10,7 @@ from .environment import Environment
from .environment import Template
-def native_concat(nodes, preserve_quotes=True):
+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
@@ -19,9 +18,6 @@ def native_concat(nodes, preserve_quotes=True):
the string is returned.
:param nodes: Iterable of nodes to concatenate.
- :param preserve_quotes: Whether to re-wrap literal strings with
- quotes, to preserve quotes around expressions for later parsing.
- Should be ``False`` in :meth:`NativeEnvironment.render`.
"""
head = list(islice(nodes, 2))
@@ -31,29 +27,17 @@ def native_concat(nodes, preserve_quotes=True):
if len(head) == 1:
raw = head[0]
else:
- if isinstance(nodes, types.GeneratorType):
- nodes = chain(head, nodes)
- raw = u"".join([text_type(v) for v in nodes])
+ raw = u"".join([text_type(v) for v in chain(head, nodes)])
try:
- literal = literal_eval(raw)
+ return literal_eval(raw)
except (ValueError, SyntaxError, MemoryError):
return raw
- # If literal_eval returned a string, re-wrap with the original
- # quote character to avoid dropping quotes between expression nodes.
- # Without this, "'{{ a }}', '{{ b }}'" results in "a, b", but should
- # be ('a', 'b').
- if preserve_quotes and isinstance(literal, str):
- return "{quote}{}{quote}".format(literal, quote=raw[0])
-
- return literal
-
class NativeCodeGenerator(CodeGenerator):
"""A code generator which renders Python types by not adding
- ``to_string()`` around output nodes, and using :func:`native_concat`
- to convert complex strings back to Python types if possible.
+ ``to_string()`` around output nodes.
"""
@staticmethod
@@ -61,7 +45,7 @@ class NativeCodeGenerator(CodeGenerator):
return value
def _output_const_repr(self, group):
- return repr(native_concat(group))
+ return repr(u"".join([text_type(v) for v in group]))
def _output_child_to_const(self, node, frame, finalize):
const = node.as_const(frame.eval_ctx)
@@ -100,10 +84,9 @@ class NativeTemplate(Template):
Otherwise, the string is returned.
"""
vars = dict(*args, **kwargs)
+
try:
- return native_concat(
- self.root_render_func(self.new_context(vars)), preserve_quotes=False
- )
+ return native_concat(self.root_render_func(self.new_context(vars)))
except Exception:
return self.environment.handle_exception()
diff --git a/third_party/python/Jinja2/src/jinja2/nodes.py b/third_party/python/Jinja2/src/jinja2/nodes.py
index 9f3edc05f9f3..95bd614a140a 100644
--- a/third_party/python/Jinja2/src/jinja2/nodes.py
+++ b/third_party/python/Jinja2/src/jinja2/nodes.py
@@ -671,7 +671,7 @@ class Filter(Expr):
# 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
@@ -684,9 +684,9 @@ class Filter(Expr):
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:
diff --git a/third_party/python/Jinja2/src/jinja2/runtime.py b/third_party/python/Jinja2/src/jinja2/runtime.py
index 527d4b5e4bf2..3ad79686242d 100644
--- a/third_party/python/Jinja2/src/jinja2/runtime.py
+++ b/third_party/python/Jinja2/src/jinja2/runtime.py
@@ -280,11 +280,11 @@ class Context(with_metaclass(ContextMeta)):
break
if callable(__obj):
- if getattr(__obj, "contextfunction", 0):
+ if getattr(__obj, "contextfunction", False) is True:
args = (__self,) + args
- elif getattr(__obj, "evalcontextfunction", 0):
+ elif getattr(__obj, "evalcontextfunction", False) is True:
args = (__self.eval_ctx,) + args
- elif getattr(__obj, "environmentfunction", 0):
+ elif getattr(__obj, "environmentfunction", False) is True:
args = (__self.environment,) + args
try:
return __obj(*args, **kwargs)
diff --git a/third_party/python/Jinja2/src/jinja2/utils.py b/third_party/python/Jinja2/src/jinja2/utils.py
index e3285e8edb45..b422ba9686fa 100644
--- a/third_party/python/Jinja2/src/jinja2/utils.py
+++ b/third_party/python/Jinja2/src/jinja2/utils.py
@@ -165,11 +165,15 @@ def object_type_repr(obj):
return "None"
elif obj is 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__
+ name = cls.__module__ + "." + cls.__name__
+
return "%s object" % name
@@ -693,7 +697,8 @@ class Namespace(object):
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]
diff --git a/third_party/python/MarkupSafe/docs/Makefile b/third_party/python/MarkupSafe/docs/Makefile
deleted file mode 100644
index 51285967a7d9..000000000000
--- a/third_party/python/MarkupSafe/docs/Makefile
+++ /dev/null
@@ -1,19 +0,0 @@
-# Minimal makefile for Sphinx documentation
-#
-
-# You can set these variables from the command line.
-SPHINXOPTS =
-SPHINXBUILD = sphinx-build
-SOURCEDIR = .
-BUILDDIR = _build
-
-# Put it first so that "make" without argument is like "make help".
-help:
- @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
-
-.PHONY: help Makefile
-
-# Catch-all target: route all unknown targets to Sphinx using the new
-# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
-%: Makefile
- @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
diff --git a/third_party/python/MarkupSafe/docs/changes.rst b/third_party/python/MarkupSafe/docs/changes.rst
deleted file mode 100644
index 955deaf27bc8..000000000000
--- a/third_party/python/MarkupSafe/docs/changes.rst
+++ /dev/null
@@ -1,4 +0,0 @@
-Changes
-=======
-
-.. include:: ../CHANGES.rst
diff --git a/third_party/python/MarkupSafe/docs/conf.py b/third_party/python/MarkupSafe/docs/conf.py
deleted file mode 100644
index f34984701bf4..000000000000
--- a/third_party/python/MarkupSafe/docs/conf.py
+++ /dev/null
@@ -1,42 +0,0 @@
-from pallets_sphinx_themes import get_version
-from pallets_sphinx_themes import ProjectLink
-
-# Project --------------------------------------------------------------
-
-project = "MarkupSafe"
-copyright = "2010 Pallets Team"
-author = "Pallets Team"
-release, version = get_version("MarkupSafe")
-
-# General --------------------------------------------------------------
-
-master_doc = "index"
-extensions = ["sphinx.ext.autodoc", "sphinx.ext.intersphinx", "pallets_sphinx_themes"]
-intersphinx_mapping = {"python": ("https://docs.python.org/3/", None)}
-
-# HTML -----------------------------------------------------------------
-
-html_theme = "flask"
-html_theme_options = {"index_sidebar_logo": False}
-html_context = {
- "project_links": [
- ProjectLink("Donate to Pallets", "https://palletsprojects.com/donate"),
- ProjectLink("Website", "https://palletsprojects.com/p/markupsafe/"),
- ProjectLink("PyPI releases", "https://pypi.org/project/MarkupSafe/"),
- ProjectLink("Source Code", "https://github.com/pallets/markupsafe/"),
- ProjectLink("Issue Tracker", "https://github.com/pallets/markupsafe/issues/"),
- ]
-}
-html_sidebars = {
- "index": ["project.html", "localtoc.html", "searchbox.html"],
- "**": ["localtoc.html", "relations.html", "searchbox.html"],
-}
-singlehtml_sidebars = {"index": ["project.html", "localtoc.html"]}
-html_title = "MarkupSafe Documentation ({})".format(version)
-html_show_sourcelink = False
-
-# LaTeX ----------------------------------------------------------------
-
-latex_documents = [
- (master_doc, "MarkupSafe-{}.tex".format(version), html_title, author, "manual")
-]
diff --git a/third_party/python/MarkupSafe/docs/escaping.rst b/third_party/python/MarkupSafe/docs/escaping.rst
deleted file mode 100644
index d99674da87c0..000000000000
--- a/third_party/python/MarkupSafe/docs/escaping.rst
+++ /dev/null
@@ -1,21 +0,0 @@
-.. module:: markupsafe
-
-Working With Safe Text
-======================
-
-.. autofunction:: escape
-
-.. autoclass:: Markup
- :members: escape, unescape, striptags
-
-
-Optional Values
----------------
-
-.. autofunction:: escape_silent
-
-
-Convert an Object to a String
------------------------------
-
-.. autofunction:: soft_unicode
diff --git a/third_party/python/MarkupSafe/docs/formatting.rst b/third_party/python/MarkupSafe/docs/formatting.rst
deleted file mode 100644
index c425134771b3..000000000000
--- a/third_party/python/MarkupSafe/docs/formatting.rst
+++ /dev/null
@@ -1,77 +0,0 @@
-.. currentmodule:: markupsafe
-
-String Formatting
-=================
-
-The :class:`Markup` class can be used as a format string. Objects
-formatted into a markup string will be escaped first.
-
-
-Format Method
--------------
-
-The ``format`` method extends the standard :meth:`str.format` behavior
-to use an ``__html_format__`` method.
-
-#. If an object has an ``__html_format__`` method, it is called as a
- replacement for the ``__format__`` method. It is passed a format
- specifier if it's given. The method must return a string or
- :class:`Markup` instance.
-
-#. If an object has an ``__html__`` method, it is called. If a format
- specifier was passed and the class defined ``__html__`` but not
- ``__html_format__``, a ``ValueError`` is raised.
-
-#. Otherwise Python's default format behavior is used and the result
- is escaped.
-
-For example, to implement a ``User`` that wraps its ``name`` in a
-``span`` tag, and adds a link when using the ``'link'`` format
-specifier:
-
-.. code-block:: python
-
- class User(object):
- def __init__(self, id, name):
- self.id = id
- self.name = name
-
- def __html_format__(self, format_spec):
- if format_spec == 'link':
- return Markup(
- '{}'
- ).format(self.id, self.__html__())
- elif format_spec:
- raise ValueError('Invalid format spec')
- return self.__html__()
-
- def __html__(self):
- return Markup(
- '{0}'
- ).format(self.name)
-
-
-.. code-block:: pycon
-
- >>> user = User(3, '