From 80c76d04aeff79ce6ce3c8260078bc463ef65b96 Mon Sep 17 00:00:00 2001 From: glaziermag Date: Fri, 25 Sep 2026 17:00:06 -0700 Subject: [PATCH] Reject underscore grouping in strict decimal parsing --- babel/numbers.py | 2 +- tests/test_numbers.py | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/babel/numbers.py b/babel/numbers.py index b782521e5..e1cd873e4 100644 --- a/babel/numbers.py +++ b/babel/numbers.py @@ -1193,7 +1193,7 @@ def parse_decimal( parsed = decimal.Decimal(string.replace(group_symbol, '').replace(decimal_symbol, '.')) except decimal.InvalidOperation as exc: raise NumberFormatError(f"{string!r} is not a valid decimal number") from exc - if strict and group_symbol in string: + if strict and (group_symbol in string or '_' in string): proper = format_decimal( parsed, locale=locale, diff --git a/tests/test_numbers.py b/tests/test_numbers.py index d20a18315..4db64d220 100644 --- a/tests/test_numbers.py +++ b/tests/test_numbers.py @@ -579,6 +579,16 @@ def test_parse_decimal_group_separator_can_be_any_space(string): assert decimal.Decimal('1099.98') == numbers.parse_decimal(string, locale='fr') +@pytest.mark.parametrize(('locale', 'grouped'), [('en_US', '1,000'), ('de', '1.000')]) +def test_parse_decimal_strict_rejects_underscores(locale, grouped): + with pytest.raises(numbers.NumberFormatError): + numbers.parse_decimal('1_000', locale=locale, strict=True) + + assert numbers.parse_decimal('1_000', locale=locale) == decimal.Decimal('1000') + assert numbers.parse_decimal('1000', locale=locale, strict=True) == decimal.Decimal('1000') + assert numbers.parse_decimal(grouped, locale=locale, strict=True) == decimal.Decimal('1000') + + def test_parse_grouping(): assert numbers.parse_grouping('##') == (1000, 1000) assert numbers.parse_grouping('#,###') == (3, 3)