|
4 | 4 |
|
5 | 5 | from math import log |
6 | 6 |
|
7 | | -from humanize.i18n import gettext as _ |
| 7 | +from humanize.i18n import _gettext as _ |
8 | 8 |
|
9 | | -_SUFFIXES = { |
| 9 | +suffixes = { |
10 | 10 | "decimal": ( |
11 | | - _(" kB"), |
12 | | - _(" MB"), |
13 | | - _(" GB"), |
14 | | - _(" TB"), |
15 | | - _(" PB"), |
16 | | - _(" EB"), |
17 | | - _(" ZB"), |
18 | | - _(" YB"), |
19 | | - _(" RB"), |
20 | | - _(" QB"), |
| 11 | + "kB", |
| 12 | + "MB", |
| 13 | + "GB", |
| 14 | + "TB", |
| 15 | + "PB", |
| 16 | + "EB", |
| 17 | + "ZB", |
| 18 | + "YB", |
| 19 | + "RB", |
| 20 | + "QB", |
21 | 21 | ), |
22 | 22 | "binary": ( |
23 | | - _(" KiB"), |
24 | | - _(" MiB"), |
25 | | - _(" GiB"), |
26 | | - _(" TiB"), |
27 | | - _(" PiB"), |
28 | | - _(" EiB"), |
29 | | - _(" ZiB"), |
30 | | - _(" YiB"), |
31 | | - _(" RiB"), |
32 | | - _(" QiB"), |
| 23 | + "KiB", |
| 24 | + "MiB", |
| 25 | + "GiB", |
| 26 | + "TiB", |
| 27 | + "PiB", |
| 28 | + "EiB", |
| 29 | + "ZiB", |
| 30 | + "YiB", |
| 31 | + "RiB", |
| 32 | + "QiB", |
33 | 33 | ), |
| 34 | + "gnu": "KMGTPEZYRQ", |
34 | 35 | } |
35 | 36 |
|
36 | 37 |
|
37 | 38 | def naturalsize( |
38 | 39 | value: float | str, |
39 | 40 | binary: bool = False, |
| 41 | + gnu: bool = False, |
40 | 42 | format: str = "%.1f", |
41 | 43 | ) -> str: |
42 | | - """Format a number of bytes like a human-readable file size. |
| 44 | + """Format a number of bytes like a human-readable filesize (e.g. 10 kB). |
| 45 | +
|
| 46 | + By default, decimal suffixes (kB, MB) are used. |
| 47 | +
|
| 48 | + Non-GNU modes are compatible with jinja2's `filesizeformat` filter. |
43 | 49 |
|
44 | 50 | Examples: |
45 | | - >>> naturalsize(42) |
46 | | - '42 Bytes' |
47 | | - >>> naturalsize(42000) |
48 | | - '42.0 kB' |
49 | | - >>> naturalsize(42000000) |
50 | | - '42.0 MB' |
51 | | -
|
52 | | - When a locale is activated via ``humanize.i18n.activate()``, |
53 | | - the unit suffixes will be translated accordingly. |
54 | | -
|
55 | | - :param value: The number of bytes. |
56 | | - :param binary: Use binary (powers of 1024) units instead of decimal. |
57 | | - :param format: Numeric format string. |
58 | | - :return: Human-readable file size. |
59 | | - """ |
60 | | - try: |
61 | | - bytes_value = float(value) |
62 | | - except (TypeError, ValueError): |
63 | | - return str(value) |
| 51 | + ```pycon |
| 52 | + >>> naturalsize(3000000) |
| 53 | + '3.0 MB' |
| 54 | + >>> naturalsize(300, False, True) |
| 55 | + '300B' |
| 56 | + >>> naturalsize(3000, False, True) |
| 57 | + '2.9K' |
| 58 | + >>> naturalsize(3000, False, True, "%.3f") |
| 59 | + '2.930K' |
| 60 | + >>> naturalsize(3000, True) |
| 61 | + '2.9 KiB' |
| 62 | + >>> naturalsize(10**28) |
| 63 | + '10.0 RB' |
| 64 | + >>> naturalsize(10**34 * 3) |
| 65 | + '30000.0 QB' |
| 66 | + >>> naturalsize(-4096, True) |
| 67 | + '-4.0 KiB' |
64 | 68 |
|
65 | | - if bytes_value == 1: |
66 | | - return _("1 Byte") |
67 | | - if bytes_value < 1024: |
68 | | - return _("%d Bytes") % bytes_value |
| 69 | + ``` |
| 70 | +
|
| 71 | + Args: |
| 72 | + value (int, float, str): Integer to convert. |
| 73 | + binary (bool): If `True`, uses binary suffixes (KiB, MiB) with base |
| 74 | + 2<sup>10</sup> instead of 10<sup>3</sup>. |
| 75 | + gnu (bool): If `True`, the binary argument is ignored and GNU-style |
| 76 | + (`ls -sh` style) prefixes are used (K, M) with the 2**10 definition. |
| 77 | + format (str): Custom formatter. |
| 78 | +
|
| 79 | + Returns: |
| 80 | + str: Human readable representation of a filesize. |
| 81 | + """ |
| 82 | + if gnu: |
| 83 | + suffix = suffixes["gnu"] |
| 84 | + elif binary: |
| 85 | + suffix = suffixes["binary"] |
| 86 | + else: |
| 87 | + suffix = suffixes["decimal"] |
69 | 88 |
|
70 | | - base = 1024 if binary else 1000 |
71 | | - exp = int(log(bytes_value, base)) |
72 | | - exp = min(exp, len(_SUFFIXES["binary"]) if binary else len(_SUFFIXES["decimal"])) |
| 89 | + base = 1024 if (gnu or binary) else 1000 |
| 90 | + bytes_ = float(value) |
| 91 | + abs_bytes = abs(bytes_) |
73 | 92 |
|
74 | | - value = bytes_value / base**exp |
| 93 | + if abs_bytes == 1 and not gnu: |
| 94 | + return f"{int(bytes_)} Byte" |
75 | 95 |
|
76 | | - suffix = _SUFFIXES["binary"][exp - 1] if binary else _SUFFIXES["decimal"][exp - 1] |
| 96 | + if abs_bytes < base: |
| 97 | + return f"{int(bytes_)}B" if gnu else f"{int(bytes_)} Bytes" |
77 | 98 |
|
78 | | - return (format % value) + suffix |
| 99 | + exp = int(min(log(abs_bytes, base), len(suffix))) |
| 100 | + space = "" if gnu else " " |
| 101 | + ret: str = format % (bytes_ / (base**exp)) + space + _(suffix[exp - 1]) |
| 102 | + return ret |
0 commit comments