diff --git a/src/humanize/time.py b/src/humanize/time.py index 4a07d52..37332ee 100644 --- a/src/humanize/time.py +++ b/src/humanize/time.py @@ -7,6 +7,7 @@ __lazy_modules__ = {"humanize.i18n", "humanize.number"} +import math from enum import Enum from functools import total_ordering @@ -91,6 +92,10 @@ def _date_and_delta( date = now - delta except (ValueError, TypeError): return None, value + except OverflowError: + if not math.isfinite(value): + return None, value + raise return date, _abs_timedelta(delta) @@ -153,6 +158,10 @@ def naturaldelta( delta = dt.timedelta(seconds=value) except (ValueError, TypeError): return str(value) + except OverflowError: + if not math.isfinite(value): + return str(value) + raise use_months = months diff --git a/tests/test_time.py b/tests/test_time.py index 7699770..f1f44d7 100644 --- a/tests/test_time.py +++ b/tests/test_time.py @@ -3,6 +3,7 @@ from __future__ import annotations import datetime as dt +import math import typing import pytest @@ -71,6 +72,8 @@ def test_date_and_delta() -> None: assert_equal_datetime(date, result[0]) assert_equal_timedelta(d, result[1]) assert time._date_and_delta("NaN") == (None, "NaN") + assert time._date_and_delta(float("inf")) == (None, float("inf")) + assert time._date_and_delta(float("-inf")) == (None, float("-inf")) # Tests for the public interface of humanize.time @@ -89,6 +92,12 @@ def test_naturaldelta_nomonths(test_input: dt.timedelta, expected: str) -> None: assert humanize.naturaldelta(test_input, months=False) == expected +def test_naturaldelta_too_large_value_raises() -> None: + """A too-large finite value still raises OverflowError (unlike inf).""" + with pytest.raises(OverflowError): + humanize.naturaldelta(1e30) + + @pytest.mark.parametrize( "test_input, expected", [ @@ -128,13 +137,17 @@ def test_naturaldelta_nomonths(test_input: dt.timedelta, expected: str) -> None: (dt.timedelta(days=365), "a year"), (dt.timedelta(days=365 * 1_141), "1,141 years"), ("NaN", "NaN"), # Returns non-numbers unchanged. + (float("inf"), "inf"), + (float("-inf"), "-inf"), # largest possible timedelta (dt.timedelta(days=999_999_999), "2,739,726 years"), ], ) def test_naturaldelta(test_input: float | dt.timedelta, expected: str) -> None: assert humanize.naturaldelta(test_input) == expected - if not isinstance(test_input, str): + if not isinstance(test_input, str) and not ( + isinstance(test_input, float) and math.isinf(test_input) + ): assert humanize.naturaldelta(-test_input) == expected @@ -179,6 +192,8 @@ def test_naturaldelta(test_input: float | dt.timedelta, expected: str) -> None: (NOW - dt.timedelta(days=365 * 2 + 65), "2 years ago"), (NOW - dt.timedelta(days=365 + 4), "1 year, 4 days ago"), ("NaN", "NaN"), + (float("inf"), "inf"), + (float("-inf"), "-inf"), ], ) def test_naturaltime( @@ -817,6 +832,8 @@ def test_precisedelta_suppress_units( def test_precisedelta_bogus_call() -> None: assert humanize.precisedelta(None) == "None" + assert humanize.precisedelta(float("inf")) == "inf" + assert humanize.precisedelta(float("-inf")) == "-inf" with pytest.raises( ValueError,