changeset 569:1b801a0cb2cb trunk

Support for context-aware methods during message extraction (fixes #229, patch by David Rios)
author fschwarz
date Mon, 26 Sep 2011 20:01:01 +0000
parents 39ff5164e8ea
children a661a0360248
files ChangeLog babel/messages/extract.py babel/messages/frontend.py babel/messages/tests/extract.py
diffstat 4 files changed, 54 insertions(+), 24 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog
+++ b/ChangeLog
@@ -34,9 +34,10 @@
  * Only use bankers round algorithm as a tie breaker if there are two nearest
    numbers, round as usual if there is only one nearest number (#267, patch by 
    Martin)
- * Allow disabling cache behaviour in LazyProxy (#208, initial patch Pedro 
+ * Allow disabling cache behaviour in LazyProxy (#208, initial patch from Pedro 
    Algarvio)
-
+ * Support for context-aware methods during message extraction (#229, patch
+   from David Rios)
 
 Version 0.9.6
 http://svn.edgewall.org/repos/babel/tags/0.9.6/
--- a/babel/messages/extract.py
+++ b/babel/messages/extract.py
@@ -41,7 +41,8 @@
     'ungettext': (1, 2),
     'dgettext': (2,),
     'dngettext': (2, 3),
-    'N_': None
+    'N_': None,
+    'pgettext': ((1, 'c'), 2)
 }
 
 DEFAULT_MAPPING = [('**.py', 'python')]
@@ -130,7 +131,8 @@
                      positional arguments, in that order
     :param strip_comment_tags: a flag that if set to `True` causes all comment
                                tags to be removed from the collected comments.
-    :return: an iterator over ``(filename, lineno, funcname, message)`` tuples
+    :return: an iterator over ``(filename, lineno, funcname, message, context)``
+             tuples
     :rtype: ``iterator``
     :see: `pathmatch`
     """
@@ -158,14 +160,14 @@
                             options = odict
                     if callback:
                         callback(filename, method, options)
-                    for lineno, message, comments in \
+                    for lineno, message, comments, context in \
                           extract_from_file(method, filepath,
                                             keywords=keywords,
                                             comment_tags=comment_tags,
                                             options=options,
                                             strip_comment_tags=
                                                 strip_comment_tags):
-                        yield filename, lineno, message, comments
+                        yield filename, lineno, message, comments, context
                     break
 
 
@@ -219,7 +221,7 @@
     >>> from StringIO import StringIO
     >>> for message in extract('python', StringIO(source)):
     ...     print message
-    (3, u'Hello, world!', [])
+    (3, u'Hello, world!', [], None)
 
     :param method: a string specifying the extraction method (.e.g. "python");
                    if this is a simple name, the extraction function will be
@@ -279,11 +281,15 @@
             continue
 
         # Validate the messages against the keyword's specification
+        context = None
         msgs = []
         invalid = False
         # last_index is 1 based like the keyword spec
         last_index = len(messages)
         for index in spec:
+            if isinstance(index, tuple):
+                context = messages[index[0] - 1]
+                continue
             if last_index < index:
                 # Not enough arguments
                 invalid = True
@@ -296,7 +302,12 @@
         if invalid:
             continue
 
-        first_msg_index = spec[0] - 1
+        # keyword spec indexes are 1 based, therefore '-1'
+        if isinstance(spec[0], tuple):
+            # context-aware *gettext method
+            first_msg_index = spec[1] - 1
+        else:
+            first_msg_index = spec[0] - 1
         if not messages[first_msg_index]:
             # An empty string msgid isn't valid, emit a warning
             where = '%s:%i' % (hasattr(fileobj, 'name') and \
@@ -310,7 +321,7 @@
 
         if strip_comment_tags:
             _strip_comment_tags(comments, comment_tags)
-        yield lineno, messages, comments
+        yield lineno, messages, comments, context
 
 
 def extract_nothing(fileobj, keywords, comment_tags, options):
--- a/babel/messages/frontend.py
+++ b/babel/messages/frontend.py
@@ -309,10 +309,10 @@
                                              callback=callback,
                                              strip_comment_tags=
                                                 self.strip_comments)
-                for filename, lineno, message, comments in extracted:
+                for filename, lineno, message, comments, context in extracted:
                     filepath = os.path.normpath(os.path.join(dirname, filename))
                     catalog.add(message, None, [(filepath, lineno)],
-                                auto_comments=comments)
+                                auto_comments=comments, context=context)
 
             log.info('writing PO template file to %s' % self.output_file)
             write_po(outfile, catalog, width=self.width,
@@ -907,10 +907,10 @@
                                              callback=callback,
                                              strip_comment_tags=
                                                 options.strip_comment_tags)
-                for filename, lineno, message, comments in extracted:
+                for filename, lineno, message, comments, context in extracted:
                     filepath = os.path.normpath(os.path.join(dirname, filename))
                     catalog.add(message, None, [(filepath, lineno)],
-                                auto_comments=comments)
+                                auto_comments=comments, context=context)
 
             if options.output not in (None, '-'):
                 self.log.info('writing PO template file to %s' % options.output)
@@ -1181,13 +1181,14 @@
 def parse_keywords(strings=[]):
     """Parse keywords specifications from the given list of strings.
 
-    >>> kw = parse_keywords(['_', 'dgettext:2', 'dngettext:2,3']).items()
+    >>> kw = parse_keywords(['_', 'dgettext:2', 'dngettext:2,3', 'pgettext:1c,2']).items()
     >>> kw.sort()
     >>> for keyword, indices in kw:
     ...     print (keyword, indices)
     ('_', None)
     ('dgettext', (2,))
     ('dngettext', (2, 3))
+    ('pgettext', ((1, 'c'), 2))
     """
     keywords = {}
     for string in strings:
@@ -1197,7 +1198,13 @@
             funcname, indices = string, None
         if funcname not in keywords:
             if indices:
-                indices = tuple([(int(x)) for x in indices.split(',')])
+                inds = []
+                for x in indices.split(','):
+                    if x[-1] == 'c':
+                        inds.append((int(x[:-1]), 'c'))
+                    else:
+                        inds.append(int(x))
+                indices = tuple(inds)
             keywords[funcname] = indices
     return keywords
 
--- a/babel/messages/tests/extract.py
+++ b/babel/messages/tests/extract.py
@@ -384,9 +384,9 @@
             list(extract.extract('javascript', buf, extract.DEFAULT_KEYWORDS,
                                  [], {}))
 
-        self.assertEqual([(1, 'simple', []),
-                          (2, 'simple', []),
-                          (3, ('s', 'p'), [])], messages)
+        self.assertEqual([(1, 'simple', [], None),
+                          (2, 'simple', [], None),
+                          (3, ('s', 'p'), [], None)], messages)
 
     def test_various_calls(self):
         buf = StringIO("""\
@@ -404,9 +404,9 @@
         messages = \
             list(extract.extract('javascript', buf, extract.DEFAULT_KEYWORDS, [],
                                  {}))
-        self.assertEqual([(5, (u'bunny', u'bunnies'), []),
-                          (8, u'Rabbit', []),
-                          (10, (u'Page', u'Pages'), [])], messages)
+        self.assertEqual([(5, (u'bunny', u'bunnies'), [], None),
+                          (8, u'Rabbit', [], None),
+                          (10, (u'Page', u'Pages'), [], None)], messages)
 
     def test_message_with_line_comment(self):
         buf = StringIO("""\
@@ -481,9 +481,9 @@
         messages = \
             list(extract.extract('python', buf, extract.DEFAULT_KEYWORDS, [],
                                  {}))
-        self.assertEqual([(5, (u'bunny', u'bunnies'), []),
-                          (8, u'Rabbit', []),
-                          (10, (u'Page', u'Pages'), [])], messages)
+        self.assertEqual([(5, (u'bunny', u'bunnies'), [], None),
+                          (8, u'Rabbit', [], None),
+                          (10, (u'Page', u'Pages'), [], None)], messages)
 
     def test_invalid_extract_method(self):
         buf = StringIO('')
@@ -520,6 +520,17 @@
         finally:
             sys.stderr = stderr
 
+    def test_warn_if_empty_string_msgid_found_in_context_aware_extraction_method(self):
+        buf = StringIO("\nmsg = pgettext('ctxt', '')\n")
+        stderr = sys.stderr
+        sys.stderr = StringIO()
+        try:
+            messages = extract.extract('python', buf)
+            self.assertEqual([], list(messages))
+            assert 'warning: Empty msgid.' in sys.stderr.getvalue()
+        finally:
+            sys.stderr = stderr
+
 
 def suite():
     suite = unittest.TestSuite()
Copyright (C) 2012-2017 Edgewall Software