Fixed #12201 -- Added a lineno attibute to template Token so e.g. we can report line numbers in errors during i18n literals extraction. Thanks madewulf for the report and Claude Paroz for the patch.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@14813 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Ramiro Morales 2010-12-04 17:42:54 +00:00
parent 5a7af25c7a
commit 23f69af454
7 changed files with 34 additions and 15 deletions

View File

@ -220,18 +220,15 @@ def make_messages(locale=None, domain='django', verbosity='1', all=False,
os.unlink(os.path.join(dirpath, thefile)) os.unlink(os.path.join(dirpath, thefile))
elif domain == 'django' and (file_ext == '.py' or file_ext in extensions): elif domain == 'django' and (file_ext == '.py' or file_ext in extensions):
thefile = file thefile = file
orig_file = os.path.join(dirpath, file)
if file_ext in extensions: if file_ext in extensions:
src = open(os.path.join(dirpath, file), "rU").read() src = open(orig_file, "rU").read()
thefile = '%s.py' % file thefile = '%s.py' % file
f = open(os.path.join(dirpath, thefile), "w")
try: try:
f = open(os.path.join(dirpath, thefile), "w") f.write(templatize(src, orig_file[2:]))
try: finally:
f.write(templatize(src)) f.close()
finally:
f.close()
except SyntaxError, msg:
msg = "%s (file: %s)" % (msg, os.path.join(dirpath, file))
raise SyntaxError(msg)
if verbosity > 1: if verbosity > 1:
sys.stdout.write('processing file %s in %s\n' % (file, dirpath)) sys.stdout.write('processing file %s in %s\n' % (file, dirpath))
cmd = ( cmd = (
@ -250,7 +247,7 @@ def make_messages(locale=None, domain='django', verbosity='1', all=False,
if thefile != file: if thefile != file:
old = '#: '+os.path.join(dirpath, thefile)[2:] old = '#: '+os.path.join(dirpath, thefile)[2:]
new = '#: '+os.path.join(dirpath, file)[2:] new = '#: '+orig_file[2:]
msgs = msgs.replace(old, new) msgs = msgs.replace(old, new)
if os.path.exists(potfile): if os.path.exists(potfile):
# Strip the header # Strip the header

View File

@ -139,6 +139,7 @@ class Token(object):
def __init__(self, token_type, contents): def __init__(self, token_type, contents):
# token_type must be TOKEN_TEXT, TOKEN_VAR, TOKEN_BLOCK or TOKEN_COMMENT. # token_type must be TOKEN_TEXT, TOKEN_VAR, TOKEN_BLOCK or TOKEN_COMMENT.
self.token_type, self.contents = token_type, contents self.token_type, self.contents = token_type, contents
self.lineno = None
def __str__(self): def __str__(self):
return '<%s token: "%s...">' % \ return '<%s token: "%s...">' % \
@ -164,6 +165,7 @@ class Lexer(object):
def __init__(self, template_string, origin): def __init__(self, template_string, origin):
self.template_string = template_string self.template_string = template_string
self.origin = origin self.origin = origin
self.lineno = 1
def tokenize(self): def tokenize(self):
"Return a list of tokens from a given template_string." "Return a list of tokens from a given template_string."
@ -193,6 +195,8 @@ class Lexer(object):
token = Token(TOKEN_COMMENT, content) token = Token(TOKEN_COMMENT, content)
else: else:
token = Token(TOKEN_TEXT, token_string) token = Token(TOKEN_TEXT, token_string)
token.lineno = self.lineno
self.lineno += token_string.count('\n')
return token return token
class Parser(object): class Parser(object):

View File

@ -104,8 +104,8 @@ def to_locale(language):
def get_language_from_request(request): def get_language_from_request(request):
return _trans.get_language_from_request(request) return _trans.get_language_from_request(request)
def templatize(src): def templatize(src, origin=None):
return _trans.templatize(src) return _trans.templatize(src, origin)
def deactivate_all(): def deactivate_all():
return _trans.deactivate_all() return _trans.deactivate_all()

View File

@ -421,7 +421,7 @@ endblock_re = re.compile(r"""^\s*endblocktrans$""")
plural_re = re.compile(r"""^\s*plural$""") plural_re = re.compile(r"""^\s*plural$""")
constant_re = re.compile(r"""_\(((?:".*?")|(?:'.*?'))\)""") constant_re = re.compile(r"""_\(((?:".*?")|(?:'.*?'))\)""")
def templatize(src): def templatize(src, origin=None):
""" """
Turns a Django template into something that is understood by xgettext. It Turns a Django template into something that is understood by xgettext. It
does so by translating the Django translation tags into standard gettext does so by translating the Django translation tags into standard gettext
@ -435,7 +435,7 @@ def templatize(src):
plural = [] plural = []
incomment = False incomment = False
comment = [] comment = []
for t in Lexer(src, None).tokenize(): for t in Lexer(src, origin).tokenize():
if incomment: if incomment:
if t.token_type == TOKEN_BLOCK and t.contents == 'endcomment': if t.token_type == TOKEN_BLOCK and t.contents == 'endcomment':
out.write(' # %s' % ''.join(comment)) out.write(' # %s' % ''.join(comment))
@ -465,7 +465,10 @@ def templatize(src):
elif pluralmatch: elif pluralmatch:
inplural = True inplural = True
else: else:
raise SyntaxError("Translation blocks must not include other block tags: %s" % t.contents) filemsg = ''
if origin:
filemsg = 'file %s, ' % origin
raise SyntaxError("Translation blocks must not include other block tags: %s (%sline %d)" % (t.contents, filemsg, t.lineno))
elif t.token_type == TOKEN_VAR: elif t.token_type == TOKEN_VAR:
if inplural: if inplural:
plural.append('%%(%s)s' % t.contents) plural.append('%%(%s)s' % t.contents)

View File

@ -59,6 +59,18 @@ class BasicExtractorTests(ExtractorTests):
self.assertMsgId('I think that 100%% is more that 50%% of anything.', po_contents) self.assertMsgId('I think that 100%% is more that 50%% of anything.', po_contents)
self.assertMsgId('I think that 100%% is more that 50%% of %\(obj\)s.', po_contents) self.assertMsgId('I think that 100%% is more that 50%% of %\(obj\)s.', po_contents)
def test_extraction_error(self):
os.chdir(self.test_dir)
shutil.copyfile('./templates/template_with_error.txt', './templates/template_with_error.html')
self.assertRaises(SyntaxError, management.call_command, 'makemessages', locale=LOCALE, verbosity=0)
try:
management.call_command('makemessages', locale=LOCALE, verbosity=0)
except SyntaxError, e:
self.assertEqual(str(e), 'Translation blocks must not include other block tags: blocktrans (file templates/template_with_error.html, line 3)')
finally:
os.remove('./templates/template_with_error.html')
os.remove('./templates/template_with_error.html.py') # Waiting for #8536 to be fixed
class JavascriptExtractorTests(ExtractorTests): class JavascriptExtractorTests(ExtractorTests):

View File

@ -0,0 +1,3 @@
{% load i18n %}
<p>This template contains an error (no endblocktrans)</p>
<p>{% blocktrans %}This should fail{% blocktrans %}</p>