mirror of https://github.com/zulip/zulip.git
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:
parent
fa31ad35c9
commit
5b5a0bdb80
|
@ -5,8 +5,19 @@ from six.moves import range
|
|||
import re
|
||||
|
||||
class TemplateParserException(Exception):
|
||||
# TODO: Have callers pass in line numbers.
|
||||
pass
|
||||
def __init__(self, message):
|
||||
# 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):
|
||||
def __init__(self):
|
||||
|
@ -72,48 +83,53 @@ def tokenize(text):
|
|||
tokens = []
|
||||
|
||||
while state.i < len(text):
|
||||
if looking_at_comment():
|
||||
s = get_html_comment(text, state.i)
|
||||
tag = s[4:-3]
|
||||
kind = 'html_comment'
|
||||
elif looking_at_html_start():
|
||||
s = get_html_tag(text, state.i)
|
||||
tag_parts = s[1:-1].split()
|
||||
try:
|
||||
if looking_at_comment():
|
||||
s = get_html_comment(text, state.i)
|
||||
tag = s[4:-3]
|
||||
kind = 'html_comment'
|
||||
elif looking_at_html_start():
|
||||
s = get_html_tag(text, state.i)
|
||||
tag_parts = s[1:-1].split()
|
||||
|
||||
if not tag_parts:
|
||||
raise TemplateParserException("Tag name missing")
|
||||
if not tag_parts:
|
||||
raise TemplateParserException("Tag name missing")
|
||||
|
||||
tag = tag_parts[0]
|
||||
tag = tag_parts[0]
|
||||
|
||||
if is_special_html_tag(s, tag):
|
||||
kind = 'html_special'
|
||||
elif s.endswith('/>'):
|
||||
kind = 'html_singleton'
|
||||
if is_special_html_tag(s, tag):
|
||||
kind = 'html_special'
|
||||
elif s.endswith('/>'):
|
||||
kind = 'html_singleton'
|
||||
else:
|
||||
kind = 'html_start'
|
||||
elif looking_at_html_end():
|
||||
s = get_html_tag(text, state.i)
|
||||
tag = s[2:-1]
|
||||
kind = 'html_end'
|
||||
elif looking_at_handlebars_start():
|
||||
s = get_handlebars_tag(text, state.i)
|
||||
tag = s[3:-2].split()[0]
|
||||
kind = 'handlebars_start'
|
||||
elif looking_at_handlebars_end():
|
||||
s = get_handlebars_tag(text, state.i)
|
||||
tag = s[3:-2]
|
||||
kind = 'handlebars_end'
|
||||
elif looking_at_django_start():
|
||||
s = get_django_tag(text, state.i)
|
||||
tag = s[3:-2].split()[0]
|
||||
kind = 'django_start'
|
||||
elif looking_at_django_end():
|
||||
s = get_django_tag(text, state.i)
|
||||
tag = s[6:-3]
|
||||
kind = 'django_end'
|
||||
else:
|
||||
kind = 'html_start'
|
||||
elif looking_at_html_end():
|
||||
s = get_html_tag(text, state.i)
|
||||
tag = s[2:-1]
|
||||
kind = 'html_end'
|
||||
elif looking_at_handlebars_start():
|
||||
s = get_handlebars_tag(text, state.i)
|
||||
tag = s[3:-2].split()[0]
|
||||
kind = 'handlebars_start'
|
||||
elif looking_at_handlebars_end():
|
||||
s = get_handlebars_tag(text, state.i)
|
||||
tag = s[3:-2]
|
||||
kind = 'handlebars_end'
|
||||
elif looking_at_django_start():
|
||||
s = get_django_tag(text, state.i)
|
||||
tag = s[3:-2].split()[0]
|
||||
kind = 'django_start'
|
||||
elif looking_at_django_end():
|
||||
s = get_django_tag(text, state.i)
|
||||
tag = s[6:-3]
|
||||
kind = 'django_end'
|
||||
else:
|
||||
advance(1)
|
||||
continue
|
||||
advance(1)
|
||||
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(
|
||||
kind=kind,
|
||||
|
@ -249,7 +265,7 @@ def get_handlebars_tag(text, i):
|
|||
while end < len(text) - 1 and text[end] != '}':
|
||||
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]
|
||||
return s
|
||||
|
||||
|
@ -259,7 +275,7 @@ def get_django_tag(text, i):
|
|||
while end < len(text) - 1 and text[end] != '%':
|
||||
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]
|
||||
return s
|
||||
|
||||
|
@ -267,20 +283,31 @@ def get_html_tag(text, i):
|
|||
# type: (str, int) -> str
|
||||
quote_count = 0
|
||||
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] == '"':
|
||||
quote_count += 1
|
||||
if not unclosed_end and text[end] == '<':
|
||||
unclosed_end = end
|
||||
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] != '>':
|
||||
raise TemplateParserException('Tag missing >')
|
||||
raise TokenizationException('Tag missing ">"', text[i:end+1])
|
||||
s = text[i:end+1]
|
||||
return s
|
||||
|
||||
def get_html_comment(text, i):
|
||||
# type: (str, int) -> str
|
||||
end = i + 7
|
||||
unclosed_end = 0
|
||||
while end <= len(text):
|
||||
if text[end-3:end] == '-->':
|
||||
return text[i:end]
|
||||
if not unclosed_end and text[end] == '<':
|
||||
unclosed_end = end
|
||||
end += 1
|
||||
raise TemplateParserException('Unclosed comment')
|
||||
raise TokenizationException('Unclosed comment', text[i:unclosed_end])
|
||||
|
|
|
@ -112,42 +112,51 @@ class ParserTest(unittest.TestCase):
|
|||
my_html = '''
|
||||
{{# 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):
|
||||
# type: () -> None
|
||||
my_html = '''
|
||||
{{# 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):
|
||||
# type: () -> None
|
||||
my_html = '''
|
||||
{% 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):
|
||||
# type: () -> None
|
||||
my_html = '''
|
||||
{% 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):
|
||||
# type: () -> None
|
||||
my_html = '''
|
||||
<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):
|
||||
# type: () -> None
|
||||
my_html = '''
|
||||
<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):
|
||||
# type: () -> None
|
||||
|
|
Loading…
Reference in New Issue