Commits

Vinay Sajip  committed 625254b

Merged upstream changes.

  • Participants
  • Parent commits 5e1b0fd

Comments (0)

Files changed (18)

    handlers (#227, reported with initial patch by dfraser)
  * Fix exception if environment contains an invalid locale setting (#200)
  * use cPickle instead of pickle for better performance (#225)
-
+ * 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 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/

File babel/compat.py

 
 long_type = integer_types[-1]
 
+try:
+    from xml.etree import ElementTree
+except ImportError:
+    from elementtree import ElementTree
+
+try:
+    any = any
+except NameError:
+    def any(iterable):
+        return filter(None, list(iterable))
+
+try:
+    import threading
+except ImportError:
+    import dummy_threading as threading
+

File babel/localedata.py

 """
 
 import os
-from babel.compat import pickle, DictMixin, PY3, u
-try:
-    import threading
-except ImportError:
-    import dummy_threading as threading
+from babel.compat import pickle, DictMixin, PY3, u, threading
 
 __all__ = ['exists', 'locale_identifiers', 'load']
 __docformat__ = 'restructuredtext en'

File babel/messages/catalog.py

         def cmp(a, b):
             return ((a > b) - (a < b))
 
-        if isinstance(obj, Message):
-            plural = self.pluralizable
-            obj_plural = obj.pluralizable
-            if plural and obj_plural:
-                return cmp(self.id[0], obj.id[0])
-            elif plural:
-                return cmp(self.id[0], obj.id)
-            elif obj_plural:
-                return cmp(self.id, obj.id[0])
-        return cmp(self.id, obj.id)
+        def values_to_compare():
+            if isinstance(obj, Message):
+                plural = self.pluralizable
+                obj_plural = obj.pluralizable
+                if plural and obj_plural:
+                    return self.id[0], obj.id[0]
+                elif plural:
+                    return self.id[0], obj.id
+                elif obj_plural:
+                    return self.id, obj.id[0]
+            return self.id, obj.id
+        this, other = values_to_compare()
+        return cmp(this, other)
 
     def __gt__(self, other):
         return self.__cmp__(other) > 0

File babel/messages/extract.py

     'ungettext': (1, 2),
     'dgettext': (2,),
     'dngettext': (2, 3),
-    'N_': None
+    'N_': None,
+    'pgettext': ((1, 'c'), 2)
 }
 
 DEFAULT_MAPPING = [('**.py', 'python')]
                      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`
     """
                             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
 
 
     """Extract messages from the given file-like object using the specified
     extraction method.
 
-    This function returns a list of tuples of the form:
+    This function returns tuples of the form:
 
         ``(lineno, message, comments)``
 
     ...     print(message[0])
     ...     print(message[1])
     ...     print(message[2])
+    ...     print(message[3])
     3
     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
     :param options: a dictionary of additional options (optional)
     :param strip_comment_tags: a flag that if set to `True` causes all comment
                                tags to be removed from the collected comments.
-    :return: the list of extracted messages
-    :rtype: `list`
+    :return: an iterator over ``(lineno, message, comments)`` tuples
+    :rtype: `iterator`
     :raise ValueError: if the extraction method is not registered
     """
     func = None
             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
         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 \
 
         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):

File babel/messages/frontend.py

                                              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,
         infile = open(self.input_file, 'r')
         try:
             # Although reading from the catalog template, read_po must be fed
-            # the locale in order to correcly calculate plurals
+            # the locale in order to correctly calculate plurals
             catalog = read_po(infile, locale=self.locale)
         finally:
             infile.close()
                                              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)
         infile = open(options.input_file, 'r')
         try:
             # Although reading from the catalog template, read_po must be fed
-            # the locale in order to correcly calculate plurals
+            # the locale in order to correctly calculate plurals
             catalog = read_po(infile, locale=options.locale)
         finally:
             infile.close()
 def parse_keywords(strings=[]):
     """Parse keywords specifications from the given list of strings.
 
-    >>> kw = sorted(parse_keywords(['_', 'dgettext:2', 'dngettext:2,3']).items())
+    >>> kw = sorted(parse_keywords(['_', 'dgettext:2', 'dngettext:2,3', 'pgettext:1c,2']).items())
     >>> for keyword, indices in sorted(kw):
     ...     print((keyword, indices))
     ('_', None)
     ('dgettext', (2,))
     ('dngettext', (2, 3))
+    ('pgettext', ((1, 'c'), 2))
     """
     keywords = {}
     for string in strings:
             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
 

File babel/messages/pofile.py

     if width and width > 0:
         prefixlen = len(prefix)
         lines = []
-        for idx, line in enumerate(string.splitlines(True)):
+        for line in string.splitlines(True):
             if len(escape(line)) + prefixlen > width:
                 chunks = WORD_SEP.split(line)
                 chunks.reverse()

File babel/messages/tests/catalog.py

 
 def suite():
     suite = unittest.TestSuite()
-    if hasattr(doctest, 'ELLIPSIS'):
-        suite.addTest(doctest.DocTestSuite(catalog, optionflags=doctest.ELLIPSIS))
-    else:
-        suite.addTest(doctest.DocTestSuite(catalog))
+    suite.addTest(doctest.DocTestSuite(catalog, optionflags=doctest.ELLIPSIS))
     suite.addTest(unittest.makeSuite(MessageTestCase))
     suite.addTest(unittest.makeSuite(CatalogTestCase))
     return suite

File babel/messages/tests/extract.py

             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("""\
         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("""\
         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('')
         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()

File babel/messages/tests/frontend.py

         self.cmd.initialize_options()
 
     def tearDown(self):
-        pot_file = os.path.join(self.datadir, 'project', 'i18n', 'temp.pot')
+        pot_file = self._pot_file()
         if os.path.isfile(pot_file):
             os.unlink(pot_file)
 
         os.chdir(self.olddir)
 
+    def  _i18n_dir(self):
+        return os.path.join(self.datadir, 'project', 'i18n')
+
+    def _pot_file(self):
+        return os.path.join(self._i18n_dir(), 'temp.pot')
+
+    def assert_pot_file_exists(self):
+        assert os.path.isfile(self._pot_file())
+
     def test_neither_default_nor_custom_keywords(self):
         self.cmd.output_file = 'dummy'
         self.cmd.no_default_keywords = True
         self.cmd.finalize_options()
         self.cmd.run()
 
-        pot_file = os.path.join(self.datadir, 'project', 'i18n', 'temp.pot')
-        assert os.path.isfile(pot_file)
+        self.assert_pot_file_exists()
 
         self.assertEqual(
 r"""# Translations template for TestProject.
        'year': time.strftime('%Y'),
        'date': format_datetime(datetime.now(LOCALTZ), 'yyyy-MM-dd HH:mmZ',
                                tzinfo=LOCALTZ, locale='en')},
-        open(pot_file, 'U').read())
+        open(self._pot_file(), 'U').read())
 
     def test_extraction_with_mapping_file(self):
         self.cmd.copyright_holder = 'FooBar, Inc.'
         self.cmd.finalize_options()
         self.cmd.run()
 
-        pot_file = os.path.join(self.datadir, 'project', 'i18n', 'temp.pot')
-        assert os.path.isfile(pot_file)
+        self.assert_pot_file_exists()
 
         self.assertEqual(
 r"""# Translations template for TestProject.
        'year': time.strftime('%Y'),
        'date': format_datetime(datetime.now(LOCALTZ), 'yyyy-MM-dd HH:mmZ',
                                tzinfo=LOCALTZ, locale='en')},
-        open(pot_file, 'U').read())
+        open(self._pot_file(), 'U').read())
 
     def test_extraction_with_mapping_dict(self):
         self.dist.message_extractors = {
         self.cmd.finalize_options()
         self.cmd.run()
 
-        pot_file = os.path.join(self.datadir, 'project', 'i18n', 'temp.pot')
-        assert os.path.isfile(pot_file)
+        self.assert_pot_file_exists()
 
         self.assertEqual(
 r"""# Translations template for TestProject.
        'year': time.strftime('%Y'),
        'date': format_datetime(datetime.now(LOCALTZ), 'yyyy-MM-dd HH:mmZ',
                                tzinfo=LOCALTZ, locale='en')},
-        open(pot_file, 'U').read())
+        open(self._pot_file(), 'U').read())
 
 
 class InitCatalogTestCase(unittest.TestCase):
 
     def tearDown(self):
         for dirname in ['en_US', 'ja_JP', 'lv_LV']:
-            locale_dir = os.path.join(self.datadir, 'project', 'i18n', dirname)
+            locale_dir = os.path.join(self._i18n_dir(), dirname)
             if os.path.isdir(locale_dir):
                 shutil.rmtree(locale_dir)
 
         os.chdir(self.olddir)
 
+    def  _i18n_dir(self):
+        return os.path.join(self.datadir, 'project', 'i18n')
+
+    def _po_file(self, locale):
+        return os.path.join(self._i18n_dir(), locale, 'LC_MESSAGES', 
+                            'messages.po')
+
     def test_no_input_file(self):
         self.cmd.locale = 'en_US'
         self.cmd.output_file = 'dummy'
         self.cmd.finalize_options()
         self.cmd.run()
 
-        po_file = os.path.join(self.datadir, 'project', 'i18n', 'en_US',
-                               'LC_MESSAGES', 'messages.po')
+        po_file = self._po_file('en_US')
         assert os.path.isfile(po_file)
 
         self.assertEqual(
         self.cmd.finalize_options()
         self.cmd.run()
 
-        po_file = os.path.join(self.datadir, 'project', 'i18n', 'en_US',
-                               'LC_MESSAGES', 'messages.po')
+        po_file = self._po_file('en_US')
         assert os.path.isfile(po_file)
 
         self.assertEqual(
         self.cmd.finalize_options()
         self.cmd.run()
 
-        po_file = os.path.join(self.datadir, 'project', 'i18n', 'lv_LV',
-                               'LC_MESSAGES', 'messages.po')
+        po_file = self._po_file('lv_LV')
         assert os.path.isfile(po_file)
 
         self.assertEqual(
         self.cmd.finalize_options()
         self.cmd.run()
 
-        po_file = os.path.join(self.datadir, 'project', 'i18n', 'ja_JP',
-                               'LC_MESSAGES', 'messages.po')
+        po_file = self._po_file('ja_JP')
         assert os.path.isfile(po_file)
 
         self.assertEqual(
         sys.stdout = self.orig_stdout
         sys.stderr = self.orig_stderr
         for dirname in ['lv_LV', 'ja_JP']: 
-            locale_dir = os.path.join(self.datadir, 'project', 'i18n', dirname)
+            locale_dir = os.path.join(self._i18n_dir(), dirname)
             if os.path.isdir(locale_dir):
                 shutil.rmtree(locale_dir)
         self._remove_log_handlers()
   update   update existing message catalogs from a pot file
 """, sys.stdout.getvalue().lower())
 
+    def _pot_file(self):
+        return os.path.join(self._i18n_dir(), 'temp.pot')
+
+    def assert_pot_file_exists(self):
+        assert os.path.isfile(self._pot_file())
+
     def test_extract_with_default_mapping(self):
-        pot_file = os.path.join(self.datadir, 'project', 'i18n', 'temp.pot')
+        pot_file = self._pot_file()
         self.cli.run(sys.argv + ['extract',
             '--copyright-holder', 'FooBar, Inc.',
             '--project', 'TestProject', '--version', '0.1',
             '--msgid-bugs-address', 'bugs.address@email.tld',
             '-c', 'TRANSLATOR', '-c', 'TRANSLATORS:',
             '-o', pot_file, 'project'])
-        assert os.path.isfile(pot_file)
+        self.assert_pot_file_exists()
         self.assertEqual(
 r"""# Translations template for TestProject.
 # Copyright (C) %(year)s FooBar, Inc.
        open(pot_file, 'U').read())
 
     def test_extract_with_mapping_file(self):
-        pot_file = os.path.join(self.datadir, 'project', 'i18n', 'temp.pot')
+        pot_file = self._pot_file()
         self.cli.run(sys.argv + ['extract',
             '--copyright-holder', 'FooBar, Inc.',
             '--project', 'TestProject', '--version', '0.1',
             '--mapping', os.path.join(self.datadir, 'mapping.cfg'),
             '-c', 'TRANSLATOR', '-c', 'TRANSLATORS:',
             '-o', pot_file, 'project'])
-        assert os.path.isfile(pot_file)
+        self.assert_pot_file_exists()
         self.assertEqual(
 r"""# Translations template for TestProject.
 # Copyright (C) %(year)s FooBar, Inc.
        open(pot_file, 'U').read())
 
     def test_init_with_output_dir(self):
-        po_file = os.path.join(self.datadir, 'project', 'i18n', 'en_US',
-                               'LC_MESSAGES', 'messages.po')
+        po_file = self._po_file('en_US')
         self.cli.run(sys.argv + ['init',
             '--locale', 'en_US',
-            '-d', os.path.join(self.datadir, 'project', 'i18n'),
-            '-i', os.path.join(self.datadir, 'project', 'i18n', 'messages.pot')])
+            '-d', os.path.join(self._i18n_dir()),
+            '-i', os.path.join(self._i18n_dir(), 'messages.pot')])
         assert os.path.isfile(po_file)
         self.assertEqual(
 r"""# English (United States) translations for TestProject.
        'date': format_datetime(datetime.now(LOCALTZ), 'yyyy-MM-dd HH:mmZ',
                                tzinfo=LOCALTZ, locale='en')},
        open(po_file, 'U').read())
-            
+
+    def  _i18n_dir(self):
+        return os.path.join(self.datadir, 'project', 'i18n')
+
     def test_init_singular_plural_forms(self):
-        po_file = os.path.join(self.datadir, 'project', 'i18n', 'ja_JP',
-                               'LC_MESSAGES', 'messages.po')
+        po_file = self._po_file('ja_JP')
         self.cli.run(sys.argv + ['init',
             '--locale', 'ja_JP',
-            '-d', os.path.join(self.datadir, 'project', 'i18n'),
-            '-i', os.path.join(self.datadir, 'project', 'i18n',
-                               'messages.pot')])
+            '-d', os.path.join(self._i18n_dir()),
+            '-i', os.path.join(self._i18n_dir(), 'messages.pot')])
         assert os.path.isfile(po_file)
         self.assertEqual(
 r"""# Japanese (Japan) translations for TestProject.
        open(po_file, 'U').read())
             
     def test_init_more_than_2_plural_forms(self):
-        po_file = os.path.join(self.datadir, 'project', 'i18n', 'lv_LV',
-                               'LC_MESSAGES', 'messages.po')
+        po_file = self._po_file('lv_LV')
         self.cli.run(sys.argv + ['init',
             '--locale', 'lv_LV',
-            '-d', os.path.join(self.datadir, 'project', 'i18n'),
-            '-i', os.path.join(self.datadir, 'project', 'i18n',
-                               'messages.pot')])
+            '-d', self._i18n_dir(),
+            '-i', os.path.join(self._i18n_dir(), 'messages.pot')])
         assert os.path.isfile(po_file)
         self.assertEqual(
 r"""# Latvian (Latvia) translations for TestProject.
        open(po_file, 'U').read())
 
     def test_compile_catalog(self):
-        po_file = os.path.join(self.datadir, 'project', 'i18n', 'de_DE',
-                               'LC_MESSAGES', 'messages.po')
+        po_file = self._po_file('de_DE')
         mo_file = po_file.replace('.po', '.mo')
         self.cli.run(sys.argv + ['compile',
             '--locale', 'de_DE',
-            '-d', os.path.join(self.datadir, 'project', 'i18n')])
+            '-d', self._i18n_dir()])
         assert not os.path.isfile(mo_file), 'Expected no file at %r' % mo_file
         self.assertEqual("""\
 catalog %r is marked as fuzzy, skipping
 """ % (po_file), sys.stderr.getvalue())
 
     def test_compile_fuzzy_catalog(self):
-        po_file = os.path.join(self.datadir, 'project', 'i18n', 'de_DE',
-                               'LC_MESSAGES', 'messages.po')
+        po_file = self._po_file('de_DE')
         mo_file = po_file.replace('.po', '.mo')
         try:
             self.cli.run(sys.argv + ['compile',
                 '--locale', 'de_DE', '--use-fuzzy',
-                '-d', os.path.join(self.datadir, 'project', 'i18n')])
+                '-d', self._i18n_dir()])
             assert os.path.isfile(mo_file)
             self.assertEqual("""\
 compiling catalog %r to %r
             if os.path.isfile(mo_file):
                 os.unlink(mo_file)
 
+    def _po_file(self, locale):
+        return os.path.join(self._i18n_dir(), locale, 'LC_MESSAGES', 
+                            'messages.po')
+
     def test_compile_catalog_with_more_than_2_plural_forms(self):
-        po_file = os.path.join(self.datadir, 'project', 'i18n', 'ru_RU',
-                               'LC_MESSAGES', 'messages.po')
+        po_file = self._po_file('ru_RU')
         mo_file = po_file.replace('.po', '.mo')
         try:
             self.cli.run(sys.argv + ['compile',
                 '--locale', 'ru_RU', '--use-fuzzy',
-                '-d', os.path.join(self.datadir, 'project', 'i18n')])
+                '-d', self._i18n_dir()])
             assert os.path.isfile(mo_file)
             self.assertEqual("""\
 compiling catalog %r to %r

File babel/numbers.py

 # TODO:
 #  Padding and rounding increments in pattern:
 #  - http://www.unicode.org/reports/tr35/ (Appendix G.6)
+from decimal import Decimal
 import math
 import re
-try:
-    from decimal import Decimal
-    have_decimal = True
-except ImportError:
-    have_decimal = False
 
-from babel.compat import u, b, long_type, PY3
+from babel.compat import u, b, long_type, PY3, xrange
 from babel.core import default_locale, Locale
 
 __all__ = ['format_number', 'format_decimal', 'format_currency',
 
 def split_number(value):
     """Convert a number into a (intasstring, fractionasstring) tuple"""
-    if have_decimal and isinstance(value, Decimal):
+    if isinstance(value, Decimal):
         text = str(value)
     else:
         text = ('%.9f' % value).rstrip('0')
         add = 1
     elif digits[i] == '5' and digits[i-1] in '13579':
         add = 1
+    elif digits[i] == '5':     # previous digit is even
+        # We round up unless all following digits are zero.
+        for j in xrange(i + 1, len(digits)):
+            if digits[j] != '0':
+                add = 1
+                break
+
     scale = 10**ndigits
-    if have_decimal and isinstance(value, Decimal):
+    if isinstance(value, Decimal):
         return Decimal(int(value * scale + add)) / scale * sign
     else:
         return float(int(value * scale + add)) / scale * sign
     else:
         integer = number
         fraction = ''
-    min_frac = max_frac = 0
 
     def parse_precision(p):
         """Calculate the min and max allowed digits"""
             # Exponent grouping
             elif self.int_prec[1]:
                 exp = int(exp) // self.int_prec[1] * self.int_prec[1]
-            if not have_decimal or not isinstance(value, Decimal):
+            if not isinstance(value, Decimal):
                 value = float(value)
             if exp < 0:
                 value = value * 10**(-exp)

File babel/support.py

     Hello, universe!
     Hello, world!
     """
-    __slots__ = ['_func', '_args', '_kwargs', '_value']
+    __slots__ = ['_func', '_args', '_kwargs', '_value', '_is_cache_enabled']
 
     def __init__(self, func, *args, **kwargs):
+        is_cache_enabled = kwargs.pop('enable_cache', True)
         # Avoid triggering our own __setattr__ implementation
         object.__setattr__(self, '_func', func)
         object.__setattr__(self, '_args', args)
         object.__setattr__(self, '_kwargs', kwargs)
+        object.__setattr__(self, '_is_cache_enabled', is_cache_enabled)
         object.__setattr__(self, '_value', None)
 
     def value(self):
         if self._value is None:
             value = self._func(*self._args, **self._kwargs)
+            if not self._is_cache_enabled:
+                return value
             object.__setattr__(self, '_value', value)
         return self._value
     value = property(value)
         """ 
         return self._domains.get(domain, self).lgettext(message)
     
-    def dugettext(self, domain, message):
+    def udgettext(self, domain, message):
         """Like ``ugettext()``, but look the message up in the specified
         domain.
         """
         return self._domains.get(domain, self).ugettext(message)
+    # backward compatibility with 0.9
+    dugettext = udgettext
     
     def dngettext(self, domain, singular, plural, num):
         """Like ``ngettext()``, but look the message up in the specified
         """
         return self._domains.get(domain, self).lngettext(singular, plural, num)
     
-    def dungettext(self, domain, singular, plural, num):
+    def udngettext(self, domain, singular, plural, num):
         """Like ``ungettext()`` but look the message up in the specified
         domain.
         """
         return self._domains.get(domain, self).ungettext(singular, plural, num)
+    # backward compatibility with 0.9
+    dungettext  = udngettext
 
     # Most of the downwards code, until it get's included in stdlib, from:
     #    http://bugs.python.org/file10036/gettext-pgettext.patch
         """
         return self._domains.get(domain, self).pgettext(context, message)
     
-    def dupgettext(self, domain, context, message):
+    def udpgettext(self, domain, context, message):
         """Like `upgettext()`, but look the message up in the specified
         `domain`.
         """
         return self._domains.get(domain, self).upgettext(context, message)
+    # backward compatibility with 0.9
+    dupgettext = udpgettext
 
     def ldpgettext(self, domain, context, message):
         """Equivalent to ``dpgettext()``, but the translation is returned in the
         return self._domains.get(domain, self).npgettext(context, singular,
                                                          plural, num)
         
-    def dunpgettext(self, domain, context, singular, plural, num):
+    def udnpgettext(self, domain, context, singular, plural, num):
         """Like ``unpgettext``, but look the message up in the specified
         `domain`.
         """
         return self._domains.get(domain, self).unpgettext(context, singular,
                                                           plural, num)
+    # backward compatibility with 0.9
+    dunpgettext = udnpgettext
 
     def ldnpgettext(self, domain, context, singular, plural, num):
         """Equivalent to ``dnpgettext()``, but the translation is returned in

File babel/tests/numbers.py

 # individuals. For the exact contribution history, see the revision
 # history and logs, available at http://babel.edgewall.org/log/.
 
-try:
-    from decimal import Decimal
-    have_decimal = True
-except ImportError:
-    have_decimal = False
-
+from decimal import Decimal
 import doctest
 import unittest
 
         self.assertEqual(numbers.format_decimal(0.1, '@@', locale='en_US'), 
                          '0.10')
 
-    if have_decimal:
-        def test_decimals(self):
-            """Test significant digits patterns"""
-            self.assertEqual(numbers.format_decimal(Decimal('1.2345'), 
-                                                    '#.00', locale='en_US'), 
-                             '1.23')
-            self.assertEqual(numbers.format_decimal(Decimal('1.2345000'), 
-                                                    '#.00', locale='en_US'), 
-                             '1.23')
-            self.assertEqual(numbers.format_decimal(Decimal('1.2345000'), 
-                                                    '@@', locale='en_US'), 
-                             '1.2')
-            self.assertEqual(numbers.format_decimal(Decimal('12345678901234567890.12345'), 
-                                                    '#.00', locale='en_US'), 
-                             '12345678901234567890.12')
+    def test_decimals(self):
+        """Test significant digits patterns"""
+        self.assertEqual(numbers.format_decimal(Decimal('1.2345'), 
+                                                '#.00', locale='en_US'), 
+                         '1.23')
+        self.assertEqual(numbers.format_decimal(Decimal('1.2345000'), 
+                                                '#.00', locale='en_US'), 
+                         '1.23')
+        self.assertEqual(numbers.format_decimal(Decimal('1.2345000'), 
+                                                '@@', locale='en_US'), 
+                         '1.2')
+        self.assertEqual(numbers.format_decimal(Decimal('12345678901234567890.12345'), 
+                                                '#.00', locale='en_US'), 
+                         '12345678901234567890.12')
 
     def test_scientific_notation(self):
         fmt = numbers.format_scientific(0.1, '#E0', locale='en_US')
         self.assertEqual(fmt, '1.23E02 m/s')
         fmt = numbers.format_scientific(0.012345, '#.##E00 m/s', locale='en_US')
         self.assertEqual(fmt, '1.23E-02 m/s')
-        if have_decimal:
-            fmt = numbers.format_scientific(Decimal('12345'), '#.##E+00 m/s', 
-            locale='en_US')
-            self.assertEqual(fmt, '1.23E+04 m/s')
+        fmt = numbers.format_scientific(Decimal('12345'), '#.##E+00 m/s', 
+        locale='en_US')
+        self.assertEqual(fmt, '1.23E+04 m/s')
         # 0 (see ticket #99)
         fmt = numbers.format_scientific(0, '#E0', locale='en_US')
         self.assertEqual(fmt, '0E0')
         self.assertRaises(numbers.NumberFormatError,
                           numbers.parse_decimal, '2,109,998', locale='de')
 
+class BankersRoundTestCase(unittest.TestCase):
+    def test_round_to_nearest_integer(self):
+        self.assertEqual(1, numbers.bankersround(Decimal('0.5001')))
+    
+    def test_round_to_even_for_two_nearest_integers(self):
+        self.assertEqual(0, numbers.bankersround(Decimal('0.5')))
+        self.assertEqual(2, numbers.bankersround(Decimal('1.5')))
+        self.assertEqual(-2, numbers.bankersround(Decimal('-2.5')))
+
+        self.assertEqual(0, numbers.bankersround(Decimal('0.05'), ndigits=1))
+        self.assertEqual(Decimal('0.2'), numbers.bankersround(Decimal('0.15'), ndigits=1))
+
 
 def suite():
     suite = unittest.TestSuite()
     suite.addTest(doctest.DocTestSuite(numbers))
     suite.addTest(unittest.makeSuite(FormatDecimalTestCase))
     suite.addTest(unittest.makeSuite(FormatNumberTestCase))
+    suite.addTest(unittest.makeSuite(BankersRoundTestCase))
     return suite
 
 if __name__ == '__main__':

File babel/tests/support.py

             b('VohsCTXD1'), self.translations.ldnpgettext('messages1', 'foo', 'foo1',
                                                        'foos1', 2))
 
+
+class LazyProxyTestCase(unittest.TestCase):
+    def test_proxy_caches_result_of_function_call(self):
+        self.counter = 0
+        def add_one():
+            self.counter += 1
+            return self.counter
+        proxy = support.LazyProxy(add_one)
+        self.assertEqual(1, proxy.value)
+        self.assertEqual(1, proxy.value)
+    
+    def test_can_disable_proxy_cache(self):
+        self.counter = 0
+        def add_one():
+            self.counter += 1
+            return self.counter
+        proxy = support.LazyProxy(add_one, enable_cache=False)
+        self.assertEqual(1, proxy.value)
+        self.assertEqual(2, proxy.value)
+
+
 def suite():
     suite = unittest.TestSuite()
     suite.addTest(doctest.DocTestSuite(support))
     suite.addTest(unittest.makeSuite(TranslationsTestCase, 'test'))
+    suite.addTest(unittest.makeSuite(LazyProxyTestCase, 'test'))
     return suite
 
 if __name__ == '__main__':

File contrib/babel.js

      */
     load: function(catalog) {
       if (catalog.messages)
-        this.update(catalog.messages)
+        this.update(catalog.messages);
       if (catalog.plural_expr)
         this.setPluralExpr(catalog.plural_expr);
       if (catalog.locale)

File scripts/dump_data.py

 
 from optparse import OptionParser
 from pprint import pprint
-import sys
 
 from babel.localedata import load, LocaleDataDict
 

File scripts/import_cldr.py

 import os
 import re
 import sys
-try:
-    from xml.etree.ElementTree import parse
-except ImportError:
-    from elementtree.ElementTree import parse
 
 # Make sure we're using Babel source, and not some previously installed version
 sys.path.insert(0, os.path.join(os.path.dirname(sys.argv[0]), '..'))
 
 from babel import dates, numbers
-from babel.compat import pickle, u, text_type
+from babel.compat import pickle, u, text_type, any, ElementTree
 from babel.plural import PluralRule
 from babel.localedata import Alias
 
+parse = ElementTree.parse
 weekdays = {'mon': 0, 'tue': 1, 'wed': 2, 'thu': 3, 'fri': 4, 'sat': 5,
             'sun': 6}
 
-try:
-    any
-except NameError:
-    def any(iterable):
-        return [_f for _f in iterable if _f]
-
 
 def _text(elem):
     buf = [elem.text or '']
 # individuals. For the exact contribution history, see the revision
 # history and logs, available at http://babel.edgewall.org/log/.
 
-from distutils.cmd import Command
-import doctest
-from glob import glob
 import os
 try:
     from setuptools import setup