From b9fe3a49b7fc1a77995b41fda3a5243d3f78e1d9 Mon Sep 17 00:00:00 2001 From: Gaurav Kumar Biswas <2025sl93041@wilp.bits-pilani.ac.in> Date: Tue, 27 Oct 2026 22:09:29 +0530 Subject: [PATCH] fix: raise NumberFormatError for invalid string in NumberPattern.apply Wrap `decimal.Decimal(str(value))` in `NumberPattern.apply()` and `_get_compact_format()` in a try/except to catch `decimal.InvalidOperation` and re-raise as `NumberFormatError`. Previously, passing an invalid string like `"not_a_number"` to any formatting function (format_decimal, format_currency, format_percent, format_compact_decimal) would propagate a raw `decimal.InvalidOperation` exception. The public API contract documents `NumberFormatError` as the error type for bad input; all other invalid-value paths already raise it. Closes #702 --- CHANGES.rst | 9 +++++++++ babel/numbers.py | 10 ++++++++-- tests/test_numbers.py | 7 +++++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index b09e184e8..4f1d71bc2 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,6 +1,15 @@ Babel Changelog =============== +Version 2.19.0 (Unreleased) +---------------------------- + +Bugfixes +~~~~~~~~ + +* Numbers: Raise ``NumberFormatError`` instead of ``decimal.InvalidOperation`` + when formatting an invalid string value by @2025sl93041-gkb in :gh:`702` + Version 2.18.0 -------------- diff --git a/babel/numbers.py b/babel/numbers.py index b782521e5..4669db167 100644 --- a/babel/numbers.py +++ b/babel/numbers.py @@ -664,7 +664,10 @@ def _get_compact_format( https://www.unicode.org/reports/tr35/tr35-45/tr35-numbers.html#Compact_Number_Formats. """ if not isinstance(number, decimal.Decimal): - number = decimal.Decimal(str(number)) + try: + number = decimal.Decimal(str(number)) + except decimal.InvalidOperation as e: + raise NumberFormatError(f"{number!r} is not a valid number") from e if number.is_nan() or number.is_infinite(): return number, None format = None @@ -1477,7 +1480,10 @@ def apply( :raise UnsupportedNumberingSystemError: If the numbering system is not supported by the locale. """ if not isinstance(value, decimal.Decimal): - value = decimal.Decimal(str(value)) + try: + value = decimal.Decimal(str(value)) + except decimal.InvalidOperation as e: + raise NumberFormatError(f"{value!r} is not a valid number") from e value = value.scaleb(self.scale) diff --git a/tests/test_numbers.py b/tests/test_numbers.py index d20a18315..551b7df4a 100644 --- a/tests/test_numbers.py +++ b/tests/test_numbers.py @@ -695,3 +695,10 @@ def test_issue_1076_fixes(): assert numbers.format_compact_decimal(2345678, format_type="long", locale="ja") == "235δΈ‡" # Swahili does not define an accounting currency format; root aliases it to the standard format. assert numbers.format_currency(-2, "USD", format_type="accounting", locale="sw") == "-US$\xa02.00" + + +def test_number_pattern_apply_invalid_string_raises_number_format_error(): + from unittest.mock import MagicMock + pattern = numbers.parse_pattern('0.###') + with pytest.raises(numbers.NumberFormatError, match="is not a valid number"): + pattern.apply('not_a_number', locale=MagicMock())