├── 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 | Khnum image 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 | --------------------------------------------------------------------------------