diff --git a/oslo_utils/strutils.py b/oslo_utils/strutils.py index d03617da..929b34db 100644 --- a/oslo_utils/strutils.py +++ b/oslo_utils/strutils.py @@ -44,6 +44,7 @@ UNIT_PREFIX_EXPONENT = { UNIT_SYSTEM_INFO = { 'IEC': (1024, re.compile(r'(^[-+]?\d*\.?\d+)([KMGT]i?)?(b|bit|B)$')), 'SI': (1000, re.compile(r'(^[-+]?\d*\.?\d+)([kMGT])?(b|bit|B)$')), + 'mixed': (None, re.compile(r'(^[-+]?\d*\.?\d+)([kKMGT]i?)?(b|bit|B)$')), } TRUE_STRINGS = ('1', 't', 'true', 'on', 'y', 'yes') @@ -164,7 +165,7 @@ def is_valid_boolstr(value): def string_to_bytes(text, unit_system='IEC', return_int=False): """Converts a string into an float representation of bytes. - The units supported for IEC :: + The units supported for IEC / mixed:: Kb(it), Kib(it), Mb(it), Mib(it), Gb(it), Gib(it), Tb(it), Tib(it) KB, KiB, MB, MiB, GB, GiB, TB, TiB @@ -174,7 +175,17 @@ def string_to_bytes(text, unit_system='IEC', return_int=False): kb(it), Mb(it), Gb(it), Tb(it) kB, MB, GB, TB - Note that the SI unit system does not support capital letter 'K' + SI units are interpreted as power-of-ten (e.g. 1kb = 1000b). Note + that the SI unit system does not support capital letter 'K' + + IEC units are interpreted as power-of-two (e.g. 1MiB = 1MB = + 1024b) + + Mixed units interpret the "i" to mean IEC, and no "i" to mean SI + (e.g. 1kb = 1000b, 1kib == 1024b). Additionaly, mixed units + interpret 'K' as power-of-ten. This mode is not particuarly + useful for new code, but can help with compatability for parsers + such as GNU parted. :param text: String input for bytes size conversion. :param unit_system: Unit system for byte size conversion. @@ -195,9 +206,22 @@ def string_to_bytes(text, unit_system='IEC', return_int=False): unit_prefix = match.group(2) if match.group(3) in ['b', 'bit']: magnitude /= 8 + + # In the mixed matcher, IEC units (with a trailing 'i') are + # interpreted as power-of-two, others as power-of-ten + if unit_system == 'mixed': + if unit_prefix and not unit_prefix.endswith('i'): + # For maximum compatability in mixed mode, we understand + # "K" (which is not strict SI) as "k" + if unit_prefix.startswith == 'K': + unit_prefix = 'k' + base = 1000 + else: + base = 1024 else: msg = _('Invalid string format: %s') % text raise ValueError(msg) + if not unit_prefix: res = magnitude else: diff --git a/oslo_utils/tests/test_strutils.py b/oslo_utils/tests/test_strutils.py index 0dae6938..af1faf13 100644 --- a/oslo_utils/tests/test_strutils.py +++ b/oslo_utils/tests/test_strutils.py @@ -187,6 +187,7 @@ class StringToBytesTest(test_base.BaseTestCase): _unit_system = [ ('si', dict(unit_system='SI')), ('iec', dict(unit_system='IEC')), + ('mixed', dict(unit_system='mixed')), ('invalid_unit_system', dict(unit_system='KKK', assert_error=True)), ] @@ -259,6 +260,14 @@ class StringToBytesTest(test_base.BaseTestCase): res = getattr(units, unit_prefix) else: res = getattr(units, '%si' % unit_prefix) + elif unit_system == 'mixed': + # Note: this will return 'i' units as power-of-two, + # and other units as power-of-ten. Additionally, for + # compatability a "K" is interpreted as "k" in mixed + # mode + if unit_prefix == 'K': + unit_prefix = 'k' + res = getattr(units, unit_prefix) return res text = ''.join([self.sign, self.magnitude, self.unit_prefix,