[Python-checkins] r86751 - in python/branches/py3k: Doc/library/string.rst Lib/decimal.py Lib/test/test_complex.py Lib/test/test_decimal.py Lib/test/test_float.py Lib/test/test_types.py Misc/ACKS Misc/NEWS Objects/stringlib/formatter.h

eric.smith python-checkins at python.org
Thu Nov 25 17:08:06 CET 2010


Author: eric.smith
Date: Thu Nov 25 17:08:06 2010
New Revision: 86751

Log:
Issue #7094: Add alternate ('#') flag to __format__ methods for float, complex and Decimal. Allows greater control over when decimal points appear. Added to make transitioning from %-formatting easier. '#g' still has a problem with Decimal which I'll fix soon.

Modified:
   python/branches/py3k/Doc/library/string.rst
   python/branches/py3k/Lib/decimal.py
   python/branches/py3k/Lib/test/test_complex.py
   python/branches/py3k/Lib/test/test_decimal.py
   python/branches/py3k/Lib/test/test_float.py
   python/branches/py3k/Lib/test/test_types.py
   python/branches/py3k/Misc/ACKS
   python/branches/py3k/Misc/NEWS
   python/branches/py3k/Objects/stringlib/formatter.h

Modified: python/branches/py3k/Doc/library/string.rst
==============================================================================
--- python/branches/py3k/Doc/library/string.rst	(original)
+++ python/branches/py3k/Doc/library/string.rst	Thu Nov 25 17:08:06 2010
@@ -350,9 +350,18 @@
    |         | positive numbers, and a minus sign on negative numbers.  |
    +---------+----------------------------------------------------------+
 
-The ``'#'`` option is only valid for integers, and only for binary, octal, or
-hexadecimal output.  If present, it specifies that the output will be prefixed
-by ``'0b'``, ``'0o'``, or ``'0x'``, respectively.
+
+The ``'#'`` option causes the "alternate form" to be used for the
+conversion.  The alternate form is defined differently for different
+types.  This option is only valid for integer, float, complex and
+Decimal types. For integers, when binary, octal, or hexadecimal output
+is used, this option adds the prefix respective ``'0b'``, ``'0o'``, or
+``'0x'`` to the output value. For floats, complex and Decimal the
+alternate form causes the result of the conversion to always contain a
+decimal-point character, even if no digits follow it. Normally, a
+decimal-point character appears in the result of these conversions
+only if a digit follows it. In addition, for ``'g'`` and ``'G'``
+conversions, trailing zeros are not removed from the result.
 
 The ``','`` option signals the use of a comma for a thousands separator.
 For a locale aware separator, use the ``'n'`` integer presentation type

Modified: python/branches/py3k/Lib/decimal.py
==============================================================================
--- python/branches/py3k/Lib/decimal.py	(original)
+++ python/branches/py3k/Lib/decimal.py	Thu Nov 25 17:08:06 2010
@@ -5991,7 +5991,7 @@
 #
 # A format specifier for Decimal looks like:
 #
-#   [[fill]align][sign][0][minimumwidth][,][.precision][type]
+#   [[fill]align][sign][#][0][minimumwidth][,][.precision][type]
 
 _parse_format_specifier_regex = re.compile(r"""\A
 (?:
@@ -5999,6 +5999,7 @@
    (?P<align>[<>=^])
 )?
 (?P<sign>[-+ ])?
+(?P<alt>\#)?
 (?P<zeropad>0)?
 (?P<minimumwidth>(?!0)\d+)?
 (?P<thousands_sep>,)?
@@ -6214,7 +6215,7 @@
 
     sign = _format_sign(is_negative, spec)
 
-    if fracpart:
+    if fracpart or spec['alt']:
         fracpart = spec['decimal_point'] + fracpart
 
     if exp != 0 or spec['type'] in 'eE':

Modified: python/branches/py3k/Lib/test/test_complex.py
==============================================================================
--- python/branches/py3k/Lib/test/test_complex.py	(original)
+++ python/branches/py3k/Lib/test/test_complex.py	Thu Nov 25 17:08:06 2010
@@ -555,8 +555,28 @@
         self.assertEqual(format(1.5e21+3j, '^40,.2f'), ' 1,500,000,000,000,000,000,000.00+3.00j ')
         self.assertEqual(format(1.5e21+3000j, ',.2f'), '1,500,000,000,000,000,000,000.00+3,000.00j')
 
-        # alternate is invalid
-        self.assertRaises(ValueError, (1.5+0.5j).__format__, '#f')
+        # Issue 7094: Alternate formatting (specified by #)
+        self.assertEqual(format(1+1j, '.0e'), '1e+00+1e+00j')
+        self.assertEqual(format(1+1j, '#.0e'), '1.e+00+1.e+00j')
+        self.assertEqual(format(1+1j, '.0f'), '1+1j')
+        self.assertEqual(format(1+1j, '#.0f'), '1.+1.j')
+        self.assertEqual(format(1.1+1.1j, 'g'), '1.1+1.1j')
+        self.assertEqual(format(1.1+1.1j, '#g'), '1.10000+1.10000j')
+
+        # Alternate doesn't make a difference for these, they format the same with or without it
+        self.assertEqual(format(1+1j, '.1e'),  '1.0e+00+1.0e+00j')
+        self.assertEqual(format(1+1j, '#.1e'), '1.0e+00+1.0e+00j')
+        self.assertEqual(format(1+1j, '.1f'),  '1.0+1.0j')
+        self.assertEqual(format(1+1j, '#.1f'), '1.0+1.0j')
+
+        # Misc. other alternate tests
+        self.assertEqual(format((-1.5+0.5j), '#f'), '-1.500000+0.500000j')
+        self.assertEqual(format((-1.5+0.5j), '#.0f'), '-2.+0.j')
+        self.assertEqual(format((-1.5+0.5j), '#e'), '-1.500000e+00+5.000000e-01j')
+        self.assertEqual(format((-1.5+0.5j), '#.0e'), '-2.e+00+5.e-01j')
+        self.assertEqual(format((-1.5+0.5j), '#g'), '-1.50000+0.500000j')
+        self.assertEqual(format((-1.5+0.5j), '.0g'), '-2+0.5j')
+        self.assertEqual(format((-1.5+0.5j), '#.0g'), '-2.+0.5j')
 
         # zero padding is invalid
         self.assertRaises(ValueError, (1.5+0.5j).__format__, '010f')

Modified: python/branches/py3k/Lib/test/test_decimal.py
==============================================================================
--- python/branches/py3k/Lib/test/test_decimal.py	(original)
+++ python/branches/py3k/Lib/test/test_decimal.py	Thu Nov 25 17:08:06 2010
@@ -818,6 +818,18 @@
 
             # issue 6850
             ('a=-7.0', '0.12345', 'aaaa0.1'),
+
+            # Issue 7094: Alternate formatting (specified by #)
+            ('.0e', '1.0', '1e+0'),
+            ('#.0e', '1.0', '1.e+0'),
+            ('.0f', '1.0', '1'),
+            ('#.0f', '1.0', '1.'),
+            ('g', '1.1', '1.1'),
+            ('#g', '1.1', '1.1'),
+            ('.0g', '1', '1'),
+            ('#.0g', '1', '1.'),
+            ('.0%', '1.0', '100%'),
+            ('#.0%', '1.0', '100.%'),
             ]
         for fmt, d, result in test_values:
             self.assertEqual(format(Decimal(d), fmt), result)

Modified: python/branches/py3k/Lib/test/test_float.py
==============================================================================
--- python/branches/py3k/Lib/test/test_float.py	(original)
+++ python/branches/py3k/Lib/test/test_float.py	Thu Nov 25 17:08:06 2010
@@ -706,11 +706,8 @@
         def test(fmt, value, expected):
             # Test with both % and format().
             self.assertEqual(fmt % value, expected, fmt)
-            if not '#' in fmt:
-                # Until issue 7094 is implemented, format() for floats doesn't
-                #  support '#' formatting
-                fmt = fmt[1:] # strip off the %
-                self.assertEqual(format(value, fmt), expected, fmt)
+            fmt = fmt[1:] # strip off the %
+            self.assertEqual(format(value, fmt), expected, fmt)
 
         for fmt in ['%e', '%f', '%g', '%.0e', '%.6f', '%.20g',
                     '%#e', '%#f', '%#g', '%#.20e', '%#.15f', '%#.3g']:

Modified: python/branches/py3k/Lib/test/test_types.py
==============================================================================
--- python/branches/py3k/Lib/test/test_types.py	(original)
+++ python/branches/py3k/Lib/test/test_types.py	Thu Nov 25 17:08:06 2010
@@ -396,13 +396,9 @@
             self.assertEqual(len(format(0, cfmt)), len(format(x, cfmt)))
 
     def test_float__format__(self):
-        # these should be rewritten to use both format(x, spec) and
-        # x.__format__(spec)
-
         def test(f, format_spec, result):
-            assert type(f) == float
-            assert type(format_spec) == str
             self.assertEqual(f.__format__(format_spec), result)
+            self.assertEqual(format(f, format_spec), result)
 
         test(0.0, 'f', '0.000000')
 
@@ -516,9 +512,27 @@
                 self.assertRaises(ValueError, format, 1e-100, format_spec)
                 self.assertRaises(ValueError, format, -1e-100, format_spec)
 
-        # Alternate formatting is not supported
-        self.assertRaises(ValueError, format, 0.0, '#')
-        self.assertRaises(ValueError, format, 0.0, '#20f')
+        # Alternate float formatting
+        test(1.0, '.0e', '1e+00')
+        test(1.0, '#.0e', '1.e+00')
+        test(1.0, '.0f', '1')
+        test(1.0, '#.0f', '1.')
+        test(1.1, 'g', '1.1')
+        test(1.1, '#g', '1.10000')
+        test(1.0, '.0%', '100%')
+        test(1.0, '#.0%', '100.%')
+
+        # Issue 7094: Alternate formatting (specified by #)
+        test(1.0, '0e',  '1.000000e+00')
+        test(1.0, '#0e', '1.000000e+00')
+        test(1.0, '0f',  '1.000000' )
+        test(1.0, '#0f', '1.000000')
+        test(1.0, '.1e',  '1.0e+00')
+        test(1.0, '#.1e', '1.0e+00')
+        test(1.0, '.1f',  '1.0')
+        test(1.0, '#.1f', '1.0')
+        test(1.0, '.1%',  '100.0%')
+        test(1.0, '#.1%', '100.0%')
 
         # Issue 6902
         test(12345.6, "0<20", '12345.60000000000000')

Modified: python/branches/py3k/Misc/ACKS
==============================================================================
--- python/branches/py3k/Misc/ACKS	(original)
+++ python/branches/py3k/Misc/ACKS	Thu Nov 25 17:08:06 2010
@@ -318,6 +318,7 @@
 Hans de Graaff
 Eddy De Greef
 Duncan Grisby
+Eric Groo
 Dag Gruneau
 Michael Guravage
 Lars Gustäbel
@@ -457,6 +458,7 @@
 Pat Knight
 Greg Kochanski
 Damon Kohler
+Vlad Korolev
 Joseph Koshy
 Maksim Kozyarchuk
 Stefan Krah
@@ -536,6 +538,7 @@
 Doug Marien
 Alex Martelli
 Anthony Martin
+Owen Martin
 Sébastien Martini
 Roger Masse
 Nick Mathewson
@@ -733,6 +736,7 @@
 Andreas Schawo
 Neil Schemenauer
 David Scherer
+Bob Schmertz
 Gregor Schmid
 Ralf Schmitt
 Michael Schneider

Modified: python/branches/py3k/Misc/NEWS
==============================================================================
--- python/branches/py3k/Misc/NEWS	(original)
+++ python/branches/py3k/Misc/NEWS	Thu Nov 25 17:08:06 2010
@@ -15,6 +15,10 @@
 - Issue #10027. st_nlink was not being set on Windows calls to os.stat or
   os.lstat. Patch by Hirokazu Yamamoto.
 
+- Issue #7094: Added alternate formatting (specified by '#') to
+  __format__ method of float, complex, and Decimal. This allows more
+  precise control over when decimal points are displayed.
+
 - Issue #10474: range().count() should return integers.
 
 - Issue #10255: Fix reference leak in Py_InitializeEx().  Patch by Neil

Modified: python/branches/py3k/Objects/stringlib/formatter.h
==============================================================================
--- python/branches/py3k/Objects/stringlib/formatter.h	(original)
+++ python/branches/py3k/Objects/stringlib/formatter.h	Thu Nov 25 17:08:06 2010
@@ -941,13 +941,8 @@
        from a hard-code pseudo-locale */
     LocaleInfo locale;
 
-    /* Alternate is not allowed on floats. */
-    if (format->alternate) {
-        PyErr_SetString(PyExc_ValueError,
-                        "Alternate form (#) not allowed in float format "
-                        "specifier");
-        goto done;
-    }
+    if (format->alternate)
+        flags |= Py_DTSF_ALT;
 
     if (type == '\0') {
         /* Omitted type specifier.  Behaves in the same way as repr(x)
@@ -1104,15 +1099,7 @@
        from a hard-code pseudo-locale */
     LocaleInfo locale;
 
-    /* Alternate is not allowed on complex. */
-    if (format->alternate) {
-        PyErr_SetString(PyExc_ValueError,
-                        "Alternate form (#) not allowed in complex format "
-                        "specifier");
-        goto done;
-    }
-
-    /* Neither is zero pading. */
+    /* Zero padding is not allowed. */
     if (format->fill_char == '0') {
         PyErr_SetString(PyExc_ValueError,
                         "Zero padding is not allowed in complex format "
@@ -1135,6 +1122,9 @@
     if (im == -1.0 && PyErr_Occurred())
         goto done;
 
+    if (format->alternate)
+        flags |= Py_DTSF_ALT;
+
     if (type == '\0') {
         /* Omitted type specifier. Should be like str(self). */
         type = 'r';


More information about the Python-checkins mailing list