Source code for toil.lib.conversions

"""
Conversion utilities for mapping memory, disk, core declarations from strings to numbers and vice versa.
Also contains general conversion functions
"""

import math
from typing import SupportsInt, Tuple, Union

# See https://en.wikipedia.org/wiki/Binary_prefix
BINARY_PREFIXES = ['ki', 'mi', 'gi', 'ti', 'pi', 'ei', 'kib', 'mib', 'gib', 'tib', 'pib', 'eib']
DECIMAL_PREFIXES = ['b', 'k', 'm', 'g', 't', 'p', 'e', 'kb', 'mb', 'gb', 'tb', 'pb', 'eb']
VALID_PREFIXES = BINARY_PREFIXES + DECIMAL_PREFIXES


[docs] def bytes_in_unit(unit: str = 'B') -> int: num_bytes = 1 if unit.lower() in ['ki', 'kib']: num_bytes = 1 << 10 if unit.lower() in ['mi', 'mib']: num_bytes = 1 << 20 if unit.lower() in ['gi', 'gib']: num_bytes = 1 << 30 if unit.lower() in ['ti', 'tib']: num_bytes = 1 << 40 if unit.lower() in ['pi', 'pib']: num_bytes = 1 << 50 if unit.lower() in ['ei', 'eib']: num_bytes = 1 << 60 if unit.lower() in ['k', 'kb']: num_bytes = 1000 if unit.lower() in ['m', 'mb']: num_bytes = 1000 ** 2 if unit.lower() in ['g', 'gb']: num_bytes = 1000 ** 3 if unit.lower() in ['t', 'tb']: num_bytes = 1000 ** 4 if unit.lower() in ['p', 'pb']: num_bytes = 1000 ** 5 if unit.lower() in ['e', 'eb']: num_bytes = 1000 ** 6 return num_bytes
[docs] def convert_units(num: float, src_unit: str, dst_unit: str = 'B') -> float: """Returns a float representing the converted input in dst_units.""" if not src_unit.lower() in VALID_PREFIXES: raise RuntimeError(f"{src_unit} not a valid unit, valid units are {VALID_PREFIXES}.") if not dst_unit.lower() in VALID_PREFIXES: raise RuntimeError(f"{dst_unit} not a valid unit, valid units are {VALID_PREFIXES}.") return (num * bytes_in_unit(src_unit)) / bytes_in_unit(dst_unit)
[docs] def parse_memory_string(string: str) -> Tuple[float, str]: """ Given a string representation of some memory (i.e. '1024 Mib'), return the number and unit. """ for i, character in enumerate(string): # find the first character of the unit if character not in '0123456789.-_ ': units = string[i:].strip() if not units.lower() in VALID_PREFIXES: raise RuntimeError(f"{units} not a valid unit, valid units are {VALID_PREFIXES}.") return float(string[:i]), units return float(string), 'b'
[docs] def human2bytes(string: str) -> int: """ Given a string representation of some memory (i.e. '1024 Mib'), return the integer number of bytes. """ value, unit = parse_memory_string(string) return int(convert_units(value, src_unit=unit, dst_unit='b'))
[docs] def bytes2human(n: SupportsInt) -> str: """Return a binary value as a human readable string with units.""" n = int(n) if n < 0: raise ValueError("n < 0") elif n < 1: return '0 b' power_level = math.floor(math.log(n, 1024)) units = ('b', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei') unit = units[power_level if power_level < len(units) else -1] value = convert_units(n, "b", unit) return f'{value:.1f} {unit}'
[docs] def b_to_mib(n: Union[int, float]) -> float: """ Convert a number from bytes to mibibytes. """ return convert_units(n, 'b', 'mib')
[docs] def mib_to_b(n: Union[int, float]) -> float: """ Convert a number from mibibytes to bytes. """ return convert_units(n, 'mib', 'b')
#General Conversions
[docs] def hms_duration_to_seconds(hms: str) -> float: """ Parses a given time string in hours:minutes:seconds, returns an equivalent total seconds value """ vals_to_convert = hms.split(':') seconds = 0.0 for val in vals_to_convert: if(float(val) < 0): raise ValueError("Invalid Time, negative value") if(len(vals_to_convert) != 3): raise ValueError("Invalid amount of fields, function takes input in 'hh:mm:ss'") seconds += float(vals_to_convert[0]) * 60 * 60 seconds += float(vals_to_convert[1]) * 60 seconds += float(vals_to_convert[2]) return seconds
[docs] def strtobool(val: str) -> bool: """ Make a human-readable string into a bool. Convert a string along the lines of "y", "1", "ON", "TrUe", or "Yes" to True, and the corresponding false-ish values to False. """ # We only track prefixes, so "y" covers "y", "yes", # and "yeah no" and makes them all True. TABLE = {True: ["1", "on", "y", "t"], False: ["0", "off", "n", "f"]} lowered = val.lower() for result, prefixes in TABLE.items(): for prefix in prefixes: if lowered.startswith(prefix): return result raise ValueError(f"Cannot convert \"{val}\" to a bool")