Index: Objects/stringlib/formatter.h =================================================================== --- Objects/stringlib/formatter.h (revision 86626) +++ Objects/stringlib/formatter.h (working copy) @@ -942,13 +942,10 @@ 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) and str(x) if no precision is given, else like 'g', but with @@ -1106,10 +1103,7 @@ /* Alternate is not allowed on complex. */ if (format->alternate) { - PyErr_SetString(PyExc_ValueError, - "Alternate form (#) not allowed in complex format " - "specifier"); - goto done; + flags |= Py_DTSF_ALT; } /* Neither is zero pading. */ Index: Doc/library/string.rst =================================================================== --- Doc/library/string.rst (revision 86626) +++ Doc/library/string.rst (working copy) @@ -350,10 +350,22 @@ | | 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 the integes and floats. + +For the integers when binary, octal, or hexadecimal output is used this opton adds +the prefix respective +``'0b'``, ``'0o'``, or ``'0x'`` to output value. + +For the floats the alternate form causes the result of conversion a +floating-point number 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.) For g and G +conversions, trailing zeros are not removed from the result. For other +conversions, the behavior is undefined." + + The ``','`` option signals the use of a comma for a thousands separator. For a locale aware separator, use the ``'n'`` integer presentation type instead. Index: Lib/decimal.py =================================================================== --- Lib/decimal.py (revision 86626) +++ Lib/decimal.py (working copy) @@ -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[<>=^]) )? (?P[-+ ])? +(?P\#)? (?P0)? (?P(?!0)\d+)? (?P,)? @@ -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': Index: Lib/test/test_complex.py =================================================================== --- Lib/test/test_complex.py (revision 86626) +++ Lib/test/test_complex.py (working copy) @@ -555,8 +555,12 @@ 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') + 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') # zero padding is invalid self.assertRaises(ValueError, (1.5+0.5j).__format__, '010f') Index: Lib/test/test_float.py =================================================================== --- Lib/test/test_float.py (revision 86626) +++ Lib/test/test_float.py (working copy) @@ -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:] + self.assertEqual(format(value, fmt), expected, fmt) for fmt in ['%e', '%f', '%g', '%.0e', '%.6f', '%.20g', '%#e', '%#f', '%#g', '%#.20e', '%#.15f', '%#.3g']: Index: Lib/test/test_types.py =================================================================== --- Lib/test/test_types.py (revision 86626) +++ Lib/test/test_types.py (working copy) @@ -516,10 +516,25 @@ 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', '1.000000e+00') + test(1.0, '#0e', '1.000000e+00') + test(1.0, '.0e', '1e+00') + test(1.0, '#.0e', '1.e+00') + test(1.0, '.1e', '1.0e+00') + test(1.0, '#.1e', '1.0e+00') + test(1.0, '0f', '1.000000' ) + test(1.0, '#0f', '1.000000' ) + test(1.0, '.0f', '1') + test(1.0, '#.0f', '1.') + test(1.0, '.1f', '1.0') + test(1.0, '#.1f', '1.0') + test(1.1, 'g', '1.1') + test(1.1, '#g', '1.10000') + test(1.0, '.0%', '100%') + test(1.0, '#.0%', '100.%') + # Issue 6902 test(12345.6, "0<20", '12345.60000000000000') test(12345.6, "1<20", '12345.61111111111111') Index: Lib/test/test_decimal.py =================================================================== --- Lib/test/test_decimal.py (revision 86626) +++ Lib/test/test_decimal.py (working copy) @@ -818,6 +818,11 @@ # issue 6850 ('a=-7.0', '0.12345', 'aaaa0.1'), + + # issue 7094 + ('#.0e', '1.0', '1.e+0'), + ('#.0f', '1.0', '1.'), + ('#g', '1', '1.'), ] for fmt, d, result in test_values: self.assertEqual(format(Decimal(d), fmt), result)