[svn] added some more jinja unittests
authorArmin Ronacher <armin.ronacher@active-4.com>
Fri, 1 Jun 2007 16:25:28 +0000 (18:25 +0200)
committerArmin Ronacher <armin.ronacher@active-4.com>
Fri, 1 Jun 2007 16:25:28 +0000 (18:25 +0200)
--HG--
branch : trunk

12 files changed:
CHANGES
docs/src/altsyntax.txt
jinja/datastructure.py
jinja/lexer.py
jinja/parser.py
jinja/translators/python.py
jinja/utils.py
setup.py
tests/test_parser.py
tests/test_security.py
tests/test_syntax.py [new file with mode: 0644]
tests/test_various.py

diff --git a/CHANGES b/CHANGES
index 8d90efb4a03958b30d60a2b92810b073a398a72b..da46f59436f7252a0adfbecb00e2a77aeb94dcca 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -3,7 +3,7 @@ Jinja Changelog
 
 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.
 
@@ -110,9 +110,11 @@ Version 1.1
 
 - 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
index 9fb3b64f654f71d8dbbb971b639455d195dceff9..0f7242f79f495fa387999e1f23afa4911323b1eb 100644 (file)
@@ -42,7 +42,7 @@ An example template then looks like this:
 
 .. sourcecode:: rhtml
 
-    <%# example eruby like configuration for jinja %>
+    <%# example eruby like configuration for Jinja %>
     <ul>
     <% for item in seq %>
       <li><%= item %></li>
@@ -63,7 +63,7 @@ designer:
 
 .. sourcecode:: html
 
-    <!--# example eruby like configuration for jinja -->
+    <!--# example SGML comment configuration for Jinja -->
     <ul>
     <!-- for item in seq -->
       <li>${item}</li>
@@ -98,6 +98,7 @@ This now allows smarty like templates:
 
 .. sourcecode:: smarty
 
+    {* example smarty-like configuration for Jinja *}
     {if something == 42}
         Something is the answer to all questions and stuff like that.
     {else}
index 0f5d9e932f17769da2ba308dca3dd388ecbf0583..5946db8f8ba02b3f6ef61359b740102e0fdc475a 100644 (file)
@@ -65,6 +65,9 @@ class AbstractUndefinedType(object):
         return self
     __deepcopy__ = __copy__
 
+    def __repr__(self):
+        return 'Undefined'
+
     def __reduce__(self):
         raise TypeError('undefined objects have to provide a __reduce__')
 
index 45398db7c38433a468712a1187cdcb266d1fa13f..2c452c559a6043f0d54e8d0ec49564e4697cef4c 100644 (file)
@@ -51,7 +51,7 @@ operator_re = re.compile('(%s)' % '|'.join([
     # braces and parenthesis
     '[', ']', '(', ')', '{', '}',
     # attribute access and comparison / logical operators
-    '.', ':', ',', '|', '==', '<', '>', '<=', '>=', '!=', '=',
+    '.', ':', ',', '|', '==', '<=', '>=', '<', '>', '!=', '=',
     ur'or\b', ur'and\b', ur'not\b', ur'in\b', ur'is\b'
 ]]))
 
@@ -275,7 +275,7 @@ class Lexer(object):
                                 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
index 80f4c72ff3edbb9d7625d3edaeffa4b2461e89f0..aba37257c910fc103e6bb6c7249725105c30d441 100644 (file)
@@ -353,7 +353,7 @@ class Parser(object):
                                           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]
@@ -369,7 +369,7 @@ class Parser(object):
                (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)
@@ -470,7 +470,7 @@ class Parser(object):
                                         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
index 34ae77c076c5947d9e1b2d53ca8191a08d7134dc..bef76198d7cf816c0160a5f06c0d9d010f91af93 100644 (file)
@@ -1002,8 +1002,8 @@ class PythonTranslator(Translator):
         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:
@@ -1209,7 +1209,4 @@ class PythonTranslator(Translator):
         """
         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])
index 135922677e519625bdee2336e2b3f91c03b1dd4a..d9608eba265a031aa3916c0b5f109e1c6117cf0d 100644 (file)
@@ -63,7 +63,9 @@ try:
 except NameError:
     def sorted(seq, reverse=False):
         rv = list(seq)
-        rv.sort(reverse=reverse)
+        rv.sort()
+        if reverse:
+            rv.reverse()
         return rv
 
 #: function types
@@ -460,7 +462,7 @@ class DebugHelper(object):
         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
@@ -478,7 +480,7 @@ class DebugHelper(object):
         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
index b457c45355fa29ba534a8f4aa6b4093f73643b10..a0be447d49ecb53b289e35de1563f4676b72274c 100644 (file)
--- a/setup.py
+++ b/setup.py
@@ -45,7 +45,7 @@ class optional_build_ext(build_ext):
 
 setup(
     name = 'Jinja',
-    version = '1.0',
+    version = '1.1',
     url = 'http://jinja.pocoo.org/',
     license = 'BSD',
     author = 'Armin Ronacher',
index d9d75c0034afdb51ea7c9f92008ee3c5fc1d833c..1c8851d58a2d0478771f57488a55ea176107b041 100644 (file)
@@ -40,6 +40,8 @@ SMARTY_SYNTAX = '''\
     {item}
 {-endfor}'''
 
+BALANCING = '''{{{'foo':'bar'}.foo}}'''
+
 
 def test_no_variable_block():
     env = Environment('{%', '%}', None, None)
@@ -74,3 +76,8 @@ def test_smarty_syntax():
     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'
index 5e0099d62e144897228062f73f0f5bd6f9e8171d..95d33719f0d356c5b6e145b5be92720086eaaf9d 100644 (file)
@@ -44,4 +44,8 @@ test_restricted = '''
 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)
 '''
diff --git a/tests/test_syntax.py b/tests/test_syntax.py
new file mode 100644 (file)
index 0000000..79aaf2f
--- /dev/null
@@ -0,0 +1,84 @@
+# -*- 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'
index 6f449a4b370d7be1a5cb89edd592ded9cc535b44..cea56b8737a16f253feff3b9b8bc4c9c769d07a2 100644 (file)
@@ -6,6 +6,7 @@
     :copyright: 2007 by Armin Ronacher.
     :license: BSD, see LICENSE for more details.
 """
+from jinja.exceptions import TemplateSyntaxError
 
 KEYWORDS = '''\
 {{ with }}
@@ -31,7 +32,11 @@ KEYWORDS = '''\
 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)
@@ -70,14 +75,6 @@ def test_cache_dict():
     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)
@@ -88,3 +85,18 @@ def test_simplefilter(env):
     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')