From b2562da2f74f1a46a316bf0f03c052747a6d10c1 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Wed, 12 Feb 2025 23:52:35 +0200 Subject: [PATCH 1/3] Move mypy dependency to autoupdateable requirements file --- .pre-commit-config.yaml | 1 + docs/requirements.txt | 2 +- requirements-mypy.txt | 4 ++++ tox.ini | 5 +---- 4 files changed, 7 insertions(+), 5 deletions(-) create mode 100644 requirements-mypy.txt 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/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} From 9e0c073be886b4a1e93db38f18ac9939482ac12f Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Thu, 13 Feb 2025 00:01:38 +0200 Subject: [PATCH 2/3] Defer typing import for faster overall import --- src/humanize/i18n.py | 2 +- src/humanize/lists.py | 4 +++- src/humanize/number.py | 11 ++++++----- src/humanize/time.py | 21 +++++++++++---------- 4 files changed, 21 insertions(+), 17 deletions(-) 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..27a9424 100644 --- a/src/humanize/number.py +++ b/src/humanize/number.py @@ -4,23 +4,24 @@ 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: diff --git a/src/humanize/time.py b/src/humanize/time.py index b40d76e..5058bcc 100644 --- a/src/humanize/time.py +++ b/src/humanize/time.py @@ -8,15 +8,18 @@ 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 +40,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 +65,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)`. @@ -368,7 +369,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 +402,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 +431,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 +450,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. From 147cbbcb9400ad2428bed232fbb4ea95d68ca895 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Thu, 13 Feb 2025 00:10:40 +0200 Subject: [PATCH 3/3] Defer other imports for faster overall import --- src/humanize/number.py | 5 ++++- src/humanize/time.py | 3 +-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/humanize/number.py b/src/humanize/number.py index 27a9424..ac12dc8 100644 --- a/src/humanize/number.py +++ b/src/humanize/number.py @@ -3,7 +3,6 @@ from __future__ import annotations import math -import re from .i18n import _gettext as _ from .i18n import _ngettext, decimal_separator, thousands_separator @@ -166,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: @@ -430,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 5058bcc..b058d05 100644 --- a/src/humanize/time.py +++ b/src/humanize/time.py @@ -5,7 +5,6 @@ from __future__ import annotations -import collections.abc import datetime as dt import math from enum import Enum @@ -328,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.