Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

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
  • Loading branch information...
commit 23f69af45409a29146f215058974898baa7e2207 1 parent 5a7af25
Ramiro Morales authored December 04, 2010
17  django/core/management/commands/makemessages.py
@@ -220,18 +220,15 @@ def make_messages(locale=None, domain='django', verbosity='1', all=False,
220 220
                 os.unlink(os.path.join(dirpath, thefile))
221 221
             elif domain == 'django' and (file_ext == '.py' or file_ext in extensions):
222 222
                 thefile = file
  223
+                orig_file = os.path.join(dirpath, file)
223 224
                 if file_ext in extensions:
224  
-                    src = open(os.path.join(dirpath, file), "rU").read()
  225
+                    src = open(orig_file, "rU").read()
225 226
                     thefile = '%s.py' % file
  227
+                    f = open(os.path.join(dirpath, thefile), "w")
226 228
                     try:
227  
-                        f = open(os.path.join(dirpath, thefile), "w")
228  
-                        try:
229  
-                            f.write(templatize(src))
230  
-                        finally:
231  
-                            f.close()
232  
-                    except SyntaxError, msg:
233  
-                        msg = "%s (file: %s)" % (msg, os.path.join(dirpath, file))
234  
-                        raise SyntaxError(msg)
  229
+                        f.write(templatize(src, orig_file[2:]))
  230
+                    finally:
  231
+                        f.close()
235 232
                 if verbosity > 1:
236 233
                     sys.stdout.write('processing file %s in %s\n' % (file, dirpath))
237 234
                 cmd = (
@@ -250,7 +247,7 @@ def make_messages(locale=None, domain='django', verbosity='1', all=False,
250 247
 
251 248
                 if thefile != file:
252 249
                     old = '#: '+os.path.join(dirpath, thefile)[2:]
253  
-                    new = '#: '+os.path.join(dirpath, file)[2:]
  250
+                    new = '#: '+orig_file[2:]
254 251
                     msgs = msgs.replace(old, new)
255 252
                 if os.path.exists(potfile):
256 253
                     # Strip the header
4  django/template/base.py
@@ -139,6 +139,7 @@ class Token(object):
139 139
     def __init__(self, token_type, contents):
140 140
         # token_type must be TOKEN_TEXT, TOKEN_VAR, TOKEN_BLOCK or TOKEN_COMMENT.
141 141
         self.token_type, self.contents = token_type, contents
  142
+        self.lineno = None
142 143
 
143 144
     def __str__(self):
144 145
         return '<%s token: "%s...">' % \
@@ -164,6 +165,7 @@ class Lexer(object):
164 165
     def __init__(self, template_string, origin):
165 166
         self.template_string = template_string
166 167
         self.origin = origin
  168
+        self.lineno = 1
167 169
 
168 170
     def tokenize(self):
169 171
         "Return a list of tokens from a given template_string."
@@ -193,6 +195,8 @@ def create_token(self, token_string, in_tag):
193 195
                 token = Token(TOKEN_COMMENT, content)
194 196
         else:
195 197
             token = Token(TOKEN_TEXT, token_string)
  198
+        token.lineno = self.lineno
  199
+        self.lineno += token_string.count('\n')
196 200
         return token
197 201
 
198 202
 class Parser(object):
4  django/utils/translation/__init__.py
@@ -104,8 +104,8 @@ def to_locale(language):
104 104
 def get_language_from_request(request):
105 105
     return _trans.get_language_from_request(request)
106 106
 
107  
-def templatize(src):
108  
-    return _trans.templatize(src)
  107
+def templatize(src, origin=None):
  108
+    return _trans.templatize(src, origin)
109 109
 
110 110
 def deactivate_all():
111 111
     return _trans.deactivate_all()
9  django/utils/translation/trans_real.py
@@ -421,7 +421,7 @@ def blankout(src, char):
421 421
 plural_re = re.compile(r"""^\s*plural$""")
422 422
 constant_re = re.compile(r"""_\(((?:".*?")|(?:'.*?'))\)""")
423 423
 
424  
-def templatize(src):
  424
+def templatize(src, origin=None):
425 425
     """
426 426
     Turns a Django template into something that is understood by xgettext. It
427 427
     does so by translating the Django translation tags into standard gettext
@@ -435,7 +435,7 @@ def templatize(src):
435 435
     plural = []
436 436
     incomment = False
437 437
     comment = []
438  
-    for t in Lexer(src, None).tokenize():
  438
+    for t in Lexer(src, origin).tokenize():
439 439
         if incomment:
440 440
             if t.token_type == TOKEN_BLOCK and t.contents == 'endcomment':
441 441
                 out.write(' # %s' % ''.join(comment))
@@ -465,7 +465,10 @@ def templatize(src):
465 465
                 elif pluralmatch:
466 466
                     inplural = True
467 467
                 else:
468  
-                    raise SyntaxError("Translation blocks must not include other block tags: %s" % t.contents)
  468
+                    filemsg = ''
  469
+                    if origin:
  470
+                        filemsg = 'file %s, ' % origin
  471
+                    raise SyntaxError("Translation blocks must not include other block tags: %s (%sline %d)" % (t.contents, filemsg, t.lineno))
469 472
             elif t.token_type == TOKEN_VAR:
470 473
                 if inplural:
471 474
                     plural.append('%%(%s)s' % t.contents)
12  tests/regressiontests/i18n/commands/extraction.py
@@ -59,6 +59,18 @@ def test_templatize(self):
59 59
         self.assertMsgId('I think that 100%% is more that 50%% of anything.', po_contents)
60 60
         self.assertMsgId('I think that 100%% is more that 50%% of %\(obj\)s.', po_contents)
61 61
 
  62
+    def test_extraction_error(self):
  63
+        os.chdir(self.test_dir)
  64
+        shutil.copyfile('./templates/template_with_error.txt', './templates/template_with_error.html')
  65
+        self.assertRaises(SyntaxError, management.call_command, 'makemessages', locale=LOCALE, verbosity=0)
  66
+        try:
  67
+            management.call_command('makemessages', locale=LOCALE, verbosity=0)
  68
+        except SyntaxError, e:
  69
+            self.assertEqual(str(e), 'Translation blocks must not include other block tags: blocktrans (file templates/template_with_error.html, line 3)')
  70
+        finally:
  71
+            os.remove('./templates/template_with_error.html')
  72
+            os.remove('./templates/template_with_error.html.py') # Waiting for #8536 to be fixed
  73
+
62 74
 
63 75
 class JavascriptExtractorTests(ExtractorTests):
64 76
 
0  tests/regressiontests/i18n/commands/locale/dummy
No changes.
3  tests/regressiontests/i18n/commands/templates/template_with_error.txt
... ...
@@ -0,0 +1,3 @@
  1
+{% load i18n %}
  2
+<p>This template contains an error (no endblocktrans)</p>
  3
+<p>{% blocktrans %}This should fail{% blocktrans %}</p>

0 notes on commit 23f69af

Please sign in to comment.
Something went wrong with that request. Please try again.