mirror of
https://gitee.com/openharmony/third_party_jinja2
synced 2024-11-30 02:40:58 +00:00
remove _compat module
This commit is contained in:
parent
b0015c72d5
commit
148a19138c
@ -1,132 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# flake8: noqa
|
||||
import marshal
|
||||
import sys
|
||||
|
||||
PY2 = sys.version_info[0] == 2
|
||||
PYPY = hasattr(sys, "pypy_translation_info")
|
||||
_identity = lambda x: x
|
||||
|
||||
if not PY2:
|
||||
unichr = chr
|
||||
range_type = range
|
||||
text_type = str
|
||||
string_types = (str,)
|
||||
integer_types = (int,)
|
||||
|
||||
iterkeys = lambda d: iter(d.keys())
|
||||
itervalues = lambda d: iter(d.values())
|
||||
iteritems = lambda d: iter(d.items())
|
||||
|
||||
import pickle
|
||||
from io import BytesIO, StringIO
|
||||
|
||||
NativeStringIO = StringIO
|
||||
|
||||
def reraise(tp, value, tb=None):
|
||||
if value.__traceback__ is not tb:
|
||||
raise value.with_traceback(tb)
|
||||
raise value
|
||||
|
||||
ifilter = filter
|
||||
imap = map
|
||||
izip = zip
|
||||
intern = sys.intern
|
||||
|
||||
implements_iterator = _identity
|
||||
implements_to_string = _identity
|
||||
encode_filename = _identity
|
||||
|
||||
marshal_dump = marshal.dump
|
||||
marshal_load = marshal.load
|
||||
|
||||
else:
|
||||
unichr = unichr
|
||||
text_type = unicode
|
||||
range_type = xrange
|
||||
string_types = (str, unicode)
|
||||
integer_types = (int, long)
|
||||
|
||||
iterkeys = lambda d: d.iterkeys()
|
||||
itervalues = lambda d: d.itervalues()
|
||||
iteritems = lambda d: d.iteritems()
|
||||
|
||||
import cPickle as pickle
|
||||
from cStringIO import StringIO as BytesIO, StringIO
|
||||
|
||||
NativeStringIO = BytesIO
|
||||
|
||||
exec("def reraise(tp, value, tb=None):\n raise tp, value, tb")
|
||||
|
||||
from itertools import imap, izip, ifilter
|
||||
|
||||
intern = intern
|
||||
|
||||
def implements_iterator(cls):
|
||||
cls.next = cls.__next__
|
||||
del cls.__next__
|
||||
return cls
|
||||
|
||||
def implements_to_string(cls):
|
||||
cls.__unicode__ = cls.__str__
|
||||
cls.__str__ = lambda x: x.__unicode__().encode("utf-8")
|
||||
return cls
|
||||
|
||||
def encode_filename(filename):
|
||||
if isinstance(filename, unicode):
|
||||
return filename.encode("utf-8")
|
||||
return filename
|
||||
|
||||
def marshal_dump(code, f):
|
||||
if isinstance(f, file):
|
||||
marshal.dump(code, f)
|
||||
else:
|
||||
f.write(marshal.dumps(code))
|
||||
|
||||
def marshal_load(f):
|
||||
if isinstance(f, file):
|
||||
return marshal.load(f)
|
||||
return marshal.loads(f.read())
|
||||
|
||||
|
||||
def with_metaclass(meta, *bases):
|
||||
"""Create a base class with a metaclass."""
|
||||
# This requires a bit of explanation: the basic idea is to make a
|
||||
# dummy metaclass for one level of class instantiation that replaces
|
||||
# itself with the actual metaclass.
|
||||
class metaclass(type):
|
||||
def __new__(cls, name, this_bases, d):
|
||||
return meta(name, bases, d)
|
||||
|
||||
return type.__new__(metaclass, "temporary_class", (), {})
|
||||
|
||||
|
||||
try:
|
||||
from urllib.parse import quote_from_bytes as url_quote
|
||||
except ImportError:
|
||||
from urllib import quote as url_quote
|
||||
|
||||
|
||||
try:
|
||||
from collections import abc
|
||||
except ImportError:
|
||||
import collections as abc
|
||||
|
||||
|
||||
try:
|
||||
from os import fspath
|
||||
except ImportError:
|
||||
try:
|
||||
from pathlib import PurePath
|
||||
except ImportError:
|
||||
PurePath = None
|
||||
|
||||
def fspath(path):
|
||||
if hasattr(path, "__fspath__"):
|
||||
return path.__fspath__()
|
||||
|
||||
# Python 3.5 doesn't have __fspath__ yet, use str.
|
||||
if PurePath is not None and isinstance(path, PurePath):
|
||||
return str(path)
|
||||
|
||||
return path
|
@ -8,19 +8,15 @@ are initialized on the first request.
|
||||
"""
|
||||
import errno
|
||||
import fnmatch
|
||||
import marshal
|
||||
import os
|
||||
import pickle
|
||||
import stat
|
||||
import sys
|
||||
import tempfile
|
||||
from hashlib import sha1
|
||||
from os import listdir
|
||||
from os import path
|
||||
from io import BytesIO
|
||||
|
||||
from ._compat import BytesIO
|
||||
from ._compat import marshal_dump
|
||||
from ._compat import marshal_load
|
||||
from ._compat import pickle
|
||||
from ._compat import text_type
|
||||
from .utils import open_if_exists
|
||||
|
||||
bc_version = 4
|
||||
@ -67,7 +63,7 @@ class Bucket(object):
|
||||
return
|
||||
# if marshal_load fails then we need to reload
|
||||
try:
|
||||
self.code = marshal_load(f)
|
||||
self.code = marshal.load(f)
|
||||
except (EOFError, ValueError, TypeError):
|
||||
self.reset()
|
||||
return
|
||||
@ -78,7 +74,7 @@ class Bucket(object):
|
||||
raise TypeError("can't write empty bucket")
|
||||
f.write(bc_magic)
|
||||
pickle.dump(self.checksum, f, 2)
|
||||
marshal_dump(self.code, f)
|
||||
marshal.dump(self.code, f)
|
||||
|
||||
def bytecode_from_string(self, string):
|
||||
"""Load bytecode from a string."""
|
||||
@ -145,7 +141,7 @@ class BytecodeCache(object):
|
||||
hash = sha1(name.encode("utf-8"))
|
||||
if filename is not None:
|
||||
filename = "|" + filename
|
||||
if isinstance(filename, text_type):
|
||||
if isinstance(filename, str):
|
||||
filename = filename.encode("utf-8")
|
||||
hash.update(filename)
|
||||
return hash.hexdigest()
|
||||
@ -241,7 +237,7 @@ class FileSystemBytecodeCache(BytecodeCache):
|
||||
return actual_dir
|
||||
|
||||
def _get_cache_filename(self, bucket):
|
||||
return path.join(self.directory, self.pattern % bucket.key)
|
||||
return os.path.join(self.directory, self.pattern % bucket.key)
|
||||
|
||||
def load_bytecode(self, bucket):
|
||||
f = open_if_exists(self._get_cache_filename(bucket), "rb")
|
||||
@ -264,10 +260,10 @@ class FileSystemBytecodeCache(BytecodeCache):
|
||||
# normally.
|
||||
from os import remove
|
||||
|
||||
files = fnmatch.filter(listdir(self.directory), self.pattern % "*")
|
||||
files = fnmatch.filter(os.listdir(self.directory), self.pattern % "*")
|
||||
for filename in files:
|
||||
try:
|
||||
remove(path.join(self.directory, filename))
|
||||
remove(os.path.join(self.directory, filename))
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
"""Compiles nodes from the parser into Python code."""
|
||||
from collections import namedtuple
|
||||
from functools import update_wrapper
|
||||
from io import StringIO
|
||||
from itertools import chain
|
||||
from keyword import iskeyword as is_python_keyword
|
||||
|
||||
@ -9,13 +10,6 @@ from markupsafe import escape
|
||||
from markupsafe import Markup
|
||||
|
||||
from . import nodes
|
||||
from ._compat import imap
|
||||
from ._compat import iteritems
|
||||
from ._compat import izip
|
||||
from ._compat import NativeStringIO
|
||||
from ._compat import range_type
|
||||
from ._compat import string_types
|
||||
from ._compat import text_type
|
||||
from .exceptions import TemplateAssertionError
|
||||
from .idtracking import Symbols
|
||||
from .idtracking import VAR_LOAD_ALIAS
|
||||
@ -38,30 +32,6 @@ operators = {
|
||||
"notin": "not in",
|
||||
}
|
||||
|
||||
# what method to iterate over items do we want to use for dict iteration
|
||||
# in generated code? on 2.x let's go with iteritems, on 3.x with items
|
||||
if hasattr(dict, "iteritems"):
|
||||
dict_item_iter = "iteritems"
|
||||
else:
|
||||
dict_item_iter = "items"
|
||||
|
||||
code_features = ["division"]
|
||||
|
||||
# does this python version support generator stops? (PEP 0479)
|
||||
try:
|
||||
exec("from __future__ import generator_stop")
|
||||
code_features.append("generator_stop")
|
||||
except SyntaxError:
|
||||
pass
|
||||
|
||||
# does this python version support yield from?
|
||||
try:
|
||||
exec("def f(): yield from x()")
|
||||
except SyntaxError:
|
||||
supports_yield_from = False
|
||||
else:
|
||||
supports_yield_from = True
|
||||
|
||||
|
||||
def optimizeconst(f):
|
||||
def new_func(self, node, frame, **kwargs):
|
||||
@ -93,20 +63,16 @@ def has_safe_repr(value):
|
||||
"""Does the node have a safe representation?"""
|
||||
if value is None or value is NotImplemented or value is Ellipsis:
|
||||
return True
|
||||
if type(value) in (bool, int, float, complex, range_type, Markup) + string_types:
|
||||
return True
|
||||
if type(value) in (tuple, list, set, frozenset):
|
||||
for item in value:
|
||||
if not has_safe_repr(item):
|
||||
return False
|
||||
return True
|
||||
elif type(value) is dict:
|
||||
for key, value in iteritems(value):
|
||||
if not has_safe_repr(key):
|
||||
return False
|
||||
if not has_safe_repr(value):
|
||||
return False
|
||||
|
||||
if type(value) in {bool, int, float, complex, range, str, Markup}:
|
||||
return True
|
||||
|
||||
if type(value) in {tuple, list, set, frozenset}:
|
||||
return all(has_safe_repr(v) for v in value)
|
||||
|
||||
if type(value) is dict:
|
||||
return all(has_safe_repr(k) and has_safe_repr(v) for k, v in value.items())
|
||||
|
||||
return False
|
||||
|
||||
|
||||
@ -249,7 +215,7 @@ class CodeGenerator(NodeVisitor):
|
||||
self, environment, name, filename, stream=None, defer_init=False, optimized=True
|
||||
):
|
||||
if stream is None:
|
||||
stream = NativeStringIO()
|
||||
stream = StringIO()
|
||||
self.environment = environment
|
||||
self.name = name
|
||||
self.filename = filename
|
||||
@ -432,7 +398,7 @@ class CodeGenerator(NodeVisitor):
|
||||
self.write(", ")
|
||||
self.visit(kwarg, frame)
|
||||
if extra_kwargs is not None:
|
||||
for key, value in iteritems(extra_kwargs):
|
||||
for key, value in extra_kwargs.items():
|
||||
self.write(", %s=%s" % (key, value))
|
||||
if node.dyn_args:
|
||||
self.write(", *")
|
||||
@ -448,7 +414,7 @@ class CodeGenerator(NodeVisitor):
|
||||
self.visit(kwarg.value, frame)
|
||||
self.write(", ")
|
||||
if extra_kwargs is not None:
|
||||
for key, value in iteritems(extra_kwargs):
|
||||
for key, value in extra_kwargs.items():
|
||||
self.write("%r: %s, " % (key, value))
|
||||
if node.dyn_kwargs is not None:
|
||||
self.write("}, **")
|
||||
@ -477,7 +443,7 @@ class CodeGenerator(NodeVisitor):
|
||||
|
||||
def enter_frame(self, frame):
|
||||
undefs = []
|
||||
for target, (action, param) in iteritems(frame.symbols.loads):
|
||||
for target, (action, param) in frame.symbols.loads.items():
|
||||
if action == VAR_LOAD_PARAMETER:
|
||||
pass
|
||||
elif action == VAR_LOAD_RESOLVE:
|
||||
@ -494,7 +460,7 @@ class CodeGenerator(NodeVisitor):
|
||||
def leave_frame(self, frame, with_python_scope=False):
|
||||
if not with_python_scope:
|
||||
undefs = []
|
||||
for target, _ in iteritems(frame.symbols.loads):
|
||||
for target in frame.symbols.loads:
|
||||
undefs.append(target)
|
||||
if undefs:
|
||||
self.writeline("%s = missing" % " = ".join(undefs))
|
||||
@ -612,7 +578,7 @@ class CodeGenerator(NodeVisitor):
|
||||
def dump_local_context(self, frame):
|
||||
return "{%s}" % ", ".join(
|
||||
"%r: %s" % (name, target)
|
||||
for name, target in iteritems(frame.symbols.dump_stores())
|
||||
for name, target in frame.symbols.dump_stores().items()
|
||||
)
|
||||
|
||||
def write_commons(self):
|
||||
@ -704,7 +670,7 @@ class CodeGenerator(NodeVisitor):
|
||||
else:
|
||||
self.writeline(
|
||||
"context.exported_vars.update((%s))"
|
||||
% ", ".join(imap(repr, public_names))
|
||||
% ", ".join(map(repr, public_names))
|
||||
)
|
||||
|
||||
# -- Statement Visitors
|
||||
@ -715,7 +681,6 @@ class CodeGenerator(NodeVisitor):
|
||||
|
||||
from .runtime import exported
|
||||
|
||||
self.writeline("from __future__ import %s" % ", ".join(code_features))
|
||||
self.writeline("from jinja2.runtime import " + ", ".join(exported))
|
||||
|
||||
if self.environment.is_async:
|
||||
@ -781,7 +746,7 @@ class CodeGenerator(NodeVisitor):
|
||||
self.indent()
|
||||
self.writeline("if parent_template is not None:")
|
||||
self.indent()
|
||||
if supports_yield_from and not self.environment.is_async:
|
||||
if not self.environment.is_async:
|
||||
self.writeline("yield from parent_template.root_render_func(context)")
|
||||
else:
|
||||
self.writeline(
|
||||
@ -795,7 +760,7 @@ class CodeGenerator(NodeVisitor):
|
||||
self.outdent(1 + (not self.has_known_extends))
|
||||
|
||||
# at this point we now have the blocks collected and can visit them too.
|
||||
for name, block in iteritems(self.blocks):
|
||||
for name, block in self.blocks.items():
|
||||
self.writeline(
|
||||
"%s(context, missing=missing%s):"
|
||||
% (self.func("block_" + name), envenv),
|
||||
@ -851,11 +816,7 @@ class CodeGenerator(NodeVisitor):
|
||||
else:
|
||||
context = self.get_context_ref()
|
||||
|
||||
if (
|
||||
supports_yield_from
|
||||
and not self.environment.is_async
|
||||
and frame.buffer is None
|
||||
):
|
||||
if not self.environment.is_async and frame.buffer is None:
|
||||
self.writeline(
|
||||
"yield from context.blocks[%r][0](%s)" % (node.name, context), node
|
||||
)
|
||||
@ -900,9 +861,7 @@ class CodeGenerator(NodeVisitor):
|
||||
self.writeline("parent_template = environment.get_template(", node)
|
||||
self.visit(node.template, frame)
|
||||
self.write(", %r)" % self.name)
|
||||
self.writeline(
|
||||
"for name, parent_block in parent_template.blocks.%s():" % dict_item_iter
|
||||
)
|
||||
self.writeline("for name, parent_block in parent_template.blocks.items():")
|
||||
self.indent()
|
||||
self.writeline("context.blocks.setdefault(name, []).append(parent_block)")
|
||||
self.outdent()
|
||||
@ -924,7 +883,7 @@ class CodeGenerator(NodeVisitor):
|
||||
|
||||
func_name = "get_or_select_template"
|
||||
if isinstance(node.template, nodes.Const):
|
||||
if isinstance(node.template.value, string_types):
|
||||
if isinstance(node.template.value, str):
|
||||
func_name = "get_template"
|
||||
elif isinstance(node.template.value, (tuple, list)):
|
||||
func_name = "select_template"
|
||||
@ -958,13 +917,8 @@ class CodeGenerator(NodeVisitor):
|
||||
"._body_stream:"
|
||||
)
|
||||
else:
|
||||
if supports_yield_from:
|
||||
self.writeline("yield from template._get_default_module()._body_stream")
|
||||
skip_event_yield = True
|
||||
else:
|
||||
self.writeline(
|
||||
"for event in template._get_default_module()._body_stream:"
|
||||
)
|
||||
self.writeline("yield from template._get_default_module()._body_stream")
|
||||
skip_event_yield = True
|
||||
|
||||
if not skip_event_yield:
|
||||
self.indent()
|
||||
@ -1071,7 +1025,7 @@ class CodeGenerator(NodeVisitor):
|
||||
else:
|
||||
self.writeline(
|
||||
"context.exported_vars.difference_"
|
||||
"update((%s))" % ", ".join(imap(repr, discarded_names))
|
||||
"update((%s))" % ", ".join(map(repr, discarded_names))
|
||||
)
|
||||
|
||||
def visit_For(self, node, frame):
|
||||
@ -1262,7 +1216,7 @@ class CodeGenerator(NodeVisitor):
|
||||
with_frame = frame.inner()
|
||||
with_frame.symbols.analyze_node(node)
|
||||
self.enter_frame(with_frame)
|
||||
for target, expr in izip(node.targets, node.values):
|
||||
for target, expr in zip(node.targets, node.values):
|
||||
self.newline()
|
||||
self.visit(target, with_frame)
|
||||
self.write(" = ")
|
||||
@ -1278,7 +1232,7 @@ class CodeGenerator(NodeVisitor):
|
||||
#: The default finalize function if the environment isn't configured
|
||||
#: with one. Or if the environment has one, this is called on that
|
||||
#: function's output for constants.
|
||||
_default_finalize = text_type
|
||||
_default_finalize = str
|
||||
_finalize = None
|
||||
|
||||
def _make_finalize(self):
|
||||
@ -1344,7 +1298,7 @@ class CodeGenerator(NodeVisitor):
|
||||
|
||||
# Template data doesn't go through finalize.
|
||||
if isinstance(node, nodes.TemplateData):
|
||||
return text_type(const)
|
||||
return str(const)
|
||||
|
||||
return finalize.const(const)
|
||||
|
||||
@ -1353,11 +1307,11 @@ class CodeGenerator(NodeVisitor):
|
||||
``Output`` node.
|
||||
"""
|
||||
if frame.eval_ctx.volatile:
|
||||
self.write("(escape if context.eval_ctx.autoescape else to_string)(")
|
||||
self.write("(escape if context.eval_ctx.autoescape else str)(")
|
||||
elif frame.eval_ctx.autoescape:
|
||||
self.write("escape(")
|
||||
else:
|
||||
self.write("to_string(")
|
||||
self.write("str(")
|
||||
|
||||
if finalize.src is not None:
|
||||
self.write(finalize.src)
|
||||
@ -1615,11 +1569,11 @@ class CodeGenerator(NodeVisitor):
|
||||
@optimizeconst
|
||||
def visit_Concat(self, node, frame):
|
||||
if frame.eval_ctx.volatile:
|
||||
func_name = "(context.eval_ctx.volatile and markup_join or unicode_join)"
|
||||
func_name = "(context.eval_ctx.volatile and markup_join or str_join)"
|
||||
elif frame.eval_ctx.autoescape:
|
||||
func_name = "markup_join"
|
||||
else:
|
||||
func_name = "unicode_join"
|
||||
func_name = "str_join"
|
||||
self.write("%s((" % func_name)
|
||||
for arg in node.nodes:
|
||||
self.visit(arg, frame)
|
||||
|
@ -1,8 +1,8 @@
|
||||
import platform
|
||||
import sys
|
||||
from types import CodeType
|
||||
|
||||
from . import TemplateSyntaxError
|
||||
from ._compat import PYPY
|
||||
from .utils import internal_code
|
||||
from .utils import missing
|
||||
|
||||
@ -14,13 +14,11 @@ def rewrite_traceback_stack(source=None):
|
||||
|
||||
This must be called within an ``except`` block.
|
||||
|
||||
:param exc_info: A :meth:`sys.exc_info` tuple. If not provided,
|
||||
the current ``exc_info`` is used.
|
||||
:param source: For ``TemplateSyntaxError``, the original source if
|
||||
known.
|
||||
:return: A :meth:`sys.exc_info` tuple that can be re-raised.
|
||||
:return: The original exception with the rewritten traceback.
|
||||
"""
|
||||
exc_type, exc_value, tb = sys.exc_info()
|
||||
_, exc_value, tb = sys.exc_info()
|
||||
|
||||
if isinstance(exc_value, TemplateSyntaxError) and not exc_value.translated:
|
||||
exc_value.translated = True
|
||||
@ -70,7 +68,7 @@ def rewrite_traceback_stack(source=None):
|
||||
for tb in reversed(stack):
|
||||
tb_next = tb_set_next(tb, tb_next)
|
||||
|
||||
return exc_type, exc_value, tb_next
|
||||
return exc_value.with_traceback(tb_next)
|
||||
|
||||
|
||||
def fake_traceback(exc_value, tb, filename, lineno):
|
||||
@ -215,7 +213,7 @@ if sys.version_info >= (3, 7):
|
||||
return tb
|
||||
|
||||
|
||||
elif PYPY:
|
||||
elif platform.python_implementation() == "PyPy":
|
||||
# PyPy might have special support, and won't work with ctypes.
|
||||
try:
|
||||
import tputil
|
||||
|
@ -1,5 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from ._compat import range_type
|
||||
from .filters import FILTERS as DEFAULT_FILTERS # noqa: F401
|
||||
from .tests import TESTS as DEFAULT_TESTS # noqa: F401
|
||||
from .utils import Cycler
|
||||
@ -24,7 +23,7 @@ KEEP_TRAILING_NEWLINE = False
|
||||
# default filters, tests and namespace
|
||||
|
||||
DEFAULT_NAMESPACE = {
|
||||
"range": range_type,
|
||||
"range": range,
|
||||
"dict": dict,
|
||||
"lipsum": generate_lorem_ipsum,
|
||||
"cycler": Cycler,
|
||||
|
@ -11,13 +11,6 @@ from functools import reduce
|
||||
from markupsafe import Markup
|
||||
|
||||
from . import nodes
|
||||
from ._compat import encode_filename
|
||||
from ._compat import implements_iterator
|
||||
from ._compat import implements_to_string
|
||||
from ._compat import iteritems
|
||||
from ._compat import reraise
|
||||
from ._compat import string_types
|
||||
from ._compat import text_type
|
||||
from .compiler import CodeGenerator
|
||||
from .compiler import generate
|
||||
from .defaults import BLOCK_END_STRING
|
||||
@ -102,7 +95,7 @@ def load_extensions(environment, extensions):
|
||||
"""
|
||||
result = {}
|
||||
for extension in extensions:
|
||||
if isinstance(extension, string_types):
|
||||
if isinstance(extension, str):
|
||||
extension = import_string(extension)
|
||||
result[extension.identifier] = extension(environment)
|
||||
return result
|
||||
@ -376,7 +369,7 @@ class Environment(object):
|
||||
yet. This is used by :ref:`extensions <writing-extensions>` to register
|
||||
callbacks and configuration values without breaking inheritance.
|
||||
"""
|
||||
for key, value in iteritems(attributes):
|
||||
for key, value in attributes.items():
|
||||
if not hasattr(self, key):
|
||||
setattr(self, key, value)
|
||||
|
||||
@ -421,7 +414,7 @@ class Environment(object):
|
||||
rv.overlayed = True
|
||||
rv.linked_to = self
|
||||
|
||||
for key, value in iteritems(args):
|
||||
for key, value in args.items():
|
||||
if value is not missing:
|
||||
setattr(rv, key, value)
|
||||
|
||||
@ -431,7 +424,7 @@ class Environment(object):
|
||||
rv.cache = copy_cache(self.cache)
|
||||
|
||||
rv.extensions = {}
|
||||
for key, value in iteritems(self.extensions):
|
||||
for key, value in self.extensions.items():
|
||||
rv.extensions[key] = value.bind(rv)
|
||||
if extensions is not missing:
|
||||
rv.extensions.update(load_extensions(rv, extensions))
|
||||
@ -449,7 +442,7 @@ class Environment(object):
|
||||
try:
|
||||
return obj[argument]
|
||||
except (AttributeError, TypeError, LookupError):
|
||||
if isinstance(argument, string_types):
|
||||
if isinstance(argument, str):
|
||||
try:
|
||||
attr = str(argument)
|
||||
except Exception:
|
||||
@ -534,7 +527,7 @@ class Environment(object):
|
||||
|
||||
def _parse(self, source, name, filename):
|
||||
"""Internal parsing function used by `parse` and `compile`."""
|
||||
return Parser(self, source, name, encode_filename(filename)).parse()
|
||||
return Parser(self, source, name, filename).parse()
|
||||
|
||||
def lex(self, source, name=None, filename=None):
|
||||
"""Lex the given sourcecode and return a generator that yields
|
||||
@ -546,7 +539,7 @@ class Environment(object):
|
||||
of the extensions to be applied you have to filter source through
|
||||
the :meth:`preprocess` method.
|
||||
"""
|
||||
source = text_type(source)
|
||||
source = str(source)
|
||||
try:
|
||||
return self.lexer.tokeniter(source, name, filename)
|
||||
except TemplateSyntaxError:
|
||||
@ -560,7 +553,7 @@ class Environment(object):
|
||||
return reduce(
|
||||
lambda s, e: e.preprocess(s, name, filename),
|
||||
self.iter_extensions(),
|
||||
text_type(source),
|
||||
str(source),
|
||||
)
|
||||
|
||||
def _tokenize(self, source, name, filename=None, state=None):
|
||||
@ -621,7 +614,7 @@ class Environment(object):
|
||||
"""
|
||||
source_hint = None
|
||||
try:
|
||||
if isinstance(source, string_types):
|
||||
if isinstance(source, str):
|
||||
source_hint = source
|
||||
source = self._parse(source, name, filename)
|
||||
source = self._generate(source, name, filename, defer_init=defer_init)
|
||||
@ -629,8 +622,6 @@ class Environment(object):
|
||||
return source
|
||||
if filename is None:
|
||||
filename = "<template>"
|
||||
else:
|
||||
filename = encode_filename(filename)
|
||||
return self._compile(source, filename)
|
||||
except TemplateSyntaxError:
|
||||
self.handle_exception(source=source_hint)
|
||||
@ -718,7 +709,7 @@ class Environment(object):
|
||||
info.external_attr = 0o755 << 16
|
||||
zip_file.writestr(info, data)
|
||||
else:
|
||||
if isinstance(data, text_type):
|
||||
if isinstance(data, str):
|
||||
data = data.encode("utf8")
|
||||
|
||||
with open(os.path.join(target, filename), "wb") as f:
|
||||
@ -795,7 +786,7 @@ class Environment(object):
|
||||
"""
|
||||
from .debug import rewrite_traceback_stack
|
||||
|
||||
reraise(*rewrite_traceback_stack(source=source))
|
||||
raise rewrite_traceback_stack(source=source)
|
||||
|
||||
def join_path(self, template, parent):
|
||||
"""Join a template with the parent. By default all the lookups are
|
||||
@ -892,7 +883,7 @@ class Environment(object):
|
||||
|
||||
.. versionadded:: 2.3
|
||||
"""
|
||||
if isinstance(template_name_or_list, (string_types, Undefined)):
|
||||
if isinstance(template_name_or_list, (str, Undefined)):
|
||||
return self.get_template(template_name_or_list, parent, globals)
|
||||
elif isinstance(template_name_or_list, Template):
|
||||
return template_name_or_list
|
||||
@ -1185,7 +1176,6 @@ class Template(object):
|
||||
return "<%s %s>" % (self.__class__.__name__, name)
|
||||
|
||||
|
||||
@implements_to_string
|
||||
class TemplateModule(object):
|
||||
"""Represents an imported template. All the exported names of the
|
||||
template are available as attributes on this object. Additionally
|
||||
@ -1239,7 +1229,6 @@ class TemplateExpression(object):
|
||||
return rv
|
||||
|
||||
|
||||
@implements_iterator
|
||||
class TemplateStream(object):
|
||||
"""A template stream works pretty much like an ordinary python generator
|
||||
but it can buffer multiple items to reduce the number of total iterations.
|
||||
@ -1265,7 +1254,7 @@ class TemplateStream(object):
|
||||
Template('Hello {{ name }}!').stream(name='foo').dump('hello.html')
|
||||
"""
|
||||
close = False
|
||||
if isinstance(fp, string_types):
|
||||
if isinstance(fp, str):
|
||||
if encoding is None:
|
||||
encoding = "utf-8"
|
||||
fp = open(fp, "wb")
|
||||
|
@ -1,44 +1,15 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from ._compat import imap
|
||||
from ._compat import implements_to_string
|
||||
from ._compat import PY2
|
||||
from ._compat import text_type
|
||||
|
||||
|
||||
class TemplateError(Exception):
|
||||
"""Baseclass for all template errors."""
|
||||
|
||||
if PY2:
|
||||
def __init__(self, message=None):
|
||||
super().__init__(message)
|
||||
|
||||
def __init__(self, message=None):
|
||||
if message is not None:
|
||||
message = text_type(message).encode("utf-8")
|
||||
Exception.__init__(self, message)
|
||||
|
||||
@property
|
||||
def message(self):
|
||||
if self.args:
|
||||
message = self.args[0]
|
||||
if message is not None:
|
||||
return message.decode("utf-8", "replace")
|
||||
|
||||
def __unicode__(self):
|
||||
return self.message or u""
|
||||
|
||||
else:
|
||||
|
||||
def __init__(self, message=None):
|
||||
Exception.__init__(self, message)
|
||||
|
||||
@property
|
||||
def message(self):
|
||||
if self.args:
|
||||
message = self.args[0]
|
||||
if message is not None:
|
||||
return message
|
||||
@property
|
||||
def message(self):
|
||||
if self.args:
|
||||
return self.args[0]
|
||||
|
||||
|
||||
@implements_to_string
|
||||
class TemplateNotFound(IOError, LookupError, TemplateError):
|
||||
"""Raised if a template does not exist.
|
||||
|
||||
@ -95,13 +66,12 @@ class TemplatesNotFound(TemplateNotFound):
|
||||
parts.append(name)
|
||||
|
||||
message = u"none of the templates given were found: " + u", ".join(
|
||||
imap(text_type, parts)
|
||||
map(str, parts)
|
||||
)
|
||||
TemplateNotFound.__init__(self, names and names[-1] or None, message)
|
||||
self.templates = list(names)
|
||||
|
||||
|
||||
@implements_to_string
|
||||
class TemplateSyntaxError(TemplateError):
|
||||
"""Raised to tell the user that there is a problem with the template."""
|
||||
|
||||
|
@ -7,9 +7,6 @@ from sys import version_info
|
||||
from markupsafe import Markup
|
||||
|
||||
from . import nodes
|
||||
from ._compat import iteritems
|
||||
from ._compat import string_types
|
||||
from ._compat import with_metaclass
|
||||
from .defaults import BLOCK_END_STRING
|
||||
from .defaults import BLOCK_START_STRING
|
||||
from .defaults import COMMENT_END_STRING
|
||||
@ -47,7 +44,7 @@ class ExtensionRegistry(type):
|
||||
return rv
|
||||
|
||||
|
||||
class Extension(with_metaclass(ExtensionRegistry, object)):
|
||||
class Extension(metaclass=ExtensionRegistry):
|
||||
"""Extensions can be used to add extra functionality to the Jinja template
|
||||
system at the parser level. Custom extensions are bound to an environment
|
||||
but may not store environment specific data on `self`. The reason for
|
||||
@ -222,7 +219,7 @@ class InternationalizationExtension(Extension):
|
||||
self.environment.globals.pop(key, None)
|
||||
|
||||
def _extract(self, source, gettext_functions=GETTEXT_FUNCTIONS):
|
||||
if isinstance(source, string_types):
|
||||
if isinstance(source, str):
|
||||
source = self.environment.parse(source)
|
||||
return extract_from_ast(source, gettext_functions)
|
||||
|
||||
@ -409,7 +406,7 @@ class InternationalizationExtension(Extension):
|
||||
# enough to handle the variable expansion and autoescape
|
||||
# handling itself
|
||||
if self.environment.newstyle_gettext:
|
||||
for key, value in iteritems(variables):
|
||||
for key, value in variables.items():
|
||||
# the function adds that later anyways in case num was
|
||||
# called num, so just skip it.
|
||||
if num_called_num and key == "num":
|
||||
@ -554,7 +551,7 @@ def extract_from_ast(node, gettext_functions=GETTEXT_FUNCTIONS, babel_style=True
|
||||
|
||||
strings = []
|
||||
for arg in node.args:
|
||||
if isinstance(arg, nodes.Const) and isinstance(arg.value, string_types):
|
||||
if isinstance(arg, nodes.Const) and isinstance(arg.value, str):
|
||||
strings.append(arg.value)
|
||||
else:
|
||||
strings.append(None)
|
||||
|
@ -3,28 +3,24 @@
|
||||
import math
|
||||
import random
|
||||
import re
|
||||
from collections import abc
|
||||
from collections import namedtuple
|
||||
from itertools import chain
|
||||
from itertools import groupby
|
||||
|
||||
from markupsafe import escape
|
||||
from markupsafe import Markup
|
||||
from markupsafe import soft_unicode
|
||||
from markupsafe import soft_str
|
||||
|
||||
from ._compat import abc
|
||||
from ._compat import imap
|
||||
from ._compat import iteritems
|
||||
from ._compat import string_types
|
||||
from ._compat import text_type
|
||||
from .exceptions import FilterArgumentError
|
||||
from .runtime import Undefined
|
||||
from .utils import htmlsafe_json_dumps
|
||||
from .utils import pformat
|
||||
from .utils import unicode_urlencode
|
||||
from .utils import url_quote
|
||||
from .utils import urlize
|
||||
|
||||
_word_re = re.compile(r"\w+", re.UNICODE)
|
||||
_word_beginning_split_re = re.compile(r"([-\s\(\{\[\<]+)", re.UNICODE)
|
||||
_word_re = re.compile(r"\w+")
|
||||
_word_beginning_split_re = re.compile(r"([-\s({\[<]+)")
|
||||
|
||||
|
||||
def contextfilter(f):
|
||||
@ -57,7 +53,7 @@ def environmentfilter(f):
|
||||
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
|
||||
return value.lower() if isinstance(value, str) else value
|
||||
|
||||
|
||||
def make_attrgetter(environment, attribute, postprocess=None, default=None):
|
||||
@ -95,7 +91,7 @@ def make_multi_attrgetter(environment, attribute, postprocess=None):
|
||||
Examples of attribute: "attr1,attr2", "attr1.inner1.0,attr2.inner2.0", etc.
|
||||
"""
|
||||
attribute_parts = (
|
||||
attribute.split(",") if isinstance(attribute, string_types) else [attribute]
|
||||
attribute.split(",") if isinstance(attribute, str) else [attribute]
|
||||
)
|
||||
attribute = [
|
||||
_prepare_attribute_parts(attribute_part) for attribute_part in attribute_parts
|
||||
@ -120,7 +116,7 @@ def make_multi_attrgetter(environment, attribute, postprocess=None):
|
||||
def _prepare_attribute_parts(attr):
|
||||
if attr is None:
|
||||
return []
|
||||
elif isinstance(attr, string_types):
|
||||
elif isinstance(attr, str):
|
||||
return [int(x) if x.isdigit() else x for x in attr.split(".")]
|
||||
else:
|
||||
return [attr]
|
||||
@ -130,7 +126,7 @@ def do_forceescape(value):
|
||||
"""Enforce HTML escaping. This will probably double escape variables."""
|
||||
if hasattr(value, "__html__"):
|
||||
value = value.__html__()
|
||||
return escape(text_type(value))
|
||||
return escape(str(value))
|
||||
|
||||
|
||||
def do_urlencode(value):
|
||||
@ -149,16 +145,16 @@ def do_urlencode(value):
|
||||
|
||||
.. versionadded:: 2.7
|
||||
"""
|
||||
if isinstance(value, string_types) or not isinstance(value, abc.Iterable):
|
||||
return unicode_urlencode(value)
|
||||
if isinstance(value, str) or not isinstance(value, abc.Iterable):
|
||||
return url_quote(value)
|
||||
|
||||
if isinstance(value, dict):
|
||||
items = iteritems(value)
|
||||
items = value.items()
|
||||
else:
|
||||
items = iter(value)
|
||||
|
||||
return u"&".join(
|
||||
"%s=%s" % (unicode_urlencode(k, for_qs=True), unicode_urlencode(v, for_qs=True))
|
||||
"%s=%s" % (url_quote(k, for_qs=True), url_quote(v, for_qs=True))
|
||||
for k, v in items
|
||||
)
|
||||
|
||||
@ -182,7 +178,7 @@ def do_replace(eval_ctx, s, old, new, count=None):
|
||||
if count is None:
|
||||
count = -1
|
||||
if not eval_ctx.autoescape:
|
||||
return text_type(s).replace(text_type(old), text_type(new), count)
|
||||
return str(s).replace(str(old), str(new), count)
|
||||
if (
|
||||
hasattr(old, "__html__")
|
||||
or hasattr(new, "__html__")
|
||||
@ -190,18 +186,18 @@ def do_replace(eval_ctx, s, old, new, count=None):
|
||||
):
|
||||
s = escape(s)
|
||||
else:
|
||||
s = soft_unicode(s)
|
||||
return s.replace(soft_unicode(old), soft_unicode(new), count)
|
||||
s = soft_str(s)
|
||||
return s.replace(soft_str(old), soft_str(new), count)
|
||||
|
||||
|
||||
def do_upper(s):
|
||||
"""Convert a value to uppercase."""
|
||||
return soft_unicode(s).upper()
|
||||
return soft_str(s).upper()
|
||||
|
||||
|
||||
def do_lower(s):
|
||||
"""Convert a value to lowercase."""
|
||||
return soft_unicode(s).lower()
|
||||
return soft_str(s).lower()
|
||||
|
||||
|
||||
@evalcontextfilter
|
||||
@ -230,7 +226,7 @@ def do_xmlattr(_eval_ctx, d, autospace=True):
|
||||
"""
|
||||
rv = u" ".join(
|
||||
u'%s="%s"' % (escape(key), escape(value))
|
||||
for key, value in iteritems(d)
|
||||
for key, value in d.items()
|
||||
if value is not None and not isinstance(value, Undefined)
|
||||
)
|
||||
if autospace and rv:
|
||||
@ -244,7 +240,7 @@ def do_capitalize(s):
|
||||
"""Capitalize a value. The first character will be uppercase, all others
|
||||
lowercase.
|
||||
"""
|
||||
return soft_unicode(s).capitalize()
|
||||
return soft_str(s).capitalize()
|
||||
|
||||
|
||||
def do_title(s):
|
||||
@ -254,7 +250,7 @@ def do_title(s):
|
||||
return "".join(
|
||||
[
|
||||
item[0].upper() + item[1:].lower()
|
||||
for item in _word_beginning_split_re.split(soft_unicode(s))
|
||||
for item in _word_beginning_split_re.split(soft_str(s))
|
||||
if item
|
||||
]
|
||||
)
|
||||
@ -471,11 +467,11 @@ def do_join(eval_ctx, value, d=u"", attribute=None):
|
||||
The `attribute` parameter was added.
|
||||
"""
|
||||
if attribute is not None:
|
||||
value = imap(make_attrgetter(eval_ctx.environment, attribute), value)
|
||||
value = map(make_attrgetter(eval_ctx.environment, attribute), value)
|
||||
|
||||
# no automatic escaping? joining is a lot easier then
|
||||
if not eval_ctx.autoescape:
|
||||
return text_type(d).join(imap(text_type, value))
|
||||
return str(d).join(map(str, value))
|
||||
|
||||
# if the delimiter doesn't have an html representation we check
|
||||
# if any of the items has. If yes we do a coercion to Markup
|
||||
@ -486,20 +482,20 @@ def do_join(eval_ctx, value, d=u"", attribute=None):
|
||||
if hasattr(item, "__html__"):
|
||||
do_escape = True
|
||||
else:
|
||||
value[idx] = text_type(item)
|
||||
value[idx] = str(item)
|
||||
if do_escape:
|
||||
d = escape(d)
|
||||
else:
|
||||
d = text_type(d)
|
||||
d = str(d)
|
||||
return d.join(value)
|
||||
|
||||
# no html involved, to normal joining
|
||||
return soft_unicode(d).join(imap(soft_unicode, value))
|
||||
return soft_str(d).join(map(soft_str, value))
|
||||
|
||||
|
||||
def do_center(value, width=80):
|
||||
"""Centers the value in a field of a given width."""
|
||||
return text_type(value).center(width)
|
||||
return str(value).center(width)
|
||||
|
||||
|
||||
@environmentfilter
|
||||
@ -764,7 +760,7 @@ def do_int(value, default=0, base=10):
|
||||
The base is ignored for decimal numbers and non-string values.
|
||||
"""
|
||||
try:
|
||||
if isinstance(value, string_types):
|
||||
if isinstance(value, str):
|
||||
return int(value, base)
|
||||
return int(value)
|
||||
except (TypeError, ValueError):
|
||||
@ -810,19 +806,19 @@ def do_format(value, *args, **kwargs):
|
||||
raise FilterArgumentError(
|
||||
"can't handle positional and keyword arguments at the same time"
|
||||
)
|
||||
return soft_unicode(value) % (kwargs or args)
|
||||
return soft_str(value) % (kwargs or args)
|
||||
|
||||
|
||||
def do_trim(value, chars=None):
|
||||
"""Strip leading and trailing characters, by default whitespace."""
|
||||
return soft_unicode(value).strip(chars)
|
||||
return soft_str(value).strip(chars)
|
||||
|
||||
|
||||
def do_striptags(value):
|
||||
"""Strip SGML/XML tags and replace adjacent whitespace by one space."""
|
||||
if hasattr(value, "__html__"):
|
||||
value = value.__html__()
|
||||
return Markup(text_type(value)).striptags()
|
||||
return Markup(str(value)).striptags()
|
||||
|
||||
|
||||
def do_slice(value, slices, fill_with=None):
|
||||
@ -995,7 +991,7 @@ def do_sum(environment, iterable, attribute=None, start=0):
|
||||
attributes. Also the `start` parameter was moved on to the right.
|
||||
"""
|
||||
if attribute is not None:
|
||||
iterable = imap(make_attrgetter(environment, attribute), iterable)
|
||||
iterable = map(make_attrgetter(environment, attribute), iterable)
|
||||
return sum(iterable, start)
|
||||
|
||||
|
||||
@ -1015,14 +1011,14 @@ def do_mark_safe(value):
|
||||
|
||||
def do_mark_unsafe(value):
|
||||
"""Mark a value as unsafe. This is the reverse operation for :func:`safe`."""
|
||||
return text_type(value)
|
||||
return str(value)
|
||||
|
||||
|
||||
def do_reverse(value):
|
||||
"""Reverse the object or return an iterator that iterates over it the other
|
||||
way round.
|
||||
"""
|
||||
if isinstance(value, string_types):
|
||||
if isinstance(value, str):
|
||||
return value[::-1]
|
||||
try:
|
||||
return reversed(value)
|
||||
@ -1355,7 +1351,7 @@ FILTERS = {
|
||||
"selectattr": do_selectattr,
|
||||
"slice": do_slice,
|
||||
"sort": do_sort,
|
||||
"string": soft_unicode,
|
||||
"string": soft_str,
|
||||
"striptags": do_striptags,
|
||||
"sum": do_sum,
|
||||
"title": do_title,
|
||||
|
@ -1,4 +1,3 @@
|
||||
from ._compat import iteritems
|
||||
from .visitor import NodeVisitor
|
||||
|
||||
VAR_LOAD_PARAMETER = "param"
|
||||
@ -114,7 +113,7 @@ class Symbols(object):
|
||||
self.loads.update(sym.loads)
|
||||
self.stores.update(sym.stores)
|
||||
|
||||
for name, branch_count in iteritems(stores):
|
||||
for name, branch_count in stores.items():
|
||||
if branch_count == len(branch_symbols):
|
||||
continue
|
||||
target = self.find_ref(name)
|
||||
@ -141,7 +140,7 @@ class Symbols(object):
|
||||
rv = set()
|
||||
node = self
|
||||
while node is not None:
|
||||
for target, (instr, _) in iteritems(self.loads):
|
||||
for target, (instr, _) in self.loads.items():
|
||||
if instr == VAR_LOAD_PARAMETER:
|
||||
rv.add(target)
|
||||
node = node.parent
|
||||
|
@ -8,11 +8,8 @@ import re
|
||||
from ast import literal_eval
|
||||
from collections import deque
|
||||
from operator import itemgetter
|
||||
from sys import intern
|
||||
|
||||
from ._compat import implements_iterator
|
||||
from ._compat import intern
|
||||
from ._compat import iteritems
|
||||
from ._compat import text_type
|
||||
from .exceptions import TemplateSyntaxError
|
||||
from .utils import LRUCache
|
||||
|
||||
@ -21,7 +18,7 @@ from .utils import LRUCache
|
||||
_lexer_cache = LRUCache(50)
|
||||
|
||||
# static regular expressions
|
||||
whitespace_re = re.compile(r"\s+", re.U)
|
||||
whitespace_re = re.compile(r"\s+")
|
||||
newline_re = re.compile(r"(\r\n|\r|\n)")
|
||||
string_re = re.compile(
|
||||
r"('([^'\\]*(?:\\.[^'\\]*)*)'" r'|"([^"\\]*(?:\\.[^"\\]*)*)")', re.S
|
||||
@ -136,7 +133,7 @@ operators = {
|
||||
";": TOKEN_SEMICOLON,
|
||||
}
|
||||
|
||||
reverse_operators = dict([(v, k) for k, v in iteritems(operators)])
|
||||
reverse_operators = dict([(v, k) for k, v in operators.items()])
|
||||
assert len(operators) == len(reverse_operators), "operators dropped"
|
||||
operator_re = re.compile(
|
||||
"(%s)" % "|".join(re.escape(x) for x in sorted(operators, key=lambda x: -len(x)))
|
||||
@ -296,7 +293,6 @@ class Token(tuple):
|
||||
return "Token(%r, %r, %r)" % (self.lineno, self.type, self.value)
|
||||
|
||||
|
||||
@implements_iterator
|
||||
class TokenStreamIterator(object):
|
||||
"""The iterator for tokenstreams. Iterate over the stream
|
||||
until the eof token is reached.
|
||||
@ -317,7 +313,6 @@ class TokenStreamIterator(object):
|
||||
return token
|
||||
|
||||
|
||||
@implements_iterator
|
||||
class TokenStream(object):
|
||||
"""A token stream is an iterable that yields :class:`Token`\\s. The
|
||||
parser however does not iterate over it but calls :meth:`next` to go
|
||||
@ -665,7 +660,6 @@ class Lexer(object):
|
||||
"""This method tokenizes the text and returns the tokens in a
|
||||
generator. Use this method if you just want to tokenize a template.
|
||||
"""
|
||||
source = text_type(source)
|
||||
lines = source.splitlines()
|
||||
if self.keep_trailing_newline and source:
|
||||
for newline in ("\r\n", "\r", "\n"):
|
||||
@ -744,7 +738,7 @@ class Lexer(object):
|
||||
# yield for the current token the first named
|
||||
# group that matched
|
||||
elif token == "#bygroup":
|
||||
for key, value in iteritems(m.groupdict()):
|
||||
for key, value in m.groupdict().items():
|
||||
if value is not None:
|
||||
yield lineno, key, value
|
||||
lineno += value.count("\n")
|
||||
@ -804,7 +798,7 @@ class Lexer(object):
|
||||
stack.pop()
|
||||
# resolve the new state by group checking
|
||||
elif new_state == "#bygroup":
|
||||
for key, value in iteritems(m.groupdict()):
|
||||
for key, value in m.groupdict().items():
|
||||
if value is not None:
|
||||
stack.append(key)
|
||||
break
|
||||
|
@ -6,15 +6,11 @@ import os
|
||||
import pkgutil
|
||||
import sys
|
||||
import weakref
|
||||
from collections import abc
|
||||
from hashlib import sha1
|
||||
from importlib import import_module
|
||||
from os import path
|
||||
from types import ModuleType
|
||||
|
||||
from ._compat import abc
|
||||
from ._compat import fspath
|
||||
from ._compat import iteritems
|
||||
from ._compat import string_types
|
||||
from .exceptions import TemplateNotFound
|
||||
from .utils import internalcode
|
||||
from .utils import open_if_exists
|
||||
@ -27,9 +23,9 @@ def split_template_path(template):
|
||||
pieces = []
|
||||
for piece in template.split("/"):
|
||||
if (
|
||||
path.sep in piece
|
||||
or (path.altsep and path.altsep in piece)
|
||||
or piece == path.pardir
|
||||
os.path.sep in piece
|
||||
or (os.path.altsep and os.path.altsep in piece)
|
||||
or piece == os.path.pardir
|
||||
):
|
||||
raise TemplateNotFound(template)
|
||||
elif piece and piece != ".":
|
||||
@ -163,22 +159,17 @@ class FileSystemLoader(BaseLoader):
|
||||
"""
|
||||
|
||||
def __init__(self, searchpath, encoding="utf-8", followlinks=False):
|
||||
if not isinstance(searchpath, abc.Iterable) or isinstance(
|
||||
searchpath, string_types
|
||||
):
|
||||
if not isinstance(searchpath, abc.Iterable) or isinstance(searchpath, str):
|
||||
searchpath = [searchpath]
|
||||
|
||||
# In Python 3.5, os.path.join doesn't support Path. This can be
|
||||
# simplified to list(searchpath) when Python 3.5 is dropped.
|
||||
self.searchpath = [fspath(p) for p in searchpath]
|
||||
|
||||
self.searchpath = list(searchpath)
|
||||
self.encoding = encoding
|
||||
self.followlinks = followlinks
|
||||
|
||||
def get_source(self, environment, template):
|
||||
pieces = split_template_path(template)
|
||||
for searchpath in self.searchpath:
|
||||
filename = path.join(searchpath, *pieces)
|
||||
filename = os.path.join(searchpath, *pieces)
|
||||
f = open_if_exists(filename)
|
||||
if f is None:
|
||||
continue
|
||||
@ -187,11 +178,11 @@ class FileSystemLoader(BaseLoader):
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
mtime = path.getmtime(filename)
|
||||
mtime = os.path.getmtime(filename)
|
||||
|
||||
def uptodate():
|
||||
try:
|
||||
return path.getmtime(filename) == mtime
|
||||
return os.path.getmtime(filename) == mtime
|
||||
except OSError:
|
||||
return False
|
||||
|
||||
@ -403,7 +394,7 @@ class FunctionLoader(BaseLoader):
|
||||
rv = self.load_func(template)
|
||||
if rv is None:
|
||||
raise TemplateNotFound(template)
|
||||
elif isinstance(rv, string_types):
|
||||
elif isinstance(rv, str):
|
||||
return rv, None, None
|
||||
return rv
|
||||
|
||||
@ -456,7 +447,7 @@ class PrefixLoader(BaseLoader):
|
||||
|
||||
def list_templates(self):
|
||||
result = []
|
||||
for prefix, loader in iteritems(self.mapping):
|
||||
for prefix, loader in self.mapping.items():
|
||||
for template in loader.list_templates():
|
||||
result.append(prefix + self.delimiter + template)
|
||||
return result
|
||||
@ -529,10 +520,10 @@ class ModuleLoader(BaseLoader):
|
||||
# path given.
|
||||
mod = _TemplateModule(package_name)
|
||||
|
||||
if not isinstance(path, abc.Iterable) or isinstance(path, string_types):
|
||||
if not isinstance(path, abc.Iterable) or isinstance(path, str):
|
||||
path = [path]
|
||||
|
||||
mod.__path__ = [fspath(p) for p in path]
|
||||
mod.__path__ = [os.fspath(p) for p in path]
|
||||
|
||||
sys.modules[package_name] = weakref.proxy(
|
||||
mod, lambda x: sys.modules.pop(package_name, None)
|
||||
|
@ -3,8 +3,6 @@
|
||||
interesting for introspection.
|
||||
"""
|
||||
from . import nodes
|
||||
from ._compat import iteritems
|
||||
from ._compat import string_types
|
||||
from .compiler import CodeGenerator
|
||||
|
||||
|
||||
@ -21,7 +19,7 @@ class TrackingCodeGenerator(CodeGenerator):
|
||||
def enter_frame(self, frame):
|
||||
"""Remember all undeclared identifiers."""
|
||||
CodeGenerator.enter_frame(self, frame)
|
||||
for _, (action, param) in iteritems(frame.symbols.loads):
|
||||
for _, (action, param) in frame.symbols.loads.items():
|
||||
if action == "resolve" and param not in self.environment.globals:
|
||||
self.undeclared_identifiers.add(param)
|
||||
|
||||
@ -35,7 +33,7 @@ def find_undeclared_variables(ast):
|
||||
>>> from jinja2 import Environment, meta
|
||||
>>> env = Environment()
|
||||
>>> ast = env.parse('{% set foo = 42 %}{{ bar + foo }}')
|
||||
>>> meta.find_undeclared_variables(ast) == set(['bar'])
|
||||
>>> meta.find_undeclared_variables(ast) == {'bar'}
|
||||
True
|
||||
|
||||
.. admonition:: Implementation
|
||||
@ -75,7 +73,7 @@ def find_referenced_templates(ast):
|
||||
# something const, only yield the strings and ignore
|
||||
# non-string consts that really just make no sense
|
||||
if isinstance(template_name, nodes.Const):
|
||||
if isinstance(template_name.value, string_types):
|
||||
if isinstance(template_name.value, str):
|
||||
yield template_name.value
|
||||
# something dynamic in there
|
||||
else:
|
||||
@ -85,7 +83,7 @@ def find_referenced_templates(ast):
|
||||
yield None
|
||||
continue
|
||||
# constant is a basestring, direct template name
|
||||
if isinstance(node.template.value, string_types):
|
||||
if isinstance(node.template.value, str):
|
||||
yield node.template.value
|
||||
# a tuple or list (latter *should* not happen) made of consts,
|
||||
# yield the consts that are strings. We could warn here for
|
||||
@ -94,7 +92,7 @@ def find_referenced_templates(ast):
|
||||
node.template.value, (tuple, list)
|
||||
):
|
||||
for template_name in node.template.value:
|
||||
if isinstance(template_name, string_types):
|
||||
if isinstance(template_name, str):
|
||||
yield template_name
|
||||
# something else we don't care about, we could warn here
|
||||
else:
|
||||
|
@ -4,7 +4,6 @@ from itertools import chain
|
||||
from itertools import islice
|
||||
|
||||
from . import nodes
|
||||
from ._compat import text_type
|
||||
from .compiler import CodeGenerator
|
||||
from .compiler import has_safe_repr
|
||||
from .environment import Environment
|
||||
@ -33,7 +32,7 @@ def native_concat(nodes, preserve_quotes=True):
|
||||
else:
|
||||
if isinstance(nodes, types.GeneratorType):
|
||||
nodes = chain(head, nodes)
|
||||
raw = u"".join([text_type(v) for v in nodes])
|
||||
raw = u"".join([str(v) for v in nodes])
|
||||
|
||||
try:
|
||||
literal = literal_eval(raw)
|
||||
@ -52,7 +51,7 @@ def native_concat(nodes, preserve_quotes=True):
|
||||
|
||||
class NativeCodeGenerator(CodeGenerator):
|
||||
"""A code generator which renders Python types by not adding
|
||||
``to_string()`` around output nodes, and using :func:`native_concat`
|
||||
``str()`` around output nodes, and using :func:`native_concat`
|
||||
to convert complex strings back to Python types if possible.
|
||||
"""
|
||||
|
||||
|
@ -8,11 +8,6 @@ from collections import deque
|
||||
|
||||
from markupsafe import Markup
|
||||
|
||||
from ._compat import izip
|
||||
from ._compat import PY2
|
||||
from ._compat import text_type
|
||||
from ._compat import with_metaclass
|
||||
|
||||
_binop_to_func = {
|
||||
"*": operator.mul,
|
||||
"/": operator.truediv,
|
||||
@ -49,9 +44,9 @@ class NodeType(type):
|
||||
def __new__(mcs, name, bases, d):
|
||||
for attr in "fields", "attributes":
|
||||
storage = []
|
||||
storage.extend(getattr(bases[0], attr, ()))
|
||||
storage.extend(getattr(bases[0] if bases else object, attr, ()))
|
||||
storage.extend(d.get(attr, ()))
|
||||
assert len(bases) == 1, "multiple inheritance not allowed"
|
||||
assert len(bases) <= 1, "multiple inheritance not allowed"
|
||||
assert len(storage) == len(set(storage)), "layout conflict"
|
||||
d[attr] = tuple(storage)
|
||||
d.setdefault("abstract", False)
|
||||
@ -91,7 +86,7 @@ def get_eval_context(node, ctx):
|
||||
return ctx
|
||||
|
||||
|
||||
class Node(with_metaclass(NodeType, object)):
|
||||
class Node(metaclass=NodeType):
|
||||
"""Baseclass for all Jinja nodes. There are a number of nodes available
|
||||
of different types. There are four major types:
|
||||
|
||||
@ -127,7 +122,7 @@ class Node(with_metaclass(NodeType, object)):
|
||||
len(self.fields) != 1 and "s" or "",
|
||||
)
|
||||
)
|
||||
for name, arg in izip(self.fields, fields):
|
||||
for name, arg in zip(self.fields, fields):
|
||||
setattr(self, name, arg)
|
||||
for attr in self.attributes:
|
||||
setattr(self, attr, attributes.pop(attr, None))
|
||||
@ -510,17 +505,7 @@ class Const(Literal):
|
||||
fields = ("value",)
|
||||
|
||||
def as_const(self, eval_ctx=None):
|
||||
rv = self.value
|
||||
if (
|
||||
PY2
|
||||
and type(rv) is text_type
|
||||
and self.environment.policies["compiler.ascii_str"]
|
||||
):
|
||||
try:
|
||||
rv = rv.encode("ascii")
|
||||
except UnicodeError:
|
||||
pass
|
||||
return rv
|
||||
return self.value
|
||||
|
||||
@classmethod
|
||||
def from_untrusted(cls, value, lineno=None, environment=None):
|
||||
@ -796,7 +781,7 @@ class Concat(Expr):
|
||||
|
||||
def as_const(self, eval_ctx=None):
|
||||
eval_ctx = get_eval_context(self, eval_ctx)
|
||||
return "".join(text_type(x.as_const(eval_ctx)) for x in self.nodes)
|
||||
return "".join(str(x.as_const(eval_ctx)) for x in self.nodes)
|
||||
|
||||
|
||||
class Compare(Expr):
|
||||
|
@ -1,7 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Parse tokens from the lexer into nodes for the compiler."""
|
||||
from . import nodes
|
||||
from ._compat import imap
|
||||
from .exceptions import TemplateAssertionError
|
||||
from .exceptions import TemplateSyntaxError
|
||||
from .lexer import describe_token
|
||||
@ -66,7 +65,7 @@ class Parser(object):
|
||||
def _fail_ut_eof(self, name, end_token_stack, lineno):
|
||||
expected = []
|
||||
for exprs in end_token_stack:
|
||||
expected.extend(imap(describe_token_expr, exprs))
|
||||
expected.extend(map(describe_token_expr, exprs))
|
||||
if end_token_stack:
|
||||
currently_looking = " or ".join(
|
||||
"'%s'" % describe_token_expr(expr) for expr in end_token_stack[-1]
|
||||
|
@ -1,22 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""The runtime functions and state used by compiled templates."""
|
||||
import sys
|
||||
from collections import abc
|
||||
from itertools import chain
|
||||
from types import MethodType
|
||||
|
||||
from markupsafe import escape # noqa: F401
|
||||
from markupsafe import Markup
|
||||
from markupsafe import soft_unicode
|
||||
from markupsafe import soft_str
|
||||
|
||||
from ._compat import abc
|
||||
from ._compat import imap
|
||||
from ._compat import implements_iterator
|
||||
from ._compat import implements_to_string
|
||||
from ._compat import iteritems
|
||||
from ._compat import PY2
|
||||
from ._compat import string_types
|
||||
from ._compat import text_type
|
||||
from ._compat import with_metaclass
|
||||
from .exceptions import TemplateNotFound # noqa: F401
|
||||
from .exceptions import TemplateRuntimeError # noqa: F401
|
||||
from .exceptions import UndefinedError
|
||||
@ -39,18 +31,13 @@ exported = [
|
||||
"concat",
|
||||
"escape",
|
||||
"markup_join",
|
||||
"unicode_join",
|
||||
"to_string",
|
||||
"str_join",
|
||||
"identity",
|
||||
"TemplateNotFound",
|
||||
"Namespace",
|
||||
"Undefined",
|
||||
]
|
||||
|
||||
#: the name of the function that is used to convert something into
|
||||
#: a string. We can just use the text type here.
|
||||
to_string = text_type
|
||||
|
||||
|
||||
def identity(x):
|
||||
"""Returns its argument. Useful for certain things in the
|
||||
@ -62,7 +49,7 @@ def identity(x):
|
||||
def markup_join(seq):
|
||||
"""Concatenation that escapes if necessary and converts to string."""
|
||||
buf = []
|
||||
iterator = imap(soft_unicode, seq)
|
||||
iterator = map(soft_str, seq)
|
||||
for arg in iterator:
|
||||
buf.append(arg)
|
||||
if hasattr(arg, "__html__"):
|
||||
@ -70,9 +57,21 @@ def markup_join(seq):
|
||||
return concat(buf)
|
||||
|
||||
|
||||
def unicode_join(seq):
|
||||
def str_join(seq):
|
||||
"""Simple args to string conversion and concatenation."""
|
||||
return concat(imap(text_type, seq))
|
||||
return concat(map(str, seq))
|
||||
|
||||
|
||||
def unicode_join(seq):
|
||||
import warnings
|
||||
|
||||
warnings.warn(
|
||||
"This template must be recompiled with at least Jinja 3.0, or"
|
||||
" it will fail in 3.1.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
return str_join(seq)
|
||||
|
||||
|
||||
def new_context(
|
||||
@ -96,7 +95,7 @@ def new_context(
|
||||
# we don't want to modify the dict passed
|
||||
if shared:
|
||||
parent = dict(parent)
|
||||
for key, value in iteritems(locals):
|
||||
for key, value in locals.items():
|
||||
if value is not missing:
|
||||
parent[key] = value
|
||||
return environment.context_class(environment, parent, template_name, blocks)
|
||||
@ -155,7 +154,8 @@ def resolve_or_missing(context, key, missing=missing):
|
||||
return missing
|
||||
|
||||
|
||||
class Context(with_metaclass(ContextMeta)):
|
||||
@abc.Mapping.register
|
||||
class Context(metaclass=ContextMeta):
|
||||
"""The template context holds the variables of a template. It stores the
|
||||
values passed to the template and also the names the template exports.
|
||||
Creating instances is neither supported nor useful as it's created
|
||||
@ -191,7 +191,7 @@ class Context(with_metaclass(ContextMeta)):
|
||||
# create the initial mapping of blocks. Whenever template inheritance
|
||||
# takes place the runtime will update this mapping with the new blocks
|
||||
# from the template.
|
||||
self.blocks = dict((k, [v]) for k, v in iteritems(blocks))
|
||||
self.blocks = {k: [v] for k, v in blocks.items()}
|
||||
|
||||
# In case we detect the fast resolve mode we can set up an alias
|
||||
# here that bypasses the legacy code logic.
|
||||
@ -304,7 +304,7 @@ class Context(with_metaclass(ContextMeta)):
|
||||
self.environment, self.name, {}, self.get_all(), True, None, locals
|
||||
)
|
||||
context.eval_ctx = self.eval_ctx
|
||||
context.blocks.update((k, list(v)) for k, v in iteritems(self.blocks))
|
||||
context.blocks.update((k, list(v)) for k, v in self.blocks.items())
|
||||
return context
|
||||
|
||||
def _all(meth): # noqa: B902
|
||||
@ -318,12 +318,6 @@ class Context(with_metaclass(ContextMeta)):
|
||||
keys = _all("keys")
|
||||
values = _all("values")
|
||||
items = _all("items")
|
||||
|
||||
# not available on python 3
|
||||
if PY2:
|
||||
iterkeys = _all("iterkeys")
|
||||
itervalues = _all("itervalues")
|
||||
iteritems = _all("iteritems")
|
||||
del _all
|
||||
|
||||
def __contains__(self, name):
|
||||
@ -346,9 +340,6 @@ class Context(with_metaclass(ContextMeta)):
|
||||
)
|
||||
|
||||
|
||||
abc.Mapping.register(Context)
|
||||
|
||||
|
||||
class BlockReference(object):
|
||||
"""One block on a template reference."""
|
||||
|
||||
@ -375,7 +366,6 @@ class BlockReference(object):
|
||||
return rv
|
||||
|
||||
|
||||
@implements_iterator
|
||||
class LoopContext:
|
||||
"""A wrapper iterable for dynamic ``for`` loops, with information
|
||||
about the loop and iteration.
|
||||
@ -688,7 +678,6 @@ class Macro(object):
|
||||
)
|
||||
|
||||
|
||||
@implements_to_string
|
||||
class Undefined(object):
|
||||
"""The default undefined type. This undefined type can be printed and
|
||||
iterated over, but every other access will raise an :exc:`UndefinedError`:
|
||||
@ -728,7 +717,7 @@ class Undefined(object):
|
||||
if self._undefined_obj is missing:
|
||||
return "%r is undefined" % self._undefined_name
|
||||
|
||||
if not isinstance(self._undefined_name, string_types):
|
||||
if not isinstance(self._undefined_name, str):
|
||||
return "%s has no element %r" % (
|
||||
object_type_repr(self._undefined_obj),
|
||||
self._undefined_name,
|
||||
@ -752,51 +741,16 @@ class Undefined(object):
|
||||
raise AttributeError(name)
|
||||
return self._fail_with_undefined_error()
|
||||
|
||||
__add__ = (
|
||||
__radd__
|
||||
) = (
|
||||
__mul__
|
||||
) = (
|
||||
__rmul__
|
||||
) = (
|
||||
__div__
|
||||
) = (
|
||||
__rdiv__
|
||||
) = (
|
||||
__truediv__
|
||||
) = (
|
||||
__rtruediv__
|
||||
) = (
|
||||
__floordiv__
|
||||
) = (
|
||||
__rfloordiv__
|
||||
) = (
|
||||
__mod__
|
||||
) = (
|
||||
__rmod__
|
||||
) = (
|
||||
__pos__
|
||||
) = (
|
||||
__neg__
|
||||
) = (
|
||||
__call__
|
||||
) = (
|
||||
__getitem__
|
||||
) = (
|
||||
__lt__
|
||||
) = (
|
||||
__le__
|
||||
) = (
|
||||
__gt__
|
||||
) = (
|
||||
__ge__
|
||||
) = (
|
||||
__int__
|
||||
) = (
|
||||
__float__
|
||||
) = (
|
||||
__complex__
|
||||
) = __pow__ = __rpow__ = __sub__ = __rsub__ = _fail_with_undefined_error
|
||||
__add__ = __radd__ = __sub__ = __rsub__ = _fail_with_undefined_error
|
||||
__mul__ = __rmul__ = __div__ = __rdiv__ = _fail_with_undefined_error
|
||||
__truediv__ = __rtruediv__ = _fail_with_undefined_error
|
||||
__floordiv__ = __rfloordiv__ = _fail_with_undefined_error
|
||||
__mod__ = __rmod__ = _fail_with_undefined_error
|
||||
__pos__ = __neg__ = _fail_with_undefined_error
|
||||
__call__ = __getitem__ = _fail_with_undefined_error
|
||||
__lt__ = __le__ = __gt__ = __ge__ = _fail_with_undefined_error
|
||||
__int__ = __float__ = __complex__ = _fail_with_undefined_error
|
||||
__pow__ = __rpow__ = _fail_with_undefined_error
|
||||
|
||||
def __eq__(self, other):
|
||||
return type(self) is type(other)
|
||||
@ -817,11 +771,9 @@ class Undefined(object):
|
||||
if 0:
|
||||
yield None
|
||||
|
||||
def __nonzero__(self):
|
||||
def __bool__(self):
|
||||
return False
|
||||
|
||||
__bool__ = __nonzero__
|
||||
|
||||
def __repr__(self):
|
||||
return "Undefined"
|
||||
|
||||
@ -858,7 +810,7 @@ def make_logging_undefined(logger=None, base=None):
|
||||
if undef._undefined_hint is None:
|
||||
if undef._undefined_obj is missing:
|
||||
hint = "%s is undefined" % undef._undefined_name
|
||||
elif not isinstance(undef._undefined_name, string_types):
|
||||
elif not isinstance(undef._undefined_name, str):
|
||||
hint = "%s has no element %s" % (
|
||||
object_type_repr(undef._undefined_obj),
|
||||
undef._undefined_name,
|
||||
@ -890,31 +842,14 @@ def make_logging_undefined(logger=None, base=None):
|
||||
_log_message(self)
|
||||
return rv
|
||||
|
||||
if PY2:
|
||||
|
||||
def __nonzero__(self):
|
||||
rv = base.__nonzero__(self)
|
||||
_log_message(self)
|
||||
return rv
|
||||
|
||||
def __unicode__(self):
|
||||
rv = base.__unicode__(self)
|
||||
_log_message(self)
|
||||
return rv
|
||||
|
||||
else:
|
||||
|
||||
def __bool__(self):
|
||||
rv = base.__bool__(self)
|
||||
_log_message(self)
|
||||
return rv
|
||||
def __bool__(self):
|
||||
rv = base.__bool__(self)
|
||||
_log_message(self)
|
||||
return rv
|
||||
|
||||
return LoggingUndefined
|
||||
|
||||
|
||||
# No @implements_to_string decorator here because __str__
|
||||
# is not overwritten from Undefined in this class.
|
||||
# This would cause a recursion error in Python 2.
|
||||
class ChainableUndefined(Undefined):
|
||||
"""An undefined that is chainable, where both ``__getattr__`` and
|
||||
``__getitem__`` return itself rather than raising an
|
||||
@ -942,7 +877,6 @@ class ChainableUndefined(Undefined):
|
||||
__getitem__ = __getattr__
|
||||
|
||||
|
||||
@implements_to_string
|
||||
class DebugUndefined(Undefined):
|
||||
"""An undefined that returns the debug info when printed.
|
||||
|
||||
@ -970,7 +904,6 @@ class DebugUndefined(Undefined):
|
||||
return u"{{ undefined value printed: %s }}" % self._undefined_hint
|
||||
|
||||
|
||||
@implements_to_string
|
||||
class StrictUndefined(Undefined):
|
||||
"""An undefined that barks on print and iteration as well as boolean
|
||||
tests and all kinds of comparisons. In other words: you can do nothing
|
||||
@ -992,17 +925,12 @@ class StrictUndefined(Undefined):
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
__iter__ = (
|
||||
__str__
|
||||
) = (
|
||||
__len__
|
||||
) = (
|
||||
__nonzero__
|
||||
) = __eq__ = __ne__ = __bool__ = __hash__ = Undefined._fail_with_undefined_error
|
||||
__iter__ = __str__ = __len__ = Undefined._fail_with_undefined_error
|
||||
__eq__ = __ne__ = __bool__ = __hash__ = Undefined._fail_with_undefined_error
|
||||
|
||||
|
||||
# remove remaining slots attributes, after the metaclass did the magic they
|
||||
# are unneeded and irritating as they contain wrong data for the subclasses.
|
||||
# Remove slots attributes, after the metaclass is applied they are
|
||||
# unneeded and contain wrong data for subclasses.
|
||||
del (
|
||||
Undefined.__slots__,
|
||||
ChainableUndefined.__slots__,
|
||||
|
@ -4,38 +4,24 @@ Useful when the template itself comes from an untrusted source.
|
||||
"""
|
||||
import operator
|
||||
import types
|
||||
from collections import abc
|
||||
from collections import deque
|
||||
from string import Formatter
|
||||
|
||||
from markupsafe import EscapeFormatter
|
||||
from markupsafe import Markup
|
||||
|
||||
from ._compat import abc
|
||||
from ._compat import PY2
|
||||
from ._compat import range_type
|
||||
from ._compat import string_types
|
||||
from .environment import Environment
|
||||
from .exceptions import SecurityError
|
||||
|
||||
#: maximum number of items a range may produce
|
||||
MAX_RANGE = 100000
|
||||
|
||||
#: attributes of function objects that are considered unsafe.
|
||||
if PY2:
|
||||
UNSAFE_FUNCTION_ATTRIBUTES = {
|
||||
"func_closure",
|
||||
"func_code",
|
||||
"func_dict",
|
||||
"func_defaults",
|
||||
"func_globals",
|
||||
}
|
||||
else:
|
||||
# On versions > python 2 the special attributes on functions are gone,
|
||||
# but they remain on methods and generators for whatever reason.
|
||||
UNSAFE_FUNCTION_ATTRIBUTES = set()
|
||||
#: Unsafe function attributes.
|
||||
UNSAFE_FUNCTION_ATTRIBUTES = set()
|
||||
|
||||
#: unsafe method attributes. function attributes are unsafe for methods too
|
||||
UNSAFE_METHOD_ATTRIBUTES = {"im_class", "im_func", "im_self"}
|
||||
#: Unsafe method attributes. Function attributes are unsafe for methods too.
|
||||
UNSAFE_METHOD_ATTRIBUTES = set()
|
||||
|
||||
#: unsafe generator attributes.
|
||||
UNSAFE_GENERATOR_ATTRIBUTES = {"gi_frame", "gi_code"}
|
||||
@ -46,36 +32,9 @@ UNSAFE_COROUTINE_ATTRIBUTES = {"cr_frame", "cr_code"}
|
||||
#: unsafe attributes on async generators
|
||||
UNSAFE_ASYNC_GENERATOR_ATTRIBUTES = {"ag_code", "ag_frame"}
|
||||
|
||||
_mutable_set_types = (set,)
|
||||
_mutable_mapping_types = (dict,)
|
||||
_mutable_sequence_types = (list,)
|
||||
|
||||
# on python 2.x we can register the user collection types
|
||||
try:
|
||||
from UserDict import UserDict, DictMixin
|
||||
from UserList import UserList
|
||||
|
||||
_mutable_mapping_types += (UserDict, DictMixin)
|
||||
_mutable_set_types += (UserList,)
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
# if sets is still available, register the mutable set from there as well
|
||||
try:
|
||||
from sets import Set
|
||||
|
||||
_mutable_set_types += (Set,)
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
#: register Python 2.6 abstract base classes
|
||||
_mutable_set_types += (abc.MutableSet,)
|
||||
_mutable_mapping_types += (abc.MutableMapping,)
|
||||
_mutable_sequence_types += (abc.MutableSequence,)
|
||||
|
||||
_mutable_spec = (
|
||||
(
|
||||
_mutable_set_types,
|
||||
abc.MutableSet,
|
||||
frozenset(
|
||||
[
|
||||
"add",
|
||||
@ -90,11 +49,11 @@ _mutable_spec = (
|
||||
),
|
||||
),
|
||||
(
|
||||
_mutable_mapping_types,
|
||||
abc.MutableMapping,
|
||||
frozenset(["clear", "pop", "popitem", "setdefault", "update"]),
|
||||
),
|
||||
(
|
||||
_mutable_sequence_types,
|
||||
abc.MutableSequence,
|
||||
frozenset(["append", "reverse", "insert", "sort", "extend", "remove"]),
|
||||
),
|
||||
(
|
||||
@ -153,7 +112,7 @@ def inspect_format_method(callable):
|
||||
) or callable.__name__ not in ("format", "format_map"):
|
||||
return None
|
||||
obj = callable.__self__
|
||||
if isinstance(obj, string_types):
|
||||
if isinstance(obj, str):
|
||||
return obj
|
||||
|
||||
|
||||
@ -161,7 +120,7 @@ def safe_range(*args):
|
||||
"""A range that can't generate ranges with a length of more than
|
||||
MAX_RANGE items.
|
||||
"""
|
||||
rng = range_type(*args)
|
||||
rng = range(*args)
|
||||
|
||||
if len(rng) > MAX_RANGE:
|
||||
raise OverflowError(
|
||||
@ -376,7 +335,7 @@ class SandboxedEnvironment(Environment):
|
||||
try:
|
||||
return obj[argument]
|
||||
except (TypeError, LookupError):
|
||||
if isinstance(argument, string_types):
|
||||
if isinstance(argument, str):
|
||||
try:
|
||||
attr = str(argument)
|
||||
except Exception:
|
||||
|
@ -1,13 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Built-in template tests used with the ``is`` operator."""
|
||||
import decimal
|
||||
import operator
|
||||
import re
|
||||
from collections import abc
|
||||
from numbers import Number
|
||||
|
||||
from ._compat import abc
|
||||
from ._compat import integer_types
|
||||
from ._compat import string_types
|
||||
from ._compat import text_type
|
||||
from .runtime import Undefined
|
||||
|
||||
number_re = re.compile(r"^-?\d+(\.\d+)?$")
|
||||
@ -87,7 +84,7 @@ def test_integer(value):
|
||||
|
||||
.. versionadded:: 2.11
|
||||
"""
|
||||
return isinstance(value, integer_types) and value is not True and value is not False
|
||||
return isinstance(value, int) and value is not True and value is not False
|
||||
|
||||
|
||||
# NOTE: The existing 'number' test matches booleans and integers
|
||||
@ -101,17 +98,17 @@ def test_float(value):
|
||||
|
||||
def test_lower(value):
|
||||
"""Return true if the variable is lowercased."""
|
||||
return text_type(value).islower()
|
||||
return str(value).islower()
|
||||
|
||||
|
||||
def test_upper(value):
|
||||
"""Return true if the variable is uppercased."""
|
||||
return text_type(value).isupper()
|
||||
return str(value).isupper()
|
||||
|
||||
|
||||
def test_string(value):
|
||||
"""Return true if the object is a string."""
|
||||
return isinstance(value, string_types)
|
||||
return isinstance(value, str)
|
||||
|
||||
|
||||
def test_mapping(value):
|
||||
@ -124,7 +121,7 @@ def test_mapping(value):
|
||||
|
||||
def test_number(value):
|
||||
"""Return true if the variable is a number."""
|
||||
return isinstance(value, integer_types + (float, complex, decimal.Decimal))
|
||||
return isinstance(value, Number)
|
||||
|
||||
|
||||
def test_sequence(value):
|
||||
|
@ -2,19 +2,16 @@
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
from collections import abc
|
||||
from collections import deque
|
||||
from random import choice
|
||||
from random import randrange
|
||||
from threading import Lock
|
||||
from urllib.parse import quote_from_bytes
|
||||
|
||||
from markupsafe import escape
|
||||
from markupsafe import Markup
|
||||
|
||||
from ._compat import abc
|
||||
from ._compat import string_types
|
||||
from ._compat import text_type
|
||||
from ._compat import url_quote
|
||||
|
||||
_word_split_re = re.compile(r"(\s+)")
|
||||
_punctuation_re = re.compile(
|
||||
"^(?P<lead>(?:%s)*)(?P<middle>.*?)(?P<trail>(?:%s)*)$"
|
||||
@ -205,8 +202,8 @@ def urlize(text, trim_url_limit=None, rel=None, target=None):
|
||||
and (x[:limit] + (len(x) >= limit and "..." or ""))
|
||||
or x
|
||||
)
|
||||
words = _word_split_re.split(text_type(escape(text)))
|
||||
rel_attr = rel and ' rel="%s"' % text_type(escape(rel)) or ""
|
||||
words = _word_split_re.split(str(escape(text)))
|
||||
rel_attr = rel and ' rel="%s"' % str(escape(rel)) or ""
|
||||
target_attr = target and ' target="%s"' % escape(target) or ""
|
||||
|
||||
for i, word in enumerate(words):
|
||||
@ -299,7 +296,7 @@ def generate_lorem_ipsum(n=5, html=True, min=20, max=100):
|
||||
return Markup(u"\n".join(u"<p>%s</p>" % escape(x) for x in result))
|
||||
|
||||
|
||||
def unicode_urlencode(obj, charset="utf-8", for_qs=False):
|
||||
def url_quote(obj, charset="utf-8", for_qs=False):
|
||||
"""Quote a string for use in a URL using the given charset.
|
||||
|
||||
This function is misnamed, it is a wrapper around
|
||||
@ -310,17 +307,14 @@ def unicode_urlencode(obj, charset="utf-8", for_qs=False):
|
||||
:param charset: Encode text to bytes using this charset.
|
||||
:param for_qs: Quote "/" and use "+" for spaces.
|
||||
"""
|
||||
if not isinstance(obj, string_types):
|
||||
obj = text_type(obj)
|
||||
if not isinstance(obj, bytes):
|
||||
if not isinstance(obj, str):
|
||||
obj = str(obj)
|
||||
|
||||
if isinstance(obj, text_type):
|
||||
obj = obj.encode(charset)
|
||||
|
||||
safe = b"" if for_qs else b"/"
|
||||
rv = url_quote(obj, safe)
|
||||
|
||||
if not isinstance(rv, text_type):
|
||||
rv = rv.decode("utf-8")
|
||||
rv = quote_from_bytes(obj, safe)
|
||||
|
||||
if for_qs:
|
||||
rv = rv.replace("%20", "+")
|
||||
@ -328,6 +322,19 @@ def unicode_urlencode(obj, charset="utf-8", for_qs=False):
|
||||
return rv
|
||||
|
||||
|
||||
def unicode_urlencode(obj, charset="utf-8", for_qs=False):
|
||||
import warnings
|
||||
|
||||
warnings.warn(
|
||||
"'unicode_urlencode' has been renamed to 'url_quote'. The old"
|
||||
" name will be removed in version 3.1.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
return url_quote(obj, charset=charset, for_qs=for_qs)
|
||||
|
||||
|
||||
@abc.MutableMapping.register
|
||||
class LRUCache(object):
|
||||
"""A simple LRU Cache implementation."""
|
||||
|
||||
@ -484,9 +491,6 @@ class LRUCache(object):
|
||||
__copy__ = copy
|
||||
|
||||
|
||||
abc.MutableMapping.register(LRUCache)
|
||||
|
||||
|
||||
def select_autoescape(
|
||||
enabled_extensions=("html", "htm", "xml"),
|
||||
disabled_extensions=(),
|
||||
|
@ -1,5 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
from io import BytesIO
|
||||
|
||||
import pytest
|
||||
|
||||
@ -7,9 +8,6 @@ from jinja2 import contextfunction
|
||||
from jinja2 import DictLoader
|
||||
from jinja2 import Environment
|
||||
from jinja2 import nodes
|
||||
from jinja2._compat import BytesIO
|
||||
from jinja2._compat import itervalues
|
||||
from jinja2._compat import text_type
|
||||
from jinja2.exceptions import TemplateAssertionError
|
||||
from jinja2.ext import Extension
|
||||
from jinja2.lexer import count_newlines
|
||||
@ -231,7 +229,7 @@ class TestExtensions(object):
|
||||
original = Environment(extensions=[ExampleExtension])
|
||||
overlay = original.overlay()
|
||||
for env in original, overlay:
|
||||
for ext in itervalues(env.extensions):
|
||||
for ext in env.extensions.values():
|
||||
assert ext.environment is env
|
||||
|
||||
def test_preprocessor_extension(self):
|
||||
@ -619,7 +617,7 @@ class TestAutoEscape(object):
|
||||
"""
|
||||
tmpl = env.from_string(tmplsource)
|
||||
assert tmpl.render(val=True).split()[0] == "Markup"
|
||||
assert tmpl.render(val=False).split()[0] == text_type.__name__
|
||||
assert tmpl.render(val=False).split()[0] == "str"
|
||||
|
||||
# looking at the source we should see <testing> there in raw
|
||||
# (and then escaped as well)
|
||||
|
@ -1,14 +1,8 @@
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
from jinja2 import contextfilter
|
||||
from jinja2 import Environment
|
||||
from jinja2 import Template
|
||||
from jinja2._compat import text_type
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.version_info < (3, 5), reason="Requires 3.5 or later")
|
||||
def test_generator_stop():
|
||||
class X(object):
|
||||
def __getattr__(self, name):
|
||||
@ -17,26 +11,3 @@ def test_generator_stop():
|
||||
t = Template("a{{ bad.bar() }}b")
|
||||
with pytest.raises(RuntimeError):
|
||||
t.render(bad=X())
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.version_info[0] > 2, reason="Feature only supported on 2.x")
|
||||
def test_ascii_str():
|
||||
@contextfilter
|
||||
def assert_func(context, value):
|
||||
assert type(value) is context["expected_type"]
|
||||
|
||||
env = Environment()
|
||||
env.filters["assert"] = assert_func
|
||||
|
||||
env.policies["compiler.ascii_str"] = False
|
||||
t = env.from_string('{{ "foo"|assert }}')
|
||||
t.render(expected_type=text_type)
|
||||
|
||||
env.policies["compiler.ascii_str"] = True
|
||||
t = env.from_string('{{ "foo"|assert }}')
|
||||
t.render(expected_type=str)
|
||||
|
||||
for val in True, False:
|
||||
env.policies["compiler.ascii_str"] = val
|
||||
t = env.from_string(u'{{ "\N{SNOWMAN}"|assert }}')
|
||||
t.render(expected_type=text_type)
|
||||
|
@ -6,27 +6,23 @@ import pytest
|
||||
|
||||
from jinja2 import Environment
|
||||
from jinja2 import Markup
|
||||
from jinja2._compat import implements_to_string
|
||||
from jinja2._compat import text_type
|
||||
|
||||
|
||||
@implements_to_string
|
||||
class Magic(object):
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
||||
def __str__(self):
|
||||
return text_type(self.value)
|
||||
return str(self.value)
|
||||
|
||||
|
||||
@implements_to_string
|
||||
class Magic2(object):
|
||||
def __init__(self, value1, value2):
|
||||
self.value1 = value1
|
||||
self.value2 = value2
|
||||
|
||||
def __str__(self):
|
||||
return u"(%s,%s)" % (text_type(self.value1), text_type(self.value2))
|
||||
return u"(%s,%s)" % (str(self.value1), str(self.value2))
|
||||
|
||||
|
||||
@pytest.mark.filter
|
||||
@ -281,7 +277,7 @@ class TestFilter(object):
|
||||
def test_string(self, env):
|
||||
x = [1, 2, 3, 4, 5]
|
||||
tmpl = env.from_string("""{{ obj|string }}""")
|
||||
assert tmpl.render(obj=x) == text_type(x)
|
||||
assert tmpl.render(obj=x) == str(x)
|
||||
|
||||
def test_title(self, env):
|
||||
tmpl = env.from_string("""{{ "foo bar"|title }}""")
|
||||
|
@ -6,9 +6,6 @@ from jinja2 import nodes
|
||||
from jinja2 import Template
|
||||
from jinja2 import TemplateSyntaxError
|
||||
from jinja2 import UndefinedError
|
||||
from jinja2._compat import iteritems
|
||||
from jinja2._compat import PY2
|
||||
from jinja2._compat import text_type
|
||||
from jinja2.lexer import Token
|
||||
from jinja2.lexer import TOKEN_BLOCK_BEGIN
|
||||
from jinja2.lexer import TOKEN_BLOCK_END
|
||||
@ -16,17 +13,6 @@ from jinja2.lexer import TOKEN_EOF
|
||||
from jinja2.lexer import TokenStream
|
||||
|
||||
|
||||
# how does a string look like in jinja syntax?
|
||||
if PY2:
|
||||
|
||||
def jinja_string_repr(string):
|
||||
return repr(string)[1:]
|
||||
|
||||
|
||||
else:
|
||||
jinja_string_repr = repr
|
||||
|
||||
|
||||
@pytest.mark.lexnparse
|
||||
@pytest.mark.tokenstream
|
||||
class TestTokenStream(object):
|
||||
@ -111,7 +97,7 @@ class TestLexer(object):
|
||||
|
||||
def test_string_escapes(self, env):
|
||||
for char in u"\0", u"\u2668", u"\xe4", u"\t", u"\r", u"\n":
|
||||
tmpl = env.from_string("{{ %s }}" % jinja_string_repr(char))
|
||||
tmpl = env.from_string("{{ %s }}" % repr(char))
|
||||
assert tmpl.render() == char
|
||||
assert env.from_string('{{ "\N{HOT SPRINGS}" }}').render() == u"\u2668"
|
||||
|
||||
@ -124,7 +110,7 @@ class TestLexer(object):
|
||||
def test_operators(self, env):
|
||||
from jinja2.lexer import operators
|
||||
|
||||
for test, expect in iteritems(operators):
|
||||
for test, expect in operators.items():
|
||||
if test in "([{}])":
|
||||
continue
|
||||
stream = env.lexer.tokenize("{{ %s }}" % test)
|
||||
@ -153,30 +139,30 @@ class TestLexer(object):
|
||||
assert result == expect, (keep, template, result, expect)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"name,valid2,valid3",
|
||||
(
|
||||
(u"foo", True, True),
|
||||
(u"föö", False, True),
|
||||
(u"き", False, True),
|
||||
(u"_", True, True),
|
||||
(u"1a", False, False), # invalid ascii start
|
||||
(u"a-", False, False), # invalid ascii continue
|
||||
(u"🐍", False, False), # invalid unicode start
|
||||
(u"a🐍", False, False), # invalid unicode continue
|
||||
("name", "valid"),
|
||||
[
|
||||
(u"foo", True),
|
||||
(u"föö", True),
|
||||
(u"き", True),
|
||||
(u"_", True),
|
||||
(u"1a", False), # invalid ascii start
|
||||
(u"a-", False), # invalid ascii continue
|
||||
(u"🐍", False), # invalid unicode start
|
||||
(u"a🐍", False), # invalid unicode continue
|
||||
# start characters not matched by \w
|
||||
(u"\u1885", False, True),
|
||||
(u"\u1886", False, True),
|
||||
(u"\u2118", False, True),
|
||||
(u"\u212e", False, True),
|
||||
(u"\u1885", True),
|
||||
(u"\u1886", True),
|
||||
(u"\u2118", True),
|
||||
(u"\u212e", True),
|
||||
# continue character not matched by \w
|
||||
(u"\xb7", False, False),
|
||||
(u"a\xb7", False, True),
|
||||
),
|
||||
(u"\xb7", False),
|
||||
(u"a\xb7", True),
|
||||
],
|
||||
)
|
||||
def test_name(self, env, name, valid2, valid3):
|
||||
def test_name(self, env, name, valid):
|
||||
t = u"{{ " + name + u" }}"
|
||||
|
||||
if (valid2 and PY2) or (valid3 and not PY2):
|
||||
if valid:
|
||||
# valid for version being tested, shouldn't raise
|
||||
env.from_string(t)
|
||||
else:
|
||||
@ -528,7 +514,7 @@ class TestSyntax(object):
|
||||
|
||||
def test_operator_precedence(self, env):
|
||||
tmpl = env.from_string("""{{ 2 * 3 + 4 % 2 + 1 - 2 }}""")
|
||||
assert tmpl.render() == text_type(2 * 3 + 4 % 2 + 1 - 2)
|
||||
assert tmpl.render() == "5"
|
||||
|
||||
def test_implicit_subscribed_tuple(self, env):
|
||||
class Foo(object):
|
||||
|
@ -1,5 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
import platform
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
@ -11,8 +12,6 @@ import pytest
|
||||
from jinja2 import Environment
|
||||
from jinja2 import loaders
|
||||
from jinja2 import PackageLoader
|
||||
from jinja2._compat import PY2
|
||||
from jinja2._compat import PYPY
|
||||
from jinja2.exceptions import TemplateNotFound
|
||||
from jinja2.loaders import split_template_path
|
||||
|
||||
@ -138,25 +137,19 @@ class TestFileSystemLoader(object):
|
||||
env = Environment(loader=filesystem_loader)
|
||||
self._test_common(env)
|
||||
|
||||
@pytest.mark.skipif(PY2, reason="pathlib is not available in Python 2")
|
||||
def test_searchpath_as_pathlib(self):
|
||||
import pathlib
|
||||
|
||||
searchpath = pathlib.Path(self.searchpath)
|
||||
|
||||
filesystem_loader = loaders.FileSystemLoader(searchpath)
|
||||
|
||||
env = Environment(loader=filesystem_loader)
|
||||
self._test_common(env)
|
||||
|
||||
@pytest.mark.skipif(PY2, reason="pathlib is not available in Python 2")
|
||||
def test_searchpath_as_list_including_pathlib(self):
|
||||
import pathlib
|
||||
|
||||
searchpath = pathlib.Path(self.searchpath)
|
||||
|
||||
filesystem_loader = loaders.FileSystemLoader(["/tmp/templates", searchpath])
|
||||
|
||||
env = Environment(loader=filesystem_loader)
|
||||
self._test_common(env)
|
||||
|
||||
@ -265,17 +258,6 @@ class TestModuleLoader(object):
|
||||
|
||||
assert name not in sys.modules
|
||||
|
||||
# This test only makes sense on non-pypy python 2
|
||||
@pytest.mark.skipif(
|
||||
not (PY2 and not PYPY), reason="This test only makes sense on non-pypy python 2"
|
||||
)
|
||||
def test_byte_compilation(self, prefix_loader):
|
||||
log = self.compile_down(prefix_loader, py_compile=True)
|
||||
assert 'Byte-compiled "a/test.html"' in log
|
||||
self.mod_env.get_template("a/test.html")
|
||||
mod = self.mod_env.loader.module.tmpl_3c4ddf650c1a73df961a6d3d2ce2752f1b8fd490
|
||||
assert mod.__file__.endswith(".pyc")
|
||||
|
||||
def test_choice_loader(self, prefix_loader):
|
||||
self.compile_down(prefix_loader)
|
||||
self.mod_env.loader = loaders.ChoiceLoader(
|
||||
@ -299,7 +281,6 @@ class TestModuleLoader(object):
|
||||
tmpl2 = self.mod_env.get_template("DICT/test.html")
|
||||
assert tmpl2.render() == "DICT_TEMPLATE"
|
||||
|
||||
@pytest.mark.skipif(PY2, reason="pathlib is not available in Python 2")
|
||||
def test_path_as_pathlib(self, prefix_loader):
|
||||
self.compile_down(prefix_loader)
|
||||
|
||||
@ -312,7 +293,6 @@ class TestModuleLoader(object):
|
||||
|
||||
self._test_common()
|
||||
|
||||
@pytest.mark.skipif(PY2, reason="pathlib is not available in Python 2")
|
||||
def test_supports_pathlib_in_list_of_paths(self, prefix_loader):
|
||||
self.compile_down(prefix_loader)
|
||||
|
||||
@ -367,7 +347,7 @@ def test_package_zip_source(package_zip_loader, template, expect):
|
||||
|
||||
|
||||
@pytest.mark.xfail(
|
||||
PYPY,
|
||||
platform.python_implementation() == "PyPy",
|
||||
reason="PyPy's zipimporter doesn't have a _files attribute.",
|
||||
raises=TypeError,
|
||||
)
|
||||
|
@ -1,6 +1,5 @@
|
||||
import pytest
|
||||
|
||||
from jinja2._compat import text_type
|
||||
from jinja2.exceptions import UndefinedError
|
||||
from jinja2.nativetypes import NativeEnvironment
|
||||
from jinja2.nativetypes import NativeTemplate
|
||||
@ -53,7 +52,7 @@ def test_multi_expression_add(env):
|
||||
def test_loops(env):
|
||||
t = env.from_string("{% for x in value %}{{ x }}{% endfor %}")
|
||||
result = t.render(value=["a", "b", "c", "d"])
|
||||
assert isinstance(result, text_type)
|
||||
assert isinstance(result, str)
|
||||
assert result == "abcd"
|
||||
|
||||
|
||||
@ -111,7 +110,7 @@ def test_constant_dunder_to_string(env):
|
||||
def test_string_literal_var(env):
|
||||
t = env.from_string("[{{ 'all' }}]")
|
||||
result = t.render()
|
||||
assert isinstance(result, text_type)
|
||||
assert isinstance(result, str)
|
||||
assert result == "[all]"
|
||||
|
||||
|
||||
|
@ -1,6 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
from jinja2 import DictLoader
|
||||
@ -10,7 +8,6 @@ from jinja2 import Template
|
||||
from jinja2 import TemplateAssertionError
|
||||
from jinja2 import TemplateNotFound
|
||||
from jinja2 import TemplateSyntaxError
|
||||
from jinja2._compat import text_type
|
||||
|
||||
|
||||
@pytest.mark.regression
|
||||
@ -134,7 +131,7 @@ class TestBug(object):
|
||||
"""
|
||||
)
|
||||
|
||||
assert tmpl.render().split() == [text_type(x) for x in range(1, 11)] * 5
|
||||
assert tmpl.render().split() == [str(x) for x in range(1, 11)] * 5
|
||||
|
||||
def test_weird_inline_comment(self, env):
|
||||
env = Environment(line_statement_prefix="%")
|
||||
@ -308,13 +305,6 @@ class TestBug(object):
|
||||
|
||||
assert output == expected
|
||||
|
||||
@pytest.mark.skipif(sys.version_info[0] > 2, reason="This only works on 2.x")
|
||||
def test_old_style_attribute(self, env):
|
||||
class Foo:
|
||||
x = 42
|
||||
|
||||
assert env.getitem(Foo(), "x") == 42
|
||||
|
||||
def test_block_set_with_extends(self):
|
||||
env = Environment(
|
||||
loader=DictLoader({"main": "{% block body %}[{{ x }}]{% endblock %}"})
|
||||
|
@ -3,8 +3,6 @@ import pytest
|
||||
|
||||
from jinja2 import Environment
|
||||
from jinja2 import escape
|
||||
from jinja2 import Markup
|
||||
from jinja2._compat import text_type
|
||||
from jinja2.exceptions import SecurityError
|
||||
from jinja2.exceptions import TemplateRuntimeError
|
||||
from jinja2.exceptions import TemplateSyntaxError
|
||||
@ -77,44 +75,6 @@ class TestSandbox(object):
|
||||
"{% for foo, bar.baz in seq %}...{% endfor %}",
|
||||
)
|
||||
|
||||
def test_markup_operations(self, env):
|
||||
# adding two strings should escape the unsafe one
|
||||
unsafe = '<script type="application/x-some-script">alert("foo");</script>'
|
||||
safe = Markup("<em>username</em>")
|
||||
assert unsafe + safe == text_type(escape(unsafe)) + text_type(safe)
|
||||
|
||||
# string interpolations are safe to use too
|
||||
assert Markup("<em>%s</em>") % "<bad user>" == "<em><bad user></em>"
|
||||
assert (
|
||||
Markup("<em>%(username)s</em>") % {"username": "<bad user>"}
|
||||
== "<em><bad user></em>"
|
||||
)
|
||||
|
||||
# an escaped object is markup too
|
||||
assert type(Markup("foo") + "bar") is Markup
|
||||
|
||||
# and it implements __html__ by returning itself
|
||||
x = Markup("foo")
|
||||
assert x.__html__() is x
|
||||
|
||||
# it also knows how to treat __html__ objects
|
||||
class Foo(object):
|
||||
def __html__(self):
|
||||
return "<em>awesome</em>"
|
||||
|
||||
def __unicode__(self):
|
||||
return "awesome"
|
||||
|
||||
assert Markup(Foo()) == "<em>awesome</em>"
|
||||
assert (
|
||||
Markup("<strong>%s</strong>") % Foo() == "<strong><em>awesome</em></strong>"
|
||||
)
|
||||
|
||||
# escaping and unescaping
|
||||
assert escape("\"<>&'") == ""<>&'"
|
||||
assert Markup("<em>Foo & Bar</em>").striptags() == "Foo & Bar"
|
||||
assert Markup("<test>").unescape() == "<test>"
|
||||
|
||||
def test_template_data(self, env):
|
||||
env = Environment(autoescape=True)
|
||||
t = env.from_string(
|
||||
@ -124,7 +84,7 @@ class TestSandbox(object):
|
||||
)
|
||||
escaped_out = "<p>Hello <blink>foo</blink>!</p>"
|
||||
assert t.render() == escaped_out
|
||||
assert text_type(t.module) == escaped_out
|
||||
assert str(t.module) == escaped_out
|
||||
assert escape(t.module) == escaped_out
|
||||
assert t.module.say_hello("<blink>foo</blink>") == escaped_out
|
||||
assert (
|
||||
|
@ -7,8 +7,6 @@ from copy import copy as shallow_copy
|
||||
import pytest
|
||||
from markupsafe import Markup
|
||||
|
||||
from jinja2._compat import range_type
|
||||
from jinja2._compat import string_types
|
||||
from jinja2.utils import consume
|
||||
from jinja2.utils import generate_lorem_ipsum
|
||||
from jinja2.utils import LRUCache
|
||||
@ -165,26 +163,26 @@ class TestLoremIpsum(object):
|
||||
|
||||
def test_lorem_ipsum_html(self):
|
||||
"""Test that output of lorem_ipsum is a string_type when not html."""
|
||||
assert isinstance(generate_lorem_ipsum(html=False), string_types)
|
||||
assert isinstance(generate_lorem_ipsum(html=False), str)
|
||||
|
||||
def test_lorem_ipsum_n(self):
|
||||
"""Test that the n (number of lines) works as expected."""
|
||||
assert generate_lorem_ipsum(n=0, html=False) == u""
|
||||
for n in range_type(1, 50):
|
||||
for n in range(1, 50):
|
||||
assert generate_lorem_ipsum(n=n, html=False).count("\n") == (n - 1) * 2
|
||||
|
||||
def test_lorem_ipsum_min(self):
|
||||
"""Test that at least min words are in the output of each line"""
|
||||
for _ in range_type(5):
|
||||
for _ in range(5):
|
||||
m = random.randrange(20, 99)
|
||||
for _ in range_type(10):
|
||||
for _ in range(10):
|
||||
assert generate_lorem_ipsum(n=1, min=m, html=False).count(" ") >= m - 1
|
||||
|
||||
def test_lorem_ipsum_max(self):
|
||||
"""Test that at least max words are in the output of each line"""
|
||||
for _ in range_type(5):
|
||||
for _ in range(5):
|
||||
m = random.randrange(21, 100)
|
||||
for _ in range_type(10):
|
||||
for _ in range(10):
|
||||
assert generate_lorem_ipsum(n=1, max=m, html=False).count(" ") < m - 1
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user