summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2013-05-15 00:39:24 +0000
committerGerrit Code Review <review@openstack.org>2013-05-15 00:39:24 +0000
commite585ef4871a001c3b3393e35017f24fcf44a523e (patch)
tree7479dd5022f3d6cf97bf1871bef17cfb1b2a9a49
parent3e0114f570d97c47b1b8eefce0bcd27146098b8d (diff)
parent536c39e8e315af4b1823cfa378f3db441a212b1a (diff)
downloadoslo-e585ef4871a001c3b3393e35017f24fcf44a523e.tar.gz
oslo-e585ef4871a001c3b3393e35017f24fcf44a523e.tar.xz
oslo-e585ef4871a001c3b3393e35017f24fcf44a523e.zip
Merge "Add 't', 'y', and `strict` to `bool_from_string`"
-rw-r--r--openstack/common/strutils.py45
-rw-r--r--tests/unit/test_strutils.py61
2 files changed, 92 insertions, 14 deletions
diff --git a/openstack/common/strutils.py b/openstack/common/strutils.py
index d6dfb13..6d227c6 100644
--- a/openstack/common/strutils.py
+++ b/openstack/common/strutils.py
@@ -21,6 +21,12 @@ System-level utilities and helper functions.
import sys
+from openstack.common.gettextutils import _
+
+
+TRUE_STRINGS = ('1', 't', 'true', 'on', 'y', 'yes')
+FALSE_STRINGS = ('0', 'f', 'false', 'off', 'n', 'no')
+
def int_from_bool_as_string(subject):
"""
@@ -37,27 +43,38 @@ def int_from_bool_as_string(subject):
return bool_from_string(subject) and 1 or 0
-def bool_from_string(subject):
+def bool_from_string(subject, strict=False):
"""
Interpret a string as a boolean.
- Any string value in:
-
- ('True', 'true', 'On', 'on', 'Yes', 'yes', '1')
+ A case-insensitive match is performed such that strings matching 't',
+ 'true', 'on', 'y', 'yes', or '1' are considered True and, when
+ `strict=False`, anything else is considered False.
- is interpreted as a boolean True.
+ Useful for JSON-decoded stuff and config file parsing.
- Useful for JSON-decoded stuff and config file parsing
+ If `strict=True`, unrecognized values, including None, will raise a
+ ValueError which is useful when parsing values passed in from an API call.
+ Strings yielding False are 'f', 'false', 'off', 'n', 'no', or '0'.
"""
- try:
- # True or 1 or '1' -> True
- # False or < 1 or > 1 or '0' -> False
- return int(subject) == 1
- except TypeError:
- # None -> False
+ if not isinstance(subject, basestring):
+ subject = str(subject)
+
+ lowered = subject.strip().lower()
+
+ if lowered in TRUE_STRINGS:
+ return True
+ elif lowered in FALSE_STRINGS:
+ return False
+ elif strict:
+ acceptable = ', '.join(
+ "'%s'" % s for s in sorted(TRUE_STRINGS + FALSE_STRINGS))
+ msg = _("Unrecognized value '%(val)s', acceptable values are:"
+ " %(acceptable)s") % {'val': subject,
+ 'acceptable': acceptable}
+ raise ValueError(msg)
+ else:
return False
- except ValueError:
- return subject.strip().lower() in ('true', 'on', 'yes')
def safe_decode(text, incoming=None, errors='strict'):
diff --git a/tests/unit/test_strutils.py b/tests/unit/test_strutils.py
index b0af958..bad50c8 100644
--- a/tests/unit/test_strutils.py
+++ b/tests/unit/test_strutils.py
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2011 OpenStack Foundation.
@@ -37,6 +38,10 @@ class StrUtilsTest(utils.BaseTestCase):
self.assertTrue(strutils.bool_from_string(c('YES')))
self.assertTrue(strutils.bool_from_string(c('yEs')))
self.assertTrue(strutils.bool_from_string(c('1')))
+ self.assertTrue(strutils.bool_from_string(c('T')))
+ self.assertTrue(strutils.bool_from_string(c('t')))
+ self.assertTrue(strutils.bool_from_string(c('Y')))
+ self.assertTrue(strutils.bool_from_string(c('y')))
self.assertFalse(strutils.bool_from_string(c('false')))
self.assertFalse(strutils.bool_from_string(c('FALSE')))
@@ -47,9 +52,15 @@ class StrUtilsTest(utils.BaseTestCase):
self.assertFalse(strutils.bool_from_string(c('42')))
self.assertFalse(strutils.bool_from_string(c(
'This should not be True')))
+ self.assertFalse(strutils.bool_from_string(c('F')))
+ self.assertFalse(strutils.bool_from_string(c('f')))
+ self.assertFalse(strutils.bool_from_string(c('N')))
+ self.assertFalse(strutils.bool_from_string(c('n')))
# Whitespace should be stripped
+ self.assertTrue(strutils.bool_from_string(c(' 1 ')))
self.assertTrue(strutils.bool_from_string(c(' true ')))
+ self.assertFalse(strutils.bool_from_string(c(' 0 ')))
self.assertFalse(strutils.bool_from_string(c(' false ')))
def test_bool_from_string(self):
@@ -57,6 +68,14 @@ class StrUtilsTest(utils.BaseTestCase):
def test_unicode_bool_from_string(self):
self._test_bool_from_string(six.text_type)
+ self.assertFalse(strutils.bool_from_string(u'使用', strict=False))
+
+ exc = self.assertRaises(ValueError, strutils.bool_from_string,
+ u'使用', strict=True)
+ expected_msg = (u"Unrecognized value '使用', acceptable values are:"
+ u" '0', '1', 'f', 'false', 'n', 'no', 'off', 'on',"
+ u" 't', 'true', 'y', 'yes'")
+ self.assertEqual(expected_msg, unicode(exc))
def test_other_bool_from_string(self):
self.assertFalse(strutils.bool_from_string(None))
@@ -69,6 +88,48 @@ class StrUtilsTest(utils.BaseTestCase):
self.assertFalse(strutils.bool_from_string(0))
self.assertFalse(strutils.bool_from_string(2))
+ def test_strict_bool_from_string(self):
+ # None isn't allowed in strict mode
+ exc = self.assertRaises(ValueError, strutils.bool_from_string, None,
+ strict=True)
+ expected_msg = ("Unrecognized value 'None', acceptable values are:"
+ " '0', '1', 'f', 'false', 'n', 'no', 'off', 'on',"
+ " 't', 'true', 'y', 'yes'")
+ self.assertEqual(expected_msg, str(exc))
+
+ # Unrecognized strings aren't allowed
+ self.assertFalse(strutils.bool_from_string('Other', strict=False))
+ exc = self.assertRaises(ValueError, strutils.bool_from_string, 'Other',
+ strict=True)
+ expected_msg = ("Unrecognized value 'Other', acceptable values are:"
+ " '0', '1', 'f', 'false', 'n', 'no', 'off', 'on',"
+ " 't', 'true', 'y', 'yes'")
+ self.assertEqual(expected_msg, str(exc))
+
+ # Unrecognized numbers aren't allowed
+ exc = self.assertRaises(ValueError, strutils.bool_from_string, 2,
+ strict=True)
+ expected_msg = ("Unrecognized value '2', acceptable values are:"
+ " '0', '1', 'f', 'false', 'n', 'no', 'off', 'on',"
+ " 't', 'true', 'y', 'yes'")
+ self.assertEqual(expected_msg, str(exc))
+
+ # False-like values are allowed
+ self.assertFalse(strutils.bool_from_string('f', strict=True))
+ self.assertFalse(strutils.bool_from_string('false', strict=True))
+ self.assertFalse(strutils.bool_from_string('off', strict=True))
+ self.assertFalse(strutils.bool_from_string('n', strict=True))
+ self.assertFalse(strutils.bool_from_string('no', strict=True))
+ self.assertFalse(strutils.bool_from_string('0', strict=True))
+
+ self.assertTrue(strutils.bool_from_string('1', strict=True))
+
+ # Avoid font-similarity issues (one looks like lowercase-el, zero like
+ # oh, etc...)
+ for char in ('O', 'o', 'L', 'l', 'I', 'i'):
+ self.assertRaises(ValueError, strutils.bool_from_string, char,
+ strict=True)
+
def test_int_from_bool_as_string(self):
self.assertEqual(1, strutils.int_from_bool_as_string(True))
self.assertEqual(0, strutils.int_from_bool_as_string(False))