1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
|
# -*- coding: utf-8 -*-
# Copyright (c) 2019 Ansible Project
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
from __future__ import annotations
import re
from ansible.module_utils.six import iteritems
SIZE_RANGES = {
'Y': 1 << 80,
'Z': 1 << 70,
'E': 1 << 60,
'P': 1 << 50,
'T': 1 << 40,
'G': 1 << 30,
'M': 1 << 20,
'K': 1 << 10,
'B': 1,
}
VALID_UNITS = {
'B': (('byte', 'B'), ('bit', 'b')),
'K': (('kilobyte', 'KB'), ('kilobit', 'Kb')),
'M': (('megabyte', 'MB'), ('megabit', 'Mb')),
'G': (('gigabyte', 'GB'), ('gigabit', 'Gb')),
'T': (('terabyte', 'TB'), ('terabit', 'Tb')),
'P': (('petabyte', 'PB'), ('petabit', 'Pb')),
'E': (('exabyte', 'EB'), ('exabit', 'Eb')),
'Z': (('zetabyte', 'ZB'), ('zetabit', 'Zb')),
'Y': (('yottabyte', 'YB'), ('yottabit', 'Yb')),
}
def lenient_lowercase(lst):
"""Lowercase elements of a list.
If an element is not a string, pass it through untouched.
"""
lowered = []
for value in lst:
try:
lowered.append(value.lower())
except AttributeError:
lowered.append(value)
return lowered
def human_to_bytes(number, default_unit=None, isbits=False):
"""Convert number in string format into bytes (ex: '2K' => 2048) or using unit argument.
example: human_to_bytes('10M') <=> human_to_bytes(10, 'M').
When isbits is False (default), converts bytes from a human-readable format to integer.
example: human_to_bytes('1MB') returns 1048576 (int).
The function expects 'B' (uppercase) as a byte identifier passed
as a part of 'name' param string or 'unit', e.g. 'MB'/'KB'/etc.
(except when the identifier is single 'b', it is perceived as a byte identifier too).
if 'Mb'/'Kb'/... is passed, the ValueError will be rased.
When isbits is True, converts bits from a human-readable format to integer.
example: human_to_bytes('1Mb', isbits=True) returns 8388608 (int) -
string bits representation was passed and return as a number or bits.
The function expects 'b' (lowercase) as a bit identifier, e.g. 'Mb'/'Kb'/etc.
if 'MB'/'KB'/... is passed, the ValueError will be rased.
"""
m = re.search(r'^([0-9]*\.?[0-9]+)(?:\s*([A-Za-z]+))?\s*$', str(number))
if m is None:
raise ValueError("human_to_bytes() can't interpret following string: %s" % str(number))
try:
num = float(m.group(1))
except Exception:
raise ValueError("human_to_bytes() can't interpret following number: %s (original input string: %s)" % (m.group(1), number))
unit = m.group(2)
if unit is None:
unit = default_unit
if unit is None:
# No unit given, returning raw number
return int(round(num))
range_key = unit[0].upper()
try:
limit = SIZE_RANGES[range_key]
except Exception:
raise ValueError("human_to_bytes() failed to convert %s (unit = %s). The suffix must be one of %s" % (number, unit, ", ".join(SIZE_RANGES.keys())))
# default value
unit_class = 'B'
unit_class_name = 'byte'
# handling bits case
if isbits:
unit_class = 'b'
unit_class_name = 'bit'
# check unit value if more than one character (KB, MB)
if len(unit) > 1:
expect_message = 'expect %s%s or %s' % (range_key, unit_class, range_key)
if range_key == 'B':
expect_message = 'expect %s or %s' % (unit_class, unit_class_name)
unit_group = VALID_UNITS.get(range_key, None)
if unit_group is None:
raise ValueError(f"human_to_bytes() can't interpret a valid unit for {range_key}")
isbits_flag = 1 if isbits else 0
if unit.lower() == unit_group[isbits_flag][0]:
pass
elif unit != unit_group[isbits_flag][1]:
raise ValueError("human_to_bytes() failed to convert %s. Value is not a valid string (%s)" % (number, expect_message))
return int(round(num * limit))
def bytes_to_human(size, isbits=False, unit=None):
base = 'Bytes'
if isbits:
base = 'bits'
suffix = ''
for suffix, limit in sorted(iteritems(SIZE_RANGES), key=lambda item: -item[1]):
if (unit is None and size >= limit) or unit is not None and unit.upper() == suffix[0]:
break
if limit != 1:
suffix += base[0]
else:
suffix = base
return '%.2f %s' % (size / limit, suffix)
|