Repository: python-babel/babel
Pull request #414 (2016-06-02 20:49:22)

parse_pattern now preserves original pattern #407
More precisely, `babel.numbers.parse_pattern()` only passes the positive string format to `NumberPattern()`. Fixed the method so that it now passes the entire original pattern string.

jtwang: Whoops, forgot to recreate .dat files prior to running `make test`, hence tests passing locally but failing on  AppVeyor.
jtwang: Argh, I got bit by the changes in string handling between Python 2 and 3.

Patch:
diff --git a/babel/core.py b/babel/core.py
index 4e00ebf..e6f432c 100644
--- a/babel/core.py
+++ b/babel/core.py
@@ -577,7 +577,7 @@ class Locale(object):
         >>> Locale('en', 'US').currency_formats['standard']
         <NumberPattern u'\\xa4#,##0.00'>
         >>> Locale('en', 'US').currency_formats['accounting']
-        <NumberPattern u'\\xa4#,##0.00'>
+        <NumberPattern u'\\xa4#,##0.00;(\\xa4#,##0.00)'>
         """
         return self._data['currency_formats']
 
diff --git a/babel/numbers.py b/babel/numbers.py
index 3ab366c..899fabb 100644
--- a/babel/numbers.py
+++ b/babel/numbers.py
@@ -487,13 +487,15 @@ def parse_pattern(pattern):
             raise ValueError('Invalid number pattern %r' % pattern)
         return rv.groups()
 
+    pos_pattern = pattern
+
     # Do we have a negative subpattern?
     if ';' in pattern:
-        pattern, neg_pattern = pattern.split(';', 1)
-        pos_prefix, number, pos_suffix = _match_number(pattern)
+        pos_pattern, neg_pattern = pattern.split(';', 1)
+        pos_prefix, number, pos_suffix = _match_number(pos_pattern)
         neg_prefix, _, neg_suffix = _match_number(neg_pattern)
     else:
-        pos_prefix, number, pos_suffix = _match_number(pattern)
+        pos_prefix, number, pos_suffix = _match_number(pos_pattern)
         neg_prefix = '-' + pos_prefix
         neg_suffix = pos_suffix
     if 'E' in number:


Test patch:
diff --git a/tests/test_core.py b/tests/test_core.py
index 29081f0..e3d8faf 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -172,7 +172,7 @@ class TestLocaleClass:
         assert (Locale('en', 'US').currency_formats['standard'].pattern ==
                 u'\xa4#,##0.00')
         assert (Locale('en', 'US').currency_formats['accounting'].pattern ==
-                u'\xa4#,##0.00')
+                u'\xa4#,##0.00;(\xa4#,##0.00)')
 
     def test_percent_formats_property(self):
         assert Locale('en', 'US').percent_formats[None].pattern == '#,##0%'
diff --git a/tests/test_numbers.py b/tests/test_numbers.py
index 19f9bc7..24b274c 100644
--- a/tests/test_numbers.py
+++ b/tests/test_numbers.py
@@ -317,5 +317,47 @@ def test_parse_grouping():
 
 
 def test_parse_pattern():
-    assert numbers.parse_pattern(u'¤#,##0.00;(¤#,##0.00)').suffix == (u'', u')')
-    assert numbers.parse_pattern(u'¤ #,##0.00;¤ #,##0.00-').suffix == (u'', u'-')
+
+    # Original pattern is preserved
+    np = numbers.parse_pattern(u'¤#,##0.00')
+    assert np.pattern == u'¤#,##0.00'
+
+    np = numbers.parse_pattern(u'¤#,##0.00;(¤#,##0.00)')
+    assert np.pattern == u'¤#,##0.00;(¤#,##0.00)'
+
+    # Given a NumberPattern object, we don't return a new instance.
+    # However, we don't cache NumberPattern objects, so calling
+    # parse_pattern with the same format string will create new
+    # instances
+    np1 = numbers.parse_pattern(u'¤ #,##0.00')
+    np2 = numbers.parse_pattern(u'¤ #,##0.00')
+    assert np1 is not np2
+    assert np1 is numbers.parse_pattern(np1)
+
+
+def test_parse_pattern_negative():
+
+    # No negative format specified
+    np = numbers.parse_pattern(u'¤#,##0.00')
+    assert np.prefix == (u'¤', u'-¤')
+    assert np.suffix == (u'', u'')
+
+    # Negative format is specified
+    np = numbers.parse_pattern(u'¤#,##0.00;(¤#,##0.00)')
+    assert np.prefix == (u'¤', u'(¤')
+    assert np.suffix == (u'', u')')
+
+    # Negative sign is a suffix
+    np = numbers.parse_pattern(u'¤ #,##0.00;¤ #,##0.00-')
+    assert np.prefix == (u'¤ ', u'¤ ')
+    assert np.suffix == (u'', u'-')
+
+
+def test_numberpattern_repr():
+    """repr() outputs the pattern string"""
+
+    # This implementation looks a bit funny, but that's cause strings are
+    # repr'd differently in Python 2 vs 3 and this test runs under both.
+    format = u'¤#,##0.00;(¤#,##0.00)'
+    np = numbers.parse_pattern(format)
+    assert repr(format) in repr(np)