diff -r eafff38a56cc Lib/enum.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/enum.py Fri May 10 10:54:12 2013 -0700 @@ -0,0 +1,364 @@ +"""\ +Provides the Enum class, which can be subclassed to create new, static, enumerations. +""" + +from collections import OrderedDict +import sys + +__all__ = ['Enum', 'IntEnum'] + + +class _StealthProperty(): + """ + Returns the value in the instance, or the virtual attribute on the class. + + A virtual attribute is one that is looked up by __getattr__, as opposed to + one that lives in __class__.__dict__ + + """ + + def __init__(self, fget=None, fset=None, fdel=None, doc=None): + self.fget = fget + self.fset = fset + self.fdel = fdel + self.__doc__ = doc or fget.__doc__ + + def __call__(self, func): + self.fget = func + self.name = func.name + if not self.__doc__: + self.__doc__ = self.fget.__doc__ + + def __get__(self, obj, objtype=None): + if obj is None: + return getattr(objtype, self.name) + if self.fget is None: + raise AttributeError("unreadable attribute") + return self.fget(obj) + + def __set__(self, obj, value): + if self.fset is None: + raise AttributeError("can't set attribute") + self.fset(obj, value) + + def __delete__(self, obj): + if self.fdel is None: + raise AttributeError("can't delete attribute") + self.fdel(obj) + + def setter(self, func): + self.fset = func + return self + + def deleter(self, func): + self.fdel = func + return self + + +class _EnumDict(dict): + """Keeps track of definition order of the enum items. + + EnumMeta will use the names found in self._enum_names as the + enumeration member names.""" + + def __init__(self): + super().__init__() + self._enum_names = [] + + def __setitem__(self, key, something): + """\ + Changes anything not dundered or that doesn't have __get__. + + If a descriptor is added with the same name as an enum member, the name + is removed from _enum_names (this may leave a hole in the numerical + sequence of values). + + If an enum member name is used twice, but the value is not the same, an + error is raised. + + """ + if key[:2] == key[-2:] == '__' or hasattr(something, '__get__'): + if key in self._enum_names: + # overwriting an enum with a method? then remove the name from + # _enum_names or it will become an enum anyway when the class + # is created + self._enum_names.remove(key) + else: + if key in self._enum_names and self[key] != something: + raise TypeError('Attempted to reuse key: %s' % key) + self._enum_names.append(key) + super().__setitem__(key, something) + +# dummy value for Enum as EnumMeta explicity checks for it, but of course until +# EnumMeta finishes running the first time the Enum class doesn't exist. This +# is also why there are checks in EnumMeta like `if Enum is not None` +Enum = None + + +class EnumMeta(type): + """\ + Metaclass for Enum + """ + + @classmethod + def __prepare__(metacls, cls, bases): + return _EnumDict() + + def __new__(metacls, cls, bases, classdict): + # an Enum class is final once enumeration items have been defined; it + # cannot be mixed with other types (int, float, etc.) if it has an + # inherited __new__ unless a new __new__ is defined (or the resulting + # class will fail). + obj_type, first_enum = metacls._get_mixins(bases) + __new__, save_new, use_args = metacls._find_new(classdict, obj_type, first_enum) + # save enum items into separate mapping so they don't get baked into + # the new class + name_value = {k: classdict[k] for k in classdict._enum_names} + for name in classdict._enum_names: + del classdict[name] + enum_map = OrderedDict() + enum_class = type.__new__(metacls, cls, bases, classdict) + enum_names= [] + enum_class._enum_names = enum_names # enum names in definition order + enum_class._enum_map = enum_map # name:value map + # instantiate them, checking for duplicates as we go + # we instantiate first instead of checking for duplicates first in case + # a custom __new__ is doing something funky with the values -- such as + # auto-numbering ;) + for e in classdict._enum_names: + value = name_value[e] + if not isinstance(value, tuple): + args = (value, ) + else: + args = value + if obj_type is tuple: # special case for tuple enums + args = (args, ) # wrap it one more time + if not use_args: + enum_item = __new__(enum_class) + else: + enum_item = __new__(enum_class, *args) + enum_item.__init__(*args) + enum_item._name = e + if not hasattr(enum_item, '_value'): + enum_item._value = value + # look for any duplicate values, and, if found, use the already + # created enum item instead of the new one so `is` will work + # (i.e. Color.green is Color.grene) + for name, canonical_enum in enum_map.items(): + if canonical_enum.value == enum_item._value: + enum_item = canonical_enum + break + else: + enum_names.append(e) + enum_map[e] = enum_item + # double check that repr and friends are not the mixin's or various + # things break (such as pickle) + for name in ('__repr__', '__str__', '__getnewargs__'): + class_method = getattr(enum_class, name) + obj_method = getattr(obj_type, name, None) + enum_method = getattr(first_enum, name, None) + if obj_method is not None and obj_method is class_method: + setattr(enum_class, name, enum_method) + # replace any other __new__ with our own (as long as Enum is not None, + # anyway) -- again, this is to support pickle + if Enum is not None: + # if the user defined their own __new__, save it before it gets + # clobbered in case they subclass later + if save_new: + enum_class.__new_member__ = __new__ + enum_class.__new__ = Enum.__new__ + return enum_class + + def __call__(cls, value, names=None, *, module=None, type=None): + if names is None: # simple value lookup + return cls.__new__(cls, value) + # otherwise, we're creating a new Enum type + class_name = value # better name for a name than value ;) + metacls = cls.__class__ + bases = (cls, ) if type is None else (type, cls) + classdict = metacls.__prepare__(class_name, bases) + if isinstance(names, str): + names = names.replace(',', ' ').split() + if isinstance(names, (tuple, list)) and isinstance(names[0], str): + names = [(e, i) for (i, e) in enumerate(names, 1)] + # otherwise names better be an iterable of (name, value) or a mapping + for item in names: + if isinstance(item, str): + e, v = item, names[item] + else: + e, v = item + classdict[e] = v + enum_class = metacls.__new__(metacls, class_name, bases, classdict) + # TODO: replace the frame hack if a blessed way to know the calling + # module is ever developed + if module is None: + try: + module = sys._getframe(1).f_globals['__name__'] + except (AttributeError, ValueError): + pass + if module is not None: + enum_class.__module__ = module + return enum_class + + def __contains__(cls, enum_item): + return isinstance(enum_item, cls) and enum_item.name in cls._enum_map + + def __dir__(self): + return (['__class__', '__doc__', '__members__'] + + list(self.__members__)) + + @property + def __members__(cls): + return cls._enum_map.copy() + + def __getattr__(cls, name): + """Return the enum item matching `name`""" + if name[:2] == name[-2:] == '__': + raise AttributeError(name) + try: + return cls._enum_map[name] + except KeyError: + raise AttributeError(name) from None + + def __getitem__(cls, name): + return cls._enum_map[name] + + def __iter__(cls): + return (cls._enum_map[name] for name in cls._enum_names) + + def __len__(cls): + return len(cls._enum_names) + + def __repr__(cls): + return "" % cls.__name__ + + @staticmethod + def _get_mixins(bases): + obj_type = first_enum = None + # double check that we are not subclassing a class with existing + # enumeration members; while we're at it, see if any other data + # type has been mixed in so we can use the correct __new__ + if bases: + for base in bases: + if (base is not Enum + and issubclass(base, Enum) + and base._enum_names): + raise TypeError("Cannot extend enumerations") + # base is now the last base in bases + if not issubclass(base, Enum): + raise TypeError("new enumerations must be created as " + "`ClassName([mixin_type,] enum_type)`") + # get correct mix-in type (either mix-in type of Enum subclass, or + # first base if last base is Enum) + if not issubclass(bases[0], Enum): + obj_type = bases[0] # first data type + first_enum = bases[-1] # enum type + else: + for base in bases[0].__mro__: + # most common: (IntEnum, int, Enum, object) + # possible: (, , + # , , + # ) + if issubclass(base, Enum): + if first_enum is None: + first_enum = base + else: + if obj_type is None: + obj_type = base + else: + obj_type = object + first_enum = Enum + return obj_type, first_enum + + @staticmethod + def _find_new(classdict, obj_type, first_enum): + # now find the correct __new__, checking to see of one was defined + # by the user; also check earlier enum classes in case a __new__ was + # saved as __new_member__ + __new__ = classdict.get('__new__', None) + # should __new__ be saved as __new_member__ later? + save_new = __new__ is not None + if __new__ is None: + # check all possibles for __new_member__ before falling back to + # __new__ + for method in ('__new_member__', '__new__'): + for possible in (obj_type, first_enum): + target = getattr(possible, method, None) + if target not in (None, + None.__new__, + object.__new__, + Enum.__new__): + __new__ = target + break + if __new__ is not None: + break + else: + __new__ = object.__new__ + # if a non-object.__new__ is used then whatever value/tuple was + # assigned to the enum member name will be passed to __new__ and to the + # new enum member's __init__ + if __new__ is object.__new__: + use_args = False + else: + use_args = True + return __new__, save_new, use_args + + +class Enum(metaclass=EnumMeta): + """valueless, unordered enumeration class""" + + # no actual assignments are made as it is a chicken-and-egg problem + # with the metaclass, which checks for the Enum class specifically + + def __new__(cls, value): + # all enum instances are actually created during class construction + # without calling this method; this method is called by the metaclass' + # __call__ (i.e. Color(3) ), and by pickle + if type(value) is cls: + return value + # by-value search for a matching enum member + for member in cls._enum_map.values(): + if member.value == value: + return member + raise ValueError("%s is not a valid %s" % (value, cls.__name__)) + + def __repr__(self): + return "<%s.%s: %r>" % ( + self.__class__.__name__, self._name, self._value) + + def __str__(self): + return "%s.%s" % (self.__class__.__name__, self._name) + + def __dir__(self): + return (['__class__', '__doc__', 'name', 'value']) + + def __eq__(self, other): + if type(other) is self.__class__: + return self is other + return NotImplemented + + def __getnewargs__(self): + return (self._value, ) + + def __hash__(self): + return hash(self._name) + + # _StealthProperty is used to provide access to the `name` and `value` + # properties of enum members while keeping some measure of protection + # from modification, while still allowing for an enumeration to have + # members named `name` and `value`. This works because enumeration + # members are not set directely on the enum class -- __getattr__ is + # used to look them up. + + @_StealthProperty + def name(self): + return self._name + + @_StealthProperty + def value(self): + return self._value + + +class IntEnum(int, Enum): + """Enum where members are also (and must be) ints""" + diff -r eafff38a56cc Lib/test/test_enum.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_enum.py Fri May 10 10:54:12 2013 -0700 @@ -0,0 +1,638 @@ +from collections import OrderedDict +from pickle import dumps, loads +import unittest +from enum import Enum, IntEnum +import sys + + +# for pickle tests +_errors = [] +try: + class Stooges(Enum): + LARRY = 1 + CURLY = 2 + MOE = 3 +except Exception as exc: + _errors.append(exc) + +try: + class IntStooges(int, Enum): + LARRY = 1 + CURLY = 2 + MOE = 3 +except Exception as exc: + _errors.append(exc) + +try: + class FloatStooges(float, Enum): + LARRY = 1.39 + CURLY = 2.72 + MOE = 3.142596 +except Exception as exc: + _errors.append(exc) + +# for pickle test and subclass tests +try: + class StrEnum(str, Enum): + 'accepts only string values' + class Name(StrEnum): + BDFL = 'Guido van Rossum' + FLUFL = 'Barry Warsaw' +except Exception as exc: + _errors.append(exc) + +try: + Question = Enum('Question', 'who what when where why', module='__main__') +except Exception as exc: + _errors.append(exc) + +try: + Answer = Enum('Answer', 'him this then there because') +except Exception as exc: + _errors.append(exc) + +class Test_Enum(unittest.TestCase): + def setUp(self): + class Season(Enum): + SPRING = 1 + SUMMER = 2 + AUTUMN = 3 + WINTER = 4 + self.Season = Season + + def test_global_class_creation(self): + self.assertFalse(_errors) + + def test_enum_in_enum_out(self): + Season = self.Season + self.assertIs(Season(Season.WINTER), Season.WINTER) + + def test_dir_on_class(self): + Season = self.Season + self.assertEqual( + set(dir(Season)), + set(['__class__', '__doc__', '__members__', + 'SPRING', 'SUMMER', 'AUTUMN', 'WINTER']), + ) + + def test_dir_on_item(self): + Season = self.Season + self.assertEqual( + set(dir(Season.WINTER)), + set(['__class__', '__doc__', 'name', 'value']), + ) + + def test_enum(self): + Season = self.Season + lst = list(Season) + self.assertEqual(len(lst), len(Season)) + self.assertEqual(len(Season), 4, Season) + self.assertEqual( + [Season.SPRING, Season.SUMMER, Season.AUTUMN, Season.WINTER], lst) + + for i, season in enumerate('SPRING SUMMER AUTUMN WINTER'.split(), 1): + e = Season(i) + self.assertEqual(e, getattr(Season, season)) + self.assertEqual(e.value, i) + self.assertNotEqual(e, i) + self.assertEqual(e.name, season) + self.assertIn(e, Season) + self.assertTrue(type(e) is Season) + self.assertIsInstance(e, Season) + self.assertEqual(str(e), 'Season.' + season) + self.assertEqual(repr(e), + ''.format(season, i)) + + def test_value_name(self): + Season = self.Season + self.assertEqual(Season.SPRING.name, 'SPRING') + self.assertEqual(Season.SPRING.value, 1) + with self.assertRaises(AttributeError): + Season.SPRING.name = 'invierno' + with self.assertRaises(AttributeError): + Season.SPRING.value = 2 + + def test_contains(self): + Season = self.Season + self.assertIn(Season.AUTUMN, Season) + self.assertNotIn(3, Season) + + val = Season(3) + self.assertIn(val, Season) + + class OtherEnum(Enum): + one = 1; two = 2 + self.assertNotIn(OtherEnum.two, Season) + + def test_comparisons(self): + Season = self.Season + with self.assertRaises(TypeError): + Season.SPRING < Season.WINTER + with self.assertRaises(TypeError): + Season.SPRING > 4 + + self.assertNotEqual(Season.SPRING, 1) + + class Part(Enum): + SPRING = 1 + CLIP = 2 + BARREL = 3 + + self.assertNotEqual(Season.SPRING, Part.SPRING) + with self.assertRaises(TypeError): + Season.SPRING < Part.CLIP + + def test_enum_duplicates(self): + class Season(Enum): + SPRING = 1 + SUMMER = 2 + AUTUMN = FALL = 3 + WINTER = 4 + ANOTHER_SPRING = 1 + lst = list(Season) + self.assertEqual( + lst, + [Season.SPRING, Season.SUMMER, + Season.AUTUMN, Season.WINTER, + ]) + self.assertIs(Season.FALL, Season.AUTUMN) + self.assertEqual(Season.FALL.value, 3) + self.assertEqual(Season.AUTUMN.value, 3) + self.assertIs(Season(3), Season.AUTUMN) + self.assertIs(Season(1), Season.SPRING) + self.assertEqual(Season.FALL.name, 'AUTUMN') + self.assertEqual([k for k,v in Season.__members__.items() if v.name != k], ['FALL', 'ANOTHER_SPRING']) + + def test_enum_with_value_name(self): + class Huh(Enum): + name = 1 + value = 2 + self.assertEqual( + list(Huh), + [Huh.name, Huh.value], + ) + self.assertTrue(type(Huh.name) is Huh) + self.assertEqual(Huh.name.name, 'name') + self.assertEqual(Huh.name.value, 1) + + def test_intenum(self): + class WeekDay(IntEnum): + SUNDAY = 1 + MONDAY = 2 + TUESDAY = 3 + WEDNESDAY = 4 + THURSDAY = 5 + FRIDAY = 6 + SATURDAY = 7 + + self.assertEqual(['a', 'b', 'c'][WeekDay.MONDAY], 'c') + self.assertEqual([i for i in range(WeekDay.TUESDAY)], [0, 1, 2]) + + lst = list(WeekDay) + self.assertEqual(len(lst), len(WeekDay)) + self.assertEqual(len(WeekDay), 7) + for i, weekday in enumerate(r'''SUNDAY MONDAY TUESDAY WEDNESDAY + THURSDAY FRIDAY SATURDAY'''.split(), 1): + e = WeekDay(i) + self.assertEqual(e, i) + self.assertEqual(int(e), i) + self.assertEqual(e.name, weekday) + self.assertIn(e, WeekDay) + self.assertEqual(lst.index(e)+1, i) + self.assertTrue(0 < e < 8) + self.assertIs(type(e), WeekDay) + self.assertIsInstance(e, int) + self.assertIsInstance(e, Enum) + + def test_intenum_duplicates(self): + class WeekDay(IntEnum): + SUNDAY = 1 + MONDAY = 2 + TUESDAY = TEUSDAY = 3 + WEDNESDAY = 4 + THURSDAY = 5 + FRIDAY = 6 + SATURDAY = 7 + self.assertTrue(WeekDay.TEUSDAY is WeekDay.TUESDAY) + self.assertEqual(WeekDay(3).name, 'TUESDAY') + self.assertEqual([k for k,v in WeekDay.__members__.items() if v.name != k], ['TEUSDAY', ]) + + def test_pickle_enum(self): + self.assertTrue(Stooges.CURLY is loads(dumps(Stooges.CURLY))) + + def test_pickle_int(self): + self.assertTrue(IntStooges.CURLY is loads(dumps(IntStooges.CURLY))) + + def test_pickle_float(self): + self.assertTrue(FloatStooges.CURLY is loads(dumps(FloatStooges.CURLY))) + + def test_pickle_enum_function(self): + self.assertIs(Answer.him, loads(dumps(Answer.him))) + + def test_pickle_enum_function_with_module(self): + self.assertIs(Question.who, loads(dumps(Question.who))) + + # not sure how to make this work; create the .py file somewhere importable? + # def test_pickle_enum_function_from_other_module(self): + # from test_module import Duh + # self.assertIs(Duh.umm, loads(dumps(Duh.umm))) + + def test_string_enum(self): + class SkillLevel(str, Enum): + master = 'what is the sound of one hand clapping?' + journeyman = 'why did the chicken cross the road?' + apprentice = 'knock, knock!' + self.assertEqual(SkillLevel.apprentice, 'knock, knock!') + + def test_getattr_getitem(self): + class Period(Enum): + morning = 1 + noon = 2 + evening = 3 + night = 4 + self.assertTrue(Period(2) is Period.noon) + self.assertTrue(getattr(Period, 'night') is Period.night) + self.assertTrue(Period['morning'] is Period.morning) + + def test_getattr_dunder(self): + Season = self.Season + self.assertTrue(getattr(Season, '__eq__')) + + def test_iteration_order(self): + class Season(Enum): + SUMMER = 2 + WINTER = 4 + AUTUMN = 3 + SPRING = 1 + self.assertEqual( + list(Season), + [Season.SUMMER, Season.WINTER, Season.AUTUMN, Season.SPRING], + ) + + def test_programatic_function_string(self): + SummerMonth = Enum('SummerMonth', 'june july august') + lst = list(SummerMonth) + self.assertEqual(len(lst), len(SummerMonth)) + self.assertEqual(len(SummerMonth), 3, SummerMonth) + self.assertEqual([SummerMonth.june, SummerMonth.july, SummerMonth.august], lst) + for i, month in enumerate('june july august'.split(), 1): + e = SummerMonth(i) + self.assertEqual(int(e.value), i) + self.assertNotEqual(e, i) + self.assertEqual(e.name, month) + self.assertIn(e, SummerMonth) + self.assertTrue(type(e) is SummerMonth) + + def test_programatic_function_string_list(self): + SummerMonth = Enum('SummerMonth', ['june', 'july', 'august']) + lst = list(SummerMonth) + self.assertEqual(len(lst), len(SummerMonth)) + self.assertEqual(len(SummerMonth), 3, SummerMonth) + self.assertEqual([SummerMonth.june, SummerMonth.july, SummerMonth.august], lst) + for i, month in enumerate('june july august'.split(), 1): + e = SummerMonth(i) + self.assertEqual(int(e.value), i) + self.assertNotEqual(e, i) + self.assertEqual(e.name, month) + self.assertIn(e, SummerMonth) + self.assertTrue(type(e) is SummerMonth) + + def test_programatic_function_iterable(self): + SummerMonth = Enum('SummerMonth', (('june', 1), ('july', 2), ('august', 3))) + lst = list(SummerMonth) + self.assertEqual(len(lst), len(SummerMonth)) + self.assertEqual(len(SummerMonth), 3, SummerMonth) + self.assertEqual([SummerMonth.june, SummerMonth.july, SummerMonth.august], lst) + for i, month in enumerate('june july august'.split(), 1): + e = SummerMonth(i) + self.assertEqual(int(e.value), i) + self.assertNotEqual(e, i) + self.assertEqual(e.name, month) + self.assertIn(e, SummerMonth) + self.assertTrue(type(e) is SummerMonth) + + def test_programatic_function_from_dict(self): + SummerMonth = Enum('SummerMonth', OrderedDict((('june', 1), ('july', 2), ('august', 3)))) + lst = list(SummerMonth) + self.assertEqual(len(lst), len(SummerMonth)) + self.assertEqual(len(SummerMonth), 3, SummerMonth) + self.assertEqual([SummerMonth.june, SummerMonth.july, SummerMonth.august], lst) + for i, month in enumerate('june july august'.split(), 1): + e = SummerMonth(i) + self.assertEqual(int(e.value), i) + self.assertNotEqual(e, i) + self.assertEqual(e.name, month) + self.assertIn(e, SummerMonth) + self.assertTrue(type(e) is SummerMonth) + + def test_programatic_function_type(self): + SummerMonth = Enum('SummerMonth', 'june july august', type=int) + lst = list(SummerMonth) + self.assertEqual(len(lst), len(SummerMonth)) + self.assertEqual(len(SummerMonth), 3, SummerMonth) + self.assertEqual([SummerMonth.june, SummerMonth.july, SummerMonth.august], lst) + for i, month in enumerate('june july august'.split(), 1): + e = SummerMonth(i) + self.assertEqual(e, i) + self.assertEqual(e.name, month) + self.assertIn(e, SummerMonth) + self.assertTrue(type(e) is SummerMonth) + + def test_programatic_function_type_from_subclass(self): + SummerMonth = IntEnum('SummerMonth', 'june july august') + lst = list(SummerMonth) + self.assertEqual(len(lst), len(SummerMonth)) + self.assertEqual(len(SummerMonth), 3, SummerMonth) + self.assertEqual([SummerMonth.june, SummerMonth.july, SummerMonth.august], lst) + for i, month in enumerate('june july august'.split(), 1): + e = SummerMonth(i) + self.assertEqual(e, i) + self.assertEqual(e.name, month) + self.assertIn(e, SummerMonth) + self.assertTrue(type(e) is SummerMonth) + + def test_subclassing(self): + self.assertEqual(Name.BDFL, 'Guido van Rossum') + self.assertTrue(Name.BDFL, Name('Guido van Rossum')) + self.assertTrue(Name.BDFL is getattr(Name, 'BDFL')) + self.assertTrue(Name.BDFL is loads(dumps(Name.BDFL))) + + def test_extending(self): + class Color(Enum): + red = 1 + green = 2 + blue = 3 + with self.assertRaises(TypeError): + class MoreColor(Color): + cyan = 4 + magenta = 5 + yellow = 6 + + def test_exclude_methods(self): + class whatever(Enum): + this = 'that' + these = 'those' + def really(self): + return 'no, not %s' % self.value + self.assertFalse(type(whatever.really) is whatever) + self.assertEqual(whatever.this.really(), 'no, not that') + + def test_overwrite_enums(self): + class Why(Enum): + question = 1 + answer = 2 + propisition = 3 + def question(self): + print(42) + self.assertFalse(type(Why.question) is Why) + self.assertFalse(Why.question in Why._enum_names) + self.assertFalse(Why.question in Why) + + def test_wrong_inheritance_order(self): + with self.assertRaises(TypeError): + class Wrong(Enum, str): + NotHere = 'error before this point' + + def test_wrong_enum_in_call(self): + class Monochrome(Enum): + black = 0 + white = 1 + class Gender(Enum): + male = 0 + female = 1 + self.assertRaises(ValueError, Monochrome, Gender.male) + + def test_wrong_enum_in_mixed_call(self): + class Monochrome(IntEnum): + black = 0 + white = 1 + class Gender(Enum): + male = 0 + female = 1 + self.assertRaises(ValueError, Monochrome, Gender.male) + + def test_mixed_enum_in_call_1(self): + class Monochrome(IntEnum): + black = 0 + white = 1 + class Gender(IntEnum): + male = 0 + female = 1 + self.assertIs(Monochrome(Gender.female), Monochrome.white) + + def test_mixed_enum_in_call_2(self): + class Monochrome(Enum): + black = 0 + white = 1 + class Gender(IntEnum): + male = 0 + female = 1 + self.assertIs(Monochrome(Gender.male), Monochrome.black) + + def test_flufl_enum(self): + class Fluflnum(Enum): + def __int__(self): + return int(self.value) + class MailManOptions(Fluflnum): + option1 = 1 + option2 = 2 + option3 = 3 + self.assertEqual(int(MailManOptions.option1), 1) + + def test_no_such_enum_member(self): + class Color(Enum): + red = 1 + green = 2 + blue = 3 + with self.assertRaises(ValueError): + Color(4) + with self.assertRaises(KeyError): + Color['chartreuse'] + + def test_new_repr(self): + class Color(Enum): + red = 1 + green = 2 + blue = 3 + def __repr__(self): + return "don't you just love shades of %s?" % self.name + self.assertEqual(repr(Color.blue), "don't you just love shades of blue?") + + def test_inherited_repr(self): + class MyEnum(Enum): + def __repr__(self): + return "My name is %s." % self.name + class MyIntEnum(int, MyEnum): + this = 1 + that = 2 + theother = 3 + self.assertEqual(repr(MyIntEnum.that), "My name is that.") + + def test_multiple_mixin_mro(self): + class auto_enum(type(Enum)): + def __new__(metacls, cls, bases, classdict): + temp = type(classdict)() + names = set(classdict._enum_names) + i = 0 + for k in classdict._enum_names: + v = classdict[k] + if v is Ellipsis: + v = i + else: + i = v + i += 1 + temp[k] = v + for k, v in classdict.items(): + if k not in names: + temp[k] = v + return super(auto_enum, metacls).__new__(metacls, cls, bases, temp) + + class AutoNumberedEnum(Enum, metaclass=auto_enum): + pass + + class AutoIntEnum(IntEnum, metaclass=auto_enum): + pass + + class TestAutoNumber(AutoNumberedEnum): + a = ... + b = 3 + c = ... + + class TestAutoInt(AutoIntEnum): + a = ... + b = 3 + c = ... + + def test_subclasses_with_getnewargs(self): + class NamedInt( int ): + def __new__( cls, *args, **kwds ): + _args = args + name, *args = args + if len( args ) == 0: + raise TypeError("name and value must be specified") + self = int.__new__( cls, *args, **kwds ) + self._intname = name + return self + @property + def __name__( self ): + return self._intname + def __repr__( self ): + # repr() is updated to include the name and type info + return "{}({!r}, {})".format(type(self).__name__, + self.__name__, + int.__repr__(self)) + def __str__( self ): + # str() is unchanged, even if it relies on the repr() fallback + base = int + base_str = base.__str__ + if base_str.__objclass__ is object: + return base.__repr__(self) + return base_str(self) + # for simplicity, we only define one operator that propagates expressions + def __add__(self, other): + temp = int( self ) + int( other ) + if isinstance( self, NamedInt ) and isinstance( other, NamedInt ): + return NamedInt( + '({0} + {1})'.format(self.__name__, other.__name__), + temp ) + else: + return temp + + class NEI( NamedInt, Enum ): + x = ('the-x', 1 ) + y = ('the-y', 2 ) + + self.assertEqual(repr(NEI.x + NEI.y), "NamedInt('(the-x + the-y)', 3)") + globals()['NamedInt'] = NamedInt + globals()['NEI'] = NEI + self.assertIs(loads(dumps(NEI.y)), NEI.y) + + def test_tuple_subclass(self): + class SomeTuple(tuple, Enum): + first = (1, 'for the money') + second = (2, 'for the show') + third = (3, 'for the music') + self.assertTrue(type(SomeTuple.first) is SomeTuple) + self.assertTrue(isinstance(SomeTuple.second, tuple)) + self.assertEqual(SomeTuple.third, (3, 'for the music')) + globals()['SomeTuple'] = SomeTuple + self.assertTrue(loads(dumps(SomeTuple.first)), SomeTuple.first) + + def test_duplicate_values_give_unique_enum_items(self): + class AutoNumber(Enum): + first = () + second = () + third = () + def __new__(cls): + value = len(cls.__members__) + 1 + obj = object.__new__(cls) + obj._value = value + return obj + def __int__(self): + return self.value + self.assertEqual(list(AutoNumber), [AutoNumber.first, AutoNumber.second, AutoNumber.third]) + self.assertEqual(int(AutoNumber.second), 2) + self.assertTrue(AutoNumber(1) is AutoNumber.first) + + def test_inherited_new_from_enhanced_enum(self): + class AutoNumber(Enum): + def __new__(cls): + value = len(cls.__members__) + 1 + obj = object.__new__(cls) + obj._value = value + return obj + def __int__(self): + return self._value + class Color(AutoNumber): + red = () + green = () + blue = () + self.assertEqual(list(Color), [Color.red, Color.green, Color.blue]) + self.assertEqual(list(map(int, Color)), [1, 2, 3]) + + def test_inherited_new_from_mixed_enum(self): + class AutoNumber(IntEnum): + def __new__(cls): + value = len(cls.__members__) + 1 + obj = int.__new__(cls, value) + obj._value = value + return obj + class Color(AutoNumber): + red = () + green = () + blue = () + self.assertEqual(list(Color), [Color.red, Color.green, Color.blue]) + self.assertEqual(list(map(int, Color)), [1, 2, 3]) + + def test_ordered_mixin(self): + class OrderedEnum(Enum): + def __ge__(self, other): + if self.__class__ is other.__class__: + return self._value >= other._value + return NotImplemented + def __gt__(self, other): + if self.__class__ is other.__class__: + return self._value > other._value + return NotImplemented + def __le__(self, other): + if self.__class__ is other.__class__: + return self._value <= other._value + return NotImplemented + def __lt__(self, other): + if self.__class__ is other.__class__: + return self._value < other._value + return NotImplemented + class Grade(OrderedEnum): + A = 5 + B = 4 + C = 3 + D = 2 + F = 1 + self.assertTrue(Grade.A > Grade.B) + self.assertTrue(Grade.F <= Grade.C) + self.assertTrue(Grade.D < Grade.A) + self.assertTrue(Grade.B >= Grade.B) + + +if __name__ == '__main__': + unittest.main()