Module coscine.utils
This file contains utility classes and functions, mostly taken from another source like StackOverflow. Credit is given where it is due.
Expand source code
###############################################################################
# Coscine Python SDK
# Copyright (c) 2018-2022 RWTH Aachen University
# Licensed under the terms of the MIT License
# #############################################################################
# Coscine, short for Collaborative Scientific Integration Environment is
# a platform for research data management (RDM).
# For more information on Coscine visit https://www.coscine.de/.
#
# Please note that this python module is open source software primarily
# developed and maintained by the scientific community. It is not
# an official service that RWTH Aachen provides support for.
###############################################################################
###############################################################################
# File description
###############################################################################
"""
This file contains utility classes and functions, mostly taken from another
source like StackOverflow. Credit is given where it is due.
"""
###############################################################################
# Dependencies
###############################################################################
from typing import List, Union
###############################################################################
# Class
###############################################################################
# Source: https://stackoverflow.com/questions/12523586/
# python-format-size-application-converting-b-to-kb-mb-gb-tb
# by Mitch McMabers (https://stackoverflow.com/users/8874388/mitch-mcmabers)
# Licensed under the Public Domain
###############################################################################
class HumanBytes:
METRIC_LABELS: List[str] = ["B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]
BINARY_LABELS: List[str] = ["B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"]
PRECISION_OFFSETS: List[float] = [0.5, 0.05, 0.005, 0.0005] # PREDEFINED FOR SPEED.
PRECISION_FORMATS: List[str] = ["{}{:.0f} {}", "{}{:.1f} {}", "{}{:.2f} {}", "{}{:.3f} {}"] # PREDEFINED FOR SPEED.
@staticmethod
def format(num: Union[int, float], metric: bool=False, precision: int=1) -> str:
"""
Human-readable formatting of bytes, using binary (powers of 1024)
or metric (powers of 1000) representation.
"""
assert isinstance(num, (int, float)), "num must be an int or float"
assert isinstance(metric, bool), "metric must be a bool"
assert isinstance(precision, int) and precision >= 0 and precision <= 3, "precision must be an int (range 0-3)"
unit_labels = HumanBytes.METRIC_LABELS if metric else HumanBytes.BINARY_LABELS
last_label = unit_labels[-1]
unit_step = 1000 if metric else 1024
unit_step_thresh = unit_step - HumanBytes.PRECISION_OFFSETS[precision]
is_negative = num < 0
if is_negative: # Faster than ternary assignment or always running abs().
num = abs(num)
for unit in unit_labels:
if num < unit_step_thresh:
# VERY IMPORTANT:
# Only accepts the CURRENT unit if we're BELOW the threshold where
# float rounding behavior would place us into the NEXT unit: F.ex.
# when rounding a float to 1 decimal, any number ">= 1023.95" will
# be rounded to "1024.0". Obviously we don't want ugly output such
# as "1024.0 KiB", since the proper term for that is "1.0 MiB".
break
if unit != last_label:
# We only shrink the number if we HAVEN'T reached the last unit.
# NOTE: These looped divisions accumulate floating point rounding
# errors, but each new division pushes the rounding errors further
# and further down in the decimals, so it doesn't matter at all.
num /= unit_step
return HumanBytes.PRECISION_FORMATS[precision].format("-" if is_negative else "", num, unit)
###############################################################################
Classes
class HumanBytes
-
Expand source code
class HumanBytes: METRIC_LABELS: List[str] = ["B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"] BINARY_LABELS: List[str] = ["B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"] PRECISION_OFFSETS: List[float] = [0.5, 0.05, 0.005, 0.0005] # PREDEFINED FOR SPEED. PRECISION_FORMATS: List[str] = ["{}{:.0f} {}", "{}{:.1f} {}", "{}{:.2f} {}", "{}{:.3f} {}"] # PREDEFINED FOR SPEED. @staticmethod def format(num: Union[int, float], metric: bool=False, precision: int=1) -> str: """ Human-readable formatting of bytes, using binary (powers of 1024) or metric (powers of 1000) representation. """ assert isinstance(num, (int, float)), "num must be an int or float" assert isinstance(metric, bool), "metric must be a bool" assert isinstance(precision, int) and precision >= 0 and precision <= 3, "precision must be an int (range 0-3)" unit_labels = HumanBytes.METRIC_LABELS if metric else HumanBytes.BINARY_LABELS last_label = unit_labels[-1] unit_step = 1000 if metric else 1024 unit_step_thresh = unit_step - HumanBytes.PRECISION_OFFSETS[precision] is_negative = num < 0 if is_negative: # Faster than ternary assignment or always running abs(). num = abs(num) for unit in unit_labels: if num < unit_step_thresh: # VERY IMPORTANT: # Only accepts the CURRENT unit if we're BELOW the threshold where # float rounding behavior would place us into the NEXT unit: F.ex. # when rounding a float to 1 decimal, any number ">= 1023.95" will # be rounded to "1024.0". Obviously we don't want ugly output such # as "1024.0 KiB", since the proper term for that is "1.0 MiB". break if unit != last_label: # We only shrink the number if we HAVEN'T reached the last unit. # NOTE: These looped divisions accumulate floating point rounding # errors, but each new division pushes the rounding errors further # and further down in the decimals, so it doesn't matter at all. num /= unit_step return HumanBytes.PRECISION_FORMATS[precision].format("-" if is_negative else "", num, unit)
Class variables
var BINARY_LABELS : List[str]
var METRIC_LABELS : List[str]
var PRECISION_FORMATS : List[str]
var PRECISION_OFFSETS : List[float]
Static methods
def format(num: Union[int, float], metric: bool = False, precision: int = 1) ‑> str
-
Human-readable formatting of bytes, using binary (powers of 1024) or metric (powers of 1000) representation.
Expand source code
@staticmethod def format(num: Union[int, float], metric: bool=False, precision: int=1) -> str: """ Human-readable formatting of bytes, using binary (powers of 1024) or metric (powers of 1000) representation. """ assert isinstance(num, (int, float)), "num must be an int or float" assert isinstance(metric, bool), "metric must be a bool" assert isinstance(precision, int) and precision >= 0 and precision <= 3, "precision must be an int (range 0-3)" unit_labels = HumanBytes.METRIC_LABELS if metric else HumanBytes.BINARY_LABELS last_label = unit_labels[-1] unit_step = 1000 if metric else 1024 unit_step_thresh = unit_step - HumanBytes.PRECISION_OFFSETS[precision] is_negative = num < 0 if is_negative: # Faster than ternary assignment or always running abs(). num = abs(num) for unit in unit_labels: if num < unit_step_thresh: # VERY IMPORTANT: # Only accepts the CURRENT unit if we're BELOW the threshold where # float rounding behavior would place us into the NEXT unit: F.ex. # when rounding a float to 1 decimal, any number ">= 1023.95" will # be rounded to "1024.0". Obviously we don't want ugly output such # as "1024.0 KiB", since the proper term for that is "1.0 MiB". break if unit != last_label: # We only shrink the number if we HAVEN'T reached the last unit. # NOTE: These looped divisions accumulate floating point rounding # errors, but each new division pushes the rounding errors further # and further down in the decimals, so it doesn't matter at all. num /= unit_step return HumanBytes.PRECISION_FORMATS[precision].format("-" if is_negative else "", num, unit)