diff --git a/src/humanize/number.py b/src/humanize/number.py index ac12dc8..5154895 100644 --- a/src/humanize/number.py +++ b/src/humanize/number.py @@ -2,8 +2,6 @@ from __future__ import annotations -import math - from .i18n import _gettext as _ from .i18n import _ngettext, decimal_separator, thousands_separator from .i18n import _ngettext_noop as NS_ @@ -25,6 +23,8 @@ def _format_not_finite(value: float) -> str: """Utility function to handle infinite and nan cases.""" + import math + if math.isnan(value): return "NaN" if math.isinf(value) and value < 0: @@ -71,6 +71,8 @@ def ordinal(value: NumberOrString, gender: str = "male") -> str: Returns: str: Ordinal string. """ + import math + try: if not math.isfinite(float(value)): return _format_not_finite(float(value)) @@ -142,6 +144,8 @@ def intcomma(value: NumberOrString, ndigits: int | None = None) -> str: Returns: str: String containing commas every three digits. """ + import math + thousands_sep = thousands_separator() decimal_sep = decimal_separator() try: @@ -226,6 +230,8 @@ def intword(value: NumberOrString, format: str = "%.1f") -> str: str: Friendly text representation as a string, unless the value passed could not be coaxed into an `int`. """ + import math + try: if not math.isfinite(float(value)): return _format_not_finite(float(value)) @@ -293,6 +299,8 @@ def apnumber(value: NumberOrString) -> str: returns a string unless the value was not `int`-able, then `str(value)` is returned. """ + import math + try: if not math.isfinite(float(value)): return _format_not_finite(float(value)) @@ -354,6 +362,8 @@ def fractional(value: NumberOrString) -> str: Returns: str: Fractional number as a string. """ + import math + try: number = float(value) if not math.isfinite(number): @@ -408,6 +418,8 @@ def scientific(value: NumberOrString, precision: int = 2) -> str: Returns: str: Number in scientific notation z.wq x 10ⁿ. """ + import math + exponents = { "0": "⁰", "1": "¹", @@ -491,6 +503,8 @@ def clamp( will be prepended with a token indicating as such. """ + import math + if value is None: return None @@ -554,6 +568,8 @@ def metric(value: float, unit: str = "", precision: int = 3) -> str: Returns: str: """ + import math + if not math.isfinite(value): return _format_not_finite(value) exponent = int(math.floor(math.log10(abs(value)))) if value != 0 else 0 diff --git a/src/humanize/time.py b/src/humanize/time.py index b058d05..099a6ca 100644 --- a/src/humanize/time.py +++ b/src/humanize/time.py @@ -5,8 +5,6 @@ from __future__ import annotations -import datetime as dt -import math from enum import Enum from functools import total_ordering @@ -16,6 +14,7 @@ TYPE_CHECKING = False if TYPE_CHECKING: + import datetime as dt from collections.abc import Iterable from typing import Any @@ -46,6 +45,8 @@ def __lt__(self, other: Any) -> Any: def _now() -> dt.datetime: + import datetime as dt + return dt.datetime.now() @@ -69,6 +70,8 @@ def _date_and_delta(value: Any, *, now: dt.datetime | None = None) -> tuple[Any, If that's not possible, return `(None, value)`. """ + import datetime as dt + if not now: now = _now() if isinstance(value, dt.datetime): @@ -123,6 +126,8 @@ def naturaldelta( assert naturaldelta(later - now) == "30 minutes" """ + import datetime as dt + tmp = Unit[minimum_unit.upper()] if tmp not in (Unit.SECONDS, Unit.MILLISECONDS, Unit.MICROSECONDS): msg = f"Minimum unit '{minimum_unit}' not supported" @@ -246,6 +251,8 @@ def naturaltime( Returns: str: A natural representation of the input in a resolution that makes sense. """ + import datetime as dt + value = _convert_aware_datetime(value) when = _convert_aware_datetime(when) @@ -271,6 +278,8 @@ def _convert_aware_datetime( value: dt.datetime | dt.timedelta | float | None, ) -> Any: """Convert aware datetime to naive datetime and pass through any other type.""" + import datetime as dt + if isinstance(value, dt.datetime) and value.tzinfo is not None: value = dt.datetime.fromtimestamp(value.timestamp()) return value @@ -284,6 +293,8 @@ def naturalday(value: dt.date | dt.datetime, format: str = "%b %d") -> str: formatted according to `format`. """ + import datetime as dt + try: value = dt.date(value.year, value.month, value.day) except AttributeError: @@ -308,6 +319,8 @@ def naturalday(value: dt.date | dt.datetime, format: str = "%b %d") -> str: def naturaldate(value: dt.date | dt.datetime) -> str: """Like `naturalday`, but append a year for dates more than ~five months away.""" + import datetime as dt + try: value = dt.date(value.year, value.month, value.day) except AttributeError: @@ -594,6 +607,8 @@ def precisedelta( if fmt_value > 0 or (not texts and unit == min_unit): _fmt_value = 2 if 1 < fmt_value < 2 else int(fmt_value) fmt_txt = _ngettext(singular_txt, plural_txt, _fmt_value) + import math + if unit == min_unit and math.modf(fmt_value)[0] > 0: fmt_txt = fmt_txt.replace("%d", format) elif unit == YEARS: