Improve check-templates error handling.

In this commit we improve the way errors are handled in our
template parser and thus improving the displayed messages in
case of errors. Eg. Errors in case of unbalanced quotes now
makes more sense displaying line and column information
including line where error might be sourced.
This commit is contained in:
adnrs96 2017-02-21 18:23:52 +05:30 committed by showell
parent fa31ad35c9
commit 5b5a0bdb80
2 changed files with 87 additions and 51 deletions

View File

@ -5,8 +5,19 @@ from six.moves import range
import re import re
class TemplateParserException(Exception): class TemplateParserException(Exception):
# TODO: Have callers pass in line numbers. def __init__(self, message):
pass # type: (str) -> None
self.message = message
def __str__(self):
# type: () -> str
return self.message
class TokenizationException(Exception):
def __init__(self, message, line_content=None):
# type: (str, str) -> None
self.message = message
self.line_content = line_content
class TokenizerState(object): class TokenizerState(object):
def __init__(self): def __init__(self):
@ -72,6 +83,7 @@ def tokenize(text):
tokens = [] tokens = []
while state.i < len(text): while state.i < len(text):
try:
if looking_at_comment(): if looking_at_comment():
s = get_html_comment(text, state.i) s = get_html_comment(text, state.i)
tag = s[4:-3] tag = s[4:-3]
@ -114,6 +126,10 @@ def tokenize(text):
else: else:
advance(1) advance(1)
continue continue
except TokenizationException as e:
raise TemplateParserException('''%s at Line %d Col %d:"%s"''' %
(e.message, state.line, state.col,
e.line_content))
token = Token( token = Token(
kind=kind, kind=kind,
@ -249,7 +265,7 @@ def get_handlebars_tag(text, i):
while end < len(text) - 1 and text[end] != '}': while end < len(text) - 1 and text[end] != '}':
end += 1 end += 1
if text[end] != '}' or text[end+1] != '}': if text[end] != '}' or text[end+1] != '}':
raise TemplateParserException('Tag missing }}') raise TokenizationException('Tag missing "}}"', text[i:end+2])
s = text[i:end+2] s = text[i:end+2]
return s return s
@ -259,7 +275,7 @@ def get_django_tag(text, i):
while end < len(text) - 1 and text[end] != '%': while end < len(text) - 1 and text[end] != '%':
end += 1 end += 1
if text[end] != '%' or text[end+1] != '}': if text[end] != '%' or text[end+1] != '}':
raise TemplateParserException('Tag missing %}') raise TokenizationException('Tag missing "%}"', text[i:end+2])
s = text[i:end+2] s = text[i:end+2]
return s return s
@ -267,20 +283,31 @@ def get_html_tag(text, i):
# type: (str, int) -> str # type: (str, int) -> str
quote_count = 0 quote_count = 0
end = i + 1 end = i + 1
while end < len(text) and (text[end] != '>' or quote_count % 2 != 0): unclosed_end = 0
while end < len(text) and (text[end] != '>' or quote_count % 2 != 0 and text[end] != '<'):
if text[end] == '"': if text[end] == '"':
quote_count += 1 quote_count += 1
if not unclosed_end and text[end] == '<':
unclosed_end = end
end += 1 end += 1
if quote_count % 2 != 0:
if unclosed_end:
raise TokenizationException('Unbalanced Quotes', text[i:unclosed_end])
else:
raise TokenizationException('Unbalanced Quotes', text[i:end+1])
if end == len(text) or text[end] != '>': if end == len(text) or text[end] != '>':
raise TemplateParserException('Tag missing >') raise TokenizationException('Tag missing ">"', text[i:end+1])
s = text[i:end+1] s = text[i:end+1]
return s return s
def get_html_comment(text, i): def get_html_comment(text, i):
# type: (str, int) -> str # type: (str, int) -> str
end = i + 7 end = i + 7
unclosed_end = 0
while end <= len(text): while end <= len(text):
if text[end-3:end] == '-->': if text[end-3:end] == '-->':
return text[i:end] return text[i:end]
if not unclosed_end and text[end] == '<':
unclosed_end = end
end += 1 end += 1
raise TemplateParserException('Unclosed comment') raise TokenizationException('Unclosed comment', text[i:unclosed_end])

View File

@ -112,42 +112,51 @@ class ParserTest(unittest.TestCase):
my_html = ''' my_html = '''
{{# foo {{# foo
''' '''
self._assert_validate_error('Tag missing }}', text=my_html) self._assert_validate_error('''Tag missing "}}" at Line 2 Col 13:"{{# foo
"''', text=my_html)
def test_validate_incomplete_handlebars_tag_2(self): def test_validate_incomplete_handlebars_tag_2(self):
# type: () -> None # type: () -> None
my_html = ''' my_html = '''
{{# foo } {{# foo }
''' '''
self._assert_validate_error('Tag missing }}', text=my_html) self._assert_validate_error('Tag missing "}}" at Line 2 Col 13:"{{# foo }\n"', text=my_html)
def test_validate_incomplete_django_tag_1(self): def test_validate_incomplete_django_tag_1(self):
# type: () -> None # type: () -> None
my_html = ''' my_html = '''
{% foo {% foo
''' '''
self._assert_validate_error('Tag missing %}', text=my_html) self._assert_validate_error('''Tag missing "%}" at Line 2 Col 13:"{% foo
"''', text=my_html)
def test_validate_incomplete_django_tag_2(self): def test_validate_incomplete_django_tag_2(self):
# type: () -> None # type: () -> None
my_html = ''' my_html = '''
{% foo % {% foo %
''' '''
self._assert_validate_error('Tag missing %}', text=my_html) self._assert_validate_error('Tag missing "%}" at Line 2 Col 13:"{% foo %\n"', text=my_html)
def test_validate_incomplete_html_tag_1(self): def test_validate_incomplete_html_tag_1(self):
# type: () -> None # type: () -> None
my_html = ''' my_html = '''
<b <b
''' '''
self._assert_validate_error('Tag missing >', text=my_html) self._assert_validate_error('''Tag missing ">" at Line 2 Col 13:"<b
"''', text=my_html)
def test_validate_incomplete_html_tag_2(self): def test_validate_incomplete_html_tag_2(self):
# type: () -> None # type: () -> None
my_html = ''' my_html = '''
<a href=" <a href="
''' '''
self._assert_validate_error('Tag missing >', text=my_html) my_html1 = '''
<a href=""
'''
self._assert_validate_error('''Tag missing ">" at Line 2 Col 13:"<a href=""
"''', text=my_html1)
self._assert_validate_error('''Unbalanced Quotes at Line 2 Col 13:"<a href="
"''', text=my_html)
def test_validate_empty_html_tag(self): def test_validate_empty_html_tag(self):
# type: () -> None # type: () -> None