summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Wieser <wieser.eric@gmail.com>2014-12-26 23:19:46 +0000
committerMarcel Hellkamp <marc@gsites.de>2015-10-24 19:57:42 +0200
commit1f6fda636a4ea88adfa4292f3c843e71b1a685d8 (patch)
treea7ea0822df9c9cb219a342ec9e9b567323dcb8f8
parentef0623cbbfebfa62993126442969c32668fdcff4 (diff)
downloadbottle-1f6fda636a4ea88adfa4292f3c843e71b1a685d8.tar.gz
Prevent syntax errors when line-wrapping comprehensions
Ignores block keywords inside parentheses/braces/brackets, allowing list/set/dict comprehensions to be wrapped in the same way as dicts. Does not check for correctly paired parentheses - we can let python do that, right? Builds upon cleanup made in https://github.com/bottlepy/bottle/pull/689
-rw-r--r--bottle.py26
-rwxr-xr-xtest/test_stpl.py12
2 files changed, 32 insertions, 6 deletions
diff --git a/bottle.py b/bottle.py
index f99f46a..a78b488 100644
--- a/bottle.py
+++ b/bottle.py
@@ -3415,15 +3415,19 @@ class StplParser(object):
_re_inl = _re_tok.replace('|\\n','') # We re-use this string pattern later
# 2: Comments (until end of line, but not the newline itself)
_re_tok += '|(#.*)'
- # 3,4: Keywords that start or continue a python block (only start of line)
+ # 3,4: Open and close grouping tokens
+ _re_tok += '|([\[\{\(])'
+ _re_tok += '|([\]\}\)])'
+ # 5,6: Keywords that start or continue a python block (only start of line)
_re_tok += '|^([ \\t]*(?:if|for|while|with|try|def|class)\\b)' \
'|^([ \\t]*(?:elif|else|except|finally)\\b)'
- # 5: Our special 'end' keyword (but only if it stands alone)
+ # 7: Our special 'end' keyword (but only if it stands alone)
_re_tok += '|((?:^|;)[ \\t]*end[ \\t]*(?=(?:%(block_close)s[ \\t]*)?\\r?$|;|#))'
- # 6: A customizable end-of-code-block template token (only end of line)
+ # 8: A customizable end-of-code-block template token (only end of line)
_re_tok += '|(%(block_close)s[ \\t]*(?=$))'
- # 7: And finally, a single newline. The 8th token is 'everything else'
+ # 9: And finally, a single newline. The 10th token is 'everything else'
_re_tok += '|(\\r?\\n)'
+
# Match the start tokens of code areas in a template
_re_split = '(?m)^[ \t]*(\\\\?)((%(line_start)s)|(%(block_start)s))(%%?)'
# Match inline statements (may contain python strings)
@@ -3437,6 +3441,7 @@ class StplParser(object):
self.code_buffer, self.text_buffer = [], []
self.lineno, self.offset = 1, 0
self.indent, self.indent_mod = 0, 0
+ self.paren_depth = 0
def get_syntax(self):
''' Tokens as a space separated string (default: <% %> % {{ }}) '''
@@ -3493,8 +3498,8 @@ class StplParser(object):
return
code_line += self.source[self.offset:self.offset+m.start()]
self.offset += m.end()
- _str, _com, _blk1, _blk2, _end, _cend, _nl = m.groups()
- if code_line and (_blk1 or _blk2): # a if b else c
+ _str, _com, _po, _pc, _blk1, _blk2, _end, _cend, _nl = m.groups()
+ if (code_line or self.paren_depth > 0) and (_blk1 or _blk2): # a if b else c
code_line += _blk1 or _blk2
continue
if _str: # Python string
@@ -3503,6 +3508,15 @@ class StplParser(object):
comment = _com
if multiline and _com.strip().endswith(self._tokens[1]):
multiline = False # Allow end-of-block in comments
+ elif _po: # open parenthesis
+ self.paren_depth += 1
+ code_line += _po
+ elif _pc: # close parenthesis
+ if self.paren_depth > 0:
+ # we could check for matching parentheses here, but it's
+ # easier to leave that to python - just check counts
+ self.paren_depth -= 1
+ code_line += _pc
elif _blk1: # Start-block keyword (if/for/while/def/try/...)
code_line, self.indent_mod = _blk1, -1
self.indent += 1
diff --git a/test/test_stpl.py b/test/test_stpl.py
index 4c38f07..75ca42b 100755
--- a/test/test_stpl.py
+++ b/test/test_stpl.py
@@ -375,6 +375,18 @@ class TestSTPLDir(unittest.TestCase):
'''
self.assertRenders(source, result)
+ def test_multiline_comprehensions_in_code_line(self):
+ self.assertRenders(source='''
+ % a = [
+ % (i + 1)
+ % for i in range(5)
+ % if i%2 == 0
+ % ]
+ {{a}}
+ ''', result='''
+ [1, 3, 5]
+ ''')
+
if __name__ == '__main__': #pragma: no cover
unittest.main()