Source code for kwutil.util_units

"""
This is effectively an interface to pint. These are scattered throughout
different codebases, and it might make sense to consolidate them here.

Currently we are using pint for units, we might consider other libraries that
handle units like:

    * pint
    * quantities
    * astropy
    * unyt

See [UnitTableComparison]_ for a table comparing some unit / quantity packages.

References:
    .. [UnTy17] https://github.com/yt-project/unyt/issues/17
    .. [UnTyCompare] https://unyt.readthedocs.io/en/latest/usage.html#working-with-code-that-uses-astropy-units
    .. [UnitTableComparison] https://socialcompare.com/en/comparison/python-units-quantities-packages
"""

try:
    from functools import cache
except ImportError:
    from ubelt import memoize as cache


__all__ = ['unit_registry', 'ureg']  # NOQA


[docs] @cache def unit_registry(): """ A memoized unit registry Returns: pint.UnitRegistry """ import pint ureg = pint.UnitRegistry() return ureg
class __module_property_definitions__: """ experimental feature for handling module level properties. References: https://github.com/scientific-python/lazy-loader/issues/127 """ def __init__(self): self.names = {'ureg'} @property def ureg(self): return unit_registry() __modprops__ = __module_property_definitions__() def __getattr__(name): """ Create a lazy properties for this module that gives quick access to the unit registry. """ if name in __modprops__.names: return getattr(__modprops__, name) else: raise AttributeError(f'Module {__name__!r} has no attribute {name!r}') def byte_str(num, unit='auto', precision=2): """ Automatically chooses relevant unit (KB, MB, or GB) for displaying some number of bytes. Args: num (int): number of bytes unit (str): which unit to use, can be auto, B, KB, MB, GB, TB, PB, EB, ZB, or YB. precision (int): number of decimals of precision References: https://en.wikipedia.org/wiki/Orders_of_magnitude_(data) Returns: str: string representing the number of bytes with appropriate units Example: >>> import ubelt as ub >>> num_list = [1, 100, 1024, 1048576, 1073741824, 1099511627776] >>> result = ub.urepr(list(map(byte_str, num_list)), nl=0) >>> print(result) ['0.00 KB', '0.10 KB', '1.00 KB', '1.00 MB', '1.00 GB', '1.00 TB'] """ import ubelt as ub abs_num = abs(num) if unit == 'auto': if abs_num < 2.0 ** 10: unit = 'KB' elif abs_num < 2.0 ** 20: unit = 'KB' elif abs_num < 2.0 ** 30: unit = 'MB' elif abs_num < 2.0 ** 40: unit = 'GB' elif abs_num < 2.0 ** 50: unit = 'TB' elif abs_num < 2.0 ** 60: unit = 'PB' elif abs_num < 2.0 ** 70: unit = 'EB' elif abs_num < 2.0 ** 80: unit = 'ZB' else: unit = 'YB' if unit.lower().startswith('b'): num_unit = num elif unit.lower().startswith('k'): num_unit = num / (2.0 ** 10) elif unit.lower().startswith('m'): num_unit = num / (2.0 ** 20) elif unit.lower().startswith('g'): num_unit = num / (2.0 ** 30) elif unit.lower().startswith('t'): num_unit = num / (2.0 ** 40) elif unit.lower().startswith('p'): num_unit = num / (2.0 ** 50) elif unit.lower().startswith('e'): num_unit = num / (2.0 ** 60) elif unit.lower().startswith('z'): num_unit = num / (2.0 ** 70) elif unit.lower().startswith('y'): num_unit = num / (2.0 ** 80) else: raise ValueError('unknown num={!r} unit={!r}'.format(num, unit)) return ub.urepr(num_unit, precision=precision) + ' ' + unit