diff options
author | Ethan Furman <ethan@stoneleaf.us> | 2022-01-16 06:41:43 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-01-16 06:41:43 (GMT) |
commit | acf7403f9baea3ae1119fc6b4a3298522188bf96 (patch) | |
tree | fcffbb83c601353ac1fce9b35b0f2424c8ce9899 /Lib/test/test_enum.py | |
parent | 37eab55ac9da6b6361f136a1da15bfcef12ed954 (diff) | |
download | cpython-acf7403f9baea3ae1119fc6b4a3298522188bf96.zip cpython-acf7403f9baea3ae1119fc6b4a3298522188bf96.tar.gz cpython-acf7403f9baea3ae1119fc6b4a3298522188bf96.tar.bz2 |
bpo-40066: [Enum] update str() and format() output (GH-30582)
Undo rejected PEP-663 changes:
- restore `repr()` to its 3.10 status
- restore `str()` to its 3.10 status
New changes:
- `IntEnum` and `IntFlag` now leave `__str__` as the original `int.__str__` so that str() and format() return the same result
- zero-valued flags without a name have a slightly changed repr(), e.g. `repr(Color(0)) == '<Color: 0>'`
- update `dir()` for mixed-in types to return all the methods and attributes of the mixed-in type
- added `_numeric_repr_` to `Flag` to control display of unnamed values
- enums without doc strings have a more comprehensive doc string added
- `ReprEnum` added -- inheriting from this makes it so only `__repr__` is replaced, not `__str__` nor `__format__`; `IntEnum`, `IntFlag`, and `StrEnum` all inherit from `ReprEnum`
Diffstat (limited to 'Lib/test/test_enum.py')
-rw-r--r-- | Lib/test/test_enum.py | 2864 |
1 files changed, 1359 insertions, 1505 deletions
diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py index 43f98c1..a0953fb 100644 --- a/Lib/test/test_enum.py +++ b/Lib/test/test_enum.py @@ -6,15 +6,18 @@ import pydoc import sys import unittest import threading +import builtins as bltns from collections import OrderedDict +from datetime import date from enum import Enum, IntEnum, StrEnum, EnumType, Flag, IntFlag, unique, auto from enum import STRICT, CONFORM, EJECT, KEEP, _simple_enum, _test_simple_enum -from enum import verify, UNIQUE, CONTINUOUS, NAMED_FLAGS +from enum import verify, UNIQUE, CONTINUOUS, NAMED_FLAGS, ReprEnum from io import StringIO from pickle import dumps, loads, PicklingError, HIGHEST_PROTOCOL from test import support from test.support import ALWAYS_EQ from test.support import threading_helper +from textwrap import dedent from datetime import timedelta python_version = sys.version_info[:2] @@ -107,6 +110,12 @@ def test_pickle_exception(assertion, exception, obj): class TestHelpers(unittest.TestCase): # _is_descriptor, _is_sunder, _is_dunder + sunder_names = '_bad_', '_good_', '_what_ho_' + dunder_names = '__mal__', '__bien__', '__que_que__' + private_names = '_MyEnum__private', '_MyEnum__still_private' + private_and_sunder_names = '_MyEnum__private_', '_MyEnum__also_private_' + random_names = 'okay', '_semi_private', '_weird__', '_MyEnum__' + def test_is_descriptor(self): class foo: pass @@ -116,21 +125,36 @@ class TestHelpers(unittest.TestCase): setattr(obj, attr, 1) self.assertTrue(enum._is_descriptor(obj)) - def test_is_sunder(self): + def test_sunder(self): + for name in self.sunder_names + self.private_and_sunder_names: + self.assertTrue(enum._is_sunder(name), '%r is a not sunder name?' % name) + for name in self.dunder_names + self.private_names + self.random_names: + self.assertFalse(enum._is_sunder(name), '%r is a sunder name?' % name) for s in ('_a_', '_aa_'): self.assertTrue(enum._is_sunder(s)) - for s in ('a', 'a_', '_a', '__a', 'a__', '__a__', '_a__', '__a_', '_', '__', '___', '____', '_____',): self.assertFalse(enum._is_sunder(s)) - def test_is_dunder(self): + def test_dunder(self): + for name in self.dunder_names: + self.assertTrue(enum._is_dunder(name), '%r is a not dunder name?' % name) + for name in self.sunder_names + self.private_names + self.private_and_sunder_names + self.random_names: + self.assertFalse(enum._is_dunder(name), '%r is a dunder name?' % name) for s in ('__a__', '__aa__'): self.assertTrue(enum._is_dunder(s)) for s in ('a', 'a_', '_a', '__a', 'a__', '_a_', '_a__', '__a_', '_', '__', '___', '____', '_____',): self.assertFalse(enum._is_dunder(s)) + + def test_is_private(self): + for name in self.private_names + self.private_and_sunder_names: + self.assertTrue(enum._is_private('MyEnum', name), '%r is a not private name?') + for name in self.sunder_names + self.dunder_names + self.random_names: + self.assertFalse(enum._is_private('MyEnum', name), '%r is a private name?') + + # for subclassing tests class classproperty: @@ -166,473 +190,658 @@ class HeadlightsC(IntFlag, boundary=enum.CONFORM): # tests -class TestEnum(unittest.TestCase): +class _EnumTests: + """ + Test for behavior that is the same across the different types of enumerations. + """ + + values = None def setUp(self): - class Season(Enum): - SPRING = 1 - SUMMER = 2 - AUTUMN = 3 - WINTER = 4 - self.Season = Season + class BaseEnum(self.enum_type): + @enum.property + def first(self): + return '%s is first!' % self.name + class MainEnum(BaseEnum): + first = auto() + second = auto() + third = auto() + if issubclass(self.enum_type, Flag): + dupe = 3 + else: + dupe = third + self.MainEnum = MainEnum + # + class NewStrEnum(self.enum_type): + def __str__(self): + return self.name.upper() + first = auto() + self.NewStrEnum = NewStrEnum + # + class NewFormatEnum(self.enum_type): + def __format__(self, spec): + return self.name.upper() + first = auto() + self.NewFormatEnum = NewFormatEnum + # + class NewStrFormatEnum(self.enum_type): + def __str__(self): + return self.name.title() + def __format__(self, spec): + return ''.join(reversed(self.name)) + first = auto() + self.NewStrFormatEnum = NewStrFormatEnum + # + class NewBaseEnum(self.enum_type): + def __str__(self): + return self.name.title() + def __format__(self, spec): + return ''.join(reversed(self.name)) + class NewSubEnum(NewBaseEnum): + first = auto() + self.NewSubEnum = NewSubEnum + # + self.is_flag = False + self.names = ['first', 'second', 'third'] + if issubclass(MainEnum, StrEnum): + self.values = self.names + elif MainEnum._member_type_ is str: + self.values = ['1', '2', '3'] + elif issubclass(self.enum_type, Flag): + self.values = [1, 2, 4] + self.is_flag = True + self.dupe2 = MainEnum(5) + else: + self.values = self.values or [1, 2, 3] + # + if not getattr(self, 'source_values', False): + self.source_values = self.values - class Konstants(float, Enum): - E = 2.7182818 - PI = 3.1415926 - TAU = 2 * PI - self.Konstants = Konstants + def assertFormatIsValue(self, spec, member): + self.assertEqual(spec.format(member), spec.format(member.value)) - class Grades(IntEnum): - A = 5 - B = 4 - C = 3 - D = 2 - F = 0 - self.Grades = Grades + def assertFormatIsStr(self, spec, member): + self.assertEqual(spec.format(member), spec.format(str(member))) - class Directional(str, Enum): - EAST = 'east' - WEST = 'west' - NORTH = 'north' - SOUTH = 'south' - self.Directional = Directional + def test_attribute_deletion(self): + class Season(self.enum_type): + SPRING = auto() + SUMMER = auto() + AUTUMN = auto() + # + def spam(cls): + pass + # + self.assertTrue(hasattr(Season, 'spam')) + del Season.spam + self.assertFalse(hasattr(Season, 'spam')) + # + with self.assertRaises(AttributeError): + del Season.SPRING + with self.assertRaises(AttributeError): + del Season.DRY + with self.assertRaises(AttributeError): + del Season.SPRING.name - from datetime import date - class Holiday(date, Enum): - NEW_YEAR = 2013, 1, 1 - IDES_OF_MARCH = 2013, 3, 15 - self.Holiday = Holiday + def test_basics(self): + TE = self.MainEnum + if self.is_flag: + self.assertEqual(repr(TE), "<flag 'MainEnum'>") + self.assertEqual(str(TE), "<flag 'MainEnum'>") + self.assertEqual(format(TE), "<flag 'MainEnum'>") + self.assertTrue(TE(5) is self.dupe2) + else: + self.assertEqual(repr(TE), "<enum 'MainEnum'>") + self.assertEqual(str(TE), "<enum 'MainEnum'>") + self.assertEqual(format(TE), "<enum 'MainEnum'>") + self.assertEqual(list(TE), [TE.first, TE.second, TE.third]) + self.assertEqual( + [m.name for m in TE], + self.names, + ) + self.assertEqual( + [m.value for m in TE], + self.values, + ) + self.assertEqual( + [m.first for m in TE], + ['first is first!', 'second is first!', 'third is first!'] + ) + for member, name in zip(TE, self.names, strict=True): + self.assertIs(TE[name], member) + for member, value in zip(TE, self.values, strict=True): + self.assertIs(TE(value), member) + if issubclass(TE, StrEnum): + self.assertTrue(TE.dupe is TE('third') is TE['dupe']) + elif TE._member_type_ is str: + self.assertTrue(TE.dupe is TE('3') is TE['dupe']) + elif issubclass(TE, Flag): + self.assertTrue(TE.dupe is TE(3) is TE['dupe']) + else: + self.assertTrue(TE.dupe is TE(self.values[2]) is TE['dupe']) - class DateEnum(date, Enum): pass - self.DateEnum = DateEnum + def test_bool_is_true(self): + class Empty(self.enum_type): + pass + self.assertTrue(Empty) + # + self.assertTrue(self.MainEnum) + for member in self.MainEnum: + self.assertTrue(member) - class FloatEnum(float, Enum): pass - self.FloatEnum = FloatEnum + def test_changing_member_fails(self): + MainEnum = self.MainEnum + with self.assertRaises(AttributeError): + self.MainEnum.second = 'really first' - class Wowser(Enum): - this = 'that' - these = 'those' - def wowser(self): - """Wowser docstring""" - return ("Wowser! I'm %s!" % self.name) - @classmethod - def classmethod_wowser(cls): pass - @staticmethod - def staticmethod_wowser(): pass - self.Wowser = Wowser - - class IntWowser(IntEnum): - this = 1 - these = 2 - def wowser(self): - """Wowser docstring""" - return ("Wowser! I'm %s!" % self.name) - @classmethod - def classmethod_wowser(cls): pass - @staticmethod - def staticmethod_wowser(): pass - self.IntWowser = IntWowser - - class FloatWowser(float, Enum): - this = 3.14 - these = 4.2 - def wowser(self): - """Wowser docstring""" - return ("Wowser! I'm %s!" % self.name) - @classmethod - def classmethod_wowser(cls): pass - @staticmethod - def staticmethod_wowser(): pass - self.FloatWowser = FloatWowser + @unittest.skipIf( + python_version >= (3, 12), + '__contains__ now returns True/False for all inputs', + ) + def test_contains_er(self): + MainEnum = self.MainEnum + self.assertIn(MainEnum.third, MainEnum) + with self.assertRaises(TypeError): + with self.assertWarns(DeprecationWarning): + self.source_values[1] in MainEnum + with self.assertRaises(TypeError): + with self.assertWarns(DeprecationWarning): + 'first' in MainEnum + val = MainEnum.dupe + self.assertIn(val, MainEnum) + # + class OtherEnum(Enum): + one = auto() + two = auto() + self.assertNotIn(OtherEnum.two, MainEnum) - class WowserNoMembers(Enum): - def wowser(self): pass - @classmethod - def classmethod_wowser(cls): pass - @staticmethod - def staticmethod_wowser(): pass - class SubclassOfWowserNoMembers(WowserNoMembers): pass - self.WowserNoMembers = WowserNoMembers - self.SubclassOfWowserNoMembers = SubclassOfWowserNoMembers - - class IntWowserNoMembers(IntEnum): - def wowser(self): pass - @classmethod - def classmethod_wowser(cls): pass - @staticmethod - def staticmethod_wowser(): pass - self.IntWowserNoMembers = IntWowserNoMembers + @unittest.skipIf( + python_version < (3, 12), + '__contains__ works only with enum memmbers before 3.12', + ) + def test_contains_tf(self): + MainEnum = self.MainEnum + self.assertIn(MainEnum.first, MainEnum) + self.assertTrue(self.source_values[0] in MainEnum) + self.assertFalse('first' in MainEnum) + val = MainEnum.dupe + self.assertIn(val, MainEnum) + # + class OtherEnum(Enum): + one = auto() + two = auto() + self.assertNotIn(OtherEnum.two, MainEnum) - class FloatWowserNoMembers(float, Enum): - def wowser(self): pass - @classmethod - def classmethod_wowser(cls): pass - @staticmethod - def staticmethod_wowser(): pass - self.FloatWowserNoMembers = FloatWowserNoMembers - - class EnumWithInit(Enum): - def __init__(self, greeting, farewell): - self.greeting = greeting - self.farewell = farewell - ENGLISH = 'hello', 'goodbye' - GERMAN = 'Guten Morgen', 'Auf Wiedersehen' - def some_method(self): pass - self.EnumWithInit = EnumWithInit + def test_dir_on_class(self): + TE = self.MainEnum + self.assertEqual(set(dir(TE)), set(enum_dir(TE))) + def test_dir_on_item(self): + TE = self.MainEnum + self.assertEqual(set(dir(TE.first)), set(member_dir(TE.first))) + + def test_dir_with_added_behavior(self): + class Test(self.enum_type): + this = auto() + these = auto() + def wowser(self): + return ("Wowser! I'm %s!" % self.name) + self.assertTrue('wowser' not in dir(Test)) + self.assertTrue('wowser' in dir(Test.this)) + + def test_dir_on_sub_with_behavior_on_super(self): # see issue22506 - class SuperEnum1(Enum): + class SuperEnum(self.enum_type): def invisible(self): return "did you see me?" - class SubEnum1(SuperEnum1): - sample = 5 - self.SubEnum1 = SubEnum1 + class SubEnum(SuperEnum): + sample = auto() + self.assertTrue('invisible' not in dir(SubEnum)) + self.assertTrue('invisible' in dir(SubEnum.sample)) - class SuperEnum2(IntEnum): - def __new__(cls, value, description=""): - obj = int.__new__(cls, value) - obj._value_ = value - obj.description = description + def test_dir_on_sub_with_behavior_including_instance_dict_on_super(self): + # see issue40084 + class SuperEnum(self.enum_type): + def __new__(cls, *value, **kwds): + new = self.enum_type._member_type_.__new__ + if self.enum_type._member_type_ is object: + obj = new(cls) + else: + if isinstance(value[0], tuple): + create_value ,= value[0] + else: + create_value = value + obj = new(cls, *create_value) + obj._value_ = value[0] if len(value) == 1 else value + obj.description = 'test description' return obj - class SubEnum2(SuperEnum2): - sample = 5 - self.SubEnum2 = SubEnum2 - - def test_dir_basics_for_all_enums(self): - enums_for_tests = ( - # Generic enums in enum.py - Enum, - IntEnum, - StrEnum, - # Generic enums defined outside of enum.py - self.DateEnum, - self.FloatEnum, - # Concrete enums derived from enum.py generics - self.Grades, - self.Season, - # Concrete enums derived from generics defined outside of enum.py - self.Konstants, - self.Holiday, - # Standard enum with added behaviour & members - self.Wowser, - # Mixin-enum-from-enum.py with added behaviour & members - self.IntWowser, - # Mixin-enum-from-oustide-enum.py with added behaviour & members - self.FloatWowser, - # Equivalents of the three immediately above, but with no members - self.WowserNoMembers, - self.IntWowserNoMembers, - self.FloatWowserNoMembers, - # Enum with members and an __init__ method - self.EnumWithInit, - # Special cases to test - self.SubEnum1, - self.SubEnum2 - ) - - for cls in enums_for_tests: - with self.subTest(cls=cls): - cls_dir = dir(cls) - # test that dir is deterministic - self.assertEqual(cls_dir, dir(cls)) - # test that dir is sorted - self.assertEqual(list(cls_dir), sorted(cls_dir)) - # test that there are no dupes in dir - self.assertEqual(len(cls_dir), len(set(cls_dir))) - # test that there are no sunders in dir - self.assertFalse(any(enum._is_sunder(attr) for attr in cls_dir)) - self.assertNotIn('__new__', cls_dir) - - for attr in ('__class__', '__doc__', '__members__', '__module__'): - with self.subTest(attr=attr): - self.assertIn(attr, cls_dir) - - def test_dir_for_enum_with_members(self): - enums_for_test = ( - # Enum with members - self.Season, - # IntEnum with members - self.Grades, - # Two custom-mixin enums with members - self.Konstants, - self.Holiday, - # several enums-with-added-behaviour and members - self.Wowser, - self.IntWowser, - self.FloatWowser, - # An enum with an __init__ method and members - self.EnumWithInit, - # Special cases to test - self.SubEnum1, - self.SubEnum2 - ) - - for cls in enums_for_test: - cls_dir = dir(cls) - member_names = cls._member_names_ - with self.subTest(cls=cls): - self.assertTrue(all(member_name in cls_dir for member_name in member_names)) - for member in cls: - member_dir = dir(member) - # test that dir is deterministic - self.assertEqual(member_dir, dir(member)) - # test that dir is sorted - self.assertEqual(list(member_dir), sorted(member_dir)) - # test that there are no dupes in dir - self.assertEqual(len(member_dir), len(set(member_dir))) - - for attr_name in cls_dir: - with self.subTest(attr_name=attr_name): - if attr_name in {'__members__', '__init__', '__new__', *member_names}: - self.assertNotIn(attr_name, member_dir) - else: - self.assertIn(attr_name, member_dir) - - self.assertFalse(any(enum._is_sunder(attr) for attr in member_dir)) - - def test_dir_for_enums_with_added_behaviour(self): - enums_for_test = ( - self.Wowser, - self.IntWowser, - self.FloatWowser, - self.WowserNoMembers, - self.SubclassOfWowserNoMembers, - self.IntWowserNoMembers, - self.FloatWowserNoMembers - ) - - for cls in enums_for_test: - with self.subTest(cls=cls): - self.assertIn('wowser', dir(cls)) - self.assertIn('classmethod_wowser', dir(cls)) - self.assertIn('staticmethod_wowser', dir(cls)) - self.assertTrue(all( - all(attr in dir(member) for attr in ('wowser', 'classmethod_wowser', 'staticmethod_wowser')) - for member in cls - )) + class SubEnum(SuperEnum): + sample = self.source_values[1] + self.assertTrue('description' not in dir(SubEnum)) + self.assertTrue('description' in dir(SubEnum.sample), dir(SubEnum.sample)) - self.assertEqual(dir(self.WowserNoMembers), dir(self.SubclassOfWowserNoMembers)) - # Check classmethods are present - self.assertIn('from_bytes', dir(self.IntWowser)) - self.assertIn('from_bytes', dir(self.IntWowserNoMembers)) - - def test_help_output_on_enum_members(self): - added_behaviour_enums = ( - self.Wowser, - self.IntWowser, - self.FloatWowser - ) - - for cls in added_behaviour_enums: - with self.subTest(cls=cls): - rendered_doc = pydoc.render_doc(cls.this) - self.assertIn('Wowser docstring', rendered_doc) - if cls in {self.IntWowser, self.FloatWowser}: - self.assertIn('float(self)', rendered_doc) - - def test_dir_for_enum_with_init(self): - EnumWithInit = self.EnumWithInit - - cls_dir = dir(EnumWithInit) - self.assertIn('__init__', cls_dir) - self.assertIn('some_method', cls_dir) - self.assertNotIn('greeting', cls_dir) - self.assertNotIn('farewell', cls_dir) - - member_dir = dir(EnumWithInit.ENGLISH) - self.assertNotIn('__init__', member_dir) - self.assertIn('some_method', member_dir) - self.assertIn('greeting', member_dir) - self.assertIn('farewell', member_dir) - - def test_mixin_dirs(self): - from datetime import date + def test_enum_in_enum_out(self): + Main = self.MainEnum + self.assertIs(Main(Main.first), Main.first) - enums_for_test = ( - # generic mixins from enum.py - (IntEnum, int), - (StrEnum, str), - # generic mixins from outside enum.py - (self.FloatEnum, float), - (self.DateEnum, date), - # concrete mixin from enum.py - (self.Grades, int), - # concrete mixin from outside enum.py - (self.Holiday, date), - # concrete mixin from enum.py with added behaviour - (self.IntWowser, int), - # concrete mixin from outside enum.py with added behaviour - (self.FloatWowser, float) - ) - - enum_dict = Enum.__dict__ - enum_dir = dir(Enum) - enum_module_names = enum.__all__ - is_from_enum_module = lambda cls: cls.__name__ in enum_module_names - is_enum_dunder = lambda attr: enum._is_dunder(attr) and attr in enum_dict - - def attr_is_inherited_from_object(cls, attr_name): - for base in cls.__mro__: - if attr_name in base.__dict__: - return base is object - return False - - # General tests - for enum_cls, mixin_cls in enums_for_test: - with self.subTest(enum_cls=enum_cls): - cls_dir = dir(enum_cls) - cls_dict = enum_cls.__dict__ - - mixin_attrs = [ - x for x in dir(mixin_cls) - if not attr_is_inherited_from_object(cls=mixin_cls, attr_name=x) - ] + def test_hash(self): + MainEnum = self.MainEnum + mapping = {} + mapping[MainEnum.first] = '1225' + mapping[MainEnum.second] = '0315' + mapping[MainEnum.third] = '0704' + self.assertEqual(mapping[MainEnum.second], '0315') + + def test_invalid_names(self): + with self.assertRaises(ValueError): + class Wrong(self.enum_type): + mro = 9 + with self.assertRaises(ValueError): + class Wrong(self.enum_type): + _create_= 11 + with self.assertRaises(ValueError): + class Wrong(self.enum_type): + _get_mixins_ = 9 + with self.assertRaises(ValueError): + class Wrong(self.enum_type): + _find_new_ = 1 + with self.assertRaises(ValueError): + class Wrong(self.enum_type): + _any_name_ = 9 + + def test_object_str_override(self): + "check that setting __str__ to object's is not reset to Enum's" + class Generic(self.enum_type): + item = self.source_values[2] + def __repr__(self): + return "%s.test" % (self._name_, ) + __str__ = object.__str__ + self.assertEqual(str(Generic.item), 'item.test') + + def test_overridden_str(self): + NS = self.NewStrEnum + self.assertEqual(str(NS.first), NS.first.name.upper()) + self.assertEqual(format(NS.first), NS.first.name.upper()) + + def test_overridden_str_format(self): + NSF = self.NewStrFormatEnum + self.assertEqual(str(NSF.first), NSF.first.name.title()) + self.assertEqual(format(NSF.first), ''.join(reversed(NSF.first.name))) - first_enum_base = next( - base for base in enum_cls.__mro__ - if is_from_enum_module(base) + def test_overridden_str_format_inherited(self): + NSE = self.NewSubEnum + self.assertEqual(str(NSE.first), NSE.first.name.title()) + self.assertEqual(format(NSE.first), ''.join(reversed(NSE.first.name))) + + def test_programmatic_function_string(self): + MinorEnum = self.enum_type('MinorEnum', 'june july august') + lst = list(MinorEnum) + self.assertEqual(len(lst), len(MinorEnum)) + self.assertEqual(len(MinorEnum), 3, MinorEnum) + self.assertEqual( + [MinorEnum.june, MinorEnum.july, MinorEnum.august], + lst, ) + values = self.values + if self.enum_type is StrEnum: + values = ['june','july','august'] + for month, av in zip('june july august'.split(), values): + e = MinorEnum[month] + self.assertEqual(e.value, av, list(MinorEnum)) + self.assertEqual(e.name, month) + if MinorEnum._member_type_ is not object and issubclass(MinorEnum, MinorEnum._member_type_): + self.assertEqual(e, av) + else: + self.assertNotEqual(e, av) + self.assertIn(e, MinorEnum) + self.assertIs(type(e), MinorEnum) + self.assertIs(e, MinorEnum(av)) - for attr in mixin_attrs: - with self.subTest(attr=attr): - if enum._is_sunder(attr): - # Unlikely, but no harm in testing - self.assertNotIn(attr, cls_dir) - elif attr in {'__class__', '__doc__', '__members__', '__module__'}: - self.assertIn(attr, cls_dir) - elif is_enum_dunder(attr): - if is_from_enum_module(enum_cls): - self.assertNotIn(attr, cls_dir) - elif getattr(enum_cls, attr) is getattr(first_enum_base, attr): - self.assertNotIn(attr, cls_dir) - else: - self.assertIn(attr, cls_dir) - else: - self.assertIn(attr, cls_dir) - - # Some specific examples - int_enum_dir = dir(IntEnum) - self.assertIn('imag', int_enum_dir) - self.assertIn('__rfloordiv__', int_enum_dir) - self.assertNotIn('__format__', int_enum_dir) - self.assertNotIn('__hash__', int_enum_dir) - self.assertNotIn('__init_subclass__', int_enum_dir) - self.assertNotIn('__subclasshook__', int_enum_dir) - - class OverridesFormatOutsideEnumModule(Enum): - def __format__(self, *args, **kwargs): - return super().__format__(*args, **kwargs) - SOME_MEMBER = 1 - - self.assertIn('__format__', dir(OverridesFormatOutsideEnumModule)) - self.assertIn('__format__', dir(OverridesFormatOutsideEnumModule.SOME_MEMBER)) + def test_programmatic_function_string_list(self): + MinorEnum = self.enum_type('MinorEnum', ['june', 'july', 'august']) + lst = list(MinorEnum) + self.assertEqual(len(lst), len(MinorEnum)) + self.assertEqual(len(MinorEnum), 3, MinorEnum) + self.assertEqual( + [MinorEnum.june, MinorEnum.july, MinorEnum.august], + lst, + ) + values = self.values + if self.enum_type is StrEnum: + values = ['june','july','august'] + for month, av in zip('june july august'.split(), values): + e = MinorEnum[month] + self.assertEqual(e.value, av) + self.assertEqual(e.name, month) + if MinorEnum._member_type_ is not object and issubclass(MinorEnum, MinorEnum._member_type_): + self.assertEqual(e, av) + else: + self.assertNotEqual(e, av) + self.assertIn(e, MinorEnum) + self.assertIs(type(e), MinorEnum) + self.assertIs(e, MinorEnum(av)) - def test_dir_on_sub_with_behavior_on_super(self): - # see issue22506 + def test_programmatic_function_iterable(self): + MinorEnum = self.enum_type( + 'MinorEnum', + (('june', self.source_values[0]), ('july', self.source_values[1]), ('august', self.source_values[2])) + ) + lst = list(MinorEnum) + self.assertEqual(len(lst), len(MinorEnum)) + self.assertEqual(len(MinorEnum), 3, MinorEnum) self.assertEqual( - set(dir(self.SubEnum1.sample)), - set(['__class__', '__doc__', '__module__', 'name', 'value', 'invisible']), + [MinorEnum.june, MinorEnum.july, MinorEnum.august], + lst, ) + for month, av in zip('june july august'.split(), self.values): + e = MinorEnum[month] + self.assertEqual(e.value, av) + self.assertEqual(e.name, month) + if MinorEnum._member_type_ is not object and issubclass(MinorEnum, MinorEnum._member_type_): + self.assertEqual(e, av) + else: + self.assertNotEqual(e, av) + self.assertIn(e, MinorEnum) + self.assertIs(type(e), MinorEnum) + self.assertIs(e, MinorEnum(av)) - def test_dir_on_sub_with_behavior_including_instance_dict_on_super(self): - # see issue40084 - self.assertTrue({'description'} <= set(dir(self.SubEnum2.sample))) + def test_programmatic_function_from_dict(self): + MinorEnum = self.enum_type( + 'MinorEnum', + OrderedDict((('june', self.source_values[0]), ('july', self.source_values[1]), ('august', self.source_values[2]))) + ) + lst = list(MinorEnum) + self.assertEqual(len(lst), len(MinorEnum)) + self.assertEqual(len(MinorEnum), 3, MinorEnum) + self.assertEqual( + [MinorEnum.june, MinorEnum.july, MinorEnum.august], + lst, + ) + for month, av in zip('june july august'.split(), self.values): + e = MinorEnum[month] + if MinorEnum._member_type_ is not object and issubclass(MinorEnum, MinorEnum._member_type_): + self.assertEqual(e, av) + else: + self.assertNotEqual(e, av) + self.assertIn(e, MinorEnum) + self.assertIs(type(e), MinorEnum) + self.assertIs(e, MinorEnum(av)) - def test_enum_in_enum_out(self): - Season = self.Season - self.assertIs(Season(Season.WINTER), Season.WINTER) + def test_repr(self): + TE = self.MainEnum + if self.is_flag: + self.assertEqual(repr(TE(0)), "<MainEnum: 0>") + self.assertEqual(repr(TE.dupe), "<MainEnum.dupe: 3>") + self.assertEqual(repr(self.dupe2), "<MainEnum.first|third: 5>") + elif issubclass(TE, StrEnum): + self.assertEqual(repr(TE.dupe), "<MainEnum.third: 'third'>") + else: + self.assertEqual(repr(TE.dupe), "<MainEnum.third: %r>" % (self.values[2], ), TE._value_repr_) + for name, value, member in zip(self.names, self.values, TE, strict=True): + self.assertEqual(repr(member), "<MainEnum.%s: %r>" % (member.name, member.value)) - def test_enum_value(self): - Season = self.Season - self.assertEqual(Season.SPRING.value, 1) + def test_repr_override(self): + class Generic(self.enum_type): + first = auto() + second = auto() + third = auto() + def __repr__(self): + return "don't you just love shades of %s?" % self.name + self.assertEqual( + repr(Generic.third), + "don't you just love shades of third?", + ) - def test_intenum_value(self): - self.assertEqual(IntStooges.CURLY.value, 2) + def test_inherited_repr(self): + class MyEnum(self.enum_type): + def __repr__(self): + return "My name is %s." % self.name + class MySubEnum(MyEnum): + this = auto() + that = auto() + theother = auto() + self.assertEqual(repr(MySubEnum.that), "My name is that.") - def test_enum(self): - Season = self.Season - lst = list(Season) - self.assertEqual(len(lst), len(Season)) - self.assertEqual(len(Season), 4, Season) + def test_reversed_iteration_order(self): self.assertEqual( - [Season.SPRING, Season.SUMMER, Season.AUTUMN, Season.WINTER], lst) + list(reversed(self.MainEnum)), + [self.MainEnum.third, self.MainEnum.second, self.MainEnum.first], + ) - 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.assertIs(type(e), Season) - self.assertIsInstance(e, Season) - self.assertEqual(str(e), season) - self.assertEqual(repr(e), 'Season.{0}'.format(season)) - - 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 +class _PlainOutputTests: - def test_changing_member(self): - Season = self.Season - with self.assertRaises(AttributeError): - Season.WINTER = 'really cold' + def test_str(self): + TE = self.MainEnum + if self.is_flag: + self.assertEqual(str(TE.dupe), "MainEnum.dupe") + self.assertEqual(str(self.dupe2), "MainEnum.first|third") + else: + self.assertEqual(str(TE.dupe), "MainEnum.third") + for name, value, member in zip(self.names, self.values, TE, strict=True): + self.assertEqual(str(member), "MainEnum.%s" % (member.name, )) - def test_attribute_deletion(self): - class Season(Enum): - SPRING = 1 - SUMMER = 2 - AUTUMN = 3 - WINTER = 4 + def test_format(self): + TE = self.MainEnum + if self.is_flag: + self.assertEqual(format(TE.dupe), "MainEnum.dupe") + self.assertEqual(format(self.dupe2), "MainEnum.first|third") + else: + self.assertEqual(format(TE.dupe), "MainEnum.third") + for name, value, member in zip(self.names, self.values, TE, strict=True): + self.assertEqual(format(member), "MainEnum.%s" % (member.name, )) - def spam(cls): - pass + def test_overridden_format(self): + NF = self.NewFormatEnum + self.assertEqual(str(NF.first), "NewFormatEnum.first", '%s %r' % (NF.__str__, NF.first)) + self.assertEqual(format(NF.first), "FIRST") - self.assertTrue(hasattr(Season, 'spam')) - del Season.spam - self.assertFalse(hasattr(Season, 'spam')) + def test_format_specs(self): + TE = self.MainEnum + self.assertFormatIsStr('{}', TE.second) + self.assertFormatIsStr('{:}', TE.second) + self.assertFormatIsStr('{:20}', TE.second) + self.assertFormatIsStr('{:^20}', TE.second) + self.assertFormatIsStr('{:>20}', TE.second) + self.assertFormatIsStr('{:<20}', TE.second) + self.assertFormatIsStr('{:5.2}', TE.second) - with self.assertRaises(AttributeError): - del Season.SPRING - with self.assertRaises(AttributeError): - del Season.DRY - with self.assertRaises(AttributeError): - del Season.SPRING.name - def test_bool_of_class(self): - class Empty(Enum): - pass - self.assertTrue(bool(Empty)) +class _MixedOutputTests: - def test_bool_of_member(self): - class Count(Enum): - zero = 0 - one = 1 - two = 2 - for member in Count: - self.assertTrue(bool(member)) + def test_str(self): + TE = self.MainEnum + if self.is_flag: + self.assertEqual(str(TE.dupe), "MainEnum.dupe") + self.assertEqual(str(self.dupe2), "MainEnum.first|third") + else: + self.assertEqual(str(TE.dupe), "MainEnum.third") + for name, value, member in zip(self.names, self.values, TE, strict=True): + self.assertEqual(str(member), "MainEnum.%s" % (member.name, )) + + def test_format(self): + TE = self.MainEnum + if self.is_flag: + self.assertEqual(format(TE.dupe), "MainEnum.dupe") + self.assertEqual(format(self.dupe2), "MainEnum.first|third") + else: + self.assertEqual(format(TE.dupe), "MainEnum.third") + for name, value, member in zip(self.names, self.values, TE, strict=True): + self.assertEqual(format(member), "MainEnum.%s" % (member.name, )) + + def test_overridden_format(self): + NF = self.NewFormatEnum + self.assertEqual(str(NF.first), "NewFormatEnum.first") + self.assertEqual(format(NF.first), "FIRST") + + def test_format_specs(self): + TE = self.MainEnum + self.assertFormatIsStr('{}', TE.first) + self.assertFormatIsStr('{:}', TE.first) + self.assertFormatIsStr('{:20}', TE.first) + self.assertFormatIsStr('{:^20}', TE.first) + self.assertFormatIsStr('{:>20}', TE.first) + self.assertFormatIsStr('{:<20}', TE.first) + self.assertFormatIsStr('{:5.2}', TE.first) + + +class _MinimalOutputTests: + + def test_str(self): + TE = self.MainEnum + if self.is_flag: + self.assertEqual(str(TE.dupe), "3") + self.assertEqual(str(self.dupe2), "5") + else: + self.assertEqual(str(TE.dupe), str(self.values[2])) + for name, value, member in zip(self.names, self.values, TE, strict=True): + self.assertEqual(str(member), str(value)) + + def test_format(self): + TE = self.MainEnum + if self.is_flag: + self.assertEqual(format(TE.dupe), "3") + self.assertEqual(format(self.dupe2), "5") + else: + self.assertEqual(format(TE.dupe), format(self.values[2])) + for name, value, member in zip(self.names, self.values, TE, strict=True): + self.assertEqual(format(member), format(value)) + + def test_overridden_format(self): + NF = self.NewFormatEnum + self.assertEqual(str(NF.first), str(self.values[0])) + self.assertEqual(format(NF.first), "FIRST") + + def test_format_specs(self): + TE = self.MainEnum + self.assertFormatIsValue('{}', TE.third) + self.assertFormatIsValue('{:}', TE.third) + self.assertFormatIsValue('{:20}', TE.third) + self.assertFormatIsValue('{:^20}', TE.third) + self.assertFormatIsValue('{:>20}', TE.third) + self.assertFormatIsValue('{:<20}', TE.third) + if TE._member_type_ is float: + self.assertFormatIsValue('{:n}', TE.third) + self.assertFormatIsValue('{:5.2}', TE.third) + self.assertFormatIsValue('{:f}', TE.third) + + +class _FlagTests: + + def test_default_missing_with_wrong_type_value(self): + with self.assertRaisesRegex( + ValueError, + "'RED' is not a valid TestFlag.Color", + ) as ctx: + self.MainEnum('RED') + self.assertIs(ctx.exception.__context__, None) + +class TestPlainEnum(_EnumTests, _PlainOutputTests, unittest.TestCase): + enum_type = Enum + + +class TestPlainFlag(_EnumTests, _PlainOutputTests, unittest.TestCase): + enum_type = Flag + + +class TestIntEnum(_EnumTests, _MinimalOutputTests, unittest.TestCase): + enum_type = IntEnum + + +class TestStrEnum(_EnumTests, _MinimalOutputTests, unittest.TestCase): + enum_type = StrEnum + + +class TestIntFlag(_EnumTests, _MinimalOutputTests, unittest.TestCase): + enum_type = IntFlag - def test_invalid_names(self): - with self.assertRaises(ValueError): - class Wrong(Enum): - mro = 9 - with self.assertRaises(ValueError): - class Wrong(Enum): - _create_= 11 - with self.assertRaises(ValueError): - class Wrong(Enum): - _get_mixins_ = 9 - with self.assertRaises(ValueError): - class Wrong(Enum): - _find_new_ = 1 - with self.assertRaises(ValueError): - class Wrong(Enum): - _any_name_ = 9 + +class TestMixedInt(_EnumTests, _MixedOutputTests, unittest.TestCase): + class enum_type(int, Enum): pass + + +class TestMixedStr(_EnumTests, _MixedOutputTests, unittest.TestCase): + class enum_type(str, Enum): pass + + +class TestMixedIntFlag(_EnumTests, _MixedOutputTests, unittest.TestCase): + class enum_type(int, Flag): pass + + +class TestMixedDate(_EnumTests, _MixedOutputTests, unittest.TestCase): + + values = [date(2021, 12, 25), date(2020, 3, 15), date(2019, 11, 27)] + source_values = [(2021, 12, 25), (2020, 3, 15), (2019, 11, 27)] + + class enum_type(date, Enum): + def _generate_next_value_(name, start, count, last_values): + values = [(2021, 12, 25), (2020, 3, 15), (2019, 11, 27)] + return values[count] + + +class TestMinimalDate(_EnumTests, _MinimalOutputTests, unittest.TestCase): + + values = [date(2023, 12, 1), date(2016, 2, 29), date(2009, 1, 1)] + source_values = [(2023, 12, 1), (2016, 2, 29), (2009, 1, 1)] + + class enum_type(date, ReprEnum): + def _generate_next_value_(name, start, count, last_values): + values = [(2023, 12, 1), (2016, 2, 29), (2009, 1, 1)] + return values[count] + + +class TestMixedFloat(_EnumTests, _MixedOutputTests, unittest.TestCase): + + values = [1.1, 2.2, 3.3] + + class enum_type(float, Enum): + def _generate_next_value_(name, start, count, last_values): + values = [1.1, 2.2, 3.3] + return values[count] + + +class TestMinimalFloat(_EnumTests, _MinimalOutputTests, unittest.TestCase): + + values = [4.4, 5.5, 6.6] + + class enum_type(float, ReprEnum): + def _generate_next_value_(name, start, count, last_values): + values = [4.4, 5.5, 6.6] + return values[count] + + +class TestSpecial(unittest.TestCase): + """ + various operations that are not attributable to every possible enum + """ + + def setUp(self): + class Season(Enum): + SPRING = 1 + SUMMER = 2 + AUTUMN = 3 + WINTER = 4 + self.Season = Season + # + class Grades(IntEnum): + A = 5 + B = 4 + C = 3 + D = 2 + F = 0 + self.Grades = Grades + # + class Directional(str, Enum): + EAST = 'east' + WEST = 'west' + NORTH = 'north' + SOUTH = 'south' + self.Directional = Directional + # + from datetime import date + class Holiday(date, Enum): + NEW_YEAR = 2013, 1, 1 + IDES_OF_MARCH = 2013, 3, 15 + self.Holiday = Holiday def test_bool(self): # plain Enum members are always True @@ -656,92 +865,56 @@ class TestEnum(unittest.TestCase): self.assertTrue(IntLogic.true) self.assertFalse(IntLogic.false) - @unittest.skipIf( - python_version >= (3, 12), - '__contains__ now returns True/False for all inputs', - ) - def test_contains_er(self): - Season = self.Season - self.assertIn(Season.AUTUMN, Season) - with self.assertRaises(TypeError): - with self.assertWarns(DeprecationWarning): - 3 in Season - with self.assertRaises(TypeError): - with self.assertWarns(DeprecationWarning): - 'AUTUMN' in Season - val = Season(3) - self.assertIn(val, Season) - # - class OtherEnum(Enum): - one = 1; two = 2 - self.assertNotIn(OtherEnum.two, Season) - - @unittest.skipIf( - python_version < (3, 12), - '__contains__ only works with enum memmbers before 3.12', - ) - def test_contains_tf(self): - Season = self.Season - self.assertIn(Season.AUTUMN, Season) - self.assertTrue(3 in Season) - self.assertFalse('AUTUMN' in 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_dir_with_custom_dunders(self): + class PlainEnum(Enum): + pass + cls_dir = dir(PlainEnum) + self.assertNotIn('__repr__', cls_dir) + self.assertNotIn('__str__', cls_dir) + self.assertNotIn('__repr__', cls_dir) + self.assertNotIn('__repr__', cls_dir) + # + class MyEnum(Enum): + def __repr__(self): + return object.__repr__(self) + def __str__(self): + return object.__repr__(self) + def __format__(self): + return object.__repr__(self) + def __init__(self): + pass + cls_dir = dir(MyEnum) + self.assertIn('__repr__', cls_dir) + self.assertIn('__str__', cls_dir) + self.assertIn('__repr__', cls_dir) + self.assertIn('__repr__', cls_dir) - def test_duplicate_name(self): + def test_duplicate_name_error(self): with self.assertRaises(TypeError): class Color(Enum): red = 1 green = 2 blue = 3 red = 4 - + # with self.assertRaises(TypeError): class Color(Enum): red = 1 @@ -749,232 +922,45 @@ class TestEnum(unittest.TestCase): blue = 3 def red(self): return 'red' - + # with self.assertRaises(TypeError): class Color(Enum): - @property + @enum.property def red(self): return 'redder' red = 1 green = 2 blue = 3 - def test_reserved__sunder_(self): - with self.assertRaisesRegex( - ValueError, - '_sunder_ names, such as ._bad_., are reserved', - ): - class Bad(Enum): - _bad_ = 1 + def test_enum_function_with_qualname(self): + if isinstance(Theory, Exception): + raise Theory + self.assertEqual(Theory.__qualname__, 'spanish_inquisition') def test_enum_with_value_name(self): class Huh(Enum): name = 1 value = 2 - self.assertEqual( - list(Huh), - [Huh.name, Huh.value], - ) + self.assertEqual(list(Huh), [Huh.name, Huh.value]) self.assertIs(type(Huh.name), Huh) self.assertEqual(Huh.name.name, 'name') self.assertEqual(Huh.name.value, 1) - def test_format_enum(self): - Season = self.Season - self.assertEqual('{}'.format(Season.SPRING), - '{}'.format(str(Season.SPRING))) - self.assertEqual( '{:}'.format(Season.SPRING), - '{:}'.format(str(Season.SPRING))) - self.assertEqual('{:20}'.format(Season.SPRING), - '{:20}'.format(str(Season.SPRING))) - self.assertEqual('{:^20}'.format(Season.SPRING), - '{:^20}'.format(str(Season.SPRING))) - self.assertEqual('{:>20}'.format(Season.SPRING), - '{:>20}'.format(str(Season.SPRING))) - self.assertEqual('{:<20}'.format(Season.SPRING), - '{:<20}'.format(str(Season.SPRING))) - - def test_str_override_enum(self): - class EnumWithStrOverrides(Enum): - one = auto() - two = auto() - - def __str__(self): - return 'Str!' - self.assertEqual(str(EnumWithStrOverrides.one), 'Str!') - self.assertEqual('{}'.format(EnumWithStrOverrides.one), 'Str!') - - def test_format_override_enum(self): - class EnumWithFormatOverride(Enum): - one = 1.0 - two = 2.0 - def __format__(self, spec): - return 'Format!!' - self.assertEqual(str(EnumWithFormatOverride.one), 'one') - self.assertEqual('{}'.format(EnumWithFormatOverride.one), 'Format!!') - - def test_str_and_format_override_enum(self): - class EnumWithStrFormatOverrides(Enum): - one = auto() - two = auto() - def __str__(self): - return 'Str!' - def __format__(self, spec): - return 'Format!' - self.assertEqual(str(EnumWithStrFormatOverrides.one), 'Str!') - self.assertEqual('{}'.format(EnumWithStrFormatOverrides.one), 'Format!') - - def test_str_override_mixin(self): - class MixinEnumWithStrOverride(float, Enum): - one = 1.0 - two = 2.0 - def __str__(self): - return 'Overridden!' - self.assertEqual(str(MixinEnumWithStrOverride.one), 'Overridden!') - self.assertEqual('{}'.format(MixinEnumWithStrOverride.one), 'Overridden!') - - def test_str_and_format_override_mixin(self): - class MixinWithStrFormatOverrides(float, Enum): - one = 1.0 - two = 2.0 - def __str__(self): - return 'Str!' - def __format__(self, spec): - return 'Format!' - self.assertEqual(str(MixinWithStrFormatOverrides.one), 'Str!') - self.assertEqual('{}'.format(MixinWithStrFormatOverrides.one), 'Format!') - - def test_format_override_mixin(self): - class TestFloat(float, Enum): - one = 1.0 - two = 2.0 - def __format__(self, spec): - return 'TestFloat success!' - self.assertEqual(str(TestFloat.one), 'one') - self.assertEqual('{}'.format(TestFloat.one), 'TestFloat success!') - - @unittest.skipIf( - python_version < (3, 12), - 'mixin-format is still using member.value', - ) - def test_mixin_format_warning(self): - class Grades(int, Enum): - A = 5 - B = 4 - C = 3 - D = 2 - F = 0 - self.assertEqual(f'{self.Grades.B}', 'B') - - @unittest.skipIf( - python_version >= (3, 12), - 'mixin-format now uses member instead of member.value', - ) - def test_mixin_format_warning(self): - class Grades(int, Enum): - A = 5 - B = 4 - C = 3 - D = 2 - F = 0 - with self.assertWarns(DeprecationWarning): - self.assertEqual(f'{Grades.B}', '4') - - def assertFormatIsValue(self, spec, member): - if python_version < (3, 12) and (not spec or spec in ('{}','{:}')): - with self.assertWarns(DeprecationWarning): - self.assertEqual(spec.format(member), spec.format(member.value)) - else: - self.assertEqual(spec.format(member), spec.format(member.value)) - - def test_format_enum_date(self): - Holiday = self.Holiday - self.assertFormatIsValue('{}', Holiday.IDES_OF_MARCH) - self.assertFormatIsValue('{:}', Holiday.IDES_OF_MARCH) - self.assertFormatIsValue('{:20}', Holiday.IDES_OF_MARCH) - self.assertFormatIsValue('{:^20}', Holiday.IDES_OF_MARCH) - self.assertFormatIsValue('{:>20}', Holiday.IDES_OF_MARCH) - self.assertFormatIsValue('{:<20}', Holiday.IDES_OF_MARCH) - self.assertFormatIsValue('{:%Y %m}', Holiday.IDES_OF_MARCH) - self.assertFormatIsValue('{:%Y %m %M:00}', Holiday.IDES_OF_MARCH) - - def test_format_enum_float(self): - Konstants = self.Konstants - self.assertFormatIsValue('{}', Konstants.TAU) - self.assertFormatIsValue('{:}', Konstants.TAU) - self.assertFormatIsValue('{:20}', Konstants.TAU) - self.assertFormatIsValue('{:^20}', Konstants.TAU) - self.assertFormatIsValue('{:>20}', Konstants.TAU) - self.assertFormatIsValue('{:<20}', Konstants.TAU) - self.assertFormatIsValue('{:n}', Konstants.TAU) - self.assertFormatIsValue('{:5.2}', Konstants.TAU) - self.assertFormatIsValue('{:f}', Konstants.TAU) - - def test_format_enum_int(self): - class Grades(int, Enum): - A = 5 - B = 4 - C = 3 - D = 2 - F = 0 - self.assertFormatIsValue('{}', Grades.C) - self.assertFormatIsValue('{:}', Grades.C) - self.assertFormatIsValue('{:20}', Grades.C) - self.assertFormatIsValue('{:^20}', Grades.C) - self.assertFormatIsValue('{:>20}', Grades.C) - self.assertFormatIsValue('{:<20}', Grades.C) - self.assertFormatIsValue('{:+}', Grades.C) - self.assertFormatIsValue('{:08X}', Grades.C) - self.assertFormatIsValue('{:b}', Grades.C) - - def test_format_enum_str(self): - Directional = self.Directional - self.assertFormatIsValue('{}', Directional.WEST) - self.assertFormatIsValue('{:}', Directional.WEST) - self.assertFormatIsValue('{:20}', Directional.WEST) - self.assertFormatIsValue('{:^20}', Directional.WEST) - self.assertFormatIsValue('{:>20}', Directional.WEST) - self.assertFormatIsValue('{:<20}', Directional.WEST) - - def test_object_str_override(self): - class Colors(Enum): - RED, GREEN, BLUE = 1, 2, 3 - def __repr__(self): - return "test.%s" % (self._name_, ) - __str__ = object.__str__ - self.assertEqual(str(Colors.RED), 'test.RED') - - def test_enum_str_override(self): - class MyStrEnum(Enum): - def __str__(self): - return 'MyStr' - class MyMethodEnum(Enum): - def hello(self): - return 'Hello! My name is %s' % self.name - class Test1Enum(MyMethodEnum, int, MyStrEnum): - One = 1 - Two = 2 - self.assertTrue(Test1Enum._member_type_ is int) - self.assertEqual(str(Test1Enum.One), 'MyStr') - self.assertEqual(format(Test1Enum.One, ''), 'MyStr') - # - class Test2Enum(MyStrEnum, MyMethodEnum): - One = 1 - Two = 2 - self.assertEqual(str(Test2Enum.One), 'MyStr') - self.assertEqual(format(Test1Enum.One, ''), 'MyStr') - def test_inherited_data_type(self): class HexInt(int): + __qualname__ = 'HexInt' def __repr__(self): return hex(self) class MyEnum(HexInt, enum.Enum): + __qualname__ = 'MyEnum' A = 1 B = 2 C = 3 - def __repr__(self): - return '<%s.%s: %r>' % (self.__class__.__name__, self._name_, self._value_) self.assertEqual(repr(MyEnum.A), '<MyEnum.A: 0x1>') + globals()['HexInt'] = HexInt + globals()['MyEnum'] = MyEnum + test_pickle_dump_load(self.assertIs, MyEnum.A) + test_pickle_dump_load(self.assertIs, MyEnum) # class SillyInt(HexInt): __qualname__ = 'SillyInt' @@ -990,7 +976,7 @@ class TestEnum(unittest.TestCase): test_pickle_dump_load(self.assertIs, MyOtherEnum.E) test_pickle_dump_load(self.assertIs, MyOtherEnum) # - # This did not work in 3.9, but does now with pickling by name + # This did not work in 3.10, but does now with pickling by name class UnBrokenInt(int): __qualname__ = 'UnBrokenInt' def __new__(cls, value): @@ -1007,6 +993,124 @@ class TestEnum(unittest.TestCase): test_pickle_dump_load(self.assertIs, MyUnBrokenEnum.I) test_pickle_dump_load(self.assertIs, MyUnBrokenEnum) + def test_floatenum_fromhex(self): + h = float.hex(FloatStooges.MOE.value) + self.assertIs(FloatStooges.fromhex(h), FloatStooges.MOE) + h = float.hex(FloatStooges.MOE.value + 0.01) + with self.assertRaises(ValueError): + FloatStooges.fromhex(h) + + def test_programmatic_function_type(self): + MinorEnum = Enum('MinorEnum', 'june july august', type=int) + lst = list(MinorEnum) + self.assertEqual(len(lst), len(MinorEnum)) + self.assertEqual(len(MinorEnum), 3, MinorEnum) + self.assertEqual( + [MinorEnum.june, MinorEnum.july, MinorEnum.august], + lst, + ) + for i, month in enumerate('june july august'.split(), 1): + e = MinorEnum(i) + self.assertEqual(e, i) + self.assertEqual(e.name, month) + self.assertIn(e, MinorEnum) + self.assertIs(type(e), MinorEnum) + + def test_programmatic_function_string_with_start(self): + MinorEnum = Enum('MinorEnum', 'june july august', start=10) + lst = list(MinorEnum) + self.assertEqual(len(lst), len(MinorEnum)) + self.assertEqual(len(MinorEnum), 3, MinorEnum) + self.assertEqual( + [MinorEnum.june, MinorEnum.july, MinorEnum.august], + lst, + ) + for i, month in enumerate('june july august'.split(), 10): + e = MinorEnum(i) + self.assertEqual(int(e.value), i) + self.assertNotEqual(e, i) + self.assertEqual(e.name, month) + self.assertIn(e, MinorEnum) + self.assertIs(type(e), MinorEnum) + + def test_programmatic_function_type_with_start(self): + MinorEnum = Enum('MinorEnum', 'june july august', type=int, start=30) + lst = list(MinorEnum) + self.assertEqual(len(lst), len(MinorEnum)) + self.assertEqual(len(MinorEnum), 3, MinorEnum) + self.assertEqual( + [MinorEnum.june, MinorEnum.july, MinorEnum.august], + lst, + ) + for i, month in enumerate('june july august'.split(), 30): + e = MinorEnum(i) + self.assertEqual(e, i) + self.assertEqual(e.name, month) + self.assertIn(e, MinorEnum) + self.assertIs(type(e), MinorEnum) + + def test_programmatic_function_string_list_with_start(self): + MinorEnum = Enum('MinorEnum', ['june', 'july', 'august'], start=20) + lst = list(MinorEnum) + self.assertEqual(len(lst), len(MinorEnum)) + self.assertEqual(len(MinorEnum), 3, MinorEnum) + self.assertEqual( + [MinorEnum.june, MinorEnum.july, MinorEnum.august], + lst, + ) + for i, month in enumerate('june july august'.split(), 20): + e = MinorEnum(i) + self.assertEqual(int(e.value), i) + self.assertNotEqual(e, i) + self.assertEqual(e.name, month) + self.assertIn(e, MinorEnum) + self.assertIs(type(e), MinorEnum) + + def test_programmatic_function_type_from_subclass(self): + MinorEnum = IntEnum('MinorEnum', 'june july august') + lst = list(MinorEnum) + self.assertEqual(len(lst), len(MinorEnum)) + self.assertEqual(len(MinorEnum), 3, MinorEnum) + self.assertEqual( + [MinorEnum.june, MinorEnum.july, MinorEnum.august], + lst, + ) + for i, month in enumerate('june july august'.split(), 1): + e = MinorEnum(i) + self.assertEqual(e, i) + self.assertEqual(e.name, month) + self.assertIn(e, MinorEnum) + self.assertIs(type(e), MinorEnum) + + def test_programmatic_function_type_from_subclass_with_start(self): + MinorEnum = IntEnum('MinorEnum', 'june july august', start=40) + lst = list(MinorEnum) + self.assertEqual(len(lst), len(MinorEnum)) + self.assertEqual(len(MinorEnum), 3, MinorEnum) + self.assertEqual( + [MinorEnum.june, MinorEnum.july, MinorEnum.august], + lst, + ) + for i, month in enumerate('june july august'.split(), 40): + e = MinorEnum(i) + self.assertEqual(e, i) + self.assertEqual(e.name, month) + self.assertIn(e, MinorEnum) + self.assertIs(type(e), MinorEnum) + + def test_intenum_from_bytes(self): + self.assertIs(IntStooges.from_bytes(b'\x00\x03', 'big'), IntStooges.MOE) + with self.assertRaises(ValueError): + IntStooges.from_bytes(b'\x00\x05', 'big') + + def test_reserved_sunder_error(self): + with self.assertRaisesRegex( + ValueError, + '_sunder_ names, such as ._bad_., are reserved', + ): + class Bad(Enum): + _bad_ = 1 + def test_too_many_data_types(self): with self.assertRaisesRegex(TypeError, 'too many data types'): class Huh(str, int, Enum): @@ -1022,122 +1126,6 @@ class TestEnum(unittest.TestCase): class Huh(MyStr, MyInt, Enum): One = 1 - def test_value_auto_assign(self): - class Some(Enum): - def __new__(cls, val): - return object.__new__(cls) - x = 1 - y = 2 - - self.assertEqual(Some.x.value, 1) - self.assertEqual(Some.y.value, 2) - - def test_hash(self): - Season = self.Season - dates = {} - dates[Season.WINTER] = '1225' - dates[Season.SPRING] = '0315' - dates[Season.SUMMER] = '0704' - dates[Season.AUTUMN] = '1031' - self.assertEqual(dates[Season.AUTUMN], '1031') - - def test_intenum_from_scratch(self): - class phy(int, Enum): - pi = 3 - tau = 2 * pi - self.assertTrue(phy.pi < phy.tau) - - def test_intenum_inherited(self): - class IntEnum(int, Enum): - pass - class phy(IntEnum): - pi = 3 - tau = 2 * pi - self.assertTrue(phy.pi < phy.tau) - - def test_floatenum_from_scratch(self): - class phy(float, Enum): - pi = 3.1415926 - tau = 2 * pi - self.assertTrue(phy.pi < phy.tau) - - def test_floatenum_inherited(self): - class FloatEnum(float, Enum): - pass - class phy(FloatEnum): - pi = 3.1415926 - tau = 2 * pi - self.assertTrue(phy.pi < phy.tau) - - def test_strenum_from_scratch(self): - class phy(str, Enum): - pi = 'Pi' - tau = 'Tau' - self.assertTrue(phy.pi < phy.tau) - - def test_strenum_inherited_methods(self): - class phy(StrEnum): - pi = 'Pi' - tau = 'Tau' - self.assertTrue(phy.pi < phy.tau) - self.assertEqual(phy.pi.upper(), 'PI') - self.assertEqual(phy.tau.count('a'), 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) - target = 'SUNDAY MONDAY TUESDAY WEDNESDAY THURSDAY FRIDAY SATURDAY' - target = target.split() - for i, weekday in enumerate(target, 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.assertIs(WeekDay.TEUSDAY, 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_intenum_from_bytes(self): - self.assertIs(IntStooges.from_bytes(b'\x00\x03', 'big'), IntStooges.MOE) - with self.assertRaises(ValueError): - IntStooges.from_bytes(b'\x00\x05', 'big') - - def test_floatenum_fromhex(self): - h = float.hex(FloatStooges.MOE.value) - self.assertIs(FloatStooges.fromhex(h), FloatStooges.MOE) - h = float.hex(FloatStooges.MOE.value + 0.01) - with self.assertRaises(ValueError): - FloatStooges.fromhex(h) def test_pickle_enum(self): if isinstance(Stooges, Exception): @@ -1169,12 +1157,7 @@ class TestEnum(unittest.TestCase): test_pickle_dump_load(self.assertIs, Question.who) test_pickle_dump_load(self.assertIs, Question) - def test_enum_function_with_qualname(self): - if isinstance(Theory, Exception): - raise Theory - self.assertEqual(Theory.__qualname__, 'spanish_inquisition') - - def test_class_nested_enum_and_pickle_protocol_four(self): + def test_pickle_nested_class(self): # would normally just have this directly in the class namespace class NestedEnum(Enum): twigs = 'common' @@ -1192,7 +1175,7 @@ class TestEnum(unittest.TestCase): for proto in range(HIGHEST_PROTOCOL): self.assertEqual(ReplaceGlobalInt.TWO.__reduce_ex__(proto), 'TWO') - def test_exploding_pickle(self): + def test_pickle_explodes(self): BadPickle = Enum( 'BadPickle', 'dill sweet bread-n-butter', module=__name__) globals()['BadPickle'] = BadPickle @@ -1233,185 +1216,6 @@ class TestEnum(unittest.TestCase): [Season.SUMMER, Season.WINTER, Season.AUTUMN, Season.SPRING], ) - def test_reversed_iteration_order(self): - self.assertEqual( - list(reversed(self.Season)), - [self.Season.WINTER, self.Season.AUTUMN, self.Season.SUMMER, - self.Season.SPRING] - ) - - def test_programmatic_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.assertIs(type(e), SummerMonth) - - def test_programmatic_function_string_with_start(self): - SummerMonth = Enum('SummerMonth', 'june july august', start=10) - 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(), 10): - e = SummerMonth(i) - self.assertEqual(int(e.value), i) - self.assertNotEqual(e, i) - self.assertEqual(e.name, month) - self.assertIn(e, SummerMonth) - self.assertIs(type(e), SummerMonth) - - def test_programmatic_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.assertIs(type(e), SummerMonth) - - def test_programmatic_function_string_list_with_start(self): - SummerMonth = Enum('SummerMonth', ['june', 'july', 'august'], start=20) - 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(), 20): - e = SummerMonth(i) - self.assertEqual(int(e.value), i) - self.assertNotEqual(e, i) - self.assertEqual(e.name, month) - self.assertIn(e, SummerMonth) - self.assertIs(type(e), SummerMonth) - - def test_programmatic_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.assertIs(type(e), SummerMonth) - - def test_programmatic_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.assertIs(type(e), SummerMonth) - - def test_programmatic_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.assertIs(type(e), SummerMonth) - - def test_programmatic_function_type_with_start(self): - SummerMonth = Enum('SummerMonth', 'june july august', type=int, start=30) - 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(), 30): - e = SummerMonth(i) - self.assertEqual(e, i) - self.assertEqual(e.name, month) - self.assertIn(e, SummerMonth) - self.assertIs(type(e), SummerMonth) - - def test_programmatic_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.assertIs(type(e), SummerMonth) - - def test_programmatic_function_type_from_subclass_with_start(self): - SummerMonth = IntEnum('SummerMonth', 'june july august', start=40) - 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(), 40): - e = SummerMonth(i) - self.assertEqual(e, i) - self.assertEqual(e.name, month) - self.assertIn(e, SummerMonth) - self.assertIs(type(e), SummerMonth) - def test_subclassing(self): if isinstance(Name, Exception): raise Name @@ -1425,15 +1229,18 @@ class TestEnum(unittest.TestCase): red = 1 green = 2 blue = 3 + # with self.assertRaises(TypeError): class MoreColor(Color): cyan = 4 magenta = 5 yellow = 6 - with self.assertRaisesRegex(TypeError, "EvenMoreColor: cannot extend enumeration 'Color'"): + # + with self.assertRaisesRegex(TypeError, "<enum .EvenMoreColor.> cannot extend <enum .Color.>"): class EvenMoreColor(Color, IntEnum): chartruese = 7 - with self.assertRaisesRegex(TypeError, "Foo: cannot extend enumeration 'Color'"): + # + with self.assertRaisesRegex(TypeError, "<enum .Foo.> cannot extend <enum .Color.>"): Color('Foo', ('pink', 'black')) def test_exclude_methods(self): @@ -1537,27 +1344,7 @@ class TestEnum(unittest.TestCase): 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.") + # tests that need to be evalualted for moving def test_multiple_mixin_mro(self): class auto_enum(type(Enum)): @@ -1610,7 +1397,7 @@ class TestEnum(unittest.TestCase): return self def __getnewargs__(self): return self._args - @property + @bltns.property def __name__(self): return self._intname def __repr__(self): @@ -1670,7 +1457,7 @@ class TestEnum(unittest.TestCase): return self def __getnewargs_ex__(self): return self._args, {} - @property + @bltns.property def __name__(self): return self._intname def __repr__(self): @@ -1730,7 +1517,7 @@ class TestEnum(unittest.TestCase): return self def __reduce__(self): return self.__class__, self._args - @property + @bltns.property def __name__(self): return self._intname def __repr__(self): @@ -1790,7 +1577,7 @@ class TestEnum(unittest.TestCase): return self def __reduce_ex__(self, proto): return self.__class__, self._args - @property + @bltns.property def __name__(self): return self._intname def __repr__(self): @@ -1847,7 +1634,7 @@ class TestEnum(unittest.TestCase): self._intname = name self._args = _args return self - @property + @bltns.property def __name__(self): return self._intname def __repr__(self): @@ -1902,7 +1689,7 @@ class TestEnum(unittest.TestCase): self._intname = name self._args = _args return self - @property + @bltns.property def __name__(self): return self._intname def __repr__(self): @@ -2091,6 +1878,7 @@ class TestEnum(unittest.TestCase): class Test(Base): test = 1 self.assertEqual(Test.test.test, 'dynamic') + self.assertEqual(Test.test.value, 1) class Base2(Enum): @enum.property def flash(self): @@ -2098,6 +1886,7 @@ class TestEnum(unittest.TestCase): class Test(Base2): flash = 1 self.assertEqual(Test.flash.flash, 'flashy dynamic') + self.assertEqual(Test.flash.value, 1) def test_no_duplicates(self): class UniqueEnum(Enum): @@ -2134,7 +1923,7 @@ class TestEnum(unittest.TestCase): def __init__(self, mass, radius): self.mass = mass # in kilograms self.radius = radius # in meters - @property + @enum.property def surface_gravity(self): # universal gravitational constant (m3 kg-1 s-2) G = 6.67300E-11 @@ -2204,90 +1993,7 @@ class TestEnum(unittest.TestCase): self.assertEqual(LabelledList.unprocessed, 1) self.assertEqual(LabelledList(1), LabelledList.unprocessed) - def test_auto_number(self): - class Color(Enum): - red = auto() - blue = auto() - green = auto() - - self.assertEqual(list(Color), [Color.red, Color.blue, Color.green]) - self.assertEqual(Color.red.value, 1) - self.assertEqual(Color.blue.value, 2) - self.assertEqual(Color.green.value, 3) - - def test_auto_name(self): - class Color(Enum): - def _generate_next_value_(name, start, count, last): - return name - red = auto() - blue = auto() - green = auto() - - self.assertEqual(list(Color), [Color.red, Color.blue, Color.green]) - self.assertEqual(Color.red.value, 'red') - self.assertEqual(Color.blue.value, 'blue') - self.assertEqual(Color.green.value, 'green') - - def test_auto_name_inherit(self): - class AutoNameEnum(Enum): - def _generate_next_value_(name, start, count, last): - return name - class Color(AutoNameEnum): - red = auto() - blue = auto() - green = auto() - - self.assertEqual(list(Color), [Color.red, Color.blue, Color.green]) - self.assertEqual(Color.red.value, 'red') - self.assertEqual(Color.blue.value, 'blue') - self.assertEqual(Color.green.value, 'green') - - def test_auto_garbage(self): - class Color(Enum): - red = 'red' - blue = auto() - self.assertEqual(Color.blue.value, 1) - - def test_auto_garbage_corrected(self): - class Color(Enum): - red = 'red' - blue = 2 - green = auto() - - self.assertEqual(list(Color), [Color.red, Color.blue, Color.green]) - self.assertEqual(Color.red.value, 'red') - self.assertEqual(Color.blue.value, 2) - self.assertEqual(Color.green.value, 3) - - def test_auto_order(self): - with self.assertRaises(TypeError): - class Color(Enum): - red = auto() - green = auto() - blue = auto() - def _generate_next_value_(name, start, count, last): - return name - - def test_auto_order_wierd(self): - weird_auto = auto() - weird_auto.value = 'pathological case' - class Color(Enum): - red = weird_auto - def _generate_next_value_(name, start, count, last): - return name - blue = auto() - self.assertEqual(list(Color), [Color.red, Color.blue]) - self.assertEqual(Color.red.value, 'pathological case') - self.assertEqual(Color.blue.value, 'blue') - - def test_duplicate_auto(self): - class Dupes(Enum): - first = primero = auto() - second = auto() - third = auto() - self.assertEqual([Dupes.first, Dupes.second, Dupes.third], list(Dupes)) - - def test_default_missing(self): + def test_default_missing_no_chained_exception(self): class Color(Enum): RED = 1 GREEN = 2 @@ -2299,7 +2005,7 @@ class TestEnum(unittest.TestCase): else: raise Exception('Exception not raised.') - def test_missing(self): + def test_missing_override(self): class Color(Enum): red = 1 green = 2 @@ -2363,9 +2069,9 @@ class TestEnum(unittest.TestCase): class_1_ref = weakref.ref(Class1()) class_2_ref = weakref.ref(Class2()) # - # The exception raised by Enum creates a reference loop and thus - # Class2 instances will stick around until the next garbage collection - # cycle, unlike Class1. + # The exception raised by Enum used to create a reference loop and thus + # Class2 instances would stick around until the next garbage collection + # cycle, unlike Class1. Verify Class2 no longer does this. gc.collect() # For PyPy or other GCs. self.assertIs(class_1_ref(), None) self.assertIs(class_2_ref(), None) @@ -2396,11 +2102,12 @@ class TestEnum(unittest.TestCase): self.assertEqual(Color.GREEN.value, 2) self.assertEqual(Color.BLUE.value, 3) self.assertEqual(Color.MAX, 3) - self.assertEqual(str(Color.BLUE), 'BLUE') + self.assertEqual(str(Color.BLUE), 'Color.BLUE') class Color(MaxMixin, StrMixin, Enum): RED = auto() GREEN = auto() BLUE = auto() + __str__ = StrMixin.__str__ # needed as of 3.11 self.assertEqual(Color.RED.value, 1) self.assertEqual(Color.GREEN.value, 2) self.assertEqual(Color.BLUE.value, 3) @@ -2410,6 +2117,7 @@ class TestEnum(unittest.TestCase): RED = auto() GREEN = auto() BLUE = auto() + __str__ = StrMixin.__str__ # needed as of 3.11 self.assertEqual(Color.RED.value, 1) self.assertEqual(Color.GREEN.value, 2) self.assertEqual(Color.BLUE.value, 3) @@ -2419,6 +2127,7 @@ class TestEnum(unittest.TestCase): RED = auto() GREEN = auto() BLUE = auto() + __str__ = StrMixin.__str__ # needed as of 3.11 self.assertEqual(CoolColor.RED.value, 1) self.assertEqual(CoolColor.GREEN.value, 2) self.assertEqual(CoolColor.BLUE.value, 3) @@ -2428,6 +2137,7 @@ class TestEnum(unittest.TestCase): RED = auto() GREEN = auto() BLUE = auto() + __str__ = StrMixin.__str__ # needed as of 3.11 self.assertEqual(CoolerColor.RED.value, 1) self.assertEqual(CoolerColor.GREEN.value, 2) self.assertEqual(CoolerColor.BLUE.value, 3) @@ -2438,6 +2148,7 @@ class TestEnum(unittest.TestCase): RED = auto() GREEN = auto() BLUE = auto() + __str__ = StrMixin.__str__ # needed as of 3.11 self.assertEqual(CoolestColor.RED.value, 1) self.assertEqual(CoolestColor.GREEN.value, 2) self.assertEqual(CoolestColor.BLUE.value, 3) @@ -2448,6 +2159,7 @@ class TestEnum(unittest.TestCase): RED = auto() GREEN = auto() BLUE = auto() + __str__ = StrMixin.__str__ # needed as of 3.11 self.assertEqual(ConfusedColor.RED.value, 1) self.assertEqual(ConfusedColor.GREEN.value, 2) self.assertEqual(ConfusedColor.BLUE.value, 3) @@ -2458,6 +2170,7 @@ class TestEnum(unittest.TestCase): RED = auto() GREEN = auto() BLUE = auto() + __str__ = StrMixin.__str__ # needed as of 3.11 self.assertEqual(ReformedColor.RED.value, 1) self.assertEqual(ReformedColor.GREEN.value, 2) self.assertEqual(ReformedColor.BLUE.value, 3) @@ -2490,11 +2203,12 @@ class TestEnum(unittest.TestCase): return hex(self) class MyIntEnum(HexMixin, MyInt, enum.Enum): - pass + __repr__ = HexMixin.__repr__ class Foo(MyIntEnum): TEST = 1 self.assertTrue(isinstance(Foo.TEST, MyInt)) + self.assertEqual(Foo._member_type_, MyInt) self.assertEqual(repr(Foo.TEST), "0x1") class Fee(MyIntEnum): @@ -2506,7 +2220,7 @@ class TestEnum(unittest.TestCase): return member self.assertEqual(Fee.TEST, 2) - def test_miltuple_mixin_with_common_data_type(self): + def test_multiple_mixin_with_common_data_type(self): class CaseInsensitiveStrEnum(str, Enum): @classmethod def _missing_(cls, value): @@ -2526,7 +2240,7 @@ class TestEnum(unittest.TestCase): unknown._value_ = value cls._member_map_[value] = unknown return unknown - @property + @enum.property def valid(self): return self._valid # @@ -2570,7 +2284,7 @@ class TestEnum(unittest.TestCase): self.assertEqual('{}'.format(GoodStrEnum.one), '1') self.assertEqual(GoodStrEnum.one, str(GoodStrEnum.one)) self.assertEqual(GoodStrEnum.one, '{}'.format(GoodStrEnum.one)) - self.assertEqual(repr(GoodStrEnum.one), 'GoodStrEnum.one') + self.assertEqual(repr(GoodStrEnum.one), "<GoodStrEnum.one: '1'>") # class DumbMixin: def __str__(self): @@ -2579,6 +2293,7 @@ class TestEnum(unittest.TestCase): five = '5' six = '6' seven = '7' + __str__ = DumbMixin.__str__ # needed as of 3.11 self.assertEqual(DumbStrEnum.seven, '7') self.assertEqual(str(DumbStrEnum.seven), "don't do this") # @@ -2620,74 +2335,6 @@ class TestEnum(unittest.TestCase): one = '1' two = b'2', 'ascii', 9 - @unittest.skipIf( - python_version >= (3, 12), - 'mixin-format now uses member instead of member.value', - ) - def test_custom_strenum_with_warning(self): - class CustomStrEnum(str, Enum): - pass - class OkayEnum(CustomStrEnum): - one = '1' - two = '2' - three = b'3', 'ascii' - four = b'4', 'latin1', 'strict' - self.assertEqual(OkayEnum.one, '1') - self.assertEqual(str(OkayEnum.one), 'one') - with self.assertWarns(DeprecationWarning): - self.assertEqual('{}'.format(OkayEnum.one), '1') - self.assertEqual(OkayEnum.one, '{}'.format(OkayEnum.one)) - self.assertEqual(repr(OkayEnum.one), 'OkayEnum.one') - # - class DumbMixin: - def __str__(self): - return "don't do this" - class DumbStrEnum(DumbMixin, CustomStrEnum): - five = '5' - six = '6' - seven = '7' - self.assertEqual(DumbStrEnum.seven, '7') - self.assertEqual(str(DumbStrEnum.seven), "don't do this") - # - class EnumMixin(Enum): - def hello(self): - print('hello from %s' % (self, )) - class HelloEnum(EnumMixin, CustomStrEnum): - eight = '8' - self.assertEqual(HelloEnum.eight, '8') - self.assertEqual(str(HelloEnum.eight), 'eight') - # - class GoodbyeMixin: - def goodbye(self): - print('%s wishes you a fond farewell') - class GoodbyeEnum(GoodbyeMixin, EnumMixin, CustomStrEnum): - nine = '9' - self.assertEqual(GoodbyeEnum.nine, '9') - self.assertEqual(str(GoodbyeEnum.nine), 'nine') - # - class FirstFailedStrEnum(CustomStrEnum): - one = 1 # this will become '1' - two = '2' - class SecondFailedStrEnum(CustomStrEnum): - one = '1' - two = 2, # this will become '2' - three = '3' - class ThirdFailedStrEnum(CustomStrEnum): - one = '1' - two = 2 # this will become '2' - with self.assertRaisesRegex(TypeError, '.encoding. must be str, not '): - class ThirdFailedStrEnum(CustomStrEnum): - one = '1' - two = b'2', sys.getdefaultencoding - with self.assertRaisesRegex(TypeError, '.errors. must be str, not '): - class ThirdFailedStrEnum(CustomStrEnum): - one = '1' - two = b'2', 'ascii', 9 - - @unittest.skipIf( - python_version < (3, 12), - 'mixin-format currently uses member.value', - ) def test_custom_strenum(self): class CustomStrEnum(str, Enum): pass @@ -2697,9 +2344,9 @@ class TestEnum(unittest.TestCase): three = b'3', 'ascii' four = b'4', 'latin1', 'strict' self.assertEqual(OkayEnum.one, '1') - self.assertEqual(str(OkayEnum.one), 'one') - self.assertEqual('{}'.format(OkayEnum.one), 'one') - self.assertEqual(repr(OkayEnum.one), 'OkayEnum.one') + self.assertEqual(str(OkayEnum.one), 'OkayEnum.one') + self.assertEqual('{}'.format(OkayEnum.one), 'OkayEnum.one') + self.assertEqual(repr(OkayEnum.one), "<OkayEnum.one: '1'>") # class DumbMixin: def __str__(self): @@ -2708,6 +2355,7 @@ class TestEnum(unittest.TestCase): five = '5' six = '6' seven = '7' + __str__ = DumbMixin.__str__ # needed as of 3.11 self.assertEqual(DumbStrEnum.seven, '7') self.assertEqual(str(DumbStrEnum.seven), "don't do this") # @@ -2717,7 +2365,7 @@ class TestEnum(unittest.TestCase): class HelloEnum(EnumMixin, CustomStrEnum): eight = '8' self.assertEqual(HelloEnum.eight, '8') - self.assertEqual(str(HelloEnum.eight), 'eight') + self.assertEqual(str(HelloEnum.eight), 'HelloEnum.eight') # class GoodbyeMixin: def goodbye(self): @@ -2725,7 +2373,7 @@ class TestEnum(unittest.TestCase): class GoodbyeEnum(GoodbyeMixin, EnumMixin, CustomStrEnum): nine = '9' self.assertEqual(GoodbyeEnum.nine, '9') - self.assertEqual(str(GoodbyeEnum.nine), 'nine') + self.assertEqual(str(GoodbyeEnum.nine), 'GoodbyeEnum.nine') # class FirstFailedStrEnum(CustomStrEnum): one = 1 # this will become '1' @@ -2771,21 +2419,6 @@ class TestEnum(unittest.TestCase): code = 'An$(5,1)', 2 description = 'Bn$', 3 - @unittest.skipUnless( - python_version == (3, 9), - 'private variables are now normal attributes', - ) - def test_warning_for_private_variables(self): - with self.assertWarns(DeprecationWarning): - class Private(Enum): - __corporal = 'Radar' - self.assertEqual(Private._Private__corporal.value, 'Radar') - try: - with self.assertWarns(DeprecationWarning): - class Private(Enum): - __major_ = 'Hoolihan' - except ValueError: - pass def test_private_variable_is_normal_attribute(self): class Private(Enum): @@ -2794,35 +2427,13 @@ class TestEnum(unittest.TestCase): self.assertEqual(Private._Private__corporal, 'Radar') self.assertEqual(Private._Private__major_, 'Hoolihan') - @unittest.skipUnless( - python_version < (3, 12), - 'member-member access now raises an exception', - ) - def test_warning_for_member_from_member_access(self): - with self.assertWarns(DeprecationWarning): - class Di(Enum): - YES = 1 - NO = 0 - nope = Di.YES.NO - self.assertIs(Di.NO, nope) - - @unittest.skipUnless( - python_version >= (3, 12), - 'member-member access currently issues a warning', - ) def test_exception_for_member_from_member_access(self): - with self.assertRaisesRegex(AttributeError, "Di: no instance attribute .NO."): + with self.assertRaisesRegex(AttributeError, "<enum .Di.> member has no attribute .NO."): class Di(Enum): YES = 1 NO = 0 nope = Di.YES.NO - def test_strenum_auto(self): - class Strings(StrEnum): - ONE = auto() - TWO = auto() - self.assertEqual([Strings.ONE, Strings.TWO], ['one', 'two']) - def test_dynamic_members_with_static_methods(self): # @@ -2839,7 +2450,7 @@ class TestEnum(unittest.TestCase): self.assertEqual(Foo.FOO_CAT.value, 'aloof') self.assertEqual(Foo.FOO_HORSE.upper(), 'BIG') # - with self.assertRaisesRegex(TypeError, "'FOO_CAT' already defined as: 'aloof'"): + with self.assertRaisesRegex(TypeError, "'FOO_CAT' already defined as 'aloof'"): class FooBar(Enum): vars().update({ k: v @@ -2851,8 +2462,42 @@ class TestEnum(unittest.TestCase): def upper(self): return self.value.upper() + def test_repr_with_dataclass(self): + "ensure dataclass-mixin has correct repr()" + from dataclasses import dataclass + @dataclass + class Foo: + __qualname__ = 'Foo' + a: int = 0 + class Entries(Foo, Enum): + ENTRY1 = Foo(1) + self.assertEqual(repr(Entries.ENTRY1), '<Entries.ENTRY1: Foo(a=1)>') + + def test_repr_with_non_data_type_mixin(self): + # non-data_type is a mixin that doesn't define __new__ + class Foo: + def __init__(self, a): + self.a = a + def __repr__(self): + return f'Foo(a={self.a!r})' + class Entries(Foo, Enum): + ENTRY1 = Foo(1) + + self.assertEqual(repr(Entries.ENTRY1), '<Entries.ENTRY1: Foo(a=1)>') + + def test_value_backup_assign(self): + # check that enum will add missing values when custom __new__ does not + class Some(Enum): + def __new__(cls, val): + return object.__new__(cls) + x = 1 + y = 2 + self.assertEqual(Some.x.value, 1) + self.assertEqual(Some.y.value, 2) + class TestOrder(unittest.TestCase): + "test usage of the `_order_` attribute" def test_same_members(self): class Color(Enum): @@ -2914,7 +2559,7 @@ class TestOrder(unittest.TestCase): verde = green -class TestFlag(unittest.TestCase): +class OldTestFlag(unittest.TestCase): """Tests of the Flags.""" class Perm(Flag): @@ -2937,65 +2582,6 @@ class TestFlag(unittest.TestCase): WHITE = RED|GREEN|BLUE BLANCO = RED|GREEN|BLUE - def test_str(self): - Perm = self.Perm - self.assertEqual(str(Perm.R), 'R') - self.assertEqual(str(Perm.W), 'W') - self.assertEqual(str(Perm.X), 'X') - self.assertEqual(str(Perm.R | Perm.W), 'R|W') - self.assertEqual(str(Perm.R | Perm.W | Perm.X), 'R|W|X') - self.assertEqual(str(Perm(0)), 'Perm(0)') - self.assertEqual(str(~Perm.R), 'W|X') - self.assertEqual(str(~Perm.W), 'R|X') - self.assertEqual(str(~Perm.X), 'R|W') - self.assertEqual(str(~(Perm.R | Perm.W)), 'X') - self.assertEqual(str(~(Perm.R | Perm.W | Perm.X)), 'Perm(0)') - self.assertEqual(str(Perm(~0)), 'R|W|X') - - Open = self.Open - self.assertEqual(str(Open.RO), 'RO') - self.assertEqual(str(Open.WO), 'WO') - self.assertEqual(str(Open.AC), 'AC') - self.assertEqual(str(Open.RO | Open.CE), 'CE') - self.assertEqual(str(Open.WO | Open.CE), 'WO|CE') - self.assertEqual(str(~Open.RO), 'WO|RW|CE') - self.assertEqual(str(~Open.WO), 'RW|CE') - self.assertEqual(str(~Open.AC), 'CE') - self.assertEqual(str(~(Open.RO | Open.CE)), 'AC') - self.assertEqual(str(~(Open.WO | Open.CE)), 'RW') - - def test_repr(self): - Perm = self.Perm - self.assertEqual(repr(Perm.R), 'Perm.R') - self.assertEqual(repr(Perm.W), 'Perm.W') - self.assertEqual(repr(Perm.X), 'Perm.X') - self.assertEqual(repr(Perm.R | Perm.W), 'Perm.R|Perm.W') - self.assertEqual(repr(Perm.R | Perm.W | Perm.X), 'Perm.R|Perm.W|Perm.X') - self.assertEqual(repr(Perm(0)), '0x0') - self.assertEqual(repr(~Perm.R), 'Perm.W|Perm.X') - self.assertEqual(repr(~Perm.W), 'Perm.R|Perm.X') - self.assertEqual(repr(~Perm.X), 'Perm.R|Perm.W') - self.assertEqual(repr(~(Perm.R | Perm.W)), 'Perm.X') - self.assertEqual(repr(~(Perm.R | Perm.W | Perm.X)), '0x0') - self.assertEqual(repr(Perm(~0)), 'Perm.R|Perm.W|Perm.X') - - Open = self.Open - self.assertEqual(repr(Open.RO), 'Open.RO') - self.assertEqual(repr(Open.WO), 'Open.WO') - self.assertEqual(repr(Open.AC), 'Open.AC') - self.assertEqual(repr(Open.RO | Open.CE), 'Open.CE') - self.assertEqual(repr(Open.WO | Open.CE), 'Open.WO|Open.CE') - self.assertEqual(repr(~Open.RO), 'Open.WO|Open.RW|Open.CE') - self.assertEqual(repr(~Open.WO), 'Open.RW|Open.CE') - self.assertEqual(repr(~Open.AC), 'Open.CE') - self.assertEqual(repr(~(Open.RO | Open.CE)), 'Open.AC') - self.assertEqual(repr(~(Open.WO | Open.CE)), 'Open.RW') - - def test_format(self): - Perm = self.Perm - self.assertEqual(format(Perm.R, ''), 'R') - self.assertEqual(format(Perm.R | Perm.X, ''), 'R|X') - def test_or(self): Perm = self.Perm for i in Perm: @@ -3088,7 +2674,7 @@ class TestFlag(unittest.TestCase): c = 4 d = 6 # - self.assertRaisesRegex(ValueError, 'invalid value: 7', Iron, 7) + self.assertRaisesRegex(ValueError, 'invalid value 7', Iron, 7) # self.assertIs(Water(7), Water.ONE|Water.TWO) self.assertIs(Water(~9), Water.TWO) @@ -3297,7 +2883,7 @@ class TestFlag(unittest.TestCase): self.assertEqual(Color.green.value, 4) def test_auto_number_garbage(self): - with self.assertRaisesRegex(TypeError, 'Invalid Flag value: .not an int.'): + with self.assertRaisesRegex(TypeError, 'invalid flag value .not an int.'): class Color(Flag): red = 'not an int' blue = auto() @@ -3332,11 +2918,12 @@ class TestFlag(unittest.TestCase): self.assertEqual(Color.GREEN.value, 2) self.assertEqual(Color.BLUE.value, 4) self.assertEqual(Color.ALL.value, 7) - self.assertEqual(str(Color.BLUE), 'BLUE') + self.assertEqual(str(Color.BLUE), 'Color.BLUE') class Color(AllMixin, StrMixin, Flag): RED = auto() GREEN = auto() BLUE = auto() + __str__ = StrMixin.__str__ self.assertEqual(Color.RED.value, 1) self.assertEqual(Color.GREEN.value, 2) self.assertEqual(Color.BLUE.value, 4) @@ -3346,6 +2933,7 @@ class TestFlag(unittest.TestCase): RED = auto() GREEN = auto() BLUE = auto() + __str__ = StrMixin.__str__ self.assertEqual(Color.RED.value, 1) self.assertEqual(Color.GREEN.value, 2) self.assertEqual(Color.BLUE.value, 4) @@ -3426,21 +3014,8 @@ class TestFlag(unittest.TestCase): self.assertFalse(NeverEnum.__dict__.get('_test1', False)) self.assertFalse(NeverEnum.__dict__.get('_test2', False)) - def test_default_missing(self): - with self.assertRaisesRegex( - ValueError, - "'RED' is not a valid TestFlag.Color", - ) as ctx: - self.Color('RED') - self.assertIs(ctx.exception.__context__, None) - - P = Flag('P', 'X Y') - with self.assertRaisesRegex(ValueError, "'X' is not a valid P") as ctx: - P('X') - self.assertIs(ctx.exception.__context__, None) - -class TestIntFlag(unittest.TestCase): +class OldTestIntFlag(unittest.TestCase): """Tests of the IntFlags.""" class Perm(IntFlag): @@ -3485,73 +3060,6 @@ class TestIntFlag(unittest.TestCase): self.assertTrue(isinstance(Open.WO | Open.RW, Open)) self.assertEqual(Open.WO | Open.RW, 3) - - def test_str(self): - Perm = self.Perm - self.assertEqual(str(Perm.R), 'R') - self.assertEqual(str(Perm.W), 'W') - self.assertEqual(str(Perm.X), 'X') - self.assertEqual(str(Perm.R | Perm.W), 'R|W') - self.assertEqual(str(Perm.R | Perm.W | Perm.X), 'R|W|X') - self.assertEqual(str(Perm.R | 8), '12') - self.assertEqual(str(Perm(0)), 'Perm(0)') - self.assertEqual(str(Perm(8)), '8') - self.assertEqual(str(~Perm.R), 'W|X') - self.assertEqual(str(~Perm.W), 'R|X') - self.assertEqual(str(~Perm.X), 'R|W') - self.assertEqual(str(~(Perm.R | Perm.W)), 'X') - self.assertEqual(str(~(Perm.R | Perm.W | Perm.X)), 'Perm(0)') - self.assertEqual(str(~(Perm.R | 8)), '-13') - self.assertEqual(str(Perm(~0)), 'R|W|X') - self.assertEqual(str(Perm(~8)), '-9') - - Open = self.Open - self.assertEqual(str(Open.RO), 'RO') - self.assertEqual(str(Open.WO), 'WO') - self.assertEqual(str(Open.AC), 'AC') - self.assertEqual(str(Open.RO | Open.CE), 'CE') - self.assertEqual(str(Open.WO | Open.CE), 'WO|CE') - self.assertEqual(str(Open(4)), '4') - self.assertEqual(str(~Open.RO), 'WO|RW|CE') - self.assertEqual(str(~Open.WO), 'RW|CE') - self.assertEqual(str(~Open.AC), 'CE') - self.assertEqual(str(~(Open.RO | Open.CE)), 'AC') - self.assertEqual(str(~(Open.WO | Open.CE)), 'RW') - self.assertEqual(str(Open(~4)), '-5') - - def test_repr(self): - Perm = self.Perm - self.assertEqual(repr(Perm.R), 'Perm.R') - self.assertEqual(repr(Perm.W), 'Perm.W') - self.assertEqual(repr(Perm.X), 'Perm.X') - self.assertEqual(repr(Perm.R | Perm.W), 'Perm.R|Perm.W') - self.assertEqual(repr(Perm.R | Perm.W | Perm.X), 'Perm.R|Perm.W|Perm.X') - self.assertEqual(repr(Perm.R | 8), '12') - self.assertEqual(repr(Perm(0)), '0x0') - self.assertEqual(repr(Perm(8)), '8') - self.assertEqual(repr(~Perm.R), 'Perm.W|Perm.X') - self.assertEqual(repr(~Perm.W), 'Perm.R|Perm.X') - self.assertEqual(repr(~Perm.X), 'Perm.R|Perm.W') - self.assertEqual(repr(~(Perm.R | Perm.W)), 'Perm.X') - self.assertEqual(repr(~(Perm.R | Perm.W | Perm.X)), '0x0') - self.assertEqual(repr(~(Perm.R | 8)), '-13') - self.assertEqual(repr(Perm(~0)), 'Perm.R|Perm.W|Perm.X') - self.assertEqual(repr(Perm(~8)), '-9') - - Open = self.Open - self.assertEqual(repr(Open.RO), 'Open.RO') - self.assertEqual(repr(Open.WO), 'Open.WO') - self.assertEqual(repr(Open.AC), 'Open.AC') - self.assertEqual(repr(Open.RO | Open.CE), 'Open.CE') - self.assertEqual(repr(Open.WO | Open.CE), 'Open.WO|Open.CE') - self.assertEqual(repr(Open(4)), '4') - self.assertEqual(repr(~Open.RO), 'Open.WO|Open.RW|Open.CE') - self.assertEqual(repr(~Open.WO), 'Open.RW|Open.CE') - self.assertEqual(repr(~Open.AC), 'Open.CE') - self.assertEqual(repr(~(Open.RO | Open.CE)), 'Open.AC') - self.assertEqual(repr(~(Open.WO | Open.CE)), 'Open.RW') - self.assertEqual(repr(Open(~4)), '-5') - def test_global_repr_keep(self): self.assertEqual( repr(HeadlightsK(0)), @@ -3559,11 +3067,11 @@ class TestIntFlag(unittest.TestCase): ) self.assertEqual( repr(HeadlightsK(2**0 + 2**2 + 2**3)), - '%(m)s.LOW_BEAM_K|%(m)s.FOG_K|0x8' % {'m': SHORT_MODULE}, + '%(m)s.LOW_BEAM_K|%(m)s.FOG_K|8' % {'m': SHORT_MODULE}, ) self.assertEqual( repr(HeadlightsK(2**3)), - '%(m)s.HeadlightsK(0x8)' % {'m': SHORT_MODULE}, + '%(m)s.HeadlightsK(8)' % {'m': SHORT_MODULE}, ) def test_global_repr_conform1(self): @@ -3705,7 +3213,7 @@ class TestIntFlag(unittest.TestCase): c = 4 d = 6 # - self.assertRaisesRegex(ValueError, 'invalid value: 5', Iron, 5) + self.assertRaisesRegex(ValueError, 'invalid value 5', Iron, 5) # self.assertIs(Water(7), Water.ONE|Water.TWO) self.assertIs(Water(~9), Water.TWO) @@ -3942,11 +3450,12 @@ class TestIntFlag(unittest.TestCase): self.assertEqual(Color.GREEN.value, 2) self.assertEqual(Color.BLUE.value, 4) self.assertEqual(Color.ALL.value, 7) - self.assertEqual(str(Color.BLUE), 'BLUE') + self.assertEqual(str(Color.BLUE), '4') class Color(AllMixin, StrMixin, IntFlag): RED = auto() GREEN = auto() BLUE = auto() + __str__ = StrMixin.__str__ self.assertEqual(Color.RED.value, 1) self.assertEqual(Color.GREEN.value, 2) self.assertEqual(Color.BLUE.value, 4) @@ -3956,6 +3465,7 @@ class TestIntFlag(unittest.TestCase): RED = auto() GREEN = auto() BLUE = auto() + __str__ = StrMixin.__str__ self.assertEqual(Color.RED.value, 1) self.assertEqual(Color.GREEN.value, 2) self.assertEqual(Color.BLUE.value, 4) @@ -4000,19 +3510,6 @@ class TestIntFlag(unittest.TestCase): 'at least one thread failed while creating composite members') self.assertEqual(256, len(seen), 'too many composite members created') - def test_default_missing(self): - with self.assertRaisesRegex( - ValueError, - "'RED' is not a valid TestIntFlag.Color", - ) as ctx: - self.Color('RED') - self.assertIs(ctx.exception.__context__, None) - - P = IntFlag('P', 'X Y') - with self.assertRaisesRegex(ValueError, "'X' is not a valid P") as ctx: - P('X') - self.assertIs(ctx.exception.__context__, None) - class TestEmptyAndNonLatinStrings(unittest.TestCase): @@ -4229,6 +3726,89 @@ class TestHelpers(unittest.TestCase): for name in self.sunder_names + self.dunder_names + self.random_names: self.assertFalse(enum._is_private('MyEnum', name), '%r is a private name?') + def test_auto_number(self): + class Color(Enum): + red = auto() + blue = auto() + green = auto() + + self.assertEqual(list(Color), [Color.red, Color.blue, Color.green]) + self.assertEqual(Color.red.value, 1) + self.assertEqual(Color.blue.value, 2) + self.assertEqual(Color.green.value, 3) + + def test_auto_name(self): + class Color(Enum): + def _generate_next_value_(name, start, count, last): + return name + red = auto() + blue = auto() + green = auto() + + self.assertEqual(list(Color), [Color.red, Color.blue, Color.green]) + self.assertEqual(Color.red.value, 'red') + self.assertEqual(Color.blue.value, 'blue') + self.assertEqual(Color.green.value, 'green') + + def test_auto_name_inherit(self): + class AutoNameEnum(Enum): + def _generate_next_value_(name, start, count, last): + return name + class Color(AutoNameEnum): + red = auto() + blue = auto() + green = auto() + + self.assertEqual(list(Color), [Color.red, Color.blue, Color.green]) + self.assertEqual(Color.red.value, 'red') + self.assertEqual(Color.blue.value, 'blue') + self.assertEqual(Color.green.value, 'green') + + def test_auto_garbage(self): + class Color(Enum): + red = 'red' + blue = auto() + self.assertEqual(Color.blue.value, 1) + + def test_auto_garbage_corrected(self): + class Color(Enum): + red = 'red' + blue = 2 + green = auto() + + self.assertEqual(list(Color), [Color.red, Color.blue, Color.green]) + self.assertEqual(Color.red.value, 'red') + self.assertEqual(Color.blue.value, 2) + self.assertEqual(Color.green.value, 3) + + def test_auto_order(self): + with self.assertRaises(TypeError): + class Color(Enum): + red = auto() + green = auto() + blue = auto() + def _generate_next_value_(name, start, count, last): + return name + + def test_auto_order_wierd(self): + weird_auto = auto() + weird_auto.value = 'pathological case' + class Color(Enum): + red = weird_auto + def _generate_next_value_(name, start, count, last): + return name + blue = auto() + self.assertEqual(list(Color), [Color.red, Color.blue]) + self.assertEqual(Color.red.value, 'pathological case') + self.assertEqual(Color.blue.value, 'blue') + + def test_duplicate_auto(self): + class Dupes(Enum): + first = primero = auto() + second = auto() + third = auto() + self.assertEqual([Dupes.first, Dupes.second, Dupes.third], list(Dupes)) + class TestEnumTypeSubclassing(unittest.TestCase): pass @@ -4238,7 +3818,35 @@ Help on class Color in module %s: class Color(enum.Enum) | Color(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None) |\x20\x20 - | An enumeration. + | A collection of name/value pairs. + |\x20\x20 + | Access them by: + |\x20\x20 + | - attribute access:: + |\x20\x20 + | >>> Color.CYAN + | <Color.CYAN: 1> + |\x20\x20 + | - value lookup: + |\x20\x20 + | >>> Color(1) + | <Color.CYAN: 1> + |\x20\x20 + | - name lookup: + |\x20\x20 + | >>> Color['CYAN'] + | <Color.CYAN: 1> + |\x20\x20 + | Enumerations can be iterated over, and know how many members they have: + |\x20\x20 + | >>> len(Color) + | 3 + |\x20\x20 + | >>> list(Color) + | [<Color.CYAN: 1>, <Color.MAGENTA: 2>, <Color.YELLOW: 3>] + |\x20\x20 + | Methods can be added to enumerations, and members can have their own + | attributes -- see the documentation for details. |\x20\x20 | Method resolution order: | Color @@ -4247,11 +3855,11 @@ class Color(enum.Enum) |\x20\x20 | Data and other attributes defined here: |\x20\x20 - | blue = Color.blue + | CYAN = <Color.CYAN: 1> |\x20\x20 - | green = Color.green + | MAGENTA = <Color.MAGENTA: 2> |\x20\x20 - | red = Color.red + | YELLOW = <Color.YELLOW: 3> |\x20\x20 | ---------------------------------------------------------------------- | Data descriptors inherited from enum.Enum: @@ -4263,6 +3871,25 @@ class Color(enum.Enum) | The value of the Enum member. |\x20\x20 | ---------------------------------------------------------------------- + | Methods inherited from enum.EnumType: + |\x20\x20 + | __contains__(member) from enum.EnumType + | Return True if member is a member of this enum + | raises TypeError if member is not an enum member + |\x20\x20\x20\x20\x20\x20 + | note: in 3.12 TypeError will no longer be raised, and True will also be + | returned if member is the value of a member in this enum + |\x20\x20 + | __getitem__(name) from enum.EnumType + | Return the member matching `name`. + |\x20\x20 + | __iter__() from enum.EnumType + | Return members in definition order. + |\x20\x20 + | __len__() from enum.EnumType + | Return the number of members (no aliases) + |\x20\x20 + | ---------------------------------------------------------------------- | Readonly properties inherited from enum.EnumType: |\x20\x20 | __members__ @@ -4284,11 +3911,11 @@ class Color(enum.Enum) |\x20\x20 | Data and other attributes defined here: |\x20\x20 - | blue = Color.blue + | YELLOW = <Color.YELLOW: 3> |\x20\x20 - | green = Color.green + | MAGENTA = <Color.MAGENTA: 2> |\x20\x20 - | red = Color.red + | CYAN = <Color.CYAN: 1> |\x20\x20 | ---------------------------------------------------------------------- | Data descriptors inherited from enum.Enum: @@ -4307,9 +3934,9 @@ class TestStdLib(unittest.TestCase): maxDiff = None class Color(Enum): - red = 1 - green = 2 - blue = 3 + CYAN = 1 + MAGENTA = 2 + YELLOW = 3 def test_pydoc(self): # indirectly test __objclass__ @@ -4321,24 +3948,34 @@ class TestStdLib(unittest.TestCase): helper = pydoc.Helper(output=output) helper(self.Color) result = output.getvalue().strip() - self.assertEqual(result, expected_text) + self.assertEqual(result, expected_text, result) def test_inspect_getmembers(self): values = dict(( ('__class__', EnumType), - ('__doc__', 'An enumeration.'), + ('__doc__', '...'), ('__members__', self.Color.__members__), ('__module__', __name__), - ('blue', self.Color.blue), - ('green', self.Color.green), + ('YELLOW', self.Color.YELLOW), + ('MAGENTA', self.Color.MAGENTA), + ('CYAN', self.Color.CYAN), ('name', Enum.__dict__['name']), - ('red', self.Color.red), ('value', Enum.__dict__['value']), + ('__len__', self.Color.__len__), + ('__contains__', self.Color.__contains__), + ('__name__', 'Color'), + ('__getitem__', self.Color.__getitem__), + ('__qualname__', 'TestStdLib.Color'), + ('__init_subclass__', getattr(self.Color, '__init_subclass__')), + ('__iter__', self.Color.__iter__), )) result = dict(inspect.getmembers(self.Color)) self.assertEqual(set(values.keys()), set(result.keys())) failed = False for k in values.keys(): + if k == '__doc__': + # __doc__ is huge, not comparing + continue if result[k] != values[k]: print() print('\n%s\n key: %s\n result: %s\nexpected: %s\n%s\n' % @@ -4353,23 +3990,42 @@ class TestStdLib(unittest.TestCase): values = [ Attribute(name='__class__', kind='data', defining_class=object, object=EnumType), + Attribute(name='__contains__', kind='method', + defining_class=EnumType, object=self.Color.__contains__), Attribute(name='__doc__', kind='data', - defining_class=self.Color, object='An enumeration.'), + defining_class=self.Color, object='...'), + Attribute(name='__getitem__', kind='method', + defining_class=EnumType, object=self.Color.__getitem__), + Attribute(name='__iter__', kind='method', + defining_class=EnumType, object=self.Color.__iter__), + Attribute(name='__init_subclass__', kind='class method', + defining_class=object, object=getattr(self.Color, '__init_subclass__')), + Attribute(name='__len__', kind='method', + defining_class=EnumType, object=self.Color.__len__), Attribute(name='__members__', kind='property', defining_class=EnumType, object=EnumType.__members__), Attribute(name='__module__', kind='data', defining_class=self.Color, object=__name__), - Attribute(name='blue', kind='data', - defining_class=self.Color, object=self.Color.blue), - Attribute(name='green', kind='data', - defining_class=self.Color, object=self.Color.green), - Attribute(name='red', kind='data', - defining_class=self.Color, object=self.Color.red), + Attribute(name='__name__', kind='data', + defining_class=self.Color, object='Color'), + Attribute(name='__qualname__', kind='data', + defining_class=self.Color, object='TestStdLib.Color'), + Attribute(name='YELLOW', kind='data', + defining_class=self.Color, object=self.Color.YELLOW), + Attribute(name='MAGENTA', kind='data', + defining_class=self.Color, object=self.Color.MAGENTA), + Attribute(name='CYAN', kind='data', + defining_class=self.Color, object=self.Color.CYAN), Attribute(name='name', kind='data', defining_class=Enum, object=Enum.__dict__['name']), Attribute(name='value', kind='data', defining_class=Enum, object=Enum.__dict__['value']), ] + for v in values: + try: + v.name + except AttributeError: + print(v) values.sort(key=lambda item: item.name) result = list(inspect.classify_class_attrs(self.Color)) result.sort(key=lambda item: item.name) @@ -4379,7 +4035,15 @@ class TestStdLib(unittest.TestCase): ) failed = False for v, r in zip(values, result): - if r != v: + if r.name in ('__init_subclass__', '__doc__'): + # not sure how to make the __init_subclass_ Attributes match + # so as long as there is one, call it good + # __doc__ is too big to check exactly, so treat the same as __init_subclass__ + for name in ('name','kind','defining_class'): + if getattr(v, name) != getattr(r, name): + print('\n%s\n%s\n%s\n%s\n' % ('=' * 75, r, v, '=' * 75), sep='') + failed = True + elif r != v: print('\n%s\n%s\n%s\n%s\n' % ('=' * 75, r, v, '=' * 75), sep='') failed = True if failed: @@ -4388,15 +4052,15 @@ class TestStdLib(unittest.TestCase): def test_test_simple_enum(self): @_simple_enum(Enum) class SimpleColor: - RED = 1 - GREEN = 2 - BLUE = 3 + CYAN = 1 + MAGENTA = 2 + YELLOW = 3 class CheckedColor(Enum): - RED = 1 - GREEN = 2 - BLUE = 3 + CYAN = 1 + MAGENTA = 2 + YELLOW = 3 self.assertTrue(_test_simple_enum(CheckedColor, SimpleColor) is None) - SimpleColor.GREEN._value_ = 9 + SimpleColor.MAGENTA._value_ = 9 self.assertRaisesRegex( TypeError, "enum mismatch", _test_simple_enum, CheckedColor, SimpleColor, @@ -4422,9 +4086,165 @@ class TestStdLib(unittest.TestCase): class MiscTestCase(unittest.TestCase): + def test__all__(self): support.check__all__(self, enum, not_exported={'bin', 'show_flag_values'}) + def test_doc_1(self): + class Single(Enum): + ONE = 1 + self.assertEqual( + Single.__doc__, + dedent("""\ + A collection of name/value pairs. + + Access them by: + + - attribute access:: + + >>> Single.ONE + <Single.ONE: 1> + + - value lookup: + + >>> Single(1) + <Single.ONE: 1> + + - name lookup: + + >>> Single['ONE'] + <Single.ONE: 1> + + Enumerations can be iterated over, and know how many members they have: + + >>> len(Single) + 1 + + >>> list(Single) + [<Single.ONE: 1>] + + Methods can be added to enumerations, and members can have their own + attributes -- see the documentation for details. + """)) + + def test_doc_2(self): + class Double(Enum): + ONE = 1 + TWO = 2 + self.assertEqual( + Double.__doc__, + dedent("""\ + A collection of name/value pairs. + + Access them by: + + - attribute access:: + + >>> Double.ONE + <Double.ONE: 1> + + - value lookup: + + >>> Double(1) + <Double.ONE: 1> + + - name lookup: + + >>> Double['ONE'] + <Double.ONE: 1> + + Enumerations can be iterated over, and know how many members they have: + + >>> len(Double) + 2 + + >>> list(Double) + [<Double.ONE: 1>, <Double.TWO: 2>] + + Methods can be added to enumerations, and members can have their own + attributes -- see the documentation for details. + """)) + + + def test_doc_1(self): + class Triple(Enum): + ONE = 1 + TWO = 2 + THREE = 3 + self.assertEqual( + Triple.__doc__, + dedent("""\ + A collection of name/value pairs. + + Access them by: + + - attribute access:: + + >>> Triple.ONE + <Triple.ONE: 1> + + - value lookup: + + >>> Triple(1) + <Triple.ONE: 1> + + - name lookup: + + >>> Triple['ONE'] + <Triple.ONE: 1> + + Enumerations can be iterated over, and know how many members they have: + + >>> len(Triple) + 3 + + >>> list(Triple) + [<Triple.ONE: 1>, <Triple.TWO: 2>, <Triple.THREE: 3>] + + Methods can be added to enumerations, and members can have their own + attributes -- see the documentation for details. + """)) + + def test_doc_1(self): + class Quadruple(Enum): + ONE = 1 + TWO = 2 + THREE = 3 + FOUR = 4 + self.assertEqual( + Quadruple.__doc__, + dedent("""\ + A collection of name/value pairs. + + Access them by: + + - attribute access:: + + >>> Quadruple.ONE + <Quadruple.ONE: 1> + + - value lookup: + + >>> Quadruple(1) + <Quadruple.ONE: 1> + + - name lookup: + + >>> Quadruple['ONE'] + <Quadruple.ONE: 1> + + Enumerations can be iterated over, and know how many members they have: + + >>> len(Quadruple) + 4 + + >>> list(Quadruple)[:3] + [<Quadruple.ONE: 1>, <Quadruple.TWO: 2>, <Quadruple.THREE: 3>] + + Methods can be added to enumerations, and members can have their own + attributes -- see the documentation for details. + """)) + # These are unordered here on purpose to ensure that declaration order # makes no difference. @@ -4442,6 +4262,10 @@ CONVERT_STRING_TEST_NAME_A = 5 # This one should sort first. CONVERT_STRING_TEST_NAME_E = 5 CONVERT_STRING_TEST_NAME_F = 5 +# global names for StrEnum._convert_ test +CONVERT_STR_TEST_2 = 'goodbye' +CONVERT_STR_TEST_1 = 'hello' + # We also need values that cannot be compared: UNCOMPARABLE_A = 5 UNCOMPARABLE_C = (9, 1) # naming order is broken on purpose @@ -4453,32 +4277,40 @@ COMPLEX_B = 3j class _ModuleWrapper: """We use this class as a namespace for swapping modules.""" - def __init__(self, module): self.__dict__.update(module.__dict__) -class TestIntEnumConvert(unittest.TestCase): +class TestConvert(unittest.TestCase): + def tearDown(self): + # Reset the module-level test variables to their original integer + # values, otherwise the already created enum values get converted + # instead. + g = globals() + for suffix in ['A', 'B', 'C', 'D', 'E', 'F']: + g['CONVERT_TEST_NAME_%s' % suffix] = 5 + g['CONVERT_STRING_TEST_NAME_%s' % suffix] = 5 + for suffix, value in (('A', 5), ('B', (9, 1)), ('C', 'value')): + g['UNCOMPARABLE_%s' % suffix] = value + for suffix, value in (('A', 2j), ('B', 3j), ('C', 1j)): + g['COMPLEX_%s' % suffix] = value + for suffix, value in (('1', 'hello'), ('2', 'goodbye')): + g['CONVERT_STR_TEST_%s' % suffix] = value + def test_convert_value_lookup_priority(self): - with support.swap_item( - sys.modules, MODULE, _ModuleWrapper(sys.modules[MODULE]), - ): - test_type = enum.IntEnum._convert_( - 'UnittestConvert', - MODULE, - filter=lambda x: x.startswith('CONVERT_TEST_')) + test_type = enum.IntEnum._convert_( + 'UnittestConvert', + MODULE, + filter=lambda x: x.startswith('CONVERT_TEST_')) # We don't want the reverse lookup value to vary when there are # multiple possible names for a given value. It should always # report the first lexigraphical name in that case. self.assertEqual(test_type(5).name, 'CONVERT_TEST_NAME_A') - def test_convert(self): - with support.swap_item( - sys.modules, MODULE, _ModuleWrapper(sys.modules[MODULE]), - ): - test_type = enum.IntEnum._convert_( - 'UnittestConvert', - MODULE, - filter=lambda x: x.startswith('CONVERT_TEST_')) + def test_convert_int(self): + test_type = enum.IntEnum._convert_( + 'UnittestConvert', + MODULE, + filter=lambda x: x.startswith('CONVERT_TEST_')) # Ensure that test_type has all of the desired names and values. self.assertEqual(test_type.CONVERT_TEST_NAME_F, test_type.CONVERT_TEST_NAME_A) @@ -4487,43 +4319,57 @@ class TestIntEnumConvert(unittest.TestCase): self.assertEqual(test_type.CONVERT_TEST_NAME_D, 5) self.assertEqual(test_type.CONVERT_TEST_NAME_E, 5) # Ensure that test_type only picked up names matching the filter. - self.assertEqual([name for name in dir(test_type) - if name[0:2] not in ('CO', '__') - and name not in dir(IntEnum)], - [], msg='Names other than CONVERT_TEST_* found.') + int_dir = dir(int) + [ + 'CONVERT_TEST_NAME_A', 'CONVERT_TEST_NAME_B', 'CONVERT_TEST_NAME_C', + 'CONVERT_TEST_NAME_D', 'CONVERT_TEST_NAME_E', 'CONVERT_TEST_NAME_F', + ] + self.assertEqual( + [name for name in dir(test_type) if name not in int_dir], + [], + msg='Names other than CONVERT_TEST_* found.', + ) def test_convert_uncomparable(self): - # We swap a module to some other object with `__dict__` - # because otherwise refleak is created. - # `_convert_` uses a module side effect that does this. See 30472 - with support.swap_item( - sys.modules, MODULE, _ModuleWrapper(sys.modules[MODULE]), - ): - uncomp = enum.Enum._convert_( - 'Uncomparable', - MODULE, - filter=lambda x: x.startswith('UNCOMPARABLE_')) - + uncomp = enum.Enum._convert_( + 'Uncomparable', + MODULE, + filter=lambda x: x.startswith('UNCOMPARABLE_')) # Should be ordered by `name` only: self.assertEqual( list(uncomp), [uncomp.UNCOMPARABLE_A, uncomp.UNCOMPARABLE_B, uncomp.UNCOMPARABLE_C], - ) + ) def test_convert_complex(self): - with support.swap_item( - sys.modules, MODULE, _ModuleWrapper(sys.modules[MODULE]), - ): - uncomp = enum.Enum._convert_( - 'Uncomparable', - MODULE, - filter=lambda x: x.startswith('COMPLEX_')) - + uncomp = enum.Enum._convert_( + 'Uncomparable', + MODULE, + filter=lambda x: x.startswith('COMPLEX_')) # Should be ordered by `name` only: self.assertEqual( list(uncomp), [uncomp.COMPLEX_A, uncomp.COMPLEX_B, uncomp.COMPLEX_C], - ) + ) + + def test_convert_str(self): + test_type = enum.StrEnum._convert_( + 'UnittestConvert', + MODULE, + filter=lambda x: x.startswith('CONVERT_STR_'), + as_global=True) + # Ensure that test_type has all of the desired names and values. + self.assertEqual(test_type.CONVERT_STR_TEST_1, 'hello') + self.assertEqual(test_type.CONVERT_STR_TEST_2, 'goodbye') + # Ensure that test_type only picked up names matching the filter. + str_dir = dir(str) + ['CONVERT_STR_TEST_1', 'CONVERT_STR_TEST_2'] + self.assertEqual( + [name for name in dir(test_type) if name not in str_dir], + [], + msg='Names other than CONVERT_STR_* found.', + ) + self.assertEqual(repr(test_type.CONVERT_STR_TEST_1), '%s.CONVERT_STR_TEST_1' % SHORT_MODULE) + self.assertEqual(str(test_type.CONVERT_STR_TEST_2), 'goodbye') + self.assertEqual(format(test_type.CONVERT_STR_TEST_1), 'hello') def test_convert_raise(self): with self.assertRaises(AttributeError): @@ -4533,50 +4379,58 @@ class TestIntEnumConvert(unittest.TestCase): filter=lambda x: x.startswith('CONVERT_TEST_')) def test_convert_repr_and_str(self): - with support.swap_item( - sys.modules, MODULE, _ModuleWrapper(sys.modules[MODULE]), - ): - test_type = enum.IntEnum._convert_( - 'UnittestConvert', - MODULE, - filter=lambda x: x.startswith('CONVERT_STRING_TEST_')) + test_type = enum.IntEnum._convert_( + 'UnittestConvert', + MODULE, + filter=lambda x: x.startswith('CONVERT_STRING_TEST_'), + as_global=True) self.assertEqual(repr(test_type.CONVERT_STRING_TEST_NAME_A), '%s.CONVERT_STRING_TEST_NAME_A' % SHORT_MODULE) - self.assertEqual(str(test_type.CONVERT_STRING_TEST_NAME_A), 'CONVERT_STRING_TEST_NAME_A') + self.assertEqual(str(test_type.CONVERT_STRING_TEST_NAME_A), '5') self.assertEqual(format(test_type.CONVERT_STRING_TEST_NAME_A), '5') -# global names for StrEnum._convert_ test -CONVERT_STR_TEST_2 = 'goodbye' -CONVERT_STR_TEST_1 = 'hello' -class TestStrEnumConvert(unittest.TestCase): - def test_convert(self): - with support.swap_item( - sys.modules, MODULE, _ModuleWrapper(sys.modules[MODULE]), - ): - test_type = enum.StrEnum._convert_( - 'UnittestConvert', - MODULE, - filter=lambda x: x.startswith('CONVERT_STR_')) - # Ensure that test_type has all of the desired names and values. - self.assertEqual(test_type.CONVERT_STR_TEST_1, 'hello') - self.assertEqual(test_type.CONVERT_STR_TEST_2, 'goodbye') - # Ensure that test_type only picked up names matching the filter. - self.assertEqual([name for name in dir(test_type) - if name[0:2] not in ('CO', '__') - and name not in dir(StrEnum)], - [], msg='Names other than CONVERT_STR_* found.') +# helpers - def test_convert_repr_and_str(self): - with support.swap_item( - sys.modules, MODULE, _ModuleWrapper(sys.modules[MODULE]), - ): - test_type = enum.StrEnum._convert_( - 'UnittestConvert', - MODULE, - filter=lambda x: x.startswith('CONVERT_STR_')) - self.assertEqual(repr(test_type.CONVERT_STR_TEST_1), '%s.CONVERT_STR_TEST_1' % SHORT_MODULE) - self.assertEqual(str(test_type.CONVERT_STR_TEST_2), 'goodbye') - self.assertEqual(format(test_type.CONVERT_STR_TEST_1), 'hello') +def enum_dir(cls): + # TODO: check for custom __init__, __new__, __format__, __repr__, __str__, __init_subclass__ + if cls._member_type_ is object: + interesting = set() + if cls.__init_subclass__ is not object.__init_subclass__: + interesting.add('__init_subclass__') + return sorted(set([ + '__class__', '__contains__', '__doc__', '__getitem__', + '__iter__', '__len__', '__members__', '__module__', + '__name__', '__qualname__', + ] + + cls._member_names_ + ) | interesting + ) + else: + # return whatever mixed-in data type has + return sorted(set( + dir(cls._member_type_) + + cls._member_names_ + )) + +def member_dir(member): + if member.__class__._member_type_ is object: + allowed = set(['__class__', '__doc__', '__eq__', '__hash__', '__module__', 'name', 'value']) + else: + allowed = set(dir(member)) + for cls in member.__class__.mro(): + for name, obj in cls.__dict__.items(): + if name[0] == '_': + continue + if isinstance(obj, enum.property): + if obj.fget is not None or name not in member._member_map_: + allowed.add(name) + else: + allowed.discard(name) + else: + allowed.add(name) + return sorted(allowed) + +missing = object() if __name__ == '__main__': |