Version 1.1
-----------
-(codename to be selected, release date unknown)
+(codename: sinka, released Jun 1, 2007)
- blocks now support ``{{ super() }}`` to render the parent output.
- improved ChoiceLoader error reporting (thanks to Bryan McLemore)
+- fixed extended slicing
+
Version 1.0
-----------
-(released Mar 23, 2007)
+(codename: siyutusan, released Mar 23, 2007)
- Initial release
.. sourcecode:: rhtml
- <%# example eruby like configuration for jinja %>
+ <%# example eruby like configuration for Jinja %>
<ul>
<% for item in seq %>
<li><%= item %></li>
.. sourcecode:: html
- <!--# example eruby like configuration for jinja -->
+ <!--# example SGML comment configuration for Jinja -->
<ul>
<!-- for item in seq -->
<li>${item}</li>
.. sourcecode:: smarty
+ {* example smarty-like configuration for Jinja *}
{if something == 42}
Something is the answer to all questions and stuff like that.
{else}
return self
__deepcopy__ = __copy__
+ def __repr__(self):
+ return 'Undefined'
+
def __reduce__(self):
raise TypeError('undefined objects have to provide a __reduce__')
# braces and parenthesis
'[', ']', '(', ')', '{', '}',
# attribute access and comparison / logical operators
- '.', ':', ',', '|', '==', '<', '>', '<=', '>=', '!=', '=',
+ '.', ':', ',', '|', '==', '<=', '>=', '<', '>', '!=', '=',
ur'or\b', ur'and\b', ur'not\b', ur'in\b', ur'is\b'
]]))
lineno += g.count('\n')
continue
# failure group
- elif isinstance(token, Failure):
+ elif token.__class__ is Failure:
raise token(lineno, filename)
# bygroup is a bit more complex, in that case we
# yield for the current token the first named
lineno, self.filename)
# plural name without trailing "_"? that's a keyword
if not name.endswith('_'):
- raise TemplateSyntaxError('illegal use of keyword \'%s\' as'
+ raise TemplateSyntaxError('illegal use of keyword \'%s\' as '
'identifier in translatable block.'
% name, lineno, self.filename)
name = name[:-1]
(self.no_variable_block and next_token == 'block_end'):
raise TemplateSyntaxError('you cannot use variable '
'expressions inside translatable '
- 'tags. apply filters in the'
+ 'tags. apply filters in the '
'trans header.', lineno,
self.filename)
buf.append('%%(%s)s' % name)
continue
block_name = block_name[:-1]
raise TemplateSyntaxError('unknown directive'
- "'%s'" % block_name,
+ " '%s'" % block_name,
lineno,
self.filename)
# we have something different and are in the
Handle variable based attribute access foo['bar'].
"""
if len(node.subs) != 1:
- raise TemplateSyntaxError('attribute access requires one argument',
- node.lineno,
+ raise TemplateSyntaxError('attribute access requires one '
+ 'argument', node.lineno,
node.filename)
assert node.flags != 'OP_DELETE', 'wtf? do we support that?'
if node.subs[0].__class__ is ast.Sliceobj:
"""
Extended Slice access.
"""
- args = []
- for n in node.nodes:
- args.append(self.handle_node(n))
- return '[%s]' % ':'.join(args)
+ return '[%s]' % ':'.join([self.handle_node(n) for n in node.nodes])
except NameError:
def sorted(seq, reverse=False):
rv = list(seq)
- rv.sort(reverse=reverse)
+ rv.sort()
+ if reverse:
+ rv.reverse()
return rv
#: function types
for name, f in filters:
if f in strip:
continue
- doc = '\n'.join(' ' + x for x in (getdoc(f) or '').splitlines())
+ doc = '\n'.join([' ' + x for x in (getdoc(f) or '').splitlines()])
result.append('`%s`\n\n%s' % (name, doc))
return '\n\n'.join(result)
filters.jinja_context_callable = True
for name, f in tests:
if f in strip:
continue
- doc = '\n'.join(' ' + x for x in (getdoc(f) or '').splitlines())
+ doc = '\n'.join([' ' + x for x in (getdoc(f) or '').splitlines()])
result.append('`%s`\n\n%s' % (name, doc))
return '\n\n'.join(result)
tests.jinja_context_callable = True
setup(
name = 'Jinja',
- version = '1.0',
+ version = '1.1',
url = 'http://jinja.pocoo.org/',
license = 'BSD',
author = 'Armin Ronacher',
{item}
{-endfor}'''
+BALANCING = '''{{{'foo':'bar'}.foo}}'''
+
def test_no_variable_block():
env = Environment('{%', '%}', None, None)
env = Environment('{', '}', '{', '}', '{*', '*}')
tmpl = env.from_string(SMARTY_SYNTAX)
assert tmpl.render(seq=range(5)) == '01234'
+
+
+def test_balancing(env):
+ tmpl = env.from_string(BALANCING)
+ assert tmpl.render() == 'bar'
Traceback (most recent call last):
...
TemplateSyntaxError: can't assign to expression. (line 1)
+>>> env.from_string("{% for foo, bar.baz in seq %}...{% endfor %}")
+Traceback (most recent call last):
+ ...
+TemplateSyntaxError: can't assign to expression. (line 1)
'''
--- /dev/null
+# -*- coding: utf-8 -*-
+"""
+ unit test for expression syntax
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: 2007 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+CALL = '''{{ foo('a', c='d', e='f', *['b'], **{'g': 'h'}) }}'''
+SLICING = '''{{ [1, 2, 3][:] }}|{{ [1, 2, 3][::-1] }}'''
+ATTR = '''{{ foo.bar }}|{{ foo['bar'] }}'''
+SUBSCRIPT = '''{{ foo[0] }}|{{ foo[-1] }}'''
+KEYATTR = '''{{ {'items': 'foo'}.items }}|{{ {}.items() }}'''
+TUPLE = '''{{ () }}'''
+MATH = '''{{ (1 + 1 * 2) - 3 / 2 }}|{{ 2**3 }}'''
+DIV = '''{{ 3 // 2 }}|{{ 3 / 2 }}|{{ 3 % 2 }}'''
+UNARY = '''{{ +3 }}|{{ -3 }}'''
+COMPARE = '''{{ 1 > 0 }}|{{ 1 >= 1 }}|{{ 2 < 3 }}|{{ 2 == 2 }}|{{ 1 <= 1 }}'''
+LITERALS = '''{{ [] }}|{{ {} }}|{{ '' }}'''
+BOOL = '''{{ true and false }}|{{ false or true }}|{{ not false }}'''
+
+
+def test_call():
+ from jinja import Environment
+ env = Environment()
+ env.globals['foo'] = lambda a, b, c, e, g: a + b + c + e + g
+ tmpl = env.from_string(CALL)
+ assert tmpl.render() == 'abdfh'
+
+
+def test_slicing(env):
+ tmpl = env.from_string(SLICING)
+ assert tmpl.render() == '[1, 2, 3]|[3, 2, 1]'
+
+
+def test_attr(env):
+ tmpl = env.from_string(ATTR)
+ assert tmpl.render(foo={'bar': 42}) == '42|42'
+
+
+def test_subscript(env):
+ tmpl = env.from_string(SUBSCRIPT)
+ assert tmpl.render(foo=[0, 1, 2]) == '0|2'
+
+
+def test_keyattr(env):
+ tmpl = env.from_string(KEYATTR)
+ assert tmpl.render() == 'foo|[]'
+
+
+def test_tuple(env):
+ tmpl = env.from_string(TUPLE)
+ assert tmpl.render() == '[]'
+
+
+def test_math(env):
+ tmpl = env.from_string(MATH)
+ assert tmpl.render() == '1.5|8'
+
+
+def test_div(env):
+ tmpl = env.from_string(DIV)
+ assert tmpl.render() == '1|1.5|1'
+
+
+def test_unary(env):
+ tmpl = env.from_string(UNARY)
+ assert tmpl.render() == '3|-3'
+
+
+def test_compare(env):
+ tmpl = env.from_string(COMPARE)
+ assert tmpl.render() == 'True|True|True|True|True'
+
+
+def test_literals(env):
+ tmpl = env.from_string(LITERALS)
+ assert tmpl.render() == '[]|{}|'
+
+
+def test_bool(env):
+ tmpl = env.from_string(BOOL)
+ assert tmpl.render() == 'False|True|True'
:copyright: 2007 by Armin Ronacher.
:license: BSD, see LICENSE for more details.
"""
+from jinja.exceptions import TemplateSyntaxError
KEYWORDS = '''\
{{ with }}
LIGHTKW = '''{{ call }}'''
UNPACKING = '''{% for a, b, c in [[1, 2, 3]] %}{{ a }}|{{ b }}|{{ c }}{% endfor %}'''
RAW = '''{% raw %}{{ FOO }} and {% BAR %}{% endraw %}'''
-CALL = '''{{ foo('a', c='d', e='f', *['b'], **{'g': 'h'}) }}'''
+CONST = '''{{ true }}|{{ false }}|{{ none }}|{{ undefined }}|\
+{{ none is defined }}|{{ undefined is defined }}'''
+CONSTASS1 = '''{% set true = 42 %}'''
+CONSTASS2 = '''{% for undefined in seq %}{% endfor %}'''
+
def test_keywords(env):
env.from_string(KEYWORDS)
assert 'a' in d and 'c' in d and 'd' in d and 'b' not in d
-def test_call():
- from jinja import Environment
- env = Environment()
- env.globals['foo'] = lambda a, b, c, e, g: a + b + c + e + g
- tmpl = env.from_string(CALL)
- assert tmpl.render() == 'abdfh'
-
-
def test_stringfilter(env):
from jinja.filters import stringfilter
f = stringfilter(lambda f, x: f + x)
from jinja.filters import simplefilter
f = simplefilter(lambda f, x: f + x)
assert f(42)(env, None, 23) == 65
+
+
+def test_const(env):
+ tmpl = env.from_string(CONST)
+ assert tmpl.render() == 'True|False|||True|False'
+
+
+def test_const_assign(env):
+ for tmpl in CONSTASS1, CONSTASS2:
+ try:
+ env.from_string(tmpl)
+ except TemplateSyntaxError:
+ pass
+ else:
+ raise AssertionError('expected syntax error')