diff options
author | Victor Stinner <vstinner@python.org> | 2022-01-17 12:58:40 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-01-17 12:58:40 (GMT) |
commit | 42a64c03ec5c443f2a5c2ee4284622f5d1f5326c (patch) | |
tree | d5fffd97234c4a8481ee3f07a69107188f1faa7d /Lib/test/test_enum.py | |
parent | 7f4b69b9076bdbcea31f6ad16eb125ee99cf0175 (diff) | |
download | cpython-42a64c03ec5c443f2a5c2ee4284622f5d1f5326c.zip cpython-42a64c03ec5c443f2a5c2ee4284622f5d1f5326c.tar.gz cpython-42a64c03ec5c443f2a5c2ee4284622f5d1f5326c.tar.bz2 |
Revert "bpo-40066: [Enum] update str() and format() output (GH-30582)" (GH-30632)
This reverts commit acf7403f9baea3ae1119fc6b4a3298522188bf96.
Diffstat (limited to 'Lib/test/test_enum.py')
-rw-r--r-- | Lib/test/test_enum.py | 2864 |
1 files changed, 1505 insertions, 1359 deletions
diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py index a0953fb..43f98c1 100644 --- a/Lib/test/test_enum.py +++ b/Lib/test/test_enum.py @@ -6,18 +6,15 @@ 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, ReprEnum +from enum import verify, UNIQUE, CONTINUOUS, NAMED_FLAGS 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] @@ -110,12 +107,6 @@ 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 @@ -125,36 +116,21 @@ class TestHelpers(unittest.TestCase): setattr(obj, attr, 1) self.assertTrue(enum._is_descriptor(obj)) - 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) + def test_is_sunder(self): 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_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) + def test_is_dunder(self): 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: @@ -190,658 +166,473 @@ class HeadlightsC(IntFlag, boundary=enum.CONFORM): # tests -class _EnumTests: - """ - Test for behavior that is the same across the different types of enumerations. - """ - - values = None +class TestEnum(unittest.TestCase): def setUp(self): - 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 - - def assertFormatIsValue(self, spec, member): - self.assertEqual(spec.format(member), spec.format(member.value)) - - def assertFormatIsStr(self, spec, member): - self.assertEqual(spec.format(member), spec.format(str(member))) - - 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 - - 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 Season(Enum): + SPRING = 1 + SUMMER = 2 + AUTUMN = 3 + WINTER = 4 + self.Season = Season - 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 Konstants(float, Enum): + E = 2.7182818 + PI = 3.1415926 + TAU = 2 * PI + self.Konstants = Konstants - def test_changing_member_fails(self): - MainEnum = self.MainEnum - with self.assertRaises(AttributeError): - self.MainEnum.second = 'really first' + class Grades(IntEnum): + A = 5 + B = 4 + C = 3 + D = 2 + F = 0 + self.Grades = Grades - @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 Directional(str, Enum): + EAST = 'east' + WEST = 'west' + NORTH = 'north' + SOUTH = 'south' + self.Directional = Directional - @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) + from datetime import date + class Holiday(date, Enum): + NEW_YEAR = 2013, 1, 1 + IDES_OF_MARCH = 2013, 3, 15 + self.Holiday = Holiday - def test_dir_on_class(self): - TE = self.MainEnum - self.assertEqual(set(dir(TE)), set(enum_dir(TE))) + class DateEnum(date, Enum): pass + self.DateEnum = DateEnum - def test_dir_on_item(self): - TE = self.MainEnum - self.assertEqual(set(dir(TE.first)), set(member_dir(TE.first))) + class FloatEnum(float, Enum): pass + self.FloatEnum = FloatEnum - def test_dir_with_added_behavior(self): - class Test(self.enum_type): - this = auto() - these = auto() + class Wowser(Enum): + this = 'that' + these = 'those' def wowser(self): + """Wowser docstring""" return ("Wowser! I'm %s!" % self.name) - self.assertTrue('wowser' not in dir(Test)) - self.assertTrue('wowser' in dir(Test.this)) + @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 + + 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 + + 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_sub_with_behavior_on_super(self): # see issue22506 - class SuperEnum(self.enum_type): + class SuperEnum1(Enum): def invisible(self): return "did you see me?" - class SubEnum(SuperEnum): - sample = auto() - self.assertTrue('invisible' not in dir(SubEnum)) - self.assertTrue('invisible' in dir(SubEnum.sample)) + class SubEnum1(SuperEnum1): + sample = 5 + self.SubEnum1 = SubEnum1 - 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' + class SuperEnum2(IntEnum): + def __new__(cls, value, description=""): + obj = int.__new__(cls, value) + obj._value_ = value + obj.description = description return obj - 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)) - - def test_enum_in_enum_out(self): - Main = self.MainEnum - self.assertIs(Main(Main.first), Main.first) - - 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()) + 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 + )) - 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))) + 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_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))) + 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_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, + first_enum_base = next( + base for base in enum_cls.__mro__ + if is_from_enum_module(base) ) - 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)) - 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)) + 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_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) + def test_dir_on_sub_with_behavior_on_super(self): + # see issue22506 self.assertEqual( - [MinorEnum.june, MinorEnum.july, MinorEnum.august], - lst, + set(dir(self.SubEnum1.sample)), + set(['__class__', '__doc__', '__module__', 'name', 'value', 'invisible']), ) - 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_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_dir_on_sub_with_behavior_including_instance_dict_on_super(self): + # see issue40084 + self.assertTrue({'description'} <= set(dir(self.SubEnum2.sample))) - 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_in_enum_out(self): + Season = self.Season + self.assertIs(Season(Season.WINTER), Season.WINTER) - 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_enum_value(self): + Season = self.Season + self.assertEqual(Season.SPRING.value, 1) - 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_intenum_value(self): + self.assertEqual(IntStooges.CURLY.value, 2) - def test_reversed_iteration_order(self): + def test_enum(self): + Season = self.Season + lst = list(Season) + self.assertEqual(len(lst), len(Season)) + self.assertEqual(len(Season), 4, Season) self.assertEqual( - list(reversed(self.MainEnum)), - [self.MainEnum.third, self.MainEnum.second, self.MainEnum.first], - ) + [Season.SPRING, Season.SUMMER, Season.AUTUMN, Season.WINTER], lst) -class _PlainOutputTests: - - 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", '%s %r' % (NF.__str__, NF.first)) - self.assertEqual(format(NF.first), "FIRST") - - 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) - - -class _MixedOutputTests: - - 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 - - -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] + 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 enum_type(float, Enum): - def _generate_next_value_(name, start, count, last_values): - values = [1.1, 2.2, 3.3] - return values[count] + def test_changing_member(self): + Season = self.Season + with self.assertRaises(AttributeError): + Season.WINTER = 'really cold' + def test_attribute_deletion(self): + class Season(Enum): + SPRING = 1 + SUMMER = 2 + AUTUMN = 3 + WINTER = 4 -class TestMinimalFloat(_EnumTests, _MinimalOutputTests, unittest.TestCase): + def spam(cls): + pass - values = [4.4, 5.5, 6.6] + self.assertTrue(hasattr(Season, 'spam')) + del Season.spam + self.assertFalse(hasattr(Season, 'spam')) - class enum_type(float, ReprEnum): - def _generate_next_value_(name, start, count, last_values): - values = [4.4, 5.5, 6.6] - return values[count] + 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 TestSpecial(unittest.TestCase): - """ - various operations that are not attributable to every possible enum - """ + def test_bool_of_member(self): + class Count(Enum): + zero = 0 + one = 1 + two = 2 + for member in Count: + self.assertTrue(bool(member)) - 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_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 def test_bool(self): # plain Enum members are always True @@ -865,56 +656,92 @@ class TestSpecial(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_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_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_duplicate_name_error(self): + def test_duplicate_name(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 @@ -922,45 +749,232 @@ class TestSpecial(unittest.TestCase): blue = 3 def red(self): return 'red' - # + with self.assertRaises(TypeError): class Color(Enum): - @enum.property + @property def red(self): return 'redder' red = 1 green = 2 blue = 3 - def test_enum_function_with_qualname(self): - if isinstance(Theory, Exception): - raise Theory - self.assertEqual(Theory.__qualname__, 'spanish_inquisition') + def test_reserved__sunder_(self): + with self.assertRaisesRegex( + ValueError, + '_sunder_ names, such as ._bad_., are reserved', + ): + class Bad(Enum): + _bad_ = 1 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' @@ -976,7 +990,7 @@ class TestSpecial(unittest.TestCase): test_pickle_dump_load(self.assertIs, MyOtherEnum.E) test_pickle_dump_load(self.assertIs, MyOtherEnum) # - # This did not work in 3.10, but does now with pickling by name + # This did not work in 3.9, but does now with pickling by name class UnBrokenInt(int): __qualname__ = 'UnBrokenInt' def __new__(cls, value): @@ -993,124 +1007,6 @@ class TestSpecial(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): @@ -1126,6 +1022,122 @@ class TestSpecial(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): @@ -1157,7 +1169,12 @@ class TestSpecial(unittest.TestCase): test_pickle_dump_load(self.assertIs, Question.who) test_pickle_dump_load(self.assertIs, Question) - def test_pickle_nested_class(self): + 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): # would normally just have this directly in the class namespace class NestedEnum(Enum): twigs = 'common' @@ -1175,7 +1192,7 @@ class TestSpecial(unittest.TestCase): for proto in range(HIGHEST_PROTOCOL): self.assertEqual(ReplaceGlobalInt.TWO.__reduce_ex__(proto), 'TWO') - def test_pickle_explodes(self): + def test_exploding_pickle(self): BadPickle = Enum( 'BadPickle', 'dill sweet bread-n-butter', module=__name__) globals()['BadPickle'] = BadPickle @@ -1216,6 +1233,185 @@ class TestSpecial(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 @@ -1229,18 +1425,15 @@ class TestSpecial(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, "<enum .EvenMoreColor.> cannot extend <enum .Color.>"): + with self.assertRaisesRegex(TypeError, "EvenMoreColor: cannot extend enumeration 'Color'"): class EvenMoreColor(Color, IntEnum): chartruese = 7 - # - with self.assertRaisesRegex(TypeError, "<enum .Foo.> cannot extend <enum .Color.>"): + with self.assertRaisesRegex(TypeError, "Foo: cannot extend enumeration 'Color'"): Color('Foo', ('pink', 'black')) def test_exclude_methods(self): @@ -1344,7 +1537,27 @@ class TestSpecial(unittest.TestCase): with self.assertRaises(KeyError): Color['chartreuse'] - # tests that need to be evalualted for moving + def test_new_repr(self): + class Color(Enum): + red = 1 + green = 2 + blue = 3 + def __repr__(self): + return "don't you just love shades of %s?" % self.name + self.assertEqual( + repr(Color.blue), + "don't you just love shades of blue?", + ) + + def test_inherited_repr(self): + class MyEnum(Enum): + def __repr__(self): + return "My name is %s." % self.name + class MyIntEnum(int, MyEnum): + this = 1 + that = 2 + theother = 3 + self.assertEqual(repr(MyIntEnum.that), "My name is that.") def test_multiple_mixin_mro(self): class auto_enum(type(Enum)): @@ -1397,7 +1610,7 @@ class TestSpecial(unittest.TestCase): return self def __getnewargs__(self): return self._args - @bltns.property + @property def __name__(self): return self._intname def __repr__(self): @@ -1457,7 +1670,7 @@ class TestSpecial(unittest.TestCase): return self def __getnewargs_ex__(self): return self._args, {} - @bltns.property + @property def __name__(self): return self._intname def __repr__(self): @@ -1517,7 +1730,7 @@ class TestSpecial(unittest.TestCase): return self def __reduce__(self): return self.__class__, self._args - @bltns.property + @property def __name__(self): return self._intname def __repr__(self): @@ -1577,7 +1790,7 @@ class TestSpecial(unittest.TestCase): return self def __reduce_ex__(self, proto): return self.__class__, self._args - @bltns.property + @property def __name__(self): return self._intname def __repr__(self): @@ -1634,7 +1847,7 @@ class TestSpecial(unittest.TestCase): self._intname = name self._args = _args return self - @bltns.property + @property def __name__(self): return self._intname def __repr__(self): @@ -1689,7 +1902,7 @@ class TestSpecial(unittest.TestCase): self._intname = name self._args = _args return self - @bltns.property + @property def __name__(self): return self._intname def __repr__(self): @@ -1878,7 +2091,6 @@ class TestSpecial(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): @@ -1886,7 +2098,6 @@ class TestSpecial(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): @@ -1923,7 +2134,7 @@ class TestSpecial(unittest.TestCase): def __init__(self, mass, radius): self.mass = mass # in kilograms self.radius = radius # in meters - @enum.property + @property def surface_gravity(self): # universal gravitational constant (m3 kg-1 s-2) G = 6.67300E-11 @@ -1993,7 +2204,90 @@ class TestSpecial(unittest.TestCase): self.assertEqual(LabelledList.unprocessed, 1) self.assertEqual(LabelledList(1), LabelledList.unprocessed) - def test_default_missing_no_chained_exception(self): + 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): class Color(Enum): RED = 1 GREEN = 2 @@ -2005,7 +2299,7 @@ class TestSpecial(unittest.TestCase): else: raise Exception('Exception not raised.') - def test_missing_override(self): + def test_missing(self): class Color(Enum): red = 1 green = 2 @@ -2069,9 +2363,9 @@ class TestSpecial(unittest.TestCase): class_1_ref = weakref.ref(Class1()) class_2_ref = weakref.ref(Class2()) # - # 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. + # The exception raised by Enum creates a reference loop and thus + # Class2 instances will stick around until the next garbage collection + # cycle, unlike Class1. gc.collect() # For PyPy or other GCs. self.assertIs(class_1_ref(), None) self.assertIs(class_2_ref(), None) @@ -2102,12 +2396,11 @@ class TestSpecial(unittest.TestCase): self.assertEqual(Color.GREEN.value, 2) self.assertEqual(Color.BLUE.value, 3) self.assertEqual(Color.MAX, 3) - self.assertEqual(str(Color.BLUE), 'Color.BLUE') + self.assertEqual(str(Color.BLUE), '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) @@ -2117,7 +2410,6 @@ class TestSpecial(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) @@ -2127,7 +2419,6 @@ class TestSpecial(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) @@ -2137,7 +2428,6 @@ class TestSpecial(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) @@ -2148,7 +2438,6 @@ class TestSpecial(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) @@ -2159,7 +2448,6 @@ class TestSpecial(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) @@ -2170,7 +2458,6 @@ class TestSpecial(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) @@ -2203,12 +2490,11 @@ class TestSpecial(unittest.TestCase): return hex(self) class MyIntEnum(HexMixin, MyInt, enum.Enum): - __repr__ = HexMixin.__repr__ + pass 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): @@ -2220,7 +2506,7 @@ class TestSpecial(unittest.TestCase): return member self.assertEqual(Fee.TEST, 2) - def test_multiple_mixin_with_common_data_type(self): + def test_miltuple_mixin_with_common_data_type(self): class CaseInsensitiveStrEnum(str, Enum): @classmethod def _missing_(cls, value): @@ -2240,7 +2526,7 @@ class TestSpecial(unittest.TestCase): unknown._value_ = value cls._member_map_[value] = unknown return unknown - @enum.property + @property def valid(self): return self._valid # @@ -2284,7 +2570,7 @@ class TestSpecial(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: '1'>") + self.assertEqual(repr(GoodStrEnum.one), 'GoodStrEnum.one') # class DumbMixin: def __str__(self): @@ -2293,7 +2579,6 @@ class TestSpecial(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") # @@ -2335,6 +2620,74 @@ class TestSpecial(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 @@ -2344,9 +2697,9 @@ class TestSpecial(unittest.TestCase): three = b'3', 'ascii' four = b'4', 'latin1', 'strict' self.assertEqual(OkayEnum.one, '1') - self.assertEqual(str(OkayEnum.one), 'OkayEnum.one') - self.assertEqual('{}'.format(OkayEnum.one), 'OkayEnum.one') - self.assertEqual(repr(OkayEnum.one), "<OkayEnum.one: '1'>") + self.assertEqual(str(OkayEnum.one), 'one') + self.assertEqual('{}'.format(OkayEnum.one), 'one') + self.assertEqual(repr(OkayEnum.one), 'OkayEnum.one') # class DumbMixin: def __str__(self): @@ -2355,7 +2708,6 @@ class TestSpecial(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") # @@ -2365,7 +2717,7 @@ class TestSpecial(unittest.TestCase): class HelloEnum(EnumMixin, CustomStrEnum): eight = '8' self.assertEqual(HelloEnum.eight, '8') - self.assertEqual(str(HelloEnum.eight), 'HelloEnum.eight') + self.assertEqual(str(HelloEnum.eight), 'eight') # class GoodbyeMixin: def goodbye(self): @@ -2373,7 +2725,7 @@ class TestSpecial(unittest.TestCase): class GoodbyeEnum(GoodbyeMixin, EnumMixin, CustomStrEnum): nine = '9' self.assertEqual(GoodbyeEnum.nine, '9') - self.assertEqual(str(GoodbyeEnum.nine), 'GoodbyeEnum.nine') + self.assertEqual(str(GoodbyeEnum.nine), 'nine') # class FirstFailedStrEnum(CustomStrEnum): one = 1 # this will become '1' @@ -2419,6 +2771,21 @@ class TestSpecial(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): @@ -2427,13 +2794,35 @@ class TestSpecial(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, "<enum .Di.> member has no attribute .NO."): + with self.assertRaisesRegex(AttributeError, "Di: no instance 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): # @@ -2450,7 +2839,7 @@ class TestSpecial(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 @@ -2462,42 +2851,8 @@ class TestSpecial(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): @@ -2559,7 +2914,7 @@ class TestOrder(unittest.TestCase): verde = green -class OldTestFlag(unittest.TestCase): +class TestFlag(unittest.TestCase): """Tests of the Flags.""" class Perm(Flag): @@ -2582,6 +2937,65 @@ class OldTestFlag(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: @@ -2674,7 +3088,7 @@ class OldTestFlag(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) @@ -2883,7 +3297,7 @@ class OldTestFlag(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() @@ -2918,12 +3332,11 @@ class OldTestFlag(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), 'Color.BLUE') + self.assertEqual(str(Color.BLUE), '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) @@ -2933,7 +3346,6 @@ class OldTestFlag(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) @@ -3014,8 +3426,21 @@ class OldTestFlag(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 OldTestIntFlag(unittest.TestCase): +class TestIntFlag(unittest.TestCase): """Tests of the IntFlags.""" class Perm(IntFlag): @@ -3060,6 +3485,73 @@ class OldTestIntFlag(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)), @@ -3067,11 +3559,11 @@ class OldTestIntFlag(unittest.TestCase): ) self.assertEqual( repr(HeadlightsK(2**0 + 2**2 + 2**3)), - '%(m)s.LOW_BEAM_K|%(m)s.FOG_K|8' % {'m': SHORT_MODULE}, + '%(m)s.LOW_BEAM_K|%(m)s.FOG_K|0x8' % {'m': SHORT_MODULE}, ) self.assertEqual( repr(HeadlightsK(2**3)), - '%(m)s.HeadlightsK(8)' % {'m': SHORT_MODULE}, + '%(m)s.HeadlightsK(0x8)' % {'m': SHORT_MODULE}, ) def test_global_repr_conform1(self): @@ -3213,7 +3705,7 @@ class OldTestIntFlag(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) @@ -3450,12 +3942,11 @@ class OldTestIntFlag(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), '4') + self.assertEqual(str(Color.BLUE), 'BLUE') 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) @@ -3465,7 +3956,6 @@ class OldTestIntFlag(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) @@ -3510,6 +4000,19 @@ class OldTestIntFlag(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): @@ -3726,89 +4229,6 @@ 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 @@ -3818,35 +4238,7 @@ 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 - | 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. + | An enumeration. |\x20\x20 | Method resolution order: | Color @@ -3855,11 +4247,11 @@ class Color(enum.Enum) |\x20\x20 | Data and other attributes defined here: |\x20\x20 - | CYAN = <Color.CYAN: 1> + | blue = Color.blue |\x20\x20 - | MAGENTA = <Color.MAGENTA: 2> + | green = Color.green |\x20\x20 - | YELLOW = <Color.YELLOW: 3> + | red = Color.red |\x20\x20 | ---------------------------------------------------------------------- | Data descriptors inherited from enum.Enum: @@ -3871,25 +4263,6 @@ 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__ @@ -3911,11 +4284,11 @@ class Color(enum.Enum) |\x20\x20 | Data and other attributes defined here: |\x20\x20 - | YELLOW = <Color.YELLOW: 3> + | blue = Color.blue |\x20\x20 - | MAGENTA = <Color.MAGENTA: 2> + | green = Color.green |\x20\x20 - | CYAN = <Color.CYAN: 1> + | red = Color.red |\x20\x20 | ---------------------------------------------------------------------- | Data descriptors inherited from enum.Enum: @@ -3934,9 +4307,9 @@ class TestStdLib(unittest.TestCase): maxDiff = None class Color(Enum): - CYAN = 1 - MAGENTA = 2 - YELLOW = 3 + red = 1 + green = 2 + blue = 3 def test_pydoc(self): # indirectly test __objclass__ @@ -3948,34 +4321,24 @@ class TestStdLib(unittest.TestCase): helper = pydoc.Helper(output=output) helper(self.Color) result = output.getvalue().strip() - self.assertEqual(result, expected_text, result) + self.assertEqual(result, expected_text) def test_inspect_getmembers(self): values = dict(( ('__class__', EnumType), - ('__doc__', '...'), + ('__doc__', 'An enumeration.'), ('__members__', self.Color.__members__), ('__module__', __name__), - ('YELLOW', self.Color.YELLOW), - ('MAGENTA', self.Color.MAGENTA), - ('CYAN', self.Color.CYAN), + ('blue', self.Color.blue), + ('green', self.Color.green), ('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' % @@ -3990,42 +4353,23 @@ 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='...'), - 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__), + defining_class=self.Color, object='An enumeration.'), Attribute(name='__members__', kind='property', defining_class=EnumType, object=EnumType.__members__), Attribute(name='__module__', kind='data', defining_class=self.Color, object=__name__), - 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='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=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) @@ -4035,15 +4379,7 @@ class TestStdLib(unittest.TestCase): ) failed = False for v, r in zip(values, result): - 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: + if r != v: print('\n%s\n%s\n%s\n%s\n' % ('=' * 75, r, v, '=' * 75), sep='') failed = True if failed: @@ -4052,15 +4388,15 @@ class TestStdLib(unittest.TestCase): def test_test_simple_enum(self): @_simple_enum(Enum) class SimpleColor: - CYAN = 1 - MAGENTA = 2 - YELLOW = 3 + RED = 1 + GREEN = 2 + BLUE = 3 class CheckedColor(Enum): - CYAN = 1 - MAGENTA = 2 - YELLOW = 3 + RED = 1 + GREEN = 2 + BLUE = 3 self.assertTrue(_test_simple_enum(CheckedColor, SimpleColor) is None) - SimpleColor.MAGENTA._value_ = 9 + SimpleColor.GREEN._value_ = 9 self.assertRaisesRegex( TypeError, "enum mismatch", _test_simple_enum, CheckedColor, SimpleColor, @@ -4086,165 +4422,9 @@ 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. @@ -4262,10 +4442,6 @@ 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 @@ -4277,40 +4453,32 @@ 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 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 - +class TestIntEnumConvert(unittest.TestCase): def test_convert_value_lookup_priority(self): - test_type = enum.IntEnum._convert_( - 'UnittestConvert', - MODULE, - filter=lambda x: x.startswith('CONVERT_TEST_')) + 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_')) # 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_int(self): - test_type = enum.IntEnum._convert_( - 'UnittestConvert', - MODULE, - filter=lambda x: x.startswith('CONVERT_TEST_')) + 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_')) # 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) @@ -4319,57 +4487,43 @@ class TestConvert(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. - 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.', - ) + 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.') def test_convert_uncomparable(self): - uncomp = enum.Enum._convert_( - 'Uncomparable', - MODULE, - filter=lambda x: x.startswith('UNCOMPARABLE_')) + # 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_')) + # Should be ordered by `name` only: self.assertEqual( list(uncomp), [uncomp.UNCOMPARABLE_A, uncomp.UNCOMPARABLE_B, uncomp.UNCOMPARABLE_C], - ) + ) def test_convert_complex(self): - uncomp = enum.Enum._convert_( - 'Uncomparable', - MODULE, - filter=lambda x: x.startswith('COMPLEX_')) + with support.swap_item( + sys.modules, MODULE, _ModuleWrapper(sys.modules[MODULE]), + ): + 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): @@ -4379,58 +4533,50 @@ class TestConvert(unittest.TestCase): filter=lambda x: x.startswith('CONVERT_TEST_')) def test_convert_repr_and_str(self): - test_type = enum.IntEnum._convert_( - 'UnittestConvert', - MODULE, - filter=lambda x: x.startswith('CONVERT_STRING_TEST_'), - as_global=True) + 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_')) 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), '5') + self.assertEqual(str(test_type.CONVERT_STRING_TEST_NAME_A), 'CONVERT_STRING_TEST_NAME_A') 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' -# helpers - -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) +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.') -missing = object() + 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') if __name__ == '__main__': |