Merge "Replace utils.to_bytes() with strutils.to_bytes()"
This commit is contained in:
commit
2bcd6b59a6
|
@ -19,18 +19,33 @@
|
||||||
System-level utilities and helper functions.
|
System-level utilities and helper functions.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
import unicodedata
|
||||||
|
|
||||||
from nova.openstack.common.gettextutils import _
|
from nova.openstack.common.gettextutils import _
|
||||||
|
|
||||||
|
|
||||||
|
# Used for looking up extensions of text
|
||||||
|
# to their 'multiplied' byte amount
|
||||||
|
BYTE_MULTIPLIERS = {
|
||||||
|
'': 1,
|
||||||
|
't': 1024 ** 4,
|
||||||
|
'g': 1024 ** 3,
|
||||||
|
'm': 1024 ** 2,
|
||||||
|
'k': 1024,
|
||||||
|
}
|
||||||
|
BYTE_REGEX = re.compile(r'(^-?\d+)(\D*)')
|
||||||
|
|
||||||
TRUE_STRINGS = ('1', 't', 'true', 'on', 'y', 'yes')
|
TRUE_STRINGS = ('1', 't', 'true', 'on', 'y', 'yes')
|
||||||
FALSE_STRINGS = ('0', 'f', 'false', 'off', 'n', 'no')
|
FALSE_STRINGS = ('0', 'f', 'false', 'off', 'n', 'no')
|
||||||
|
|
||||||
|
SLUGIFY_STRIP_RE = re.compile(r"[^\w\s-]")
|
||||||
|
SLUGIFY_HYPHENATE_RE = re.compile(r"[-\s]+")
|
||||||
|
|
||||||
|
|
||||||
def int_from_bool_as_string(subject):
|
def int_from_bool_as_string(subject):
|
||||||
"""
|
"""Interpret a string as a boolean and return either 1 or 0.
|
||||||
Interpret a string as a boolean and return either 1 or 0.
|
|
||||||
|
|
||||||
Any string value in:
|
Any string value in:
|
||||||
|
|
||||||
|
@ -44,8 +59,7 @@ def int_from_bool_as_string(subject):
|
||||||
|
|
||||||
|
|
||||||
def bool_from_string(subject, strict=False):
|
def bool_from_string(subject, strict=False):
|
||||||
"""
|
"""Interpret a string as a boolean.
|
||||||
Interpret a string as a boolean.
|
|
||||||
|
|
||||||
A case-insensitive match is performed such that strings matching 't',
|
A case-insensitive match is performed such that strings matching 't',
|
||||||
'true', 'on', 'y', 'yes', or '1' are considered True and, when
|
'true', 'on', 'y', 'yes', or '1' are considered True and, when
|
||||||
|
@ -78,9 +92,7 @@ def bool_from_string(subject, strict=False):
|
||||||
|
|
||||||
|
|
||||||
def safe_decode(text, incoming=None, errors='strict'):
|
def safe_decode(text, incoming=None, errors='strict'):
|
||||||
"""
|
"""Decodes incoming str using `incoming` if they're not already unicode.
|
||||||
Decodes incoming str using `incoming` if they're
|
|
||||||
not already unicode.
|
|
||||||
|
|
||||||
:param incoming: Text's current encoding
|
:param incoming: Text's current encoding
|
||||||
:param errors: Errors handling policy. See here for valid
|
:param errors: Errors handling policy. See here for valid
|
||||||
|
@ -119,11 +131,10 @@ def safe_decode(text, incoming=None, errors='strict'):
|
||||||
|
|
||||||
def safe_encode(text, incoming=None,
|
def safe_encode(text, incoming=None,
|
||||||
encoding='utf-8', errors='strict'):
|
encoding='utf-8', errors='strict'):
|
||||||
"""
|
"""Encodes incoming str/unicode using `encoding`.
|
||||||
Encodes incoming str/unicode using `encoding`. If
|
|
||||||
incoming is not specified, text is expected to
|
If incoming is not specified, text is expected to be encoded with
|
||||||
be encoded with current python's default encoding.
|
current python's default encoding. (`sys.getdefaultencoding`)
|
||||||
(`sys.getdefaultencoding`)
|
|
||||||
|
|
||||||
:param incoming: Text's current encoding
|
:param incoming: Text's current encoding
|
||||||
:param encoding: Expected encoding for text (Default UTF-8)
|
:param encoding: Expected encoding for text (Default UTF-8)
|
||||||
|
@ -148,3 +159,58 @@ def safe_encode(text, incoming=None,
|
||||||
return text.encode(encoding, errors)
|
return text.encode(encoding, errors)
|
||||||
|
|
||||||
return text
|
return text
|
||||||
|
|
||||||
|
|
||||||
|
def to_bytes(text, default=0):
|
||||||
|
"""Converts a string into an integer of bytes.
|
||||||
|
|
||||||
|
Looks at the last characters of the text to determine
|
||||||
|
what conversion is needed to turn the input text into a byte number.
|
||||||
|
Supports "B, K(B), M(B), G(B), and T(B)". (case insensitive)
|
||||||
|
|
||||||
|
:param text: String input for bytes size conversion.
|
||||||
|
:param default: Default return value when text is blank.
|
||||||
|
|
||||||
|
"""
|
||||||
|
match = BYTE_REGEX.search(text)
|
||||||
|
if match:
|
||||||
|
magnitude = int(match.group(1))
|
||||||
|
mult_key_org = match.group(2)
|
||||||
|
if not mult_key_org:
|
||||||
|
return magnitude
|
||||||
|
elif text:
|
||||||
|
msg = _('Invalid string format: %s') % text
|
||||||
|
raise TypeError(msg)
|
||||||
|
else:
|
||||||
|
return default
|
||||||
|
mult_key = mult_key_org.lower().replace('b', '', 1)
|
||||||
|
multiplier = BYTE_MULTIPLIERS.get(mult_key)
|
||||||
|
if multiplier is None:
|
||||||
|
msg = _('Unknown byte multiplier: %s') % mult_key_org
|
||||||
|
raise TypeError(msg)
|
||||||
|
return magnitude * multiplier
|
||||||
|
|
||||||
|
|
||||||
|
def to_slug(value, incoming=None, errors="strict"):
|
||||||
|
"""Normalize string.
|
||||||
|
|
||||||
|
Convert to lowercase, remove non-word characters, and convert spaces
|
||||||
|
to hyphens.
|
||||||
|
|
||||||
|
Inspired by Django's `slugify` filter.
|
||||||
|
|
||||||
|
:param value: Text to slugify
|
||||||
|
:param incoming: Text's current encoding
|
||||||
|
:param errors: Errors handling policy. See here for valid
|
||||||
|
values http://docs.python.org/2/library/codecs.html
|
||||||
|
:returns: slugified unicode representation of `value`
|
||||||
|
:raises TypeError: If text is not an instance of basestring
|
||||||
|
"""
|
||||||
|
value = safe_decode(value, incoming, errors)
|
||||||
|
# NOTE(aababilov): no need to use safe_(encode|decode) here:
|
||||||
|
# encodings are always "ascii", error handling is always "ignore"
|
||||||
|
# and types are always known (first: unicode; second: str)
|
||||||
|
value = unicodedata.normalize("NFKD", value).encode(
|
||||||
|
"ascii", "ignore").decode("ascii")
|
||||||
|
value = SLUGIFY_STRIP_RE.sub("", value).strip().lower()
|
||||||
|
return SLUGIFY_HYPHENATE_RE.sub("-", value)
|
||||||
|
|
|
@ -38,41 +38,6 @@ from nova import utils
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
|
|
||||||
|
|
||||||
class ByteConversionTest(test.TestCase):
|
|
||||||
def test_string_conversions(self):
|
|
||||||
working_examples = {
|
|
||||||
'1024KB': 1048576,
|
|
||||||
'1024TB': 1125899906842624,
|
|
||||||
'1024K': 1048576,
|
|
||||||
'1024T': 1125899906842624,
|
|
||||||
'1TB': 1099511627776,
|
|
||||||
'1T': 1099511627776,
|
|
||||||
'1KB': 1024,
|
|
||||||
'1K': 1024,
|
|
||||||
'1B': 1,
|
|
||||||
'1B': 1,
|
|
||||||
'1': 1,
|
|
||||||
'1MB': 1048576,
|
|
||||||
'7MB': 7340032,
|
|
||||||
'0MB': 0,
|
|
||||||
'0KB': 0,
|
|
||||||
'0TB': 0,
|
|
||||||
'': 0,
|
|
||||||
}
|
|
||||||
for (in_value, expected_value) in working_examples.items():
|
|
||||||
b_value = utils.to_bytes(in_value)
|
|
||||||
self.assertEquals(expected_value, b_value)
|
|
||||||
if len(in_value):
|
|
||||||
in_value = "-" + in_value
|
|
||||||
b_value = utils.to_bytes(in_value)
|
|
||||||
self.assertEquals(expected_value * -1, b_value)
|
|
||||||
breaking_examples = [
|
|
||||||
'junk1KB', '1023BBBB',
|
|
||||||
]
|
|
||||||
for v in breaking_examples:
|
|
||||||
self.assertRaises(TypeError, utils.to_bytes, v)
|
|
||||||
|
|
||||||
|
|
||||||
class GetFromPathTestCase(test.TestCase):
|
class GetFromPathTestCase(test.TestCase):
|
||||||
def test_tolerates_nones(self):
|
def test_tolerates_nones(self):
|
||||||
f = utils.get_from_path
|
f = utils.get_from_path
|
||||||
|
|
|
@ -411,34 +411,6 @@ def utf8(value):
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
def to_bytes(text, default=0):
|
|
||||||
"""Try to turn a string into a number of bytes. Looks at the last
|
|
||||||
characters of the text to determine what conversion is needed to
|
|
||||||
turn the input text into a byte number.
|
|
||||||
|
|
||||||
Supports: B/b, K/k, M/m, G/g, T/t (or the same with b/B on the end)
|
|
||||||
|
|
||||||
"""
|
|
||||||
# Take off everything not number 'like' (which should leave
|
|
||||||
# only the byte 'identifier' left)
|
|
||||||
mult_key_org = text.lstrip('-1234567890')
|
|
||||||
mult_key = mult_key_org.lower()
|
|
||||||
mult_key_len = len(mult_key)
|
|
||||||
if mult_key.endswith("b"):
|
|
||||||
mult_key = mult_key[0:-1]
|
|
||||||
try:
|
|
||||||
multiplier = BYTE_MULTIPLIERS[mult_key]
|
|
||||||
if mult_key_len:
|
|
||||||
# Empty cases shouldn't cause text[0:-0]
|
|
||||||
text = text[0:-mult_key_len]
|
|
||||||
return int(text) * multiplier
|
|
||||||
except KeyError:
|
|
||||||
msg = _('Unknown byte multiplier: %s') % mult_key_org
|
|
||||||
raise TypeError(msg)
|
|
||||||
except ValueError:
|
|
||||||
return default
|
|
||||||
|
|
||||||
|
|
||||||
def get_from_path(items, path):
|
def get_from_path(items, path):
|
||||||
"""Returns a list of items matching the specified path.
|
"""Returns a list of items matching the specified path.
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,7 @@ from nova import exception
|
||||||
from nova.image import glance
|
from nova.image import glance
|
||||||
from nova.openstack.common import fileutils
|
from nova.openstack.common import fileutils
|
||||||
from nova.openstack.common import log as logging
|
from nova.openstack.common import log as logging
|
||||||
|
from nova.openstack.common import strutils
|
||||||
from nova import utils
|
from nova import utils
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
@ -90,8 +91,8 @@ class QemuImgInfo(object):
|
||||||
if real_size:
|
if real_size:
|
||||||
details = real_size.group(1)
|
details = real_size.group(1)
|
||||||
try:
|
try:
|
||||||
details = utils.to_bytes(details)
|
details = strutils.to_bytes(details)
|
||||||
except (TypeError, ValueError):
|
except TypeError:
|
||||||
pass
|
pass
|
||||||
return details
|
return details
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue