From 14fa7b8af704d7748473967b73886bd0d8730aec Mon Sep 17 00:00:00 2001 From: Anthony Monthe Date: Thu, 17 Mar 2022 14:33:02 +0000 Subject: [PATCH 1/2] Added intsuffix --- src/humanize/__init__.py | 2 ++ src/humanize/number.py | 69 ++++++++++++++++++++++++++++++++++++++++ tests/test_number.py | 36 +++++++++++++++++++++ 3 files changed, 107 insertions(+) diff --git a/src/humanize/__init__.py b/src/humanize/__init__.py index af593d7a..09ec8388 100644 --- a/src/humanize/__init__.py +++ b/src/humanize/__init__.py @@ -8,6 +8,7 @@ fractional, intcomma, intword, + intsuffix, ordinal, scientific, ) @@ -37,6 +38,7 @@ "deactivate", "fractional", "intcomma", + "intsuffix", "intword", "naturaldate", "naturalday", diff --git a/src/humanize/number.py b/src/humanize/number.py index 6611a21f..cb45c016 100644 --- a/src/humanize/number.py +++ b/src/humanize/number.py @@ -211,6 +211,75 @@ def intword(value, format="%.1f"): return str(value) +suffix_powers = [10**x for x in (3, 6, 9, 12, 15, 18, 21, 24, 27)] +suffix_human_powers = ( + NS_("k", "k"), + NS_("M", "M"), + NS_("G", "G"), + NS_("T", "T"), + NS_("P", "P"), + NS_("E", "E"), + NS_("Z", "Z"), + NS_("Y", "Y"), +) + + +def intsuffix(value, format="%.1f"): + """Converts a large integer to a friendly text representation with only suffix. + + Works best for numbers over 1 million. For example, 1_000_000 becomes "1.0 M", + 1200000 becomes "1.2 M" and "1_200_000_000" becomes "1.2 T". Supports up + to decillion (33 digits) and googol (100 digits). + + Examples: + ```pycon + >>> intsuffix("100") + '100' + >>> intsuffix("12400") + '12.4 k' + >>> intsuffix("1000000") + '1.0 M' + >>> intsuffix(1_200_000_000) + '1.2 G' + >>> intsuffix(None) is None + True + >>> intsuffix("1234000", "%0.3f") + '1.234 M' + + ``` + Args: + value (int, float, str): Integer to convert. + format (str): To change the number of decimal or general format of the number + portion. + + Returns: + str: Friendly text representation as a string, unless the value passed could not + be coaxed into an `int`. + """ + try: + value = int(value) + except (TypeError, ValueError): + return value + + if value < suffix_powers[0]: + return str(value) + for ordinal, power in enumerate(suffix_powers[1:], 1): + if value < power: + chopped = value / float(suffix_powers[ordinal - 1]) + if float(format % chopped) == float(10**3): + chopped = value / float(suffix_powers[ordinal]) + singular, plural = suffix_human_powers[ordinal] + return ( + " ".join([format, _ngettext(singular, plural, math.ceil(chopped))]) + ) % chopped + else: + singular, plural = suffix_human_powers[ordinal - 1] + return ( + " ".join([format, _ngettext(singular, plural, math.ceil(chopped))]) + ) % chopped + return str(value) + + def apnumber(value): """Converts an integer to Associated Press style. diff --git a/tests/test_number.py b/tests/test_number.py index eec6217b..059d6226 100644 --- a/tests/test_number.py +++ b/tests/test_number.py @@ -96,6 +96,42 @@ def test_intword(test_args, expected): assert humanize.intword(*test_args) == expected +def test_intsuffix_powers(): + # make sure that suffix_powers & suffix_human_powers have the same number of items + assert len(number.powers) == len(number.human_powers) + + +@pytest.mark.parametrize( + "test_args, expected", + [ + (["100"], "100"), + (["1000"], "1.0 k"), + (["12400"], "12.4 k"), + (["12490"], "12.5 k"), + (["1000000"], "1.0 M"), + (["1200000"], "1.2 M"), + (["1290000"], "1.3 M"), + (["999999999"], "1.0 G"), + (["1000000000"], "1.0 G"), + (["2000000000"], "2.0 G"), + (["999999999999"], "1.0 T"), + (["1000000000000"], "1.0 T"), + (["6000000000000"], "6.0 T"), + (["999999999999999"], "1.0 P"), + (["1000000000000000"], "1.0 P"), + (["1300000000000000"], "1.3 P"), + (["1400000000000000000"], "1.4 E"), + (["3500000000000000000000"], "3.5 Z"), + (["3600000000000000000000000"], "3.6 Y"), + ([None], None), + (["1230000", "%0.2f"], "1.23 M"), + ([10**101], "1" + "0" * 101), + ], +) +def test_intsuffix(test_args, expected): + assert humanize.intsuffix(*test_args) == expected + + @pytest.mark.parametrize( "test_input, expected", [ From 8b809c17faa07d92f2c47e973ca5515f28165b1b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 17 Apr 2022 11:02:52 +0000 Subject: [PATCH 2/2] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/humanize/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/humanize/__init__.py b/src/humanize/__init__.py index 09ec8388..362ffe9b 100644 --- a/src/humanize/__init__.py +++ b/src/humanize/__init__.py @@ -7,8 +7,8 @@ clamp, fractional, intcomma, - intword, intsuffix, + intword, ordinal, scientific, )