Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/cardinal_pythonlib/sizeformatter.py : 13%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1#!/usr/bin/env python
2# cardinal_pythonlib/sizeformatter.py
4"""
5===============================================================================
7 Original code copyright (C) 2009-2021 Rudolf Cardinal (rudolf@pobox.com).
9 This file is part of cardinal_pythonlib.
11 Licensed under the Apache License, Version 2.0 (the "License");
12 you may not use this file except in compliance with the License.
13 You may obtain a copy of the License at
15 https://www.apache.org/licenses/LICENSE-2.0
17 Unless required by applicable law or agreed to in writing, software
18 distributed under the License is distributed on an "AS IS" BASIS,
19 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 See the License for the specific language governing permissions and
21 limitations under the License.
23===============================================================================
24"""
26from typing import Union
29def sizeof_fmt(num: float, suffix: str = 'B') -> str:
30 """
31 Formats a number of bytes in a human-readable binary format (e.g. ``2048``
32 becomes ``'2 KiB'``); from https://stackoverflow.com/questions/1094841.
33 """
34 for unit in ('', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi'):
35 if abs(num) < 1024.0:
36 return "%3.1f%s%s" % (num, unit, suffix)
37 num /= 1024.0
38 return "%.1f%s%s" % (num, 'Yi', suffix)
41# see: https://en.wikipedia.org/wiki/Binary_prefix
42SYMBOLS = {
43 'customary': ('B', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'),
44 'customary_ext': ('byte', 'kilo', 'mega', 'giga', 'tera', 'peta', 'exa',
45 'zetta', 'iotta'),
46 'iec': ('Bi', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi'),
47 'iec_ext': ('byte', 'kibi', 'mebi', 'gibi', 'tebi', 'pebi', 'exbi',
48 'zebi', 'yobi'),
49}
52def bytes2human(n: Union[int, float],
53 format: str = '%(value).1f %(symbol)s',
54 symbols: str = 'customary') -> str:
55 """
56 Converts a number of bytes into a human-readable format.
57 From https://code.activestate.com/recipes/578019-bytes-to-human-human-to-bytes-converter/.
59 Args:
60 n: number of bytes
61 format: a format specification string
62 symbols: can be one of ``"customary"``, ``"customary_ext"``, ``"iec"``
63 or ``"iec_ext"``; see https://goo.gl/kTQMs
65 Returns:
66 the formatted number
68 Examples:
70 >>> bytes2human(0)
71 '0.0 B'
72 >>> bytes2human(0.9)
73 '0.0 B'
74 >>> bytes2human(1)
75 '1.0 B'
76 >>> bytes2human(1.9)
77 '1.0 B'
78 >>> bytes2human(1024)
79 '1.0 K'
80 >>> bytes2human(1048576)
81 '1.0 M'
82 >>> bytes2human(1099511627776127398123789121)
83 '909.5 Y'
85 >>> bytes2human(9856, symbols="customary")
86 '9.6 K'
87 >>> bytes2human(9856, symbols="customary_ext")
88 '9.6 kilo'
89 >>> bytes2human(9856, symbols="iec")
90 '9.6 Ki'
91 >>> bytes2human(9856, symbols="iec_ext")
92 '9.6 kibi'
94 >>> bytes2human(10000, "%(value).1f %(symbol)s/sec")
95 '9.8 K/sec'
97 >>> # precision can be adjusted by playing with %f operator
98 >>> bytes2human(10000, format="%(value).5f %(symbol)s")
99 '9.76562 K'
101 """ # noqa
102 n = int(n)
103 if n < 0:
104 raise ValueError("n < 0")
105 symbols = SYMBOLS[symbols]
106 prefix = {}
107 for i, s in enumerate(symbols[1:]):
108 prefix[s] = 1 << (i + 1) * 10
109 for symbol in reversed(symbols[1:]):
110 if n >= prefix[symbol]:
111 value = float(n) / prefix[symbol]
112 return format % locals()
113 return format % dict(symbol=symbols[0], value=n)
116def human2bytes(s: str) -> int:
117 """
118 Modified from
119 https://code.activestate.com/recipes/578019-bytes-to-human-human-to-bytes-converter/.
121 Attempts to guess the string format based on default symbols
122 set and return the corresponding bytes as an integer.
123 When unable to recognize the format, :exc:`ValueError` is raised.
125 >>> human2bytes('0 B')
126 0
127 >>> human2bytes('1 K')
128 1024
129 >>> human2bytes('1 M')
130 1048576
131 >>> human2bytes('1 Gi')
132 1073741824
133 >>> human2bytes('1 tera')
134 1099511627776
136 >>> human2bytes('0.5kilo')
137 512
138 >>> human2bytes('0.1 byte')
139 0
140 >>> human2bytes('1 k') # k is an alias for K
141 1024
142 >>> human2bytes('12 foo')
143 Traceback (most recent call last):
144 ...
145 ValueError: can't interpret '12 foo'
146 """ # noqa
147 if not s:
148 raise ValueError(f"Can't interpret {s!r} as integer")
149 try:
150 return int(s)
151 except ValueError:
152 pass
153 init = s
154 num = ""
155 while s and s[0:1].isdigit() or s[0:1] == '.':
156 num += s[0]
157 s = s[1:]
158 num = float(num)
159 letter = s.strip()
160 for name, sset in SYMBOLS.items():
161 if letter in sset:
162 break
163 else:
164 if letter == 'k':
165 # treat 'k' as an alias for 'K' as per https://en.wikipedia.org/wiki/Binary_prefix # noqa
166 sset = SYMBOLS['customary']
167 letter = letter.upper()
168 else:
169 raise ValueError("can't interpret %r" % init)
170 prefix = {sset[0]: 1}
171 for i, s in enumerate(sset[1:]):
172 prefix[s] = 1 << (i + 1) * 10
173 return int(num * prefix[letter])