diff --git a/Lib/_pydecimal.py b/Lib/_pydecimal.py index 359690003fe1605..6c0177d08bf8466 100644 --- a/Lib/_pydecimal.py +++ b/Lib/_pydecimal.py @@ -6152,7 +6152,7 @@ def _convert_for_comparison(self, other, equality_op=False): (?P\#)? (?P0)? (?P(?!0)\d+)? -(?P,)? +(?P[,'])? (?:\.(?P0|(?!0)\d+))? (?P[eEfFgGn%])? \Z @@ -6241,10 +6241,16 @@ def _parse_format_specifier(format_spec, _localeconv=None): format_dict['grouping'] = _localeconv['grouping'] format_dict['decimal_point'] = _localeconv['decimal_point'] else: - if format_dict['thousands_sep'] is None: - format_dict['thousands_sep'] = '' format_dict['grouping'] = [3, 0] format_dict['decimal_point'] = '.' + if format_dict['thousands_sep'] is None: + format_dict['thousands_sep'] = '' + elif format_dict['thousands_sep'] == "'": + if _localeconv is None: + _localeconv = _locale.localeconv() + format_dict['thousands_sep'] = _localeconv['thousands_sep'] + format_dict['grouping'] = _localeconv['grouping'] + format_dict['decimal_point'] = _localeconv['decimal_point'] return format_dict diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py index 58bde54b0a88a16..1977ae7dd33813d 100644 --- a/Lib/test/test_decimal.py +++ b/Lib/test/test_decimal.py @@ -1075,7 +1075,7 @@ def test_formatting(self): # bytes format argument self.assertRaises(TypeError, Decimal(1).__format__, b'-020') - def test_n_format(self): + def test_locale_format(self): Decimal = self.decimal.Decimal try: @@ -1164,6 +1164,40 @@ def get_fmt(x, override=None, fmt='n'): self.assertEqual(get_fmt(Decimal('-1.5'), dotsep_wide, '020n'), '-0\u00b4000\u00b4000\u00b4000\u00b4001\u00bf5') + # locale grouping modifier + self.assertEqual(get_fmt(123456789, en_US, "'f"), '123,456,789') + self.assertEqual(get_fmt(123456789, fr_FR, "'f"), '123456789') + self.assertEqual(get_fmt(123456789, ru_RU, "'f"), '123 456 789') + self.assertEqual(get_fmt(1234567890123, crazy, "'f"), + '123456-78-9012-3') + + self.assertEqual(get_fmt('123456789.123', en_US, "'.2f"), + '123,456,789.12') + self.assertEqual(get_fmt('123456789.123', fr_FR, "'.2f"), + '123456789,12') + self.assertEqual(get_fmt('123456789.123', ru_RU, "'.2f"), + '123 456 789,12') + self.assertEqual(get_fmt('1234567890123.123', crazy, "'.2f"), + '123456-78-9012-3&12') + + self.assertEqual(get_fmt(123456789, en_US, "'.6e"), '1.234568e+8') + self.assertEqual(get_fmt(123456789, fr_FR, "'.6e"), '1,234568e+8') + self.assertEqual(get_fmt(123456789, ru_RU, "'.6e"), '1,234568e+8') + self.assertEqual(get_fmt(123456789, crazy, "'.6e"), '1&234568e+8') + + self.assertEqual(get_fmt(123456789, en_US, "'.6g"), '1.23457e+8') + self.assertEqual(get_fmt(123456789, fr_FR, "'.6g"), '1,23457e+8') + self.assertEqual(get_fmt(123456789, ru_RU, "'.6g"), '1,23457e+8') + self.assertEqual(get_fmt(123456789, crazy, "'.6g"), '1&23457e+8') + + self.assertEqual(get_fmt(12345, en_US, "05'g"), '12,345') + self.assertEqual(get_fmt(12345, en_US, "06'g"), '12,345') + self.assertEqual(get_fmt(12345, en_US, "07'g"), '012,345') + self.assertEqual(get_fmt(12345, en_US, "08'g"), '0,012,345') + self.assertEqual(get_fmt(12345, en_US, "09'g"), '0,012,345') + self.assertEqual(get_fmt(12345, en_US, "010'g"), '00,012,345') + + @run_with_locale('LC_ALL', 'ps_AF') def test_wide_char_separator_decimal_point(self): # locale with wide char separator and decimal point diff --git a/Modules/_decimal/libmpdec/io.c b/Modules/_decimal/libmpdec/io.c index f45e558f1a95734..e86bc8ef5526236 100644 --- a/Modules/_decimal/libmpdec/io.c +++ b/Modules/_decimal/libmpdec/io.c @@ -846,6 +846,15 @@ mpd_parse_fmt_str(mpd_spec_t *spec, const char *fmt, int caps) cp++; } + /* locale specific thousands separator */ + if (*cp == '\'') { + struct lconv *lc = localeconv(); + spec->dot = lc->decimal_point; + spec->sep = lc->thousands_sep; + spec->grouping = lc->grouping; + cp++; + } + /* fraction digits or significant digits */ if (*cp == '.') { cp++;