├── LICENSE
├── README.md
├── khnum
├── __init__.py
├── datapoint.py
└── khnum.py
├── setup.py
└── tests
└── test_basic.py
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2019 siznax
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | SOFTWARE.
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | khnum 𓎹𓃝
2 | ==========
3 |
4 |
7 |
8 | the god of human readable numbers
9 |
10 |
11 | Install
12 | -------
13 |
14 | ```
15 | pip install khnum
16 | 𓎹𓃝
17 | ```
18 |
19 | Usage
20 | -----
21 |
22 | ```
23 | import khnum
24 | ```
25 |
26 | Examples
27 | --------
28 |
29 | ```
30 | >>> khnum.cnum(1234567890)
31 | '1,234,567,890'
32 | ```
33 |
34 | ```
35 | >>> khnum.hnum(1234567890)
36 | '1.2B'
37 | ```
38 |
39 | ```
40 | >>> khnum.hnum(1234567890, 'bytes')
41 | '1.2GB'
42 | ```
43 |
44 | ```
45 | >>> khnum.hnum(1234567890, 'si')
46 | '1.1GiB'
47 | ```
48 |
49 | ```
50 | >>> num = khnum.num(1234567890, 'b')
51 | >>> print(num)
52 | {
53 | "_num": 1234567890,
54 | "cnum": "1,234,567,890",
55 | "hnum": "1.2GB",
56 | "line": "1.2GB (1,234,567,890)",
57 | "type": "",
58 | "units": "b"
59 | }
60 | ```
61 |
62 |
63 | Help
64 | ----
65 |
66 | ```
67 | $ pydoc khnum
68 | ```
69 |
70 | ```
71 | >>> help(khnum)
72 | ```
73 |
74 |
75 | Test
76 | ----
77 |
78 | ```
79 | $ pytest
80 | ```
81 |
82 |
83 | Contributors
84 | ------------
85 |
86 | * [Fred Cirera](https://stackoverflow.com/questions/1094841/reusable-library-to-get-human-readable-version-of-file-size)
87 |
88 |
89 | @siznax
90 |
--------------------------------------------------------------------------------
/khnum/__init__.py:
--------------------------------------------------------------------------------
1 | '''
2 | human readable numbers
3 | '''
4 |
5 | __author__ = 'steve@siznax.net'
6 | __version__ = '0.0.3'
7 |
8 |
9 | from .datapoint import Datapoint as num
10 | from .khnum import cnum, hnum, pretty
11 |
--------------------------------------------------------------------------------
/khnum/datapoint.py:
--------------------------------------------------------------------------------
1 | '''
2 | Datapoint class
3 | ~~~~~~~~~~~~~~~
4 | '''
5 |
6 | from . import khnum
7 |
8 |
9 | class Datapoint():
10 |
11 | '''
12 | represents a human readable number
13 | '''
14 |
15 | def __init__(self, num, units='d'):
16 | '''
17 | returns Datapoint instance
18 | '''
19 | self.update(num, units)
20 |
21 | def __str__(self):
22 | return khnum.pretty(dict(self.__dict__))
23 |
24 | def update(self, num, units='d'):
25 | '''
26 | update datapoint value with number and (optional) khnum units
27 | '''
28 | self.type = str(type(num))
29 | self._num = num
30 | self.cnum = khnum.cnum(num)
31 | self.hnum = khnum.hnum(num, units)
32 | self.line = '{} ({})'.format(self.hnum, self.cnum)
33 | self.units = units
34 |
--------------------------------------------------------------------------------
/khnum/khnum.py:
--------------------------------------------------------------------------------
1 | '''
2 | Khnum functions
3 | ~~~~~~~~~~~~~~~
4 | '''
5 |
6 | import json
7 |
8 | UNITS = ['decimal', 'bytes', 'si']
9 |
10 |
11 | def cnum(num):
12 | '''
13 | returns str with thousands separated by commas
14 |
15 | >>> khnum.cnum(123456789)
16 | '123,456,789'
17 | '''
18 | return "{:,}".format(num)
19 |
20 |
21 | def hnum(num, units='decimal'):
22 | '''
23 | returns rounded human readable str with units suffix
24 |
25 | >>> khnum.hnum(123456789) # decimal
26 | '123.5M' # million
27 |
28 | >>> khnum.hnum(123456789, 'b') # bytes
29 | '123.5MB' # megabytes
30 |
31 | >>> khnum.hnum(123456789, 's') # SI
32 | '117.7MiB' # mebibytes
33 |
34 | >>> khnum.hnum(123456789e24, 'si')
35 | '102121062.3YiB' # yobibytes
36 |
37 | raises ValueError for un-supported units
38 |
39 | Power Decimal Bytes SI (binary)
40 | ---------------------------------------------
41 | 10^3 Kilo (K) Kilo (KB) 1024^1 Kibi (KiB)
42 | 10^6 Mill (M) Mega (MB) 1024^2 Mebi (MiB)
43 | 10^9 Bill (B) Giga (GB) 1024^3 Gibi (GiB)
44 | 10^12 Tril (T) Tera (TB) 1024^4 Tebi (TiB)
45 | 10^15 Quad (Q) Peta (PB) 1024^5 Pebi (PiB)
46 | 10^18 Quin (Qn) Exa- (EB) 1024^6 Exbi (EiB)
47 | 10^21 Sext (S) Zeta (ZB) 1024^7 Zebi (ZiB)
48 | 10^24 Sept (Sp) Yota (YB) 1024^8 Yobi (YiB)
49 | '''
50 |
51 | if units.lower().startswith('d'): # decimal
52 | units = ['', 'K', 'M', 'B', 'T', 'Q', 'Qn', 'S']
53 | boundary = 1000.0
54 | last_unit = 'Sp'
55 | suffix = ''
56 | elif units.lower().startswith('b'): # bytes
57 | units = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z']
58 | boundary = 1000.0
59 | last_unit = 'Y'
60 | suffix = 'B'
61 | elif units.lower().startswith('s'): # SI
62 | units = ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi']
63 | boundary = 1024.0
64 | last_unit = 'Yi'
65 | suffix = 'B'
66 | else:
67 | raise ValueError('unsupported units: %s' % units)
68 |
69 | for unit in units:
70 | if abs(num) < boundary:
71 | return "%3.1f%s%s" % (num, unit, suffix)
72 | num /= boundary
73 |
74 | return "%.1f%s%s" % (num, last_unit, suffix)
75 |
76 |
77 | def pretty(data):
78 | '''
79 | returns prettified JSON from data
80 | '''
81 | return json.dumps(data,
82 | indent=4,
83 | sort_keys=True,
84 | separators=(',', ': '))
85 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup, find_packages
2 |
3 | with open('README.md') as rfh:
4 | long_description = rfh.read()
5 |
6 | setup(
7 | author='siznax',
8 | author_email='steve@siznax.net',
9 | classifiers=[
10 | "Programming Language :: Python :: 3",
11 | "License :: OSI Approved :: MIT License",
12 | "Operating System :: OS Independent",
13 | ],
14 | description='Human readable numbers',
15 | long_description=long_description,
16 | long_description_content_type="text/markdown",
17 | name='khnum',
18 | packages=find_packages(),
19 | tests_require=['pytest'],
20 | url='https://github.com/siznax/khnum/',
21 | version='0.0.3'
22 | )
23 |
--------------------------------------------------------------------------------
/tests/test_basic.py:
--------------------------------------------------------------------------------
1 | '''
2 | Khnum basic tests
3 | '''
4 |
5 | from khnum import khnum # export PYTHONPATH=..
6 |
7 |
8 | def test_datapoint():
9 | num = khnum.num(1234567890)
10 | assert num.cnum == '1,234,567,890'
11 | assert num.hnum == '1.2B'
12 |
13 | def test_datapoint_bytes():
14 | num = khnum.num(1234567890, 'byte')
15 | assert num.cnum == '1,234,567,890'
16 | assert num.hnum == '1.2GB'
17 |
18 | def test_datapoint_print():
19 | num = khnum.num(1234567890)
20 | print(num)
21 |
22 | def test_datapoint_si():
23 | num = khnum.num(1234567890, 'si')
24 | assert num.cnum == '1,234,567,890'
25 | assert num.hnum == '1.1GiB'
26 |
27 | def test_datapoint_update():
28 | num = khnum.num(1234567890)
29 |
30 | assert num.cnum == '1,234,567,890'
31 | assert num.hnum == '1.2B'
32 |
33 | num.update(987654321, 'b')
34 |
35 | assert num.cnum == '987,654,321'
36 | assert num.hnum == '987.7MB'
37 |
38 | def test_datapoint_compact():
39 | num = khnum.num(1234567890, 'b')
40 | assert num.line == '1.2GB (1,234,567,890)'
41 |
--------------------------------------------------------------------------------