+++ /dev/null
-# -*- 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
"""
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 ._compat import BytesIO
-from ._compat import marshal_dump
-from ._compat import marshal_load
-from ._compat import pickle
-from ._compat import text_type
+from io import BytesIO
+
from .utils import open_if_exists
bc_version = 4
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
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."""
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()
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")
# 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
"""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
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
"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):
"""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
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
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(", *")
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("}, **")
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:
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))
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):
else:
self.writeline(
"context.exported_vars.update((%s))"
- % ", ".join(imap(repr, public_names))
+ % ", ".join(map(repr, public_names))
)
# -- Statement Visitors
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:
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(
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),
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
)
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()
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"
"._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()
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):
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(" = ")
#: 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):
# Template data doesn't go through finalize.
if isinstance(node, nodes.TemplateData):
- return text_type(const)
+ return str(const)
return finalize.const(const)
``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)
@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)
+import platform
import sys
from types import CodeType
from . import TemplateSyntaxError
-from ._compat import PYPY
from .utils import internal_code
from .utils import missing
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
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):
return tb
-elif PYPY:
+elif platform.python_implementation() == "PyPy":
# PyPy might have special support, and won't work with ctypes.
try:
import tputil
# -*- 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
# default filters, tests and namespace
DEFAULT_NAMESPACE = {
- "range": range_type,
+ "range": range,
"dict": dict,
"lipsum": generate_lorem_ipsum,
"cycler": Cycler,
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
"""
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
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)
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)
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))
try:
return obj[argument]
except (AttributeError, TypeError, LookupError):
- if isinstance(argument, string_types):
+ if isinstance(argument, str):
try:
attr = str(argument)
except Exception:
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
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:
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):
"""
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)
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)
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:
"""
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
.. 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
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
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.
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")
-# -*- 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):
- 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)
+ def __init__(self, message=None):
+ super().__init__(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.
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."""
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
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
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)
# 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":
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)
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):
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):
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
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]
"""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):
.. 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
)
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__")
):
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
"""
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:
"""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):
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
]
)
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
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
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):
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):
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)
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)
"selectattr": do_selectattr,
"slice": do_slice,
"sort": do_sort,
- "string": soft_unicode,
+ "string": soft_str,
"striptags": do_striptags,
"sum": do_sum,
"title": do_title,
-from ._compat import iteritems
from .visitor import NodeVisitor
VAR_LOAD_PARAMETER = "param"
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)
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
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
_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
";": 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)))
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.
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
"""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"):
# 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")
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
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
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 != ".":
"""
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
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
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
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
# 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)
interesting for introspection.
"""
from . import nodes
-from ._compat import iteritems
-from ._compat import string_types
from .compiler import 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)
>>> 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
# 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:
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
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:
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
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)
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.
"""
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,
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)
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:
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))
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):
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):
# -*- 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
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]
# -*- 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 ._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 markupsafe import soft_str
+
from .exceptions import TemplateNotFound # noqa: F401
from .exceptions import TemplateRuntimeError # noqa: F401
from .exceptions import UndefinedError
"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
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__"):
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(
# 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)
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
# 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.
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
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):
)
-abc.Mapping.register(Context)
-
-
class BlockReference(object):
"""One block on a template reference."""
return rv
-@implements_iterator
class LoopContext:
"""A wrapper iterable for dynamic ``for`` loops, with information
about the loop and iteration.
)
-@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`:
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,
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)
if 0:
yield None
- def __nonzero__(self):
+ def __bool__(self):
return False
- __bool__ = __nonzero__
-
def __repr__(self):
return "Undefined"
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,
_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
__getitem__ = __getattr__
-@implements_to_string
class DebugUndefined(Undefined):
"""An undefined that returns the debug info when printed.
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
"""
__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__,
"""
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"}
#: 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",
),
),
(
- _mutable_mapping_types,
+ abc.MutableMapping,
frozenset(["clear", "pop", "popitem", "setdefault", "update"]),
),
(
- _mutable_sequence_types,
+ abc.MutableSequence,
frozenset(["append", "reverse", "insert", "sort", "extend", "remove"]),
),
(
) or callable.__name__ not in ("format", "format_map"):
return None
obj = callable.__self__
- if isinstance(obj, string_types):
+ if isinstance(obj, str):
return obj
"""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(
try:
return obj[argument]
except (TypeError, LookupError):
- if isinstance(argument, string_types):
+ if isinstance(argument, str):
try:
attr = str(argument)
except Exception:
# -*- 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+)?$")
.. 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
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):
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):
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)*)$"
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):
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
: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", "+")
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."""
__copy__ = copy
-abc.MutableMapping.register(LRUCache)
-
-
def select_autoescape(
enabled_extensions=("html", "htm", "xml"),
disabled_extensions=(),
# -*- coding: utf-8 -*-
import re
+from io import BytesIO
import pytest
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
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):
"""
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)
-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):
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)
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
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 }}""")
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
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):
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"
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)
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:
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):
# -*- coding: utf-8 -*-
import os
+import platform
import shutil
import sys
import tempfile
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
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)
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(
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)
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)
@pytest.mark.xfail(
- PYPY,
+ platform.python_implementation() == "PyPy",
reason="PyPy's zipimporter doesn't have a _files attribute.",
raises=TypeError,
)
import pytest
-from jinja2._compat import text_type
from jinja2.exceptions import UndefinedError
from jinja2.nativetypes import NativeEnvironment
from jinja2.nativetypes import NativeTemplate
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"
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]"
# -*- coding: utf-8 -*-
-import sys
-
import pytest
from jinja2 import DictLoader
from jinja2 import TemplateAssertionError
from jinja2 import TemplateNotFound
from jinja2 import TemplateSyntaxError
-from jinja2._compat import text_type
@pytest.mark.regression
"""
)
- 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="%")
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 %}"})
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
"{% 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(
)
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 (
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
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