]> granicus.if.org Git - python/commitdiff
Merged revisions 74281 via svnmerge from
authorMark Dickinson <dickinsm@gmail.com>
Sun, 2 Aug 2009 11:01:01 +0000 (11:01 +0000)
committerMark Dickinson <dickinsm@gmail.com>
Sun, 2 Aug 2009 11:01:01 +0000 (11:01 +0000)
svn+ssh://pythondev@svn.python.org/python/trunk

........
  r74281 | mark.dickinson | 2009-08-02 11:59:36 +0100 (Sun, 02 Aug 2009) | 4 lines

  Issue #6595: Allow Decimal constructor to accept non-European decimal
  digits, as recommended by the specification.  (Backport of r74279 from
  py3k.)
........

Doc/library/decimal.rst
Lib/decimal.py
Lib/test/test_decimal.py
Misc/NEWS

index 4a869135450c1740812b4420916e84dcfee48b8f..7cd8952fa3e671808fcce8e42bff8de4880235cc 100644 (file)
@@ -330,6 +330,12 @@ Decimal objects
       numeric-value  ::=  decimal-part [exponent-part] | infinity
       numeric-string ::=  [sign] numeric-value | [sign] nan
 
+   If *value* is a unicode string then other Unicode decimal digits
+   are also permitted where ``digit`` appears above.  These include
+   decimal digits from various other alphabets (for example,
+   Arabic-Indic and Devanāgarī digits) along with the fullwidth digits
+   ``u'\uff10'`` through ``u'\uff19'``.
+
    If *value* is a :class:`tuple`, it should have three components, a sign
    (:const:`0` for positive or :const:`1` for negative), a :class:`tuple` of
    digits, and an integer exponent. For example, ``Decimal((0, (1, 4, 1, 4), -3))``
index 6529cc8d77a70e45299cb702892536a343fb2c33..ffc0fe8179d8edc9099b2bfd60affb1133cec007 100644 (file)
@@ -551,20 +551,16 @@ class Decimal(object):
             intpart = m.group('int')
             if intpart is not None:
                 # finite number
-                fracpart = m.group('frac')
+                fracpart = m.group('frac') or ''
                 exp = int(m.group('exp') or '0')
-                if fracpart is not None:
-                    self._int = str((intpart+fracpart).lstrip('0') or '0')
-                    self._exp = exp - len(fracpart)
-                else:
-                    self._int = str(intpart.lstrip('0') or '0')
-                    self._exp = exp
+                self._int = str(int(intpart+fracpart))
+                self._exp = exp - len(fracpart)
                 self._is_special = False
             else:
                 diag = m.group('diag')
                 if diag is not None:
                     # NaN
-                    self._int = str(diag.lstrip('0'))
+                    self._int = str(int(diag or '0')).lstrip('0')
                     if m.group('signal'):
                         self._exp = 'N'
                     else:
@@ -5349,29 +5345,26 @@ ExtendedContext = Context(
 # number between the optional sign and the optional exponent must have
 # at least one decimal digit, possibly after the decimal point.  The
 # lookahead expression '(?=\d|\.\d)' checks this.
-#
-# As the flag UNICODE is not enabled here, we're explicitly avoiding any
-# other meaning for \d than the numbers [0-9].
 
 import re
 _parser = re.compile(r"""        # A numeric string consists of:
 #    \s*
     (?P<sign>[-+])?              # an optional sign, followed by either...
     (
-        (?=[0-9]|\.[0-9])        # ...a number (with at least one digit)
-        (?P<int>[0-9]*)          # having a (possibly empty) integer part
-        (\.(?P<frac>[0-9]*))?    # followed by an optional fractional part
-        (E(?P<exp>[-+]?[0-9]+))? # followed by an optional exponent, or...
+        (?=\d|\.\d)              # ...a number (with at least one digit)
+        (?P<int>\d*)             # having a (possibly empty) integer part
+        (\.(?P<frac>\d*))?       # followed by an optional fractional part
+        (E(?P<exp>[-+]?\d+))?    # followed by an optional exponent, or...
     |
         Inf(inity)?              # ...an infinity, or...
     |
         (?P<signal>s)?           # ...an (optionally signaling)
         NaN                      # NaN
-        (?P<diag>[0-9]*)         # with (possibly empty) diagnostic info.
+        (?P<diag>\d*)            # with (possibly empty) diagnostic info.
     )
 #    \s*
     \Z
-""", re.VERBOSE | re.IGNORECASE).match
+""", re.VERBOSE | re.IGNORECASE | re.UNICODE).match
 
 _all_zeros = re.compile('0*$').match
 _exact_half = re.compile('50*$').match
index 92b029e8edc786b3a7d13c8db6c73274e180e8f6..e82a9b9023677deb05367e8e3a939dd8857b3f48 100644 (file)
@@ -432,9 +432,6 @@ class DecimalExplicitConstructionTest(unittest.TestCase):
         self.assertEqual(str(Decimal(u'-Inf')), '-Infinity')
         self.assertEqual(str(Decimal(u'NaN123')), 'NaN123')
 
-        #but alternate unicode digits should not
-        self.assertEqual(str(Decimal(u'\uff11')), 'NaN')
-
     def test_explicit_from_tuples(self):
 
         #zero
@@ -541,6 +538,15 @@ class DecimalExplicitConstructionTest(unittest.TestCase):
         d = nc.create_decimal(prevdec)
         self.assertEqual(str(d), '5.00E+8')
 
+    def test_unicode_digits(self):
+        test_values = {
+            u'\uff11': '1',
+            u'\u0660.\u0660\u0663\u0667\u0662e-\u0663' : '0.0000372',
+            u'-nan\u0c68\u0c6a\u0c66\u0c66' : '-NaN2400',
+            }
+        for input, expected in test_values.items():
+            self.assertEqual(str(Decimal(input)), expected)
+
 
 class DecimalImplicitConstructionTest(unittest.TestCase):
     '''Unit tests for Implicit Construction cases of Decimal.'''
index 3edf5572cb758bfa7d6687893b255cdff708f879..a9cb534f66b1d5888295139cc1c51a2ba339d354 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -279,6 +279,10 @@ Core and Builtins
 Library
 -------
 
+- Issue #6595: The Decimal constructor now allows arbitrary Unicode
+  decimal digits in input, as recommended by the standard.  Previously
+  it was restricted to accepting [0-9].
+
 - Issue #6553: Fixed a crash in cPickle.load(), when given a file-like object
   containing incomplete data.