diff options
author | Eric Wieser <wieser.eric@gmail.com> | 2014-12-26 23:19:46 +0000 |
---|---|---|
committer | Marcel Hellkamp <marc@gsites.de> | 2015-10-24 19:57:42 +0200 |
commit | 1f6fda636a4ea88adfa4292f3c843e71b1a685d8 (patch) | |
tree | a7ea0822df9c9cb219a342ec9e9b567323dcb8f8 | |
parent | ef0623cbbfebfa62993126442969c32668fdcff4 (diff) | |
download | bottle-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.py | 26 | ||||
-rwxr-xr-x | test/test_stpl.py | 12 |
2 files changed, 32 insertions, 6 deletions
@@ -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() |