Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

More Python 3 support.

--HG--
branch : trunk
  • Loading branch information...
commit bd3577237d2502158cc5725d3aa79d7643f033df 1 parent 42a1988
@mitsuhiko authored
View
130 jinja2/_stringdefs.py
130 additions, 0 deletions not shown
View
6 jinja2/compiler.py
@@ -13,7 +13,7 @@
from jinja2 import nodes
from jinja2.visitor import NodeVisitor, NodeTransformer
from jinja2.exceptions import TemplateAssertionError
-from jinja2.utils import Markup, concat, escape, is_python_keyword
+from jinja2.utils import Markup, concat, escape, is_python_keyword, next
operators = {
@@ -1191,7 +1191,7 @@ def visit_Output(self, node, frame):
if self.environment.autoescape:
self.write('escape(')
else:
- self.write('unicode(')
+ self.write('to_string(')
if self.environment.finalize is not None:
self.write('environment.finalize(')
close += 1
@@ -1255,7 +1255,7 @@ def visit_Assign(self, node, frame):
public_names = [x for x in assignment_frame.toplevel_assignments
if not x.startswith('_')]
if len(assignment_frame.toplevel_assignments) == 1:
- name = iter(assignment_frame.toplevel_assignments).next()
+ name = next(iter(assignment_frame.toplevel_assignments))
self.writeline('context.vars[%r] = l_%s' % (name, name))
else:
self.writeline('context.vars.update({')
View
12 jinja2/debug.py
@@ -16,6 +16,15 @@
from jinja2.exceptions import TemplateSyntaxError
+# how does the raise helper look like?
+try:
+ exec "raise TypeError, 'foo'"
+except SyntaxError:
+ raise_helper = 'raise __jinja_exception__[1]'
+except TypeError:
+ raise_helper = 'raise __jinja_exception__[0], __jinja_exception__[1]'
+
+
class TracebackFrameProxy(object):
"""Proxies a traceback frame."""
@@ -193,8 +202,7 @@ def fake_exc_info(exc_info, filename, lineno):
}
# and fake the exception
- code = compile('\n' * (lineno - 1) + 'raise __jinja_exception__[0], ' +
- '__jinja_exception__[1]', filename, 'exec')
+ code = compile('\n' * (lineno - 1) + raise_helper, filename, 'exec')
# if it's possible, change the name of the code. This won't work
# on some python environments such as google appengine
View
19 jinja2/environment.py
@@ -370,7 +370,8 @@ def parse(self, source, name=None, filename=None):
try:
return Parser(self, source, name, filename).parse()
except TemplateSyntaxError:
- self.handle_exception(sys.exc_info(), source_hint=source)
+ exc_info = sys.exc_info()
+ self.handle_exception(exc_info, source_hint=source)
def lex(self, source, name=None, filename=None):
"""Lex the given sourcecode and return a generator that yields
@@ -386,7 +387,8 @@ def lex(self, source, name=None, filename=None):
try:
return self.lexer.tokeniter(source, name, filename)
except TemplateSyntaxError:
- self.handle_exception(sys.exc_info(), source_hint=source)
+ exc_info = sys.exc_info()
+ self.handle_exception(exc_info, source_hint=source)
def preprocess(self, source, name=None, filename=None):
"""Preprocesses the source with all extensions. This is automatically
@@ -464,6 +466,7 @@ def compile_expression(self, source, undefined_to_none=True):
**new in Jinja 2.1**
"""
parser = Parser(self, source, state='variable')
+ exc_info = None
try:
expr = parser.parse_expression()
if not parser.stream.eos:
@@ -471,7 +474,9 @@ def compile_expression(self, source, undefined_to_none=True):
parser.stream.current.lineno,
None, None)
except TemplateSyntaxError:
- self.handle_exception(sys.exc_info(), source_hint=source)
+ exc_info = sys.exc_info()
+ if exc_info is not None:
+ self.handle_exception(exc_info, source_hint=source)
body = [nodes.Assign(nodes.Name('result', 'store'), expr, lineno=1)]
template = self.from_string(nodes.Template(body, lineno=1))
return TemplateExpression(template, undefined_to_none)
@@ -650,7 +655,8 @@ def render(self, *args, **kwargs):
try:
return concat(self.root_render_func(self.new_context(vars)))
except:
- return self.environment.handle_exception(sys.exc_info(), True)
+ exc_info = sys.exc_info()
+ return self.environment.handle_exception(exc_info, True)
def stream(self, *args, **kwargs):
"""Works exactly like :meth:`generate` but returns a
@@ -671,7 +677,10 @@ def generate(self, *args, **kwargs):
for event in self.root_render_func(self.new_context(vars)):
yield event
except:
- yield self.environment.handle_exception(sys.exc_info(), True)
+ exc_info = sys.exc_info()
+ else:
+ return
+ yield self.environment.handle_exception(exc_info, True)
def new_context(self, vars=None, shared=False, locals=None):
"""Create a new :class:`Context` for this template. The vars
View
22 jinja2/ext.py
@@ -16,7 +16,7 @@
from jinja2.environment import get_spontaneous_environment
from jinja2.runtime import Undefined, concat
from jinja2.exceptions import TemplateAssertionError, TemplateSyntaxError
-from jinja2.utils import contextfunction, import_string, Markup
+from jinja2.utils import contextfunction, import_string, Markup, next
# the only real useful gettext functions for a Jinja template. Note
@@ -167,7 +167,7 @@ def _extract(self, source, gettext_functions=GETTEXT_FUNCTIONS):
def parse(self, parser):
"""Parse a translatable tag."""
- lineno = parser.stream.next().lineno
+ lineno = next(parser.stream).lineno
# find all the variables referenced. Additionally a variable can be
# defined in the body of the trans block too, but this is checked at
@@ -190,7 +190,7 @@ def parse(self, parser):
# expressions
if parser.stream.current.type == 'assign':
- parser.stream.next()
+ next(parser.stream)
variables[name.value] = var = parser.parse_expression()
else:
variables[name.value] = var = nodes.Name(name.value, 'load')
@@ -213,7 +213,7 @@ def parse(self, parser):
# if we have a pluralize block, we parse that too
if parser.stream.current.test('name:pluralize'):
have_plural = True
- parser.stream.next()
+ next(parser.stream)
if parser.stream.current.type != 'block_end':
name = parser.stream.expect('name')
if name.value not in variables:
@@ -223,10 +223,10 @@ def parse(self, parser):
plural_expr = variables[name.value]
parser.stream.expect('block_end')
plural_names, plural = self._parse_block(parser, False)
- parser.stream.next()
+ next(parser.stream)
referenced.update(plural_names)
else:
- parser.stream.next()
+ next(parser.stream)
# register free names as simple name expressions
for var in referenced:
@@ -261,15 +261,15 @@ def _parse_block(self, parser, allow_pluralize):
while 1:
if parser.stream.current.type == 'data':
buf.append(parser.stream.current.value.replace('%', '%%'))
- parser.stream.next()
+ next(parser.stream)
elif parser.stream.current.type == 'variable_begin':
- parser.stream.next()
+ next(parser.stream)
name = parser.stream.expect('name').value
referenced.append(name)
buf.append('%%(%s)s' % name)
parser.stream.expect('variable_end')
elif parser.stream.current.type == 'block_begin':
- parser.stream.next()
+ next(parser.stream)
if parser.stream.current.test('name:endtrans'):
break
elif parser.stream.current.test('name:pluralize'):
@@ -320,7 +320,7 @@ class ExprStmtExtension(Extension):
tags = set(['do'])
def parse(self, parser):
- node = nodes.ExprStmt(lineno=parser.stream.next().lineno)
+ node = nodes.ExprStmt(lineno=next(parser.stream).lineno)
node.node = parser.parse_tuple()
return node
@@ -330,7 +330,7 @@ class LoopControlExtension(Extension):
tags = set(['break', 'continue'])
def parse(self, parser):
- token = parser.stream.next()
+ token = next(parser.stream)
if token.value == 'break':
return nodes.Break(lineno=token.lineno)
return nodes.Continue(lineno=token.lineno)
View
27 jinja2/lexer.py
@@ -18,7 +18,7 @@
from operator import itemgetter
from collections import deque
from jinja2.exceptions import TemplateSyntaxError
-from jinja2.utils import LRUCache
+from jinja2.utils import LRUCache, next
# cache for the lexers. Exists in order to be able to have multiple
@@ -30,7 +30,18 @@
string_re = re.compile(r"('([^'\\]*(?:\\.[^'\\]*)*)'"
r'|"([^"\\]*(?:\\.[^"\\]*)*)")', re.S)
integer_re = re.compile(r'\d+')
-name_re = re.compile(r'\b[a-zA-Z_][a-zA-Z0-9_]*\b')
+
+# we use the unicode identifier rule if this python version is able
+# to handle unicode identifiers, otherwise the standard ASCII one.
+try:
+ compile('föö', '<unknown>', 'eval')
+except SyntaxError:
+ name_re = re.compile(r'\b[a-zA-Z_][a-zA-Z0-9_]*\b')
+else:
+ from jinja2 import _stringdefs
+ name_re = re.compile(r'[%s][%s]*' % (_stringdefs.xid_start,
+ _stringdefs.xid_continue))
+
float_re = re.compile(r'(?<!\.)\d+\.\d+')
newline_re = re.compile(r'(\r\n|\r|\n)')
@@ -230,7 +241,7 @@ def next(self):
if token.type is TOKEN_EOF:
self.stream.close()
raise StopIteration()
- self.stream.next()
+ next(self.stream)
return token
@@ -247,7 +258,7 @@ def __init__(self, generator, name, filename):
self.filename = filename
self.closed = False
self.current = Token(1, TOKEN_INITIAL, '')
- self.next()
+ next(self)
def __iter__(self):
return TokenStreamIterator(self)
@@ -263,7 +274,7 @@ def push(self, token):
def look(self):
"""Look at the next token."""
- old_token = self.next()
+ old_token = next(self)
result = self.current
self.push(result)
self.current = old_token
@@ -272,14 +283,14 @@ def look(self):
def skip(self, n=1):
"""Got n tokens ahead."""
for x in xrange(n):
- self.next()
+ next(self)
def next_if(self, expr):
"""Perform the token test and return the token if it matched.
Otherwise the return value is `None`.
"""
if self.current.test(expr):
- return self.next()
+ return next(self)
def skip_if(self, expr):
"""Like :meth:`next_if` but only returns `True` or `False`."""
@@ -322,7 +333,7 @@ def expect(self, expr):
try:
return self.current
finally:
- self.next()
+ next(self)
def get_lexer(environment):
View
95 jinja2/parser.py
@@ -10,6 +10,7 @@
"""
from jinja2 import nodes
from jinja2.exceptions import TemplateSyntaxError, TemplateAssertionError
+from jinja2.utils import next
_statement_keywords = frozenset(['for', 'if', 'block', 'extends', 'print',
@@ -95,12 +96,12 @@ def parse_statements(self, end_tokens, drop_needle=False):
result = self.subparse(end_tokens)
if drop_needle:
- self.stream.next()
+ next(self.stream)
return result
def parse_set(self):
"""Parse an assign statement."""
- lineno = self.stream.next().lineno
+ lineno = next(self.stream).lineno
target = self.parse_assign_target()
self.stream.expect('assign')
expr = self.parse_tuple()
@@ -118,7 +119,7 @@ def parse_for(self):
test = self.parse_expression()
recursive = self.stream.skip_if('name:recursive')
body = self.parse_statements(('name:endfor', 'name:else'))
- if self.stream.next().value == 'endfor':
+ if next(self.stream).value == 'endfor':
else_ = []
else:
else_ = self.parse_statements(('name:endfor',), drop_needle=True)
@@ -132,7 +133,7 @@ def parse_if(self):
node.test = self.parse_tuple(with_condexpr=False)
node.body = self.parse_statements(('name:elif', 'name:else',
'name:endif'))
- token = self.stream.next()
+ token = next(self.stream)
if token.test('name:elif'):
new_node = nodes.If(lineno=self.stream.current.lineno)
node.else_ = [new_node]
@@ -147,7 +148,7 @@ def parse_if(self):
return result
def parse_block(self):
- node = nodes.Block(lineno=self.stream.next().lineno)
+ node = nodes.Block(lineno=next(self.stream).lineno)
node.name = self.stream.expect('name').value
node.scoped = self.stream.skip_if('name:scoped')
node.body = self.parse_statements(('name:endblock',), drop_needle=True)
@@ -155,21 +156,21 @@ def parse_block(self):
return node
def parse_extends(self):
- node = nodes.Extends(lineno=self.stream.next().lineno)
+ node = nodes.Extends(lineno=next(self.stream).lineno)
node.template = self.parse_expression()
return node
def parse_import_context(self, node, default):
if self.stream.current.test_any('name:with', 'name:without') and \
self.stream.look().test('name:context'):
- node.with_context = self.stream.next().value == 'with'
+ node.with_context = next(self.stream).value == 'with'
self.stream.skip()
else:
node.with_context = default
return node
def parse_include(self):
- node = nodes.Include(lineno=self.stream.next().lineno)
+ node = nodes.Include(lineno=next(self.stream).lineno)
node.template = self.parse_expression()
if self.stream.current.test('name:ignore') and \
self.stream.look().test('name:missing'):
@@ -180,14 +181,14 @@ def parse_include(self):
return self.parse_import_context(node, True)
def parse_import(self):
- node = nodes.Import(lineno=self.stream.next().lineno)
+ node = nodes.Import(lineno=next(self.stream).lineno)
node.template = self.parse_expression()
self.stream.expect('name:as')
node.target = self.parse_assign_target(name_only=True).name
return self.parse_import_context(node, False)
def parse_from(self):
- node = nodes.FromImport(lineno=self.stream.next().lineno)
+ node = nodes.FromImport(lineno=next(self.stream).lineno)
node.template = self.parse_expression()
self.stream.expect('name:import')
node.names = []
@@ -195,7 +196,7 @@ def parse_from(self):
def parse_context():
if self.stream.current.value in ('with', 'without') and \
self.stream.look().test('name:context'):
- node.with_context = self.stream.next().value == 'with'
+ node.with_context = next(self.stream).value == 'with'
self.stream.skip()
return True
return False
@@ -240,7 +241,7 @@ def parse_signature(self, node):
self.stream.expect('rparen')
def parse_call_block(self):
- node = nodes.CallBlock(lineno=self.stream.next().lineno)
+ node = nodes.CallBlock(lineno=next(self.stream).lineno)
if self.stream.current.type == 'lparen':
self.parse_signature(node)
else:
@@ -254,14 +255,14 @@ def parse_call_block(self):
return node
def parse_filter_block(self):
- node = nodes.FilterBlock(lineno=self.stream.next().lineno)
+ node = nodes.FilterBlock(lineno=next(self.stream).lineno)
node.filter = self.parse_filter(None, start_inline=True)
node.body = self.parse_statements(('name:endfilter',),
drop_needle=True)
return node
def parse_macro(self):
- node = nodes.Macro(lineno=self.stream.next().lineno)
+ node = nodes.Macro(lineno=next(self.stream).lineno)
node.name = self.parse_assign_target(name_only=True).name
self.parse_signature(node)
node.body = self.parse_statements(('name:endmacro',),
@@ -269,7 +270,7 @@ def parse_macro(self):
return node
def parse_print(self):
- node = nodes.Output(lineno=self.stream.next().lineno)
+ node = nodes.Output(lineno=next(self.stream).lineno)
node.nodes = []
while self.stream.current.type != 'block_end':
if node.nodes:
@@ -343,7 +344,7 @@ def parse_and(self):
def parse_not(self):
if self.stream.current.test('name:not'):
- lineno = self.stream.next().lineno
+ lineno = next(self.stream).lineno
return nodes.Not(self.parse_not(), lineno=lineno)
return self.parse_compare()
@@ -354,7 +355,7 @@ def parse_compare(self):
while 1:
token_type = self.stream.current.type
if token_type in _compare_operators:
- self.stream.next()
+ next(self.stream)
ops.append(nodes.Operand(token_type, self.parse_add()))
elif self.stream.skip_if('name:in'):
ops.append(nodes.Operand('in', self.parse_add()))
@@ -373,7 +374,7 @@ def parse_add(self):
lineno = self.stream.current.lineno
left = self.parse_sub()
while self.stream.current.type == 'add':
- self.stream.next()
+ next(self.stream)
right = self.parse_sub()
left = nodes.Add(left, right, lineno=lineno)
lineno = self.stream.current.lineno
@@ -383,7 +384,7 @@ def parse_sub(self):
lineno = self.stream.current.lineno
left = self.parse_concat()
while self.stream.current.type == 'sub':
- self.stream.next()
+ next(self.stream)
right = self.parse_concat()
left = nodes.Sub(left, right, lineno=lineno)
lineno = self.stream.current.lineno
@@ -393,7 +394,7 @@ def parse_concat(self):
lineno = self.stream.current.lineno
args = [self.parse_mul()]
while self.stream.current.type == 'tilde':
- self.stream.next()
+ next(self.stream)
args.append(self.parse_mul())
if len(args) == 1:
return args[0]
@@ -403,7 +404,7 @@ def parse_mul(self):
lineno = self.stream.current.lineno
left = self.parse_div()
while self.stream.current.type == 'mul':
- self.stream.next()
+ next(self.stream)
right = self.parse_div()
left = nodes.Mul(left, right, lineno=lineno)
lineno = self.stream.current.lineno
@@ -413,7 +414,7 @@ def parse_div(self):
lineno = self.stream.current.lineno
left = self.parse_floordiv()
while self.stream.current.type == 'div':
- self.stream.next()
+ next(self.stream)
right = self.parse_floordiv()
left = nodes.Div(left, right, lineno=lineno)
lineno = self.stream.current.lineno
@@ -423,7 +424,7 @@ def parse_floordiv(self):
lineno = self.stream.current.lineno
left = self.parse_mod()
while self.stream.current.type == 'floordiv':
- self.stream.next()
+ next(self.stream)
right = self.parse_mod()
left = nodes.FloorDiv(left, right, lineno=lineno)
lineno = self.stream.current.lineno
@@ -433,7 +434,7 @@ def parse_mod(self):
lineno = self.stream.current.lineno
left = self.parse_pow()
while self.stream.current.type == 'mod':
- self.stream.next()
+ next(self.stream)
right = self.parse_pow()
left = nodes.Mod(left, right, lineno=lineno)
lineno = self.stream.current.lineno
@@ -443,7 +444,7 @@ def parse_pow(self):
lineno = self.stream.current.lineno
left = self.parse_unary()
while self.stream.current.type == 'pow':
- self.stream.next()
+ next(self.stream)
right = self.parse_unary()
left = nodes.Pow(left, right, lineno=lineno)
lineno = self.stream.current.lineno
@@ -453,11 +454,11 @@ def parse_unary(self):
token_type = self.stream.current.type
lineno = self.stream.current.lineno
if token_type == 'sub':
- self.stream.next()
+ next(self.stream)
node = self.parse_unary()
return nodes.Neg(node, lineno=lineno)
if token_type == 'add':
- self.stream.next()
+ next(self.stream)
node = self.parse_unary()
return nodes.Pos(node, lineno=lineno)
return self.parse_primary()
@@ -472,20 +473,20 @@ def parse_primary(self, with_postfix=True):
node = nodes.Const(None, lineno=token.lineno)
else:
node = nodes.Name(token.value, 'load', lineno=token.lineno)
- self.stream.next()
+ next(self.stream)
elif token.type == 'string':
- self.stream.next()
+ next(self.stream)
buf = [token.value]
lineno = token.lineno
while self.stream.current.type == 'string':
buf.append(self.stream.current.value)
- self.stream.next()
+ next(self.stream)
node = nodes.Const(''.join(buf), lineno=lineno)
elif token.type in ('integer', 'float'):
- self.stream.next()
+ next(self.stream)
node = nodes.Const(token.value, lineno=token.lineno)
elif token.type == 'lparen':
- self.stream.next()
+ next(self.stream)
node = self.parse_tuple()
self.stream.expect('rparen')
elif token.type == 'lbracket':
@@ -581,10 +582,10 @@ def parse_postfix(self, node):
return node
def parse_subscript(self, node):
- token = self.stream.next()
+ token = next(self.stream)
if token.type == 'dot':
attr_token = self.stream.current
- self.stream.next()
+ next(self.stream)
if attr_token.type == 'name':
return nodes.Getattr(node, attr_token.value, 'load',
lineno=token.lineno)
@@ -611,13 +612,13 @@ def parse_subscribed(self):
lineno = self.stream.current.lineno
if self.stream.current.type == 'colon':
- self.stream.next()
+ next(self.stream)
args = [None]
else:
node = self.parse_expression()
if self.stream.current.type != 'colon':
return node
- self.stream.next()
+ next(self.stream)
args = [node]
if self.stream.current.type == 'colon':
@@ -628,7 +629,7 @@ def parse_subscribed(self):
args.append(None)
if self.stream.current.type == 'colon':
- self.stream.next()
+ next(self.stream)
if self.stream.current.type not in ('rbracket', 'comma'):
args.append(self.parse_expression())
else:
@@ -658,11 +659,11 @@ def ensure(expr):
break
if self.stream.current.type == 'mul':
ensure(dyn_args is None and dyn_kwargs is None)
- self.stream.next()
+ next(self.stream)
dyn_args = self.parse_expression()
elif self.stream.current.type == 'pow':
ensure(dyn_kwargs is None)
- self.stream.next()
+ next(self.stream)
dyn_kwargs = self.parse_expression()
else:
ensure(dyn_args is None and dyn_kwargs is None)
@@ -688,11 +689,11 @@ def ensure(expr):
def parse_filter(self, node, start_inline=False):
while self.stream.current.type == 'pipe' or start_inline:
if not start_inline:
- self.stream.next()
+ next(self.stream)
token = self.stream.expect('name')
name = token.value
while self.stream.current.type == 'dot':
- self.stream.next()
+ next(self.stream)
name += '.' + self.stream.expect('name').value
if self.stream.current.type == 'lparen':
args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None)
@@ -706,15 +707,15 @@ def parse_filter(self, node, start_inline=False):
return node
def parse_test(self, node):
- token = self.stream.next()
+ token = next(self.stream)
if self.stream.current.test('name:not'):
- self.stream.next()
+ next(self.stream)
negated = True
else:
negated = False
name = self.stream.expect('name').value
while self.stream.current.type == 'dot':
- self.stream.next()
+ next(self.stream)
name += '.' + self.stream.expect('name').value
dyn_args = dyn_kwargs = None
kwargs = []
@@ -753,14 +754,14 @@ def flush_data():
if token.value:
add_data(nodes.TemplateData(token.value,
lineno=token.lineno))
- self.stream.next()
+ next(self.stream)
elif token.type == 'variable_begin':
- self.stream.next()
+ next(self.stream)
add_data(self.parse_tuple(with_condexpr=True))
self.stream.expect('variable_end')
elif token.type == 'block_begin':
flush_data()
- self.stream.next()
+ next(self.stream)
if end_tokens is not None and \
self.stream.current.test_any(*end_tokens):
return body
View
14 jinja2/runtime.py
@@ -11,7 +11,7 @@
import sys
from itertools import chain, imap
from jinja2.utils import Markup, partial, soft_unicode, escape, missing, \
- concat, MethodType, FunctionType, internalcode
+ concat, MethodType, FunctionType, internalcode, next
from jinja2.exceptions import UndefinedError, TemplateRuntimeError, \
TemplateNotFound
@@ -19,12 +19,18 @@
# these variables are exported to the template runtime
__all__ = ['LoopContext', 'TemplateReference', 'Macro', 'Markup',
'TemplateRuntimeError', 'missing', 'concat', 'escape',
- 'markup_join', 'unicode_join', 'TemplateNotFound']
+ 'markup_join', 'unicode_join', 'to_string',
+ 'TemplateNotFound']
#: the types we support for context functions
_context_function_types = (FunctionType, MethodType)
+#: the name of the function that is used to convert something into
+#: a string. 2to3 will adopt that automatically and the generated
+#: code can take advantage of it.
+to_string = unicode
+
def markup_join(seq):
"""Concatenation that escapes if necessary and converts to unicode."""
@@ -330,7 +336,7 @@ def __iter__(self):
def next(self):
ctx = self.context
ctx.index0 += 1
- return ctx._iterator.next(), ctx
+ return next(ctx._iterator), ctx
class Macro(object):
@@ -378,7 +384,7 @@ def __call__(self, *args, **kwargs):
arguments.append(kwargs)
elif kwargs:
raise TypeError('macro %r takes no keyword argument %r' %
- (self.name, iter(kwargs).next()))
+ (self.name, next(iter(kwargs))))
if self.catch_varargs:
arguments.append(args[self._argument_count:])
elif len(args) > self._argument_count:
View
9 jinja2/utils.py
@@ -63,6 +63,15 @@ def concat(gen):
del _test_gen_bug, _error
+# for python 2.x we create outselves a next() function that does the
+# basics without exception catching.
+try:
+ next = next
+except NameError:
+ def next(x):
+ return x.next()
+
+
# ironpython without stdlib doesn't have keyword
try:
from keyword import iskeyword as is_python_keyword
Please sign in to comment.
Something went wrong with that request. Please try again.