Skip to content

Commit a2ff89c

Browse files
lohitredddyhugovk
authored andcommitted
Correct clamp annotations for callable formatters and None
1 parent 4b806c4 commit a2ff89c

2 files changed

Lines changed: 38 additions & 4 deletions

File tree

‎src/humanize/number.py‎

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
__lazy_modules__ = {"bisect"}
66

77
import bisect
8+
from typing import overload
89

910
from .i18n import _gettext as _
1011
from .i18n import _gettext_noop as N_
@@ -15,6 +16,7 @@
1516

1617
TYPE_CHECKING = False
1718
if TYPE_CHECKING:
19+
from collections.abc import Callable
1820
from typing import TypeAlias
1921

2022
# This type can be better defined by typing.SupportsFloat
@@ -457,14 +459,36 @@ def scientific(value: NumberOrString, precision: int = 2) -> str:
457459
return part1 + " x 10" + str(int(part2)).translate(_SUPERSCRIPT_TRANS)
458460

459461

462+
@overload
463+
def clamp(
464+
value: None,
465+
format: str | Callable[[float], str] = "{:}",
466+
floor: float | None = None,
467+
ceil: float | None = None,
468+
floor_token: str = "<",
469+
ceil_token: str = ">",
470+
) -> None: ...
471+
472+
473+
@overload
460474
def clamp(
461475
value: float,
462-
format: str = "{:}",
476+
format: str | Callable[[float], str] = "{:}",
463477
floor: float | None = None,
464478
ceil: float | None = None,
465479
floor_token: str = "<",
466480
ceil_token: str = ">",
467-
) -> str:
481+
) -> str: ...
482+
483+
484+
def clamp(
485+
value: float | None,
486+
format: str | Callable[[float], str] = "{:}",
487+
floor: float | None = None,
488+
ceil: float | None = None,
489+
floor_token: str = "<",
490+
ceil_token: str = ">",
491+
) -> str | None:
468492
"""Returns number with the specified format, clamped between floor and ceil.
469493
470494
If the number is larger than ceil or smaller than floor, then the respective limit
@@ -488,7 +512,7 @@ def clamp(
488512
```
489513
490514
Args:
491-
value (int, float): Input number.
515+
value (int, float, None): Input number, or `None` to return `None`.
492516
format (str OR callable): Can either be a formatting string, or a callable
493517
function that receives value and returns a string.
494518
floor (int, float): Smallest value before clamping.
@@ -499,7 +523,8 @@ def clamp(
499523
to output.
500524
501525
Returns:
502-
str: Formatted number. The output is clamped between the indicated floor and
526+
str | None: Formatted number, or `None` when `value` is `None`.
527+
The output is clamped between the indicated floor and
503528
ceil. If the number is larger than ceil or smaller than floor, the output
504529
will be prepended with a token indicating as such.
505530

‎tests/test_number.py‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,15 @@ def test_clamp(test_args: list[typing.Any], expected: str) -> None:
297297
assert humanize.clamp(*test_args) == expected
298298

299299

300+
def test_clamp_return_types() -> None:
301+
assert typing.assert_type(humanize.clamp(1.0), str) == "1.0"
302+
assert typing.assert_type(humanize.clamp(None), None) is None
303+
assert (
304+
typing.assert_type(humanize.clamp(1, format=humanize.intword, floor=1e6), str)
305+
== "<1.0 million"
306+
)
307+
308+
300309
@pytest.mark.parametrize(
301310
"test_args, expected",
302311
[

0 commit comments

Comments
 (0)