Source code for cardinal_pythonlib.sizeformatter

#!/usr/bin/env python
# cardinal_pythonlib/sizeformatter.py

"""
===============================================================================

    Original code copyright (C) 2009-2022 Rudolf Cardinal (rudolf@pobox.com).

    This file is part of cardinal_pythonlib.

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

        https://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.

===============================================================================
"""

from typing import Union


[docs]def sizeof_fmt(num: float, suffix: str = "B") -> str: """ Formats a number of bytes in a human-readable binary format (e.g. ``2048`` becomes ``'2 KiB'``); from https://stackoverflow.com/questions/1094841. """ for unit in ("", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi"): if abs(num) < 1024.0: return "%3.1f%s%s" % (num, unit, suffix) num /= 1024.0 return "%.1f%s%s" % (num, "Yi", suffix)
# see: https://en.wikipedia.org/wiki/Binary_prefix SYMBOLS = { "customary": ("B", "K", "M", "G", "T", "P", "E", "Z", "Y"), "customary_ext": ( "byte", "kilo", "mega", "giga", "tera", "peta", "exa", "zetta", "iotta", ), "iec": ("Bi", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi", "Yi"), "iec_ext": ( "byte", "kibi", "mebi", "gibi", "tebi", "pebi", "exbi", "zebi", "yobi", ), }
[docs]def bytes2human( n: Union[int, float], format: str = "%(value).1f %(symbol)s", symbols: str = "customary", ) -> str: """ Converts a number of bytes into a human-readable format. From https://code.activestate.com/recipes/578019-bytes-to-human-human-to-bytes-converter/. Args: n: number of bytes format: a format specification string symbols: can be one of ``"customary"``, ``"customary_ext"``, ``"iec"`` or ``"iec_ext"``; see https://goo.gl/kTQMs Returns: the formatted number Examples: >>> bytes2human(0) '0.0 B' >>> bytes2human(0.9) '0.0 B' >>> bytes2human(1) '1.0 B' >>> bytes2human(1.9) '1.0 B' >>> bytes2human(1024) '1.0 K' >>> bytes2human(1048576) '1.0 M' >>> bytes2human(1099511627776127398123789121) '909.5 Y' >>> bytes2human(9856, symbols="customary") '9.6 K' >>> bytes2human(9856, symbols="customary_ext") '9.6 kilo' >>> bytes2human(9856, symbols="iec") '9.6 Ki' >>> bytes2human(9856, symbols="iec_ext") '9.6 kibi' >>> bytes2human(10000, "%(value).1f %(symbol)s/sec") '9.8 K/sec' >>> # precision can be adjusted by playing with %f operator >>> bytes2human(10000, format="%(value).5f %(symbol)s") '9.76562 K' """ # noqa n = int(n) if n < 0: raise ValueError("n < 0") symbols = SYMBOLS[symbols] prefix = {} for i, s in enumerate(symbols[1:]): prefix[s] = 1 << (i + 1) * 10 for symbol in reversed(symbols[1:]): if n >= prefix[symbol]: value = float(n) / prefix[symbol] return format % locals() return format % dict(symbol=symbols[0], value=n)
[docs]def human2bytes(s: str) -> int: """ Modified from https://code.activestate.com/recipes/578019-bytes-to-human-human-to-bytes-converter/. Attempts to guess the string format based on default symbols set and return the corresponding bytes as an integer. When unable to recognize the format, :exc:`ValueError` is raised. >>> human2bytes('0 B') 0 >>> human2bytes('1 K') 1024 >>> human2bytes('1 M') 1048576 >>> human2bytes('1 Gi') 1073741824 >>> human2bytes('1 tera') 1099511627776 >>> human2bytes('0.5kilo') 512 >>> human2bytes('0.1 byte') 0 >>> human2bytes('1 k') # k is an alias for K 1024 >>> human2bytes('12 foo') Traceback (most recent call last): ... ValueError: can't interpret '12 foo' """ # noqa if not s: raise ValueError(f"Can't interpret {s!r} as integer") try: return int(s) except ValueError: pass init = s num = "" while s and s[0:1].isdigit() or s[0:1] == ".": num += s[0] s = s[1:] num = float(num) letter = s.strip() for name, sset in SYMBOLS.items(): if letter in sset: break else: if letter == "k": # treat 'k' as an alias for 'K' as per https://en.wikipedia.org/wiki/Binary_prefix # noqa sset = SYMBOLS["customary"] letter = letter.upper() else: raise ValueError("can't interpret %r" % init) prefix = {sset[0]: 1} for i, s in enumerate(sset[1:]): prefix[s] = 1 << (i + 1) * 10 return int(num * prefix[letter])