Repository: python-babel/babel
Pull request #456 (2016-11-23 10:39:55)

Upgrade to CLDR 31
Yay, new CLDR!

sils: @akx I can't review this right now, if you think it's good it's probably best to just merge this or find someone else :/ super stressed
akx: @sils Hey, no worries, man :) 
codecov-io: # [Codecov](https://codecov.io/gh/python-babel/babel/pull/456?src=pr&el=h1) Report
> Merging [#456](https://codecov.io/gh/python-babel/babel/pull/456?src=pr&el=desc) into [master](https://codecov.io/gh/python-babel/babel/commit/e6a926a78d7286b84ff482ed1f61131518532190?src=pr&el=desc) will **decrease** coverage by `0.57%`.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/python-babel/babel/pull/456/graphs/tree.svg?width=650&src=pr&token=lEYaWCjCnu&height=150)](https://codecov.io/gh/python-babel/babel/pull/456?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #456      +/-   ##
==========================================
- Coverage   90.18%   89.61%   -0.58%     
==========================================
  Files          24       24              
  Lines        4015     4015              
==========================================
- Hits         3621     3598      -23     
- Misses        394      417      +23
```


| [Impacted Files](https://codecov.io/gh/python-babel/babel/pull/456?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [babel/units.py](https://codecov.io/gh/python-babel/babel/pull/456?src=pr&el=tree#diff-YmFiZWwvdW5pdHMucHk=) | `100% <ø> (ø)` | :arrow_up: |
| [babel/core.py](https://codecov.io/gh/python-babel/babel/pull/456?src=pr&el=tree#diff-YmFiZWwvY29yZS5weQ==) | `95.51% <ø> (ø)` | :arrow_up: |
| [babel/numbers.py](https://codecov.io/gh/python-babel/babel/pull/456?src=pr&el=tree#diff-YmFiZWwvbnVtYmVycy5weQ==) | `97.86% <100%> (ø)` | :arrow_up: |
| [babel/localtime/\_win32.py](https://codecov.io/gh/python-babel/babel/pull/456?src=pr&el=tree#diff-YmFiZWwvbG9jYWx0aW1lL193aW4zMi5weQ==) | `25.92% <0%> (-37.04%)` | :arrow_down: |
| [babel/localtime/\_\_init\_\_.py](https://codecov.io/gh/python-babel/babel/pull/456?src=pr&el=tree#diff-YmFiZWwvbG9jYWx0aW1lL19faW5pdF9fLnB5) | `62.5% <0%> (-2.5%)` | :arrow_down: |
| [babel/messages/frontend.py](https://codecov.io/gh/python-babel/babel/pull/456?src=pr&el=tree#diff-YmFiZWwvbWVzc2FnZXMvZnJvbnRlbmQucHk=) | `85.41% <0%> (-0.85%)` | :arrow_down: |
| [babel/dates.py](https://codecov.io/gh/python-babel/babel/pull/456?src=pr&el=tree#diff-YmFiZWwvZGF0ZXMucHk=) | `91.53% <0%> (+0.29%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/python-babel/babel/pull/456?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/python-babel/babel/pull/456?src=pr&el=footer). Last update [e6a926a...025c711](https://codecov.io/gh/python-babel/babel/pull/456?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

bdeeney: +1  We have a project that currently uses babel and would immediately benefit from an update to the most recent CLDR.

@akx / @jtwang - are there any obstacles to merging and releasing this PR?  If there is anything I can do to help with testing, etc., please let me know.  Thanks!

Patch:
diff --git a/babel/core.py b/babel/core.py
index c8b1802..5a9091f 100644
--- a/babel/core.py
+++ b/babel/core.py
@@ -39,7 +39,7 @@ def get_global(key):
     information independent of individual locales.
 
     >>> get_global('zone_aliases')['UTC']
-    u'Etc/GMT'
+    u'Etc/UTC'
     >>> get_global('zone_territories')['Europe/Berlin']
     u'DE'
 
diff --git a/babel/numbers.py b/babel/numbers.py
index 73ff816..cbd1474 100644
--- a/babel/numbers.py
+++ b/babel/numbers.py
@@ -611,7 +611,7 @@ PREFIX_END = r'[^0-9@#.,]'
 NUMBER_TOKEN = r'[0-9@#.,E+]'
 
 PREFIX_PATTERN = r"(?P<prefix>(?:'[^']*'|%s)*)" % PREFIX_END
-NUMBER_PATTERN = r"(?P<number>%s+)" % NUMBER_TOKEN
+NUMBER_PATTERN = r"(?P<number>%s*)" % NUMBER_TOKEN
 SUFFIX_PATTERN = r"(?P<suffix>.*)"
 
 number_re = re.compile(r"%s%s%s" % (PREFIX_PATTERN, NUMBER_PATTERN,
diff --git a/babel/units.py b/babel/units.py
index 1ea5b17..4260b3d 100644
--- a/babel/units.py
+++ b/babel/units.py
@@ -201,7 +201,7 @@ def format_compound_unit(
     '150 kilometer per timme'
 
     >>> format_compound_unit(150, "kilowatt", denominator_unit="year", locale="fi")
-    '150 kilowattia vuodessa'
+    '150 kilowattia / vuosi'
 
     >>> format_compound_unit(32.5, "ton", 15, denominator_unit="hour", locale="en")
     '32.5 tons per 15 hours'
diff --git a/scripts/download_import_cldr.py b/scripts/download_import_cldr.py
index 4cc6a4b..61b2773 100755
--- a/scripts/download_import_cldr.py
+++ b/scripts/download_import_cldr.py
@@ -13,9 +13,9 @@ except ImportError:
     from urllib import urlretrieve
 
 
-URL = 'http://unicode.org/Public/cldr/29/core.zip'
-FILENAME = 'core-29.zip'
-FILESUM = '44d117e6e591a8f9655602ff0abdee105df3cabe'
+URL = 'http://www.unicode.org/Public/cldr/31.0.1/core.zip'
+FILENAME = 'core-31.0.1.zip'
+FILESUM = '01ade6c2d1f358e63c2ab6e2861d4caa7114ff45'
 BLKSIZE = 131072
 
 
@@ -82,7 +82,7 @@ def main():
             os.remove(zip_path)
         urlretrieve(URL, zip_path, reporthook)
         changed = True
-        print
+        print()
     common_path = os.path.join(cldr_path, 'common')
 
     if changed or not os.path.isdir(common_path):
diff --git a/scripts/import_cldr.py b/scripts/import_cldr.py
index 7b9e773..94fe85e 100755
--- a/scripts/import_cldr.py
+++ b/scripts/import_cldr.py
@@ -708,14 +708,30 @@ def parse_number_symbols(data, tree):
 def parse_decimal_formats(data, tree):
     decimal_formats = data.setdefault('decimal_formats', {})
     for elem in tree.findall('.//decimalFormats/decimalFormatLength'):
-        type = elem.attrib.get('type')
-        if _should_skip_elem(elem, type, decimal_formats):
+        length_type = elem.attrib.get('type')
+        if _should_skip_elem(elem, length_type, decimal_formats):
             continue
         if elem.findall('./alias'):
             # TODO map the alias to its target
             continue
-        pattern = text_type(elem.findtext('./decimalFormat/pattern'))
-        decimal_formats[type] = numbers.parse_pattern(pattern)
+        for pattern_el in elem.findall('./decimalFormat/pattern'):
+            pattern_type = pattern_el.attrib.get('type')
+            pattern = numbers.parse_pattern(text_type(pattern_el.text))
+            if pattern_type:
+                # This is a compact decimal format, see:
+                # http://www.unicode.org/reports/tr35/tr35-45/tr35-numbers.html#Compact_Number_Formats
+
+                # These are mapped into a `compact_decimal_formats` dictionary
+                # with the format {length: {count: {multiplier: pattern}}}.
+
+                # TODO: Add support for formatting them.
+                compact_decimal_formats = data.setdefault('compact_decimal_formats', {})
+                length_map = compact_decimal_formats.setdefault(length_type, {})
+                length_count_map = length_map.setdefault(pattern_el.attrib['count'], {})
+                length_count_map[pattern_type] = pattern
+            else:
+                # Regular decimal format.
+                decimal_formats[length_type] = pattern
 
 
 def parse_scientific_formats(data, tree):


Test patch:
diff --git a/tests/test_core.py b/tests/test_core.py
index e3d8faf..4f985ca 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -58,7 +58,8 @@ def test_ignore_invalid_locales_in_lc_ctype(os_environ):
 
 
 def test_get_global():
-    assert core.get_global('zone_aliases')['UTC'] == 'Etc/GMT'
+    assert core.get_global('zone_aliases')['GMT'] == 'Etc/GMT'
+    assert core.get_global('zone_aliases')['UTC'] == 'Etc/UTC'
     assert core.get_global('zone_territories')['Europe/Berlin'] == 'DE'
 
 
diff --git a/tests/test_date_intervals.py b/tests/test_date_intervals.py
index e5a797a..2e5f0d6 100644
--- a/tests/test_date_intervals.py
+++ b/tests/test_date_intervals.py
@@ -13,7 +13,7 @@ TEST_DATE = TEST_DT.date()
 
 
 def test_format_interval_same_instant_1():
-    assert dates.format_interval(TEST_DT, TEST_DT, "yMMMd", fuzzy=False, locale="fi") == "8. tammikuuta 2016"
+    assert dates.format_interval(TEST_DT, TEST_DT, "yMMMd", fuzzy=False, locale="fi") == "8. tammik. 2016"
 
 
 def test_format_interval_same_instant_2():
diff --git a/tests/test_dates.py b/tests/test_dates.py
index f74cd39..9a01d98 100644
--- a/tests/test_dates.py
+++ b/tests/test_dates.py
@@ -292,7 +292,7 @@ class FormatDatetimeTestCase(unittest.TestCase):
         d = datetime(2012, 4, 1, 15, 30, 29, tzinfo=timezone('UTC'))
         epoch = float(calendar.timegm(d.timetuple()))
         formatted_string = dates.format_datetime(epoch, format='long', locale='en_US')
-        self.assertEqual(u'April 1, 2012 at 3:30:29 PM +0000', formatted_string)
+        self.assertEqual(u'April 1, 2012 at 3:30:29 PM UTC', formatted_string)
 
     def test_timezone_formats(self):
         dt = datetime(2016, 1, 13, 7, 8, 35)
@@ -358,9 +358,9 @@ class FormatDatetimeTestCase(unittest.TestCase):
         formatted_string = dates.format_datetime(dt, 'OOOO', locale='en')
         self.assertEqual(u'GMT+00:00', formatted_string)
         formatted_string = dates.format_datetime(dt, 'VV', locale='en')
-        self.assertEqual(u'Etc/GMT', formatted_string)
+        self.assertEqual(u'Etc/UTC', formatted_string)
         formatted_string = dates.format_datetime(dt, 'VVV', locale='en')
-        self.assertEqual(u'GMT', formatted_string)
+        self.assertEqual(u'UTC', formatted_string)
         formatted_string = dates.format_datetime(dt, 'X', locale='en')
         self.assertEqual(u'Z', formatted_string)
         formatted_string = dates.format_datetime(dt, 'XX', locale='en')
@@ -430,7 +430,7 @@ class FormatTimeTestCase(unittest.TestCase):
         d = datetime(2012, 4, 1, 15, 30, 29, tzinfo=timezone('UTC'))
         epoch = float(calendar.timegm(d.timetuple()))
         formatted_time = dates.format_time(epoch, format='long', locale='en_US')
-        self.assertEqual(u'3:30:29 PM +0000', formatted_time)
+        self.assertEqual(u'3:30:29 PM UTC', formatted_time)
 
     def test_with_date_fields_in_pattern(self):
         self.assertRaises(AttributeError, dates.format_time, date(2007, 4, 1),
@@ -511,7 +511,7 @@ class TimeZoneAdjustTestCase(unittest.TestCase):
         utc = self._utc()
         t = datetime(2007, 4, 1, 15, 30, tzinfo=utc)
         formatted_time = dates.format_time(t, 'long', tzinfo=utc, locale='en')
-        self.assertEqual('3:30:00 PM +0000', formatted_time)
+        self.assertEqual('3:30:00 PM UTC', formatted_time)
 
 
 def test_get_period_names():
@@ -633,8 +633,8 @@ def test_get_timezone_name():
 
     assert (dates.get_timezone_name('Europe/Berlin', locale='en_US') == "Central European Time")
 
-    assert (dates.get_timezone_name(1400000000, locale='en_US', width='short') == "Unknown Region (GMT) Time")
-    assert (dates.get_timezone_name(time(16, 20), locale='en_US', width='short') == "+0000")
+    assert (dates.get_timezone_name(1400000000, locale='en_US', width='short') == "Unknown Region (UTC) Time")
+    assert (dates.get_timezone_name(time(16, 20), locale='en_US', width='short') == "UTC")
 
 
 def test_format_date():
diff --git a/tests/test_languages.py b/tests/test_languages.py
index 23af26d..32f0d67 100644
--- a/tests/test_languages.py
+++ b/tests/test_languages.py
@@ -11,6 +11,7 @@ def test_official_languages():
 
 
 def test_get_language_info():
-    assert set(get_territory_language_info("HU").keys()) == {"hu", "en", "de",
-                                                             "ro", "hr", "sk",
-                                                             "sl"}
+    assert (
+        set(get_territory_language_info("HU")) ==
+        {"hu", "fr", "en", "de", "ro", "hr", "sk", "sl"}
+    )
diff --git a/tests/test_lists.py b/tests/test_lists.py
index f5021ea..bd297ec 100644
--- a/tests/test_lists.py
+++ b/tests/test_lists.py
@@ -9,6 +9,6 @@ def test_format_list():
         (['string1', 'string2'], 'en', u'string1 and string2'),
         (['string1', 'string2', 'string3'], 'en', u'string1, string2, and string3'),
         (['string1', 'string2', 'string3'], 'zh', u'string1、string2和string3'),
-        (['string1', 'string2', 'string3', 'string4'], 'ne', u'string1 र string2, string3 र string4'),
+        (['string1', 'string2', 'string3', 'string4'], 'ne', u'string1,string2, string3 र string4'),
     ]:
         assert lists.format_list(list, locale=locale) == expected
diff --git a/tests/test_numbers.py b/tests/test_numbers.py
index 2795b0f..8399005 100644
--- a/tests/test_numbers.py
+++ b/tests/test_numbers.py
@@ -180,7 +180,7 @@ def test_list_currencies():
     assert list_currencies(locale='pa_Arab') == {'PKR', 'INR', 'EUR'}
     assert list_currencies(locale='kok') == set([])
 
-    assert len(list_currencies()) == 296
+    assert len(list_currencies()) == 297
 
 
 def test_validate_currency():
@@ -589,3 +589,8 @@ def test_numberpattern_repr():
     format = u'¤#,##0.00;(¤#,##0.00)'
     np = numbers.parse_pattern(format)
     assert repr(format) in repr(np)
+
+
+def test_parse_static_pattern():
+    assert numbers.parse_pattern('Kun')  # in the So locale in CLDR 30
+    # TODO: static patterns might not be correctly `apply()`ed at present