diff --git a/arrow/arrow.py b/arrow/arrow.py index eecf23266..6db40b3b4 100644 --- a/arrow/arrow.py +++ b/arrow/arrow.py @@ -1394,6 +1394,11 @@ def dehumanize(self, input_string: str, locale: str = "en_us") -> "Arrow": # Create a regex pattern object for numbers num_pattern = re.compile(r"\d+") + if not isinstance(input_string, str): + raise TypeError( + f"input_string must be str, not {type(input_string).__name__}." + ) + # Search input string for each time unit within locale for unit, unit_object in locale_obj.timeframes.items(): # Need to check the type of unit_object to create the correct dictionary diff --git a/tests/test_arrow.py b/tests/test_arrow.py index b595e4e21..edd197c19 100644 --- a/tests/test_arrow.py +++ b/tests/test_arrow.py @@ -2868,6 +2868,14 @@ def test_normalized_locale(self): assert arw.dehumanize(second_ago_string, locale="zh_hk") == second_ago assert arw.dehumanize(second_future_string, locale="zh_hk") == second_future + # Non-str input should raise a clear TypeError rather than crashing + # deep in the regex / timeframes iteration with AttributeError. + def test_non_str_input_raises_typeerror(self): + arw = arrow.Arrow(2024, 1, 1) + for bad in (123, None, [1, 2], b"bytes"): + with pytest.raises(TypeError, match="input_string must be str"): + arw.dehumanize(bad) + # Ensures relative units are required in string def test_require_relative_unit(self, locale_list_no_weeks: List[str]): for lang in locale_list_no_weeks: