diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c358c76..ee23bbc 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -22,6 +22,7 @@ repos: - id: debug-statements - id: end-of-file-fixer - id: forbid-submodules + - id: requirements-txt-fixer - id: trailing-whitespace exclude: \.github/ISSUE_TEMPLATE\.md|\.github/PULL_REQUEST_TEMPLATE\.md diff --git a/docs/requirements.txt b/docs/requirements.txt index 2373928..235b349 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,6 +1,6 @@ mkdocs==1.6.1 +mkdocs-include-markdown-plugin mkdocs-material mkdocstrings[python]==0.27.0 -mkdocs-include-markdown-plugin pygments pymdown-extensions==10.14.3 diff --git a/requirements-mypy.txt b/requirements-mypy.txt new file mode 100644 index 0000000..a9d99a5 --- /dev/null +++ b/requirements-mypy.txt @@ -0,0 +1,4 @@ +mypy==1.15.0 +pytest +types-freezegun +types-setuptools diff --git a/src/humanize/i18n.py b/src/humanize/i18n.py index dd94c7a..a6a5cb1 100644 --- a/src/humanize/i18n.py +++ b/src/humanize/i18n.py @@ -4,8 +4,8 @@ import gettext as gettext_module from threading import local -from typing import TYPE_CHECKING +TYPE_CHECKING = False if TYPE_CHECKING: import os import pathlib diff --git a/src/humanize/lists.py b/src/humanize/lists.py index 1573be8..3a52f1e 100644 --- a/src/humanize/lists.py +++ b/src/humanize/lists.py @@ -2,7 +2,9 @@ from __future__ import annotations -from typing import Any +TYPE_CHECKING = False +if TYPE_CHECKING: + from typing import Any __all__ = ["natural_list"] diff --git a/src/humanize/number.py b/src/humanize/number.py index 084d6a6..ac12dc8 100644 --- a/src/humanize/number.py +++ b/src/humanize/number.py @@ -3,24 +3,24 @@ from __future__ import annotations import math -import re -import sys -from typing import TYPE_CHECKING from .i18n import _gettext as _ from .i18n import _ngettext, decimal_separator, thousands_separator from .i18n import _ngettext_noop as NS_ from .i18n import _pgettext as P_ +TYPE_CHECKING = False if TYPE_CHECKING: + import sys + if sys.version_info >= (3, 10): from typing import TypeAlias else: from typing_extensions import TypeAlias -# This type can be better defined by typing.SupportsInt, typing.SupportsFloat -# but that's a Python 3.8 only typing option. -NumberOrString: TypeAlias = "float | str" + # This type can be better defined by typing.SupportsFloat + # but that's a Python 3.8 only typing option. + NumberOrString: TypeAlias = float | str def _format_not_finite(value: float) -> str: @@ -165,6 +165,8 @@ def intcomma(value: NumberOrString, ndigits: int | None = None) -> str: else: orig = str(value) orig = orig.replace(".", decimal_sep) + import re + while True: new = re.sub(r"^(-?\d+)(\d{3})", rf"\g<1>{thousands_sep}\g<2>", orig) if orig == new: @@ -429,6 +431,8 @@ def scientific(value: NumberOrString, precision: int = 2) -> str: n = fmt.format(value) part1, part2 = n.split("e") # Remove redundant leading '+' or '0's (preserving the last '0' for 10⁰). + import re + part2 = re.sub(r"^\+?(\-?)0*(.+)$", r"\1\2", part2) new_part2 = [] diff --git a/src/humanize/time.py b/src/humanize/time.py index b40d76e..b058d05 100644 --- a/src/humanize/time.py +++ b/src/humanize/time.py @@ -5,18 +5,20 @@ from __future__ import annotations -import collections.abc import datetime as dt import math -import typing from enum import Enum from functools import total_ordering -from typing import Any from .i18n import _gettext as _ from .i18n import _ngettext from .number import intcomma +TYPE_CHECKING = False +if TYPE_CHECKING: + from collections.abc import Iterable + from typing import Any + __all__ = [ "naturaldate", "naturalday", @@ -37,7 +39,7 @@ class Unit(Enum): MONTHS = 6 YEARS = 7 - def __lt__(self, other: typing.Any) -> typing.Any: + def __lt__(self, other: Any) -> Any: if self.__class__ is other.__class__: return self.value < other.value return NotImplemented @@ -62,9 +64,7 @@ def _abs_timedelta(delta: dt.timedelta) -> dt.timedelta: return delta -def _date_and_delta( - value: typing.Any, *, now: dt.datetime | None = None -) -> tuple[typing.Any, typing.Any]: +def _date_and_delta(value: Any, *, now: dt.datetime | None = None) -> tuple[Any, Any]: """Turn a value into a date and a timedelta which represents how long ago it was. If that's not possible, return `(None, value)`. @@ -327,7 +327,7 @@ def _quotient_and_remainder( divisor: float, unit: Unit, minimum_unit: Unit, - suppress: collections.abc.Iterable[Unit], + suppress: Iterable[Unit], ) -> tuple[float, float]: """Divide `value` by `divisor` returning the quotient and remainder. @@ -368,7 +368,7 @@ def _carry( ratio: float, unit: Unit, min_unit: Unit, - suppress: typing.Iterable[Unit], + suppress: Iterable[Unit], ) -> tuple[float, float]: """Return a tuple with two values. @@ -401,7 +401,7 @@ def _carry( return value1, value2 -def _suitable_minimum_unit(min_unit: Unit, suppress: typing.Iterable[Unit]) -> Unit: +def _suitable_minimum_unit(min_unit: Unit, suppress: Iterable[Unit]) -> Unit: """Return a minimum unit suitable that is not suppressed. If not suppressed, return the same unit: @@ -430,7 +430,7 @@ def _suitable_minimum_unit(min_unit: Unit, suppress: typing.Iterable[Unit]) -> U return min_unit -def _suppress_lower_units(min_unit: Unit, suppress: typing.Iterable[Unit]) -> set[Unit]: +def _suppress_lower_units(min_unit: Unit, suppress: Iterable[Unit]) -> set[Unit]: """Extend suppressed units (if any) with all units lower than the minimum unit. >>> from humanize.time import _suppress_lower_units, Unit @@ -449,7 +449,7 @@ def _suppress_lower_units(min_unit: Unit, suppress: typing.Iterable[Unit]) -> se def precisedelta( value: dt.timedelta | int | None, minimum_unit: str = "seconds", - suppress: typing.Iterable[str] = (), + suppress: Iterable[str] = (), format: str = "%0.2f", ) -> str: """Return a precise representation of a timedelta. diff --git a/tox.ini b/tox.ini index 508f747..9c75151 100644 --- a/tox.ini +++ b/tox.ini @@ -40,10 +40,7 @@ commands = [testenv:mypy] deps = - mypy==1.12 - pytest - types-freezegun - types-setuptools + -r requirements-mypy.txt commands = mypy . {posargs}