# HG changeset patch # User fschwarz # Date 1317067261 0 # Node ID 1b801a0cb2cbd6c10aacbdd9ccddde6c0851f24a # Parent 39ff5164e8eaaac98df528653d88190aad89c322 Support for context-aware methods during message extraction (fixes #229, patch by David Rios) diff --git a/ChangeLog b/ChangeLog --- 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/ diff --git a/babel/messages/extract.py b/babel/messages/extract.py --- 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): diff --git a/babel/messages/frontend.py b/babel/messages/frontend.py --- 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 diff --git a/babel/messages/tests/extract.py b/babel/messages/tests/extract.py --- 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()