https://github.com/python/cpython/commit/e25531331b8af9aff46986890826855e602c8201 commit: e25531331b8af9aff46986890826855e602c8201 branch: 3.14 author: Miss Islington (bot) <31488909+miss-isling...@users.noreply.github.com> committer: hugovk <1324225+hug...@users.noreply.github.com> date: 2025-07-07T19:24:30+03:00 summary:
[3.14] gh-87790: support thousands separators for formatting fractional part of Decimal (GH-132202) (#136365) Co-authored-by: Sergey B Kirpichev <skirpic...@gmail.com> Co-authored-by: Serhiy Storchaka <storch...@gmail.com> files: A Misc/NEWS.d/next/Library/2025-04-07-09-53-54.gh-issue-87790.6nj3zQ.rst M Lib/_pydecimal.py M Lib/test/test_decimal.py diff --git a/Lib/_pydecimal.py b/Lib/_pydecimal.py index 781b38ec26ba33..9b8e42a2342536 100644 --- a/Lib/_pydecimal.py +++ b/Lib/_pydecimal.py @@ -6122,7 +6122,11 @@ def _convert_for_comparison(self, other, equality_op=False): (?P<zeropad>0)? (?P<minimumwidth>\d+)? (?P<thousands_sep>[,_])? -(?:\.(?P<precision>\d+))? +(?:\. + (?=[\d,_]) # lookahead for digit or separator + (?P<precision>\d+)? + (?P<frac_separators>[,_])? +)? (?P<type>[eEfFgGn%])? \z """, re.VERBOSE|re.DOTALL) @@ -6215,6 +6219,9 @@ def _parse_format_specifier(format_spec, _localeconv=None): format_dict['grouping'] = [3, 0] format_dict['decimal_point'] = '.' + if format_dict['frac_separators'] is None: + format_dict['frac_separators'] = '' + return format_dict 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) + 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']: fracpart = spec['decimal_point'] + fracpart diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py index ca2b0b18fb0a6d..c621b7ac08c3ac 100644 --- a/Lib/test/test_decimal.py +++ b/Lib/test/test_decimal.py @@ -1090,6 +1090,15 @@ def test_formatting(self): ('07_', '1234.56', '1_234.56'), ('_', '1.23456789', '1.23456789'), ('_%', '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 ('.1f', '-0', '-0.0'), @@ -1163,6 +1172,10 @@ def test_formatting(self): # bytes format argument 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): with self.decimal.localcontext() as ctx: ctx.rounding = ROUND_CEILING diff --git a/Misc/NEWS.d/next/Library/2025-04-07-09-53-54.gh-issue-87790.6nj3zQ.rst b/Misc/NEWS.d/next/Library/2025-04-07-09-53-54.gh-issue-87790.6nj3zQ.rst new file mode 100644 index 00000000000000..cf80c71271bbd1 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-04-07-09-53-54.gh-issue-87790.6nj3zQ.rst @@ -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. _______________________________________________ Python-checkins mailing list -- python-checkins@python.org To unsubscribe send an email to python-checkins-le...@python.org https://mail.python.org/mailman3//lists/python-checkins.python.org Member address: arch...@mail-archive.com