Bug 1264482 - Add an enumeration-like string type with a limited set of possible values. r=ted
authorMike Hommey <mh+mozilla@glandium.org>
Wed, 06 Apr 2016 09:29:47 +0900
changeset 331687 bb77bbe1fbd702545e71867dec66651d30b69526
parent 331686 f464affb29486b7f718300c0b5680c84937b1373
child 331688 177380345f2fc53853aa53e0aceca74a022a8e2f
push id6048
push userkmoir@mozilla.com
push dateMon, 06 Jun 2016 19:02:08 +0000
treeherdermozilla-beta@46d72a56c57d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersted
bugs1264482
milestone48.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1264482 - Add an enumeration-like string type with a limited set of possible values. r=ted
python/mozbuild/mozbuild/test/test_util.py
python/mozbuild/mozbuild/util.py
--- a/python/mozbuild/mozbuild/test/test_util.py
+++ b/python/mozbuild/mozbuild/test/test_util.py
@@ -25,16 +25,18 @@ from mozbuild.util import (
     group_unified_files,
     hash_file,
     memoize,
     memoized_property,
     pair,
     resolve_target_to_make,
     MozbuildDeletionError,
     HierarchicalStringList,
+    EnumString,
+    EnumStringComparisonError,
     ListWithAction,
     StrictOrderingOnAppendList,
     StrictOrderingOnAppendListWithFlagsFactory,
     TypedList,
     TypedNamedTuple,
     UnsortedError,
 )
 
@@ -856,11 +858,34 @@ class TestMisc(unittest.TestCase):
         self.assertEqual(
             expand_variables('before $(string) between $(list) after', {
                 'string': 'abc',
                 'list': ['a', 'b', 'c']
             }),
             'before abc between a b c after'
         )
 
+class TestEnumString(unittest.TestCase):
+    def test_string(self):
+        CompilerType = EnumString.subclass('msvc', 'gcc', 'clang', 'clang-cl')
+
+        type = CompilerType('msvc')
+        self.assertEquals(type, 'msvc')
+        self.assertNotEquals(type, 'gcc')
+        self.assertNotEquals(type, 'clang')
+        self.assertNotEquals(type, 'clang-cl')
+        self.assertIn(type, ('msvc', 'clang-cl'))
+        self.assertNotIn(type, ('gcc', 'clang'))
+
+        with self.assertRaises(EnumStringComparisonError):
+            self.assertEquals(type, 'foo')
+
+        with self.assertRaises(EnumStringComparisonError):
+            self.assertNotEquals(type, 'foo')
+
+        with self.assertRaises(EnumStringComparisonError):
+            self.assertIn(type, ('foo', 'gcc'))
+
+        with self.assertRaises(ValueError):
+            type = CompilerType('foo')
 
 if __name__ == '__main__':
     main()
--- a/python/mozbuild/mozbuild/util.py
+++ b/python/mozbuild/mozbuild/util.py
@@ -1132,8 +1132,43 @@ class DefinesAction(argparse.Action):
         if len(values) == 1:
             name, value = values[0], 1
         else:
             name, value = values
             if value.isdigit():
                 value = int(value)
         defines[name] = value
         setattr(namespace, self.dest, defines)
+
+
+class EnumStringComparisonError(Exception):
+    pass
+
+
+class EnumString(unicode):
+    '''A string type that only can have a limited set of values, similarly to
+    an Enum, and can only be compared against that set of values.
+
+    The class is meant to be subclassed, where the subclass defines
+    POSSIBLE_VALUES. The `subclass` method is a helper to create such
+    subclasses.
+    '''
+    POSSIBLE_VALUES = ()
+    def __init__(self, value):
+        if value not in self.POSSIBLE_VALUES:
+            raise ValueError("'%s' is not a valid value for %s"
+                             % (value, self.__class__.__name__))
+
+    def __eq__(self, other):
+        if other not in self.POSSIBLE_VALUES:
+            raise EnumStringComparisonError(
+                'Can only compare with %s'
+                % ', '.join("'%s'" % v for v in self.POSSIBLE_VALUES))
+        return super(EnumString, self).__eq__(other)
+
+    def __ne__(self, other):
+        return not (self == other)
+
+    @staticmethod
+    def subclass(*possible_values):
+        class EnumStringSubclass(EnumString):
+            POSSIBLE_VALUES = possible_values
+        return EnumStringSubclass