mirror of
https://github.com/python/cpython.git
synced 2025-07-07 19:35:27 +00:00
gh-87790: support thousands separators for formatting fractional part of Decimal (#132202)
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
This commit is contained in:
parent
0c3e3da195
commit
90a5b4402b
3 changed files with 28 additions and 1 deletions
|
@ -6122,7 +6122,11 @@ _parse_format_specifier_regex = re.compile(r"""\A
|
||||||
(?P<zeropad>0)?
|
(?P<zeropad>0)?
|
||||||
(?P<minimumwidth>\d+)?
|
(?P<minimumwidth>\d+)?
|
||||||
(?P<thousands_sep>[,_])?
|
(?P<thousands_sep>[,_])?
|
||||||
(?:\.(?P<precision>\d+))?
|
(?:\.
|
||||||
|
(?=[\d,_]) # lookahead for digit or separator
|
||||||
|
(?P<precision>\d+)?
|
||||||
|
(?P<frac_separators>[,_])?
|
||||||
|
)?
|
||||||
(?P<type>[eEfFgGn%])?
|
(?P<type>[eEfFgGn%])?
|
||||||
\z
|
\z
|
||||||
""", re.VERBOSE|re.DOTALL)
|
""", re.VERBOSE|re.DOTALL)
|
||||||
|
@ -6215,6 +6219,9 @@ def _parse_format_specifier(format_spec, _localeconv=None):
|
||||||
format_dict['grouping'] = [3, 0]
|
format_dict['grouping'] = [3, 0]
|
||||||
format_dict['decimal_point'] = '.'
|
format_dict['decimal_point'] = '.'
|
||||||
|
|
||||||
|
if format_dict['frac_separators'] is None:
|
||||||
|
format_dict['frac_separators'] = ''
|
||||||
|
|
||||||
return format_dict
|
return format_dict
|
||||||
|
|
||||||
def _format_align(sign, body, spec):
|
def _format_align(sign, body, spec):
|
||||||
|
@ -6334,6 +6341,11 @@ def _format_number(is_negative, intpart, fracpart, exp, spec):
|
||||||
|
|
||||||
sign = _format_sign(is_negative, spec)
|
sign = _format_sign(is_negative, spec)
|
||||||
|
|
||||||
|
frac_sep = spec['frac_separators']
|
||||||
|
if fracpart and frac_sep:
|
||||||
|
fracpart = frac_sep.join(fracpart[pos:pos + 3]
|
||||||
|
for pos in range(0, len(fracpart), 3))
|
||||||
|
|
||||||
if fracpart or spec['alt']:
|
if fracpart or spec['alt']:
|
||||||
fracpart = spec['decimal_point'] + fracpart
|
fracpart = spec['decimal_point'] + fracpart
|
||||||
|
|
||||||
|
|
|
@ -1089,6 +1089,15 @@ class FormatTest:
|
||||||
('07_', '1234.56', '1_234.56'),
|
('07_', '1234.56', '1_234.56'),
|
||||||
('_', '1.23456789', '1.23456789'),
|
('_', '1.23456789', '1.23456789'),
|
||||||
('_%', '123.456789', '12_345.6789%'),
|
('_%', '123.456789', '12_345.6789%'),
|
||||||
|
# and now for something completely different...
|
||||||
|
('.,', '1.23456789', '1.234,567,89'),
|
||||||
|
('._', '1.23456789', '1.234_567_89'),
|
||||||
|
('.6_f', '12345.23456789', '12345.234_568'),
|
||||||
|
(',._%', '123.456789', '12,345.678_9%'),
|
||||||
|
(',._e', '123456', '1.234_56e+5'),
|
||||||
|
(',.4_e', '123456', '1.234_6e+5'),
|
||||||
|
(',.3_e', '123456', '1.235e+5'),
|
||||||
|
(',._E', '123456', '1.234_56E+5'),
|
||||||
|
|
||||||
# negative zero: default behavior
|
# negative zero: default behavior
|
||||||
('.1f', '-0', '-0.0'),
|
('.1f', '-0', '-0.0'),
|
||||||
|
@ -1162,6 +1171,10 @@ class FormatTest:
|
||||||
# bytes format argument
|
# bytes format argument
|
||||||
self.assertRaises(TypeError, Decimal(1).__format__, b'-020')
|
self.assertRaises(TypeError, Decimal(1).__format__, b'-020')
|
||||||
|
|
||||||
|
# precision or fractional part separator should follow after dot
|
||||||
|
self.assertRaises(ValueError, format, Decimal(1), '.f')
|
||||||
|
self.assertRaises(ValueError, format, Decimal(1), '._6f')
|
||||||
|
|
||||||
def test_negative_zero_format_directed_rounding(self):
|
def test_negative_zero_format_directed_rounding(self):
|
||||||
with self.decimal.localcontext() as ctx:
|
with self.decimal.localcontext() as ctx:
|
||||||
ctx.rounding = ROUND_CEILING
|
ctx.rounding = ROUND_CEILING
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Support underscore and comma as thousands separators in the fractional part
|
||||||
|
for :class:`~decimal.Decimal`'s formatting. Patch by Sergey B Kirpichev.
|
Loading…
Add table
Add a link
Reference in a new issue