[tor-commits] [stem/master] Configuration parse_enum_csv() function
atagar at torproject.org
atagar at torproject.org
Tue Jan 1 23:20:29 UTC 2013
commit 3521953c1be9110f3c22cc20bf697990a5287e3a
Author: Damian Johnson <atagar at torproject.org>
Date: Mon Dec 31 23:26:31 2012 -0800
Configuration parse_enum_csv() function
While swapping over arm's config usage I realized that we likely don't need the
int or str csv functions. In practice the csvs have been for enumerations.
Adding a helper function that better handles configurations for enum values.
---
stem/util/conf.py | 58 ++++++++++++++++++++++++++++++++++++++++++++++++
test/unit/util/conf.py | 50 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 108 insertions(+), 0 deletions(-)
diff --git a/stem/util/conf.py b/stem/util/conf.py
index f2bc490..408b165 100644
--- a/stem/util/conf.py
+++ b/stem/util/conf.py
@@ -118,6 +118,7 @@ Alternatively you can get a read-only dictionary that stays in sync with the
config_dict - provides a dictionary that's kept synchronized with a config
get_config - singleton for getting configurations
+ parse_enum_csv - helper funcion for parsing confguration entries for enums
Config - Custom configuration
|- load - reads a configuration file
@@ -193,6 +194,63 @@ def get_config(handle):
if not handle in CONFS: CONFS[handle] = Config()
return CONFS[handle]
+def parse_enum_csv(key, value, enumeration, count = None):
+ """
+ Parses a given value as being a comma separated listing of enumeration keys,
+ returning the corresponding enumeration values. This is intended to be a
+ helper for config handlers. The checks this does are case insensitive.
+
+ The **count** attribute can be used to make assertions based on the number of
+ values. This can be...
+
+ * None to indicate that there's no restrictions.
+ * An int to indicate that we should have this many values.
+ * An (int, int) tuple to indicate the range that values can be in. This range
+ is inclusive and either can be None to indicate the lack of a lower or
+ upper bound.
+
+ :param str key: configuration key being looked up
+ :param str value: value to be parsed
+ :param stem.util.enum.Enum enumeration: enumeration the values should be in
+ :param int,tuple count: validates that we have this many items
+
+ :returns: list with the enumeration values
+
+ :raises: **ValueError** if the count assertion fails or the **value** entries
+ don't match the enumeration keys
+ """
+
+ values = [val.upper().strip() for val in value.split(',')]
+ if values == ['']: return []
+
+ if count is None:
+ pass # no count validateion checks to do
+ elif isinstance(count, int):
+ if len(values) != count:
+ raise ValueError("Config entry '%s' is expected to be %i comma separated values, got '%s'" % (key, count, value))
+ elif isinstance(count, tuple) and len(count) == 2:
+ minimum, maximum = count
+
+ if minimum is not None and len(values) < minimum:
+ raise ValueError("Config entry '%s' must have at least %i comma separated values, got '%s'" % (key, minimum, value))
+
+ if maximum is not None and len(values) > maximum:
+ raise ValueError("Config entry '%s' can have at most %i comma separated values, got '%s'" % (key, maximum, value))
+ else:
+ raise ValueError("The count must be None, an int, or two value tuple. Got '%s' (%s)'" % (count, type(count)))
+
+ result = []
+ enum_keys = [key.upper() for key in enumeration.keys()]
+ enum_values = list(enumeration)
+
+ for val in values:
+ if val in enum_keys:
+ result.append(enum_values[enum_keys.index(val)])
+ else:
+ raise ValueError("The '%s' entry of config entry '%s' wasn't in the enumeration (expected %s)" % (val, key, ', '.join(enum_keys)))
+
+ return result
+
class Config(object):
"""
Handler for easily working with custom configurations, providing persistence
diff --git a/test/unit/util/conf.py b/test/unit/util/conf.py
index c2fbed0..25d312e 100644
--- a/test/unit/util/conf.py
+++ b/test/unit/util/conf.py
@@ -5,6 +5,9 @@ Unit tests for the stem.util.conf class and functions.
import unittest
import stem.util.conf
+import stem.util.enum
+
+from stem.util.conf import parse_enum_csv
class TestConf(unittest.TestCase):
def tearDown(self):
@@ -50,6 +53,53 @@ class TestConf(unittest.TestCase):
test_config.set("list_value", "c", False)
self.assertEquals(["a", "b", "c"], my_config["list_value"])
+ def test_parse_enum_csv(self):
+ """
+ Tests the parse_enum_csv function.
+ """
+
+ Insects = stem.util.enum.Enum("BUTTERFLY", "LADYBUG", "CRICKET")
+
+ # check the case insensitivity
+
+ self.assertEqual([Insects.LADYBUG], parse_enum_csv("my_option", "ladybug", Insects))
+ self.assertEqual([Insects.LADYBUG], parse_enum_csv("my_option", "Ladybug", Insects))
+ self.assertEqual([Insects.LADYBUG], parse_enum_csv("my_option", "LaDyBuG", Insects))
+ self.assertEqual([Insects.LADYBUG], parse_enum_csv("my_option", "LADYBUG", Insects))
+
+ # various number of values
+
+ self.assertEqual([], parse_enum_csv("my_option", "", Insects))
+ self.assertEqual([Insects.LADYBUG], parse_enum_csv("my_option", "ladybug", Insects))
+
+ self.assertEqual([Insects.LADYBUG, Insects.BUTTERFLY],
+ parse_enum_csv("my_option", "ladybug, butterfly", Insects))
+
+ self.assertEqual([Insects.LADYBUG, Insects.BUTTERFLY, Insects.CRICKET],
+ parse_enum_csv("my_option", "ladybug, butterfly, cricket", Insects))
+
+ # edge cases for count argument where things are ok
+
+ self.assertEqual([Insects.LADYBUG, Insects.BUTTERFLY],
+ parse_enum_csv("my_option", "ladybug, butterfly", Insects, 2))
+
+ self.assertEqual([Insects.LADYBUG, Insects.BUTTERFLY],
+ parse_enum_csv("my_option", "ladybug, butterfly", Insects, (1, 2)))
+
+ self.assertEqual([Insects.LADYBUG, Insects.BUTTERFLY],
+ parse_enum_csv("my_option", "ladybug, butterfly", Insects, (2, 3)))
+
+ self.assertEqual([Insects.LADYBUG, Insects.BUTTERFLY],
+ parse_enum_csv("my_option", "ladybug, butterfly", Insects, (2, 2)))
+
+ # failure cases
+
+ self.assertRaises(ValueError, parse_enum_csv, "my_option", "ugabuga", Insects)
+ self.assertRaises(ValueError, parse_enum_csv, "my_option", "ladybug, ugabuga", Insects)
+ self.assertRaises(ValueError, parse_enum_csv, "my_option", "ladybug butterfly", Insects) # no comma
+ self.assertRaises(ValueError, parse_enum_csv, "my_option", "ladybug", Insects, 2)
+ self.assertRaises(ValueError, parse_enum_csv, "my_option", "ladybug", Insects, (2, 3))
+
def test_clear(self):
"""
Tests the clear method.
More information about the tor-commits
mailing list