1import copy 2import enum 3import doctest 4import inspect 5import os 6import pydoc 7import sys 8import unittest 9import threading 10import typing 11import builtins as bltns 12from collections import OrderedDict 13from datetime import date 14from functools import partial 15from enum import Enum, EnumMeta, IntEnum, StrEnum, EnumType, Flag, IntFlag, unique, auto 16from enum import STRICT, CONFORM, EJECT, KEEP, _simple_enum, _test_simple_enum 17from enum import verify, UNIQUE, CONTINUOUS, NAMED_FLAGS, ReprEnum 18from enum import member, nonmember, _iter_bits_lsb 19from io import StringIO 20from pickle import dumps, loads, PicklingError, HIGHEST_PROTOCOL 21from test import support 22from test.support import ALWAYS_EQ, REPO_ROOT 23from test.support import threading_helper 24from datetime import timedelta 25 26python_version = sys.version_info[:2] 27 28def load_tests(loader, tests, ignore): 29 tests.addTests(doctest.DocTestSuite(enum)) 30 31 lib_tests = os.path.join(REPO_ROOT, 'Doc/library/enum.rst') 32 if os.path.exists(lib_tests): 33 tests.addTests(doctest.DocFileSuite( 34 lib_tests, 35 module_relative=False, 36 optionflags=doctest.ELLIPSIS|doctest.NORMALIZE_WHITESPACE, 37 )) 38 howto_tests = os.path.join(REPO_ROOT, 'Doc/howto/enum.rst') 39 if os.path.exists(howto_tests): 40 tests.addTests(doctest.DocFileSuite( 41 howto_tests, 42 module_relative=False, 43 optionflags=doctest.ELLIPSIS|doctest.NORMALIZE_WHITESPACE, 44 )) 45 return tests 46 47def reraise_if_not_enum(*enum_types_or_exceptions): 48 from functools import wraps 49 50 def decorator(func): 51 @wraps(func) 52 def inner(*args, **kwargs): 53 excs = [ 54 e 55 for e in enum_types_or_exceptions 56 if isinstance(e, Exception) 57 ] 58 if len(excs) == 1: 59 raise excs[0] 60 elif excs: 61 raise ExceptionGroup('Enum Exceptions', excs) 62 return func(*args, **kwargs) 63 return inner 64 return decorator 65 66MODULE = __name__ 67SHORT_MODULE = MODULE.split('.')[-1] 68 69# for pickle tests 70try: 71 class Stooges(Enum): 72 LARRY = 1 73 CURLY = 2 74 MOE = 3 75except Exception as exc: 76 Stooges = exc 77 78try: 79 class IntStooges(int, Enum): 80 LARRY = 1 81 CURLY = 2 82 MOE = 3 83except Exception as exc: 84 IntStooges = exc 85 86try: 87 class FloatStooges(float, Enum): 88 LARRY = 1.39 89 CURLY = 2.72 90 MOE = 3.142596 91except Exception as exc: 92 FloatStooges = exc 93 94try: 95 class FlagStooges(Flag): 96 LARRY = 1 97 CURLY = 2 98 MOE = 4 99 BIG = 389 100except Exception as exc: 101 FlagStooges = exc 102 103try: 104 class FlagStoogesWithZero(Flag): 105 NOFLAG = 0 106 LARRY = 1 107 CURLY = 2 108 MOE = 4 109 BIG = 389 110except Exception as exc: 111 FlagStoogesWithZero = exc 112 113try: 114 class IntFlagStooges(IntFlag): 115 LARRY = 1 116 CURLY = 2 117 MOE = 4 118 BIG = 389 119except Exception as exc: 120 IntFlagStooges = exc 121 122try: 123 class IntFlagStoogesWithZero(IntFlag): 124 NOFLAG = 0 125 LARRY = 1 126 CURLY = 2 127 MOE = 4 128 BIG = 389 129except Exception as exc: 130 IntFlagStoogesWithZero = exc 131 132# for pickle test and subclass tests 133try: 134 class Name(StrEnum): 135 BDFL = 'Guido van Rossum' 136 FLUFL = 'Barry Warsaw' 137except Exception as exc: 138 Name = exc 139 140try: 141 Question = Enum('Question', 'who what when where why', module=__name__) 142except Exception as exc: 143 Question = exc 144 145try: 146 Answer = Enum('Answer', 'him this then there because') 147except Exception as exc: 148 Answer = exc 149 150try: 151 Theory = Enum('Theory', 'rule law supposition', qualname='spanish_inquisition') 152except Exception as exc: 153 Theory = exc 154 155# for doctests 156try: 157 class Fruit(Enum): 158 TOMATO = 1 159 BANANA = 2 160 CHERRY = 3 161except Exception: 162 pass 163 164def test_pickle_dump_load(assertion, source, target=None): 165 if target is None: 166 target = source 167 for protocol in range(HIGHEST_PROTOCOL + 1): 168 assertion(loads(dumps(source, protocol=protocol)), target) 169 170def test_pickle_exception(assertion, exception, obj): 171 for protocol in range(HIGHEST_PROTOCOL + 1): 172 with assertion(exception): 173 dumps(obj, protocol=protocol) 174 175class TestHelpers(unittest.TestCase): 176 # _is_descriptor, _is_sunder, _is_dunder 177 178 sunder_names = '_bad_', '_good_', '_what_ho_' 179 dunder_names = '__mal__', '__bien__', '__que_que__' 180 private_names = '_MyEnum__private', '_MyEnum__still_private', '_MyEnum___triple_private' 181 private_and_sunder_names = '_MyEnum__private_', '_MyEnum__also_private_' 182 random_names = 'okay', '_semi_private', '_weird__', '_MyEnum__' 183 184 def test_is_descriptor(self): 185 class foo: 186 pass 187 for attr in ('__get__','__set__','__delete__'): 188 obj = foo() 189 self.assertFalse(enum._is_descriptor(obj)) 190 setattr(obj, attr, 1) 191 self.assertTrue(enum._is_descriptor(obj)) 192 193 def test_sunder(self): 194 for name in self.sunder_names + self.private_and_sunder_names: 195 self.assertTrue(enum._is_sunder(name), '%r is a not sunder name?' % name) 196 for name in self.dunder_names + self.private_names + self.random_names: 197 self.assertFalse(enum._is_sunder(name), '%r is a sunder name?' % name) 198 for s in ('_a_', '_aa_'): 199 self.assertTrue(enum._is_sunder(s)) 200 for s in ('a', 'a_', '_a', '__a', 'a__', '__a__', '_a__', '__a_', '_', 201 '__', '___', '____', '_____',): 202 self.assertFalse(enum._is_sunder(s)) 203 204 def test_dunder(self): 205 for name in self.dunder_names: 206 self.assertTrue(enum._is_dunder(name), '%r is a not dunder name?' % name) 207 for name in self.sunder_names + self.private_names + self.private_and_sunder_names + self.random_names: 208 self.assertFalse(enum._is_dunder(name), '%r is a dunder name?' % name) 209 for s in ('__a__', '__aa__'): 210 self.assertTrue(enum._is_dunder(s)) 211 for s in ('a', 'a_', '_a', '__a', 'a__', '_a_', '_a__', '__a_', '_', 212 '__', '___', '____', '_____',): 213 self.assertFalse(enum._is_dunder(s)) 214 215 216 def test_is_private(self): 217 for name in self.private_names + self.private_and_sunder_names: 218 self.assertTrue(enum._is_private('MyEnum', name), '%r is a not private name?') 219 for name in self.sunder_names + self.dunder_names + self.random_names: 220 self.assertFalse(enum._is_private('MyEnum', name), '%r is a private name?') 221 222 def test_iter_bits_lsb(self): 223 self.assertEqual(list(_iter_bits_lsb(7)), [1, 2, 4]) 224 self.assertRaisesRegex(ValueError, '-8 is not a positive integer', list, _iter_bits_lsb(-8)) 225 226 227# for subclassing tests 228 229class classproperty: 230 231 def __init__(self, fget=None, fset=None, fdel=None, doc=None): 232 self.fget = fget 233 self.fset = fset 234 self.fdel = fdel 235 if doc is None and fget is not None: 236 doc = fget.__doc__ 237 self.__doc__ = doc 238 239 def __get__(self, instance, ownerclass): 240 return self.fget(ownerclass) 241 242# for global repr tests 243 244try: 245 @enum.global_enum 246 class HeadlightsK(IntFlag, boundary=enum.KEEP): 247 OFF_K = 0 248 LOW_BEAM_K = auto() 249 HIGH_BEAM_K = auto() 250 FOG_K = auto() 251except Exception as exc: 252 HeadlightsK = exc 253 254 255try: 256 @enum.global_enum 257 class HeadlightsC(IntFlag, boundary=enum.CONFORM): 258 OFF_C = 0 259 LOW_BEAM_C = auto() 260 HIGH_BEAM_C = auto() 261 FOG_C = auto() 262except Exception as exc: 263 HeadlightsC = exc 264 265 266try: 267 @enum.global_enum 268 class NoName(Flag): 269 ONE = 1 270 TWO = 2 271except Exception as exc: 272 NoName = exc 273 274 275# tests 276 277class _EnumTests: 278 """ 279 Test for behavior that is the same across the different types of enumerations. 280 """ 281 282 values = None 283 284 def setUp(self): 285 if self.__class__.__name__[-5:] == 'Class': 286 class BaseEnum(self.enum_type): 287 @enum.property 288 def first(self): 289 return '%s is first!' % self.name 290 class MainEnum(BaseEnum): 291 first = auto() 292 second = auto() 293 third = auto() 294 if issubclass(self.enum_type, Flag): 295 dupe = 3 296 else: 297 dupe = third 298 self.MainEnum = MainEnum 299 # 300 class NewStrEnum(self.enum_type): 301 def __str__(self): 302 return self.name.upper() 303 first = auto() 304 self.NewStrEnum = NewStrEnum 305 # 306 class NewFormatEnum(self.enum_type): 307 def __format__(self, spec): 308 return self.name.upper() 309 first = auto() 310 self.NewFormatEnum = NewFormatEnum 311 # 312 class NewStrFormatEnum(self.enum_type): 313 def __str__(self): 314 return self.name.title() 315 def __format__(self, spec): 316 return ''.join(reversed(self.name)) 317 first = auto() 318 self.NewStrFormatEnum = NewStrFormatEnum 319 # 320 class NewBaseEnum(self.enum_type): 321 def __str__(self): 322 return self.name.title() 323 def __format__(self, spec): 324 return ''.join(reversed(self.name)) 325 self.NewBaseEnum = NewBaseEnum 326 class NewSubEnum(NewBaseEnum): 327 first = auto() 328 self.NewSubEnum = NewSubEnum 329 # 330 class LazyGNV(self.enum_type): 331 def _generate_next_value_(name, start, last, values): 332 pass 333 self.LazyGNV = LazyGNV 334 # 335 class BusyGNV(self.enum_type): 336 @staticmethod 337 def _generate_next_value_(name, start, last, values): 338 pass 339 self.BusyGNV = BusyGNV 340 # 341 self.is_flag = False 342 self.names = ['first', 'second', 'third'] 343 if issubclass(MainEnum, StrEnum): 344 self.values = self.names 345 elif MainEnum._member_type_ is str: 346 self.values = ['1', '2', '3'] 347 elif issubclass(self.enum_type, Flag): 348 self.values = [1, 2, 4] 349 self.is_flag = True 350 self.dupe2 = MainEnum(5) 351 else: 352 self.values = self.values or [1, 2, 3] 353 # 354 if not getattr(self, 'source_values', False): 355 self.source_values = self.values 356 elif self.__class__.__name__[-8:] == 'Function': 357 @enum.property 358 def first(self): 359 return '%s is first!' % self.name 360 BaseEnum = self.enum_type('BaseEnum', {'first':first}) 361 # 362 first = auto() 363 second = auto() 364 third = auto() 365 if issubclass(self.enum_type, Flag): 366 dupe = 3 367 else: 368 dupe = third 369 self.MainEnum = MainEnum = BaseEnum('MainEnum', dict(first=first, second=second, third=third, dupe=dupe)) 370 # 371 def __str__(self): 372 return self.name.upper() 373 first = auto() 374 self.NewStrEnum = self.enum_type('NewStrEnum', (('first',first),('__str__',__str__))) 375 # 376 def __format__(self, spec): 377 return self.name.upper() 378 first = auto() 379 self.NewFormatEnum = self.enum_type('NewFormatEnum', [('first',first),('__format__',__format__)]) 380 # 381 def __str__(self): 382 return self.name.title() 383 def __format__(self, spec): 384 return ''.join(reversed(self.name)) 385 first = auto() 386 self.NewStrFormatEnum = self.enum_type('NewStrFormatEnum', dict(first=first, __format__=__format__, __str__=__str__)) 387 # 388 def __str__(self): 389 return self.name.title() 390 def __format__(self, spec): 391 return ''.join(reversed(self.name)) 392 self.NewBaseEnum = self.enum_type('NewBaseEnum', dict(__format__=__format__, __str__=__str__)) 393 self.NewSubEnum = self.NewBaseEnum('NewSubEnum', 'first') 394 # 395 def _generate_next_value_(name, start, last, values): 396 pass 397 self.LazyGNV = self.enum_type('LazyGNV', {'_generate_next_value_':_generate_next_value_}) 398 # 399 @staticmethod 400 def _generate_next_value_(name, start, last, values): 401 pass 402 self.BusyGNV = self.enum_type('BusyGNV', {'_generate_next_value_':_generate_next_value_}) 403 # 404 self.is_flag = False 405 self.names = ['first', 'second', 'third'] 406 if issubclass(MainEnum, StrEnum): 407 self.values = self.names 408 elif MainEnum._member_type_ is str: 409 self.values = ['1', '2', '3'] 410 elif issubclass(self.enum_type, Flag): 411 self.values = [1, 2, 4] 412 self.is_flag = True 413 self.dupe2 = MainEnum(5) 414 else: 415 self.values = self.values or [1, 2, 3] 416 # 417 if not getattr(self, 'source_values', False): 418 self.source_values = self.values 419 else: 420 raise ValueError('unknown enum style: %r' % self.__class__.__name__) 421 422 def assertFormatIsValue(self, spec, member): 423 self.assertEqual(spec.format(member), spec.format(member.value)) 424 425 def assertFormatIsStr(self, spec, member): 426 self.assertEqual(spec.format(member), spec.format(str(member))) 427 428 def test_attribute_deletion(self): 429 class Season(self.enum_type): 430 SPRING = auto() 431 SUMMER = auto() 432 AUTUMN = auto() 433 # 434 def spam(cls): 435 pass 436 # 437 self.assertTrue(hasattr(Season, 'spam')) 438 del Season.spam 439 self.assertFalse(hasattr(Season, 'spam')) 440 # 441 with self.assertRaises(AttributeError): 442 del Season.SPRING 443 with self.assertRaises(AttributeError): 444 del Season.DRY 445 with self.assertRaises(AttributeError): 446 del Season.SPRING.name 447 448 def test_bad_new_super(self): 449 with self.assertRaisesRegex( 450 TypeError, 451 'do not use .super...__new__;', 452 ): 453 class BadSuper(self.enum_type): 454 def __new__(cls, value): 455 obj = super().__new__(cls, value) 456 return obj 457 failed = 1 458 459 def test_basics(self): 460 TE = self.MainEnum 461 if self.is_flag: 462 self.assertEqual(repr(TE), "<flag 'MainEnum'>") 463 self.assertEqual(str(TE), "<flag 'MainEnum'>") 464 self.assertEqual(format(TE), "<flag 'MainEnum'>") 465 self.assertTrue(TE(5) is self.dupe2) 466 else: 467 self.assertEqual(repr(TE), "<enum 'MainEnum'>") 468 self.assertEqual(str(TE), "<enum 'MainEnum'>") 469 self.assertEqual(format(TE), "<enum 'MainEnum'>") 470 self.assertEqual(list(TE), [TE.first, TE.second, TE.third]) 471 self.assertEqual( 472 [m.name for m in TE], 473 self.names, 474 ) 475 self.assertEqual( 476 [m.value for m in TE], 477 self.values, 478 ) 479 self.assertEqual( 480 [m.first for m in TE], 481 ['first is first!', 'second is first!', 'third is first!'] 482 ) 483 for member, name in zip(TE, self.names, strict=True): 484 self.assertIs(TE[name], member) 485 for member, value in zip(TE, self.values, strict=True): 486 self.assertIs(TE(value), member) 487 if issubclass(TE, StrEnum): 488 self.assertTrue(TE.dupe is TE('third') is TE['dupe']) 489 elif TE._member_type_ is str: 490 self.assertTrue(TE.dupe is TE('3') is TE['dupe']) 491 elif issubclass(TE, Flag): 492 self.assertTrue(TE.dupe is TE(3) is TE['dupe']) 493 else: 494 self.assertTrue(TE.dupe is TE(self.values[2]) is TE['dupe']) 495 496 def test_bool_is_true(self): 497 class Empty(self.enum_type): 498 pass 499 self.assertTrue(Empty) 500 # 501 self.assertTrue(self.MainEnum) 502 for member in self.MainEnum: 503 self.assertTrue(member) 504 505 def test_changing_member_fails(self): 506 MainEnum = self.MainEnum 507 with self.assertRaises(AttributeError): 508 self.MainEnum.second = 'really first' 509 510 def test_contains_tf(self): 511 MainEnum = self.MainEnum 512 self.assertIn(MainEnum.first, MainEnum) 513 self.assertTrue(self.values[0] in MainEnum) 514 if type(self) not in (TestStrEnumClass, TestStrEnumFunction): 515 self.assertFalse('first' in MainEnum) 516 val = MainEnum.dupe 517 self.assertIn(val, MainEnum) 518 self.assertNotIn(float('nan'), MainEnum) 519 # 520 class OtherEnum(Enum): 521 one = auto() 522 two = auto() 523 self.assertNotIn(OtherEnum.two, MainEnum) 524 # 525 if MainEnum._member_type_ is object: 526 # enums without mixed data types will always be False 527 class NotEqualEnum(self.enum_type): 528 this = self.source_values[0] 529 that = self.source_values[1] 530 self.assertNotIn(NotEqualEnum.this, MainEnum) 531 self.assertNotIn(NotEqualEnum.that, MainEnum) 532 else: 533 # enums with mixed data types may be True 534 class EqualEnum(self.enum_type): 535 this = self.source_values[0] 536 that = self.source_values[1] 537 self.assertIn(EqualEnum.this, MainEnum) 538 self.assertIn(EqualEnum.that, MainEnum) 539 540 def test_contains_same_name_diff_enum_diff_values(self): 541 MainEnum = self.MainEnum 542 # 543 class OtherEnum(Enum): 544 first = "brand" 545 second = "new" 546 third = "values" 547 # 548 self.assertIn(MainEnum.first, MainEnum) 549 self.assertIn(MainEnum.second, MainEnum) 550 self.assertIn(MainEnum.third, MainEnum) 551 self.assertNotIn(MainEnum.first, OtherEnum) 552 self.assertNotIn(MainEnum.second, OtherEnum) 553 self.assertNotIn(MainEnum.third, OtherEnum) 554 # 555 self.assertIn(OtherEnum.first, OtherEnum) 556 self.assertIn(OtherEnum.second, OtherEnum) 557 self.assertIn(OtherEnum.third, OtherEnum) 558 self.assertNotIn(OtherEnum.first, MainEnum) 559 self.assertNotIn(OtherEnum.second, MainEnum) 560 self.assertNotIn(OtherEnum.third, MainEnum) 561 562 def test_dir_on_class(self): 563 TE = self.MainEnum 564 self.assertEqual(set(dir(TE)), set(enum_dir(TE))) 565 566 def test_dir_on_item(self): 567 TE = self.MainEnum 568 self.assertEqual(set(dir(TE.first)), set(member_dir(TE.first))) 569 570 def test_dir_with_added_behavior(self): 571 class Test(self.enum_type): 572 this = auto() 573 these = auto() 574 def wowser(self): 575 return ("Wowser! I'm %s!" % self.name) 576 self.assertTrue('wowser' not in dir(Test)) 577 self.assertTrue('wowser' in dir(Test.this)) 578 579 def test_dir_on_sub_with_behavior_on_super(self): 580 # see issue22506 581 class SuperEnum(self.enum_type): 582 def invisible(self): 583 return "did you see me?" 584 class SubEnum(SuperEnum): 585 sample = auto() 586 self.assertTrue('invisible' not in dir(SubEnum)) 587 self.assertTrue('invisible' in dir(SubEnum.sample)) 588 589 def test_dir_on_sub_with_behavior_including_instance_dict_on_super(self): 590 # see issue40084 591 class SuperEnum(self.enum_type): 592 def __new__(cls, *value, **kwds): 593 new = self.enum_type._member_type_.__new__ 594 if self.enum_type._member_type_ is object: 595 obj = new(cls) 596 else: 597 if isinstance(value[0], tuple): 598 create_value ,= value[0] 599 else: 600 create_value = value 601 obj = new(cls, *create_value) 602 obj._value_ = value[0] if len(value) == 1 else value 603 obj.description = 'test description' 604 return obj 605 class SubEnum(SuperEnum): 606 sample = self.source_values[1] 607 self.assertTrue('description' not in dir(SubEnum)) 608 self.assertTrue('description' in dir(SubEnum.sample), dir(SubEnum.sample)) 609 610 def test_empty_enum_has_no_values(self): 611 with self.assertRaisesRegex(TypeError, "<.... 'NewBaseEnum'> has no members"): 612 self.NewBaseEnum(7) 613 614 def test_enum_in_enum_out(self): 615 Main = self.MainEnum 616 self.assertIs(Main(Main.first), Main.first) 617 618 def test_gnv_is_static(self): 619 lazy = self.LazyGNV 620 busy = self.BusyGNV 621 self.assertTrue(type(lazy.__dict__['_generate_next_value_']) is staticmethod) 622 self.assertTrue(type(busy.__dict__['_generate_next_value_']) is staticmethod) 623 624 def test_hash(self): 625 MainEnum = self.MainEnum 626 mapping = {} 627 mapping[MainEnum.first] = '1225' 628 mapping[MainEnum.second] = '0315' 629 mapping[MainEnum.third] = '0704' 630 self.assertEqual(mapping[MainEnum.second], '0315') 631 632 def test_invalid_names(self): 633 with self.assertRaises(ValueError): 634 class Wrong(self.enum_type): 635 mro = 9 636 with self.assertRaises(ValueError): 637 class Wrong(self.enum_type): 638 _create_= 11 639 with self.assertRaises(ValueError): 640 class Wrong(self.enum_type): 641 _get_mixins_ = 9 642 with self.assertRaises(ValueError): 643 class Wrong(self.enum_type): 644 _find_new_ = 1 645 with self.assertRaises(ValueError): 646 class Wrong(self.enum_type): 647 _any_name_ = 9 648 649 def test_object_str_override(self): 650 "check that setting __str__ to object's is not reset to Enum's" 651 class Generic(self.enum_type): 652 item = self.source_values[2] 653 def __repr__(self): 654 return "%s.test" % (self._name_, ) 655 __str__ = object.__str__ 656 self.assertEqual(str(Generic.item), 'item.test') 657 658 def test_overridden_str(self): 659 NS = self.NewStrEnum 660 self.assertEqual(str(NS.first), NS.first.name.upper()) 661 self.assertEqual(format(NS.first), NS.first.name.upper()) 662 663 def test_overridden_str_format(self): 664 NSF = self.NewStrFormatEnum 665 self.assertEqual(str(NSF.first), NSF.first.name.title()) 666 self.assertEqual(format(NSF.first), ''.join(reversed(NSF.first.name))) 667 668 def test_overridden_str_format_inherited(self): 669 NSE = self.NewSubEnum 670 self.assertEqual(str(NSE.first), NSE.first.name.title()) 671 self.assertEqual(format(NSE.first), ''.join(reversed(NSE.first.name))) 672 673 def test_programmatic_function_string(self): 674 MinorEnum = self.enum_type('MinorEnum', 'june july august') 675 lst = list(MinorEnum) 676 self.assertEqual(len(lst), len(MinorEnum)) 677 self.assertEqual(len(MinorEnum), 3, MinorEnum) 678 self.assertEqual( 679 [MinorEnum.june, MinorEnum.july, MinorEnum.august], 680 lst, 681 ) 682 values = self.values 683 if self.enum_type is StrEnum: 684 values = ['june','july','august'] 685 for month, av in zip('june july august'.split(), values): 686 e = MinorEnum[month] 687 self.assertEqual(e.value, av, list(MinorEnum)) 688 self.assertEqual(e.name, month) 689 if MinorEnum._member_type_ is not object and issubclass(MinorEnum, MinorEnum._member_type_): 690 self.assertEqual(e, av) 691 else: 692 self.assertNotEqual(e, av) 693 self.assertIn(e, MinorEnum) 694 self.assertIs(type(e), MinorEnum) 695 self.assertIs(e, MinorEnum(av)) 696 697 def test_programmatic_function_string_list(self): 698 MinorEnum = self.enum_type('MinorEnum', ['june', 'july', 'august']) 699 lst = list(MinorEnum) 700 self.assertEqual(len(lst), len(MinorEnum)) 701 self.assertEqual(len(MinorEnum), 3, MinorEnum) 702 self.assertEqual( 703 [MinorEnum.june, MinorEnum.july, MinorEnum.august], 704 lst, 705 ) 706 values = self.values 707 if self.enum_type is StrEnum: 708 values = ['june','july','august'] 709 for month, av in zip('june july august'.split(), values): 710 e = MinorEnum[month] 711 self.assertEqual(e.value, av) 712 self.assertEqual(e.name, month) 713 if MinorEnum._member_type_ is not object and issubclass(MinorEnum, MinorEnum._member_type_): 714 self.assertEqual(e, av) 715 else: 716 self.assertNotEqual(e, av) 717 self.assertIn(e, MinorEnum) 718 self.assertIs(type(e), MinorEnum) 719 self.assertIs(e, MinorEnum(av)) 720 721 def test_programmatic_function_iterable(self): 722 MinorEnum = self.enum_type( 723 'MinorEnum', 724 (('june', self.source_values[0]), ('july', self.source_values[1]), ('august', self.source_values[2])) 725 ) 726 lst = list(MinorEnum) 727 self.assertEqual(len(lst), len(MinorEnum)) 728 self.assertEqual(len(MinorEnum), 3, MinorEnum) 729 self.assertEqual( 730 [MinorEnum.june, MinorEnum.july, MinorEnum.august], 731 lst, 732 ) 733 for month, av in zip('june july august'.split(), self.values): 734 e = MinorEnum[month] 735 self.assertEqual(e.value, av) 736 self.assertEqual(e.name, month) 737 if MinorEnum._member_type_ is not object and issubclass(MinorEnum, MinorEnum._member_type_): 738 self.assertEqual(e, av) 739 else: 740 self.assertNotEqual(e, av) 741 self.assertIn(e, MinorEnum) 742 self.assertIs(type(e), MinorEnum) 743 self.assertIs(e, MinorEnum(av)) 744 745 def test_programmatic_function_from_dict(self): 746 MinorEnum = self.enum_type( 747 'MinorEnum', 748 OrderedDict((('june', self.source_values[0]), ('july', self.source_values[1]), ('august', self.source_values[2]))) 749 ) 750 lst = list(MinorEnum) 751 self.assertEqual(len(lst), len(MinorEnum)) 752 self.assertEqual(len(MinorEnum), 3, MinorEnum) 753 self.assertEqual( 754 [MinorEnum.june, MinorEnum.july, MinorEnum.august], 755 lst, 756 ) 757 for month, av in zip('june july august'.split(), self.values): 758 e = MinorEnum[month] 759 if MinorEnum._member_type_ is not object and issubclass(MinorEnum, MinorEnum._member_type_): 760 self.assertEqual(e, av) 761 else: 762 self.assertNotEqual(e, av) 763 self.assertIn(e, MinorEnum) 764 self.assertIs(type(e), MinorEnum) 765 self.assertIs(e, MinorEnum(av)) 766 767 def test_repr(self): 768 TE = self.MainEnum 769 if self.is_flag: 770 self.assertEqual(repr(TE(0)), "<MainEnum: 0>") 771 self.assertEqual(repr(TE.dupe), "<MainEnum.dupe: 3>") 772 self.assertEqual(repr(self.dupe2), "<MainEnum.first|third: 5>") 773 elif issubclass(TE, StrEnum): 774 self.assertEqual(repr(TE.dupe), "<MainEnum.third: 'third'>") 775 else: 776 self.assertEqual(repr(TE.dupe), "<MainEnum.third: %r>" % (self.values[2], ), TE._value_repr_) 777 for name, value, member in zip(self.names, self.values, TE, strict=True): 778 self.assertEqual(repr(member), "<MainEnum.%s: %r>" % (member.name, member.value)) 779 780 def test_repr_override(self): 781 class Generic(self.enum_type): 782 first = auto() 783 second = auto() 784 third = auto() 785 def __repr__(self): 786 return "don't you just love shades of %s?" % self.name 787 self.assertEqual( 788 repr(Generic.third), 789 "don't you just love shades of third?", 790 ) 791 792 def test_inherited_repr(self): 793 class MyEnum(self.enum_type): 794 def __repr__(self): 795 return "My name is %s." % self.name 796 class MySubEnum(MyEnum): 797 this = auto() 798 that = auto() 799 theother = auto() 800 self.assertEqual(repr(MySubEnum.that), "My name is that.") 801 802 def test_multiple_superclasses_repr(self): 803 class _EnumSuperClass(metaclass=EnumMeta): 804 pass 805 class E(_EnumSuperClass, Enum): 806 A = 1 807 self.assertEqual(repr(E.A), "<E.A: 1>") 808 809 def test_reversed_iteration_order(self): 810 self.assertEqual( 811 list(reversed(self.MainEnum)), 812 [self.MainEnum.third, self.MainEnum.second, self.MainEnum.first], 813 ) 814 815class _PlainOutputTests: 816 817 def test_str(self): 818 TE = self.MainEnum 819 if self.is_flag: 820 self.assertEqual(str(TE(0)), "MainEnum(0)") 821 self.assertEqual(str(TE.dupe), "MainEnum.dupe") 822 self.assertEqual(str(self.dupe2), "MainEnum.first|third") 823 else: 824 self.assertEqual(str(TE.dupe), "MainEnum.third") 825 for name, value, member in zip(self.names, self.values, TE, strict=True): 826 self.assertEqual(str(member), "MainEnum.%s" % (member.name, )) 827 828 def test_format(self): 829 TE = self.MainEnum 830 if self.is_flag: 831 self.assertEqual(format(TE.dupe), "MainEnum.dupe") 832 self.assertEqual(format(self.dupe2), "MainEnum.first|third") 833 else: 834 self.assertEqual(format(TE.dupe), "MainEnum.third") 835 for name, value, member in zip(self.names, self.values, TE, strict=True): 836 self.assertEqual(format(member), "MainEnum.%s" % (member.name, )) 837 838 def test_overridden_format(self): 839 NF = self.NewFormatEnum 840 self.assertEqual(str(NF.first), "NewFormatEnum.first", '%s %r' % (NF.__str__, NF.first)) 841 self.assertEqual(format(NF.first), "FIRST") 842 843 def test_format_specs(self): 844 TE = self.MainEnum 845 self.assertFormatIsStr('{}', TE.second) 846 self.assertFormatIsStr('{:}', TE.second) 847 self.assertFormatIsStr('{:20}', TE.second) 848 self.assertFormatIsStr('{:^20}', TE.second) 849 self.assertFormatIsStr('{:>20}', TE.second) 850 self.assertFormatIsStr('{:<20}', TE.second) 851 self.assertFormatIsStr('{:5.2}', TE.second) 852 853 854class _MixedOutputTests: 855 856 def test_str(self): 857 TE = self.MainEnum 858 if self.is_flag: 859 self.assertEqual(str(TE.dupe), "MainEnum.dupe") 860 self.assertEqual(str(self.dupe2), "MainEnum.first|third") 861 else: 862 self.assertEqual(str(TE.dupe), "MainEnum.third") 863 for name, value, member in zip(self.names, self.values, TE, strict=True): 864 self.assertEqual(str(member), "MainEnum.%s" % (member.name, )) 865 866 def test_format(self): 867 TE = self.MainEnum 868 if self.is_flag: 869 self.assertEqual(format(TE.dupe), "MainEnum.dupe") 870 self.assertEqual(format(self.dupe2), "MainEnum.first|third") 871 else: 872 self.assertEqual(format(TE.dupe), "MainEnum.third") 873 for name, value, member in zip(self.names, self.values, TE, strict=True): 874 self.assertEqual(format(member), "MainEnum.%s" % (member.name, )) 875 876 def test_overridden_format(self): 877 NF = self.NewFormatEnum 878 self.assertEqual(str(NF.first), "NewFormatEnum.first") 879 self.assertEqual(format(NF.first), "FIRST") 880 881 def test_format_specs(self): 882 TE = self.MainEnum 883 self.assertFormatIsStr('{}', TE.first) 884 self.assertFormatIsStr('{:}', TE.first) 885 self.assertFormatIsStr('{:20}', TE.first) 886 self.assertFormatIsStr('{:^20}', TE.first) 887 self.assertFormatIsStr('{:>20}', TE.first) 888 self.assertFormatIsStr('{:<20}', TE.first) 889 self.assertFormatIsStr('{:5.2}', TE.first) 890 891 892class _MinimalOutputTests: 893 894 def test_str(self): 895 TE = self.MainEnum 896 if self.is_flag: 897 self.assertEqual(str(TE.dupe), "3") 898 self.assertEqual(str(self.dupe2), "5") 899 else: 900 self.assertEqual(str(TE.dupe), str(self.values[2])) 901 for name, value, member in zip(self.names, self.values, TE, strict=True): 902 self.assertEqual(str(member), str(value)) 903 904 def test_format(self): 905 TE = self.MainEnum 906 if self.is_flag: 907 self.assertEqual(format(TE.dupe), "3") 908 self.assertEqual(format(self.dupe2), "5") 909 else: 910 self.assertEqual(format(TE.dupe), format(self.values[2])) 911 for name, value, member in zip(self.names, self.values, TE, strict=True): 912 self.assertEqual(format(member), format(value)) 913 914 def test_overridden_format(self): 915 NF = self.NewFormatEnum 916 self.assertEqual(str(NF.first), str(self.values[0])) 917 self.assertEqual(format(NF.first), "FIRST") 918 919 def test_format_specs(self): 920 TE = self.MainEnum 921 self.assertFormatIsValue('{}', TE.third) 922 self.assertFormatIsValue('{:}', TE.third) 923 self.assertFormatIsValue('{:20}', TE.third) 924 self.assertFormatIsValue('{:^20}', TE.third) 925 self.assertFormatIsValue('{:>20}', TE.third) 926 self.assertFormatIsValue('{:<20}', TE.third) 927 if TE._member_type_ is float: 928 self.assertFormatIsValue('{:n}', TE.third) 929 self.assertFormatIsValue('{:5.2}', TE.third) 930 self.assertFormatIsValue('{:f}', TE.third) 931 932 def test_copy(self): 933 TE = self.MainEnum 934 copied = copy.copy(TE) 935 self.assertEqual(copied, TE) 936 self.assertIs(copied, TE) 937 deep = copy.deepcopy(TE) 938 self.assertEqual(deep, TE) 939 self.assertIs(deep, TE) 940 941 def test_copy_member(self): 942 TE = self.MainEnum 943 copied = copy.copy(TE.first) 944 self.assertIs(copied, TE.first) 945 deep = copy.deepcopy(TE.first) 946 self.assertIs(deep, TE.first) 947 948class _FlagTests: 949 950 def test_default_missing_with_wrong_type_value(self): 951 with self.assertRaisesRegex( 952 ValueError, 953 "'RED' is not a valid ", 954 ) as ctx: 955 self.MainEnum('RED') 956 self.assertIs(ctx.exception.__context__, None) 957 958 def test_closed_invert_expectations(self): 959 class ClosedAB(self.enum_type): 960 A = 1 961 B = 2 962 MASK = 3 963 A, B = ClosedAB 964 AB_MASK = ClosedAB.MASK 965 # 966 self.assertIs(~A, B) 967 self.assertIs(~B, A) 968 self.assertIs(~(A|B), ClosedAB(0)) 969 self.assertIs(~AB_MASK, ClosedAB(0)) 970 self.assertIs(~ClosedAB(0), (A|B)) 971 # 972 class ClosedXYZ(self.enum_type): 973 X = 4 974 Y = 2 975 Z = 1 976 MASK = 7 977 X, Y, Z = ClosedXYZ 978 XYZ_MASK = ClosedXYZ.MASK 979 # 980 self.assertIs(~X, Y|Z) 981 self.assertIs(~Y, X|Z) 982 self.assertIs(~Z, X|Y) 983 self.assertIs(~(X|Y), Z) 984 self.assertIs(~(X|Z), Y) 985 self.assertIs(~(Y|Z), X) 986 self.assertIs(~(X|Y|Z), ClosedXYZ(0)) 987 self.assertIs(~XYZ_MASK, ClosedXYZ(0)) 988 self.assertIs(~ClosedXYZ(0), (X|Y|Z)) 989 990 def test_open_invert_expectations(self): 991 class OpenAB(self.enum_type): 992 A = 1 993 B = 2 994 MASK = 255 995 A, B = OpenAB 996 AB_MASK = OpenAB.MASK 997 # 998 if OpenAB._boundary_ in (EJECT, KEEP): 999 self.assertIs(~A, OpenAB(254)) 1000 self.assertIs(~B, OpenAB(253)) 1001 self.assertIs(~(A|B), OpenAB(252)) 1002 self.assertIs(~AB_MASK, OpenAB(0)) 1003 self.assertIs(~OpenAB(0), AB_MASK) 1004 else: 1005 self.assertIs(~A, B) 1006 self.assertIs(~B, A) 1007 self.assertIs(~(A|B), OpenAB(0)) 1008 self.assertIs(~AB_MASK, OpenAB(0)) 1009 self.assertIs(~OpenAB(0), (A|B)) 1010 # 1011 class OpenXYZ(self.enum_type): 1012 X = 4 1013 Y = 2 1014 Z = 1 1015 MASK = 31 1016 X, Y, Z = OpenXYZ 1017 XYZ_MASK = OpenXYZ.MASK 1018 # 1019 if OpenXYZ._boundary_ in (EJECT, KEEP): 1020 self.assertIs(~X, OpenXYZ(27)) 1021 self.assertIs(~Y, OpenXYZ(29)) 1022 self.assertIs(~Z, OpenXYZ(30)) 1023 self.assertIs(~(X|Y), OpenXYZ(25)) 1024 self.assertIs(~(X|Z), OpenXYZ(26)) 1025 self.assertIs(~(Y|Z), OpenXYZ(28)) 1026 self.assertIs(~(X|Y|Z), OpenXYZ(24)) 1027 self.assertIs(~XYZ_MASK, OpenXYZ(0)) 1028 self.assertTrue(~OpenXYZ(0), XYZ_MASK) 1029 else: 1030 self.assertIs(~X, Y|Z) 1031 self.assertIs(~Y, X|Z) 1032 self.assertIs(~Z, X|Y) 1033 self.assertIs(~(X|Y), Z) 1034 self.assertIs(~(X|Z), Y) 1035 self.assertIs(~(Y|Z), X) 1036 self.assertIs(~(X|Y|Z), OpenXYZ(0)) 1037 self.assertIs(~XYZ_MASK, OpenXYZ(0)) 1038 self.assertTrue(~OpenXYZ(0), (X|Y|Z)) 1039 1040 1041class TestPlainEnumClass(_EnumTests, _PlainOutputTests, unittest.TestCase): 1042 enum_type = Enum 1043 1044 1045class TestPlainEnumFunction(_EnumTests, _PlainOutputTests, unittest.TestCase): 1046 enum_type = Enum 1047 1048 1049class TestPlainFlagClass(_EnumTests, _PlainOutputTests, _FlagTests, unittest.TestCase): 1050 enum_type = Flag 1051 1052 def test_none_member(self): 1053 class FlagWithNoneMember(Flag): 1054 A = 1 1055 E = None 1056 1057 self.assertEqual(FlagWithNoneMember.A.value, 1) 1058 self.assertIs(FlagWithNoneMember.E.value, None) 1059 with self.assertRaisesRegex(TypeError, r"'FlagWithNoneMember.E' cannot be combined with other flags with |"): 1060 FlagWithNoneMember.A | FlagWithNoneMember.E 1061 with self.assertRaisesRegex(TypeError, r"'FlagWithNoneMember.E' cannot be combined with other flags with &"): 1062 FlagWithNoneMember.E & FlagWithNoneMember.A 1063 with self.assertRaisesRegex(TypeError, r"'FlagWithNoneMember.E' cannot be combined with other flags with \^"): 1064 FlagWithNoneMember.A ^ FlagWithNoneMember.E 1065 with self.assertRaisesRegex(TypeError, r"'FlagWithNoneMember.E' cannot be inverted"): 1066 ~FlagWithNoneMember.E 1067 1068 1069class TestPlainFlagFunction(_EnumTests, _PlainOutputTests, _FlagTests, unittest.TestCase): 1070 enum_type = Flag 1071 1072 1073class TestIntEnumClass(_EnumTests, _MinimalOutputTests, unittest.TestCase): 1074 enum_type = IntEnum 1075 # 1076 def test_shadowed_attr(self): 1077 class Number(IntEnum): 1078 divisor = 1 1079 numerator = 2 1080 # 1081 self.assertEqual(Number.divisor.numerator, 1) 1082 self.assertIs(Number.numerator.divisor, Number.divisor) 1083 1084 1085class TestIntEnumFunction(_EnumTests, _MinimalOutputTests, unittest.TestCase): 1086 enum_type = IntEnum 1087 # 1088 def test_shadowed_attr(self): 1089 Number = IntEnum('Number', ('divisor', 'numerator')) 1090 # 1091 self.assertEqual(Number.divisor.numerator, 1) 1092 self.assertIs(Number.numerator.divisor, Number.divisor) 1093 1094 1095class TestStrEnumClass(_EnumTests, _MinimalOutputTests, unittest.TestCase): 1096 enum_type = StrEnum 1097 # 1098 def test_shadowed_attr(self): 1099 class Book(StrEnum): 1100 author = 'author' 1101 title = 'title' 1102 # 1103 self.assertEqual(Book.author.title(), 'Author') 1104 self.assertEqual(Book.title.title(), 'Title') 1105 self.assertIs(Book.title.author, Book.author) 1106 1107 1108class TestStrEnumFunction(_EnumTests, _MinimalOutputTests, unittest.TestCase): 1109 enum_type = StrEnum 1110 # 1111 def test_shadowed_attr(self): 1112 Book = StrEnum('Book', ('author', 'title')) 1113 # 1114 self.assertEqual(Book.author.title(), 'Author') 1115 self.assertEqual(Book.title.title(), 'Title') 1116 self.assertIs(Book.title.author, Book.author) 1117 1118 1119class TestIntFlagClass(_EnumTests, _MinimalOutputTests, _FlagTests, unittest.TestCase): 1120 enum_type = IntFlag 1121 1122 1123class TestIntFlagFunction(_EnumTests, _MinimalOutputTests, _FlagTests, unittest.TestCase): 1124 enum_type = IntFlag 1125 1126 1127class TestMixedIntClass(_EnumTests, _MixedOutputTests, unittest.TestCase): 1128 class enum_type(int, Enum): pass 1129 1130 1131class TestMixedIntFunction(_EnumTests, _MixedOutputTests, unittest.TestCase): 1132 enum_type = Enum('enum_type', type=int) 1133 1134 1135class TestMixedStrClass(_EnumTests, _MixedOutputTests, unittest.TestCase): 1136 class enum_type(str, Enum): pass 1137 1138 1139class TestMixedStrFunction(_EnumTests, _MixedOutputTests, unittest.TestCase): 1140 enum_type = Enum('enum_type', type=str) 1141 1142 1143class TestMixedIntFlagClass(_EnumTests, _MixedOutputTests, _FlagTests, unittest.TestCase): 1144 class enum_type(int, Flag): pass 1145 1146 1147class TestMixedIntFlagFunction(_EnumTests, _MixedOutputTests, _FlagTests, unittest.TestCase): 1148 enum_type = Flag('enum_type', type=int) 1149 1150 1151class TestMixedDateClass(_EnumTests, _MixedOutputTests, unittest.TestCase): 1152 # 1153 values = [date(2021, 12, 25), date(2020, 3, 15), date(2019, 11, 27)] 1154 source_values = [(2021, 12, 25), (2020, 3, 15), (2019, 11, 27)] 1155 # 1156 class enum_type(date, Enum): 1157 @staticmethod 1158 def _generate_next_value_(name, start, count, last_values): 1159 values = [(2021, 12, 25), (2020, 3, 15), (2019, 11, 27)] 1160 return values[count] 1161 1162 1163class TestMixedDateFunction(_EnumTests, _MixedOutputTests, unittest.TestCase): 1164 # 1165 values = [date(2021, 12, 25), date(2020, 3, 15), date(2019, 11, 27)] 1166 source_values = [(2021, 12, 25), (2020, 3, 15), (2019, 11, 27)] 1167 # 1168 # staticmethod decorator will be added by EnumType if not present 1169 def _generate_next_value_(name, start, count, last_values): 1170 values = [(2021, 12, 25), (2020, 3, 15), (2019, 11, 27)] 1171 return values[count] 1172 # 1173 enum_type = Enum('enum_type', {'_generate_next_value_':_generate_next_value_}, type=date) 1174 1175 1176class TestMinimalDateClass(_EnumTests, _MinimalOutputTests, unittest.TestCase): 1177 # 1178 values = [date(2023, 12, 1), date(2016, 2, 29), date(2009, 1, 1)] 1179 source_values = [(2023, 12, 1), (2016, 2, 29), (2009, 1, 1)] 1180 # 1181 class enum_type(date, ReprEnum): 1182 # staticmethod decorator will be added by EnumType if absent 1183 def _generate_next_value_(name, start, count, last_values): 1184 values = [(2023, 12, 1), (2016, 2, 29), (2009, 1, 1)] 1185 return values[count] 1186 1187 1188class TestMinimalDateFunction(_EnumTests, _MinimalOutputTests, unittest.TestCase): 1189 # 1190 values = [date(2023, 12, 1), date(2016, 2, 29), date(2009, 1, 1)] 1191 source_values = [(2023, 12, 1), (2016, 2, 29), (2009, 1, 1)] 1192 # 1193 @staticmethod 1194 def _generate_next_value_(name, start, count, last_values): 1195 values = [(2023, 12, 1), (2016, 2, 29), (2009, 1, 1)] 1196 return values[count] 1197 # 1198 enum_type = ReprEnum('enum_type', {'_generate_next_value_':_generate_next_value_}, type=date) 1199 1200 1201class TestMixedFloatClass(_EnumTests, _MixedOutputTests, unittest.TestCase): 1202 # 1203 values = [1.1, 2.2, 3.3] 1204 # 1205 class enum_type(float, Enum): 1206 def _generate_next_value_(name, start, count, last_values): 1207 values = [1.1, 2.2, 3.3] 1208 return values[count] 1209 1210 1211class TestMixedFloatFunction(_EnumTests, _MixedOutputTests, unittest.TestCase): 1212 # 1213 values = [1.1, 2.2, 3.3] 1214 # 1215 def _generate_next_value_(name, start, count, last_values): 1216 values = [1.1, 2.2, 3.3] 1217 return values[count] 1218 # 1219 enum_type = Enum('enum_type', {'_generate_next_value_':_generate_next_value_}, type=float) 1220 1221 1222class TestMinimalFloatClass(_EnumTests, _MinimalOutputTests, unittest.TestCase): 1223 # 1224 values = [4.4, 5.5, 6.6] 1225 # 1226 class enum_type(float, ReprEnum): 1227 def _generate_next_value_(name, start, count, last_values): 1228 values = [4.4, 5.5, 6.6] 1229 return values[count] 1230 1231 1232class TestMinimalFloatFunction(_EnumTests, _MinimalOutputTests, unittest.TestCase): 1233 # 1234 values = [4.4, 5.5, 6.6] 1235 # 1236 def _generate_next_value_(name, start, count, last_values): 1237 values = [4.4, 5.5, 6.6] 1238 return values[count] 1239 # 1240 enum_type = ReprEnum('enum_type', {'_generate_next_value_':_generate_next_value_}, type=float) 1241 1242 1243class TestSpecial(unittest.TestCase): 1244 """ 1245 various operations that are not attributable to every possible enum 1246 """ 1247 1248 def setUp(self): 1249 class Season(Enum): 1250 SPRING = 1 1251 SUMMER = 2 1252 AUTUMN = 3 1253 WINTER = 4 1254 self.Season = Season 1255 # 1256 class Grades(IntEnum): 1257 A = 5 1258 B = 4 1259 C = 3 1260 D = 2 1261 F = 0 1262 self.Grades = Grades 1263 # 1264 class Directional(str, Enum): 1265 EAST = 'east' 1266 WEST = 'west' 1267 NORTH = 'north' 1268 SOUTH = 'south' 1269 self.Directional = Directional 1270 # 1271 from datetime import date 1272 class Holiday(date, Enum): 1273 NEW_YEAR = 2013, 1, 1 1274 IDES_OF_MARCH = 2013, 3, 15 1275 self.Holiday = Holiday 1276 1277 def test_bool(self): 1278 # plain Enum members are always True 1279 class Logic(Enum): 1280 true = True 1281 false = False 1282 self.assertTrue(Logic.true) 1283 self.assertTrue(Logic.false) 1284 # unless overridden 1285 class RealLogic(Enum): 1286 true = True 1287 false = False 1288 def __bool__(self): 1289 return bool(self._value_) 1290 self.assertTrue(RealLogic.true) 1291 self.assertFalse(RealLogic.false) 1292 # mixed Enums depend on mixed-in type 1293 class IntLogic(int, Enum): 1294 true = 1 1295 false = 0 1296 self.assertTrue(IntLogic.true) 1297 self.assertFalse(IntLogic.false) 1298 1299 def test_comparisons(self): 1300 Season = self.Season 1301 with self.assertRaises(TypeError): 1302 Season.SPRING < Season.WINTER 1303 with self.assertRaises(TypeError): 1304 Season.SPRING > 4 1305 # 1306 self.assertNotEqual(Season.SPRING, 1) 1307 # 1308 class Part(Enum): 1309 SPRING = 1 1310 CLIP = 2 1311 BARREL = 3 1312 # 1313 self.assertNotEqual(Season.SPRING, Part.SPRING) 1314 with self.assertRaises(TypeError): 1315 Season.SPRING < Part.CLIP 1316 1317 @unittest.skip('to-do list') 1318 def test_dir_with_custom_dunders(self): 1319 class PlainEnum(Enum): 1320 pass 1321 cls_dir = dir(PlainEnum) 1322 self.assertNotIn('__repr__', cls_dir) 1323 self.assertNotIn('__str__', cls_dir) 1324 self.assertNotIn('__format__', cls_dir) 1325 self.assertNotIn('__init__', cls_dir) 1326 # 1327 class MyEnum(Enum): 1328 def __repr__(self): 1329 return object.__repr__(self) 1330 def __str__(self): 1331 return object.__repr__(self) 1332 def __format__(self): 1333 return object.__repr__(self) 1334 def __init__(self): 1335 pass 1336 cls_dir = dir(MyEnum) 1337 self.assertIn('__repr__', cls_dir) 1338 self.assertIn('__str__', cls_dir) 1339 self.assertIn('__format__', cls_dir) 1340 self.assertIn('__init__', cls_dir) 1341 1342 def test_duplicate_name_error(self): 1343 with self.assertRaises(TypeError): 1344 class Color(Enum): 1345 red = 1 1346 green = 2 1347 blue = 3 1348 red = 4 1349 # 1350 with self.assertRaises(TypeError): 1351 class Color(Enum): 1352 red = 1 1353 green = 2 1354 blue = 3 1355 def red(self): 1356 return 'red' 1357 # 1358 with self.assertRaises(TypeError): 1359 class Color(Enum): 1360 @enum.property 1361 def red(self): 1362 return 'redder' 1363 red = 1 1364 green = 2 1365 blue = 3 1366 1367 @reraise_if_not_enum(Theory) 1368 def test_enum_function_with_qualname(self): 1369 self.assertEqual(Theory.__qualname__, 'spanish_inquisition') 1370 1371 def test_enum_of_types(self): 1372 """Support using Enum to refer to types deliberately.""" 1373 class MyTypes(Enum): 1374 i = int 1375 f = float 1376 s = str 1377 self.assertEqual(MyTypes.i.value, int) 1378 self.assertEqual(MyTypes.f.value, float) 1379 self.assertEqual(MyTypes.s.value, str) 1380 class Foo: 1381 pass 1382 class Bar: 1383 pass 1384 class MyTypes2(Enum): 1385 a = Foo 1386 b = Bar 1387 self.assertEqual(MyTypes2.a.value, Foo) 1388 self.assertEqual(MyTypes2.b.value, Bar) 1389 class SpamEnumNotInner: 1390 pass 1391 class SpamEnum(Enum): 1392 spam = SpamEnumNotInner 1393 self.assertEqual(SpamEnum.spam.value, SpamEnumNotInner) 1394 1395 def test_enum_of_generic_aliases(self): 1396 class E(Enum): 1397 a = typing.List[int] 1398 b = list[int] 1399 self.assertEqual(E.a.value, typing.List[int]) 1400 self.assertEqual(E.b.value, list[int]) 1401 self.assertEqual(repr(E.a), '<E.a: typing.List[int]>') 1402 self.assertEqual(repr(E.b), '<E.b: list[int]>') 1403 1404 @unittest.skipIf( 1405 python_version >= (3, 13), 1406 'inner classes are not members', 1407 ) 1408 def test_nested_classes_in_enum_are_members(self): 1409 """ 1410 Check for warnings pre-3.13 1411 """ 1412 with self.assertWarnsRegex(DeprecationWarning, 'will not become a member'): 1413 class Outer(Enum): 1414 a = 1 1415 b = 2 1416 class Inner(Enum): 1417 foo = 10 1418 bar = 11 1419 self.assertTrue(isinstance(Outer.Inner, Outer)) 1420 self.assertEqual(Outer.a.value, 1) 1421 self.assertEqual(Outer.Inner.value.foo.value, 10) 1422 self.assertEqual( 1423 list(Outer.Inner.value), 1424 [Outer.Inner.value.foo, Outer.Inner.value.bar], 1425 ) 1426 self.assertEqual( 1427 list(Outer), 1428 [Outer.a, Outer.b, Outer.Inner], 1429 ) 1430 1431 @unittest.skipIf( 1432 python_version < (3, 13), 1433 'inner classes are still members', 1434 ) 1435 def test_nested_classes_in_enum_are_not_members(self): 1436 """Support locally-defined nested classes.""" 1437 class Outer(Enum): 1438 a = 1 1439 b = 2 1440 class Inner(Enum): 1441 foo = 10 1442 bar = 11 1443 self.assertTrue(isinstance(Outer.Inner, type)) 1444 self.assertEqual(Outer.a.value, 1) 1445 self.assertEqual(Outer.Inner.foo.value, 10) 1446 self.assertEqual( 1447 list(Outer.Inner), 1448 [Outer.Inner.foo, Outer.Inner.bar], 1449 ) 1450 self.assertEqual( 1451 list(Outer), 1452 [Outer.a, Outer.b], 1453 ) 1454 1455 def test_nested_classes_in_enum_with_nonmember(self): 1456 class Outer(Enum): 1457 a = 1 1458 b = 2 1459 @nonmember 1460 class Inner(Enum): 1461 foo = 10 1462 bar = 11 1463 self.assertTrue(isinstance(Outer.Inner, type)) 1464 self.assertEqual(Outer.a.value, 1) 1465 self.assertEqual(Outer.Inner.foo.value, 10) 1466 self.assertEqual( 1467 list(Outer.Inner), 1468 [Outer.Inner.foo, Outer.Inner.bar], 1469 ) 1470 self.assertEqual( 1471 list(Outer), 1472 [Outer.a, Outer.b], 1473 ) 1474 1475 def test_enum_of_types_with_nonmember(self): 1476 """Support using Enum to refer to types deliberately.""" 1477 class MyTypes(Enum): 1478 i = int 1479 f = nonmember(float) 1480 s = str 1481 self.assertEqual(MyTypes.i.value, int) 1482 self.assertTrue(MyTypes.f is float) 1483 self.assertEqual(MyTypes.s.value, str) 1484 class Foo: 1485 pass 1486 class Bar: 1487 pass 1488 class MyTypes2(Enum): 1489 a = Foo 1490 b = nonmember(Bar) 1491 self.assertEqual(MyTypes2.a.value, Foo) 1492 self.assertTrue(MyTypes2.b is Bar) 1493 class SpamEnumIsInner: 1494 pass 1495 class SpamEnum(Enum): 1496 spam = nonmember(SpamEnumIsInner) 1497 self.assertTrue(SpamEnum.spam is SpamEnumIsInner) 1498 1499 def test_using_members_as_nonmember(self): 1500 class Example(Flag): 1501 A = 1 1502 B = 2 1503 ALL = nonmember(A | B) 1504 1505 self.assertEqual(Example.A.value, 1) 1506 self.assertEqual(Example.B.value, 2) 1507 self.assertEqual(Example.ALL, 3) 1508 self.assertIs(type(Example.ALL), int) 1509 1510 class Example(Flag): 1511 A = auto() 1512 B = auto() 1513 ALL = nonmember(A | B) 1514 1515 self.assertEqual(Example.A.value, 1) 1516 self.assertEqual(Example.B.value, 2) 1517 self.assertEqual(Example.ALL, 3) 1518 self.assertIs(type(Example.ALL), int) 1519 1520 def test_nested_classes_in_enum_with_member(self): 1521 """Support locally-defined nested classes.""" 1522 class Outer(Enum): 1523 a = 1 1524 b = 2 1525 @member 1526 class Inner(Enum): 1527 foo = 10 1528 bar = 11 1529 self.assertTrue(isinstance(Outer.Inner, Outer)) 1530 self.assertEqual(Outer.a.value, 1) 1531 self.assertEqual(Outer.Inner.value.foo.value, 10) 1532 self.assertEqual( 1533 list(Outer.Inner.value), 1534 [Outer.Inner.value.foo, Outer.Inner.value.bar], 1535 ) 1536 self.assertEqual( 1537 list(Outer), 1538 [Outer.a, Outer.b, Outer.Inner], 1539 ) 1540 1541 def test_partial(self): 1542 def func(a, b=5): 1543 return a, b 1544 with self.assertWarnsRegex(FutureWarning, r'partial.*enum\.member') as cm: 1545 class E(Enum): 1546 a = 1 1547 b = partial(func) 1548 self.assertEqual(cm.filename, __file__) 1549 self.assertIsInstance(E.b, partial) 1550 self.assertEqual(E.b(2), (2, 5)) 1551 with self.assertWarnsRegex(FutureWarning, 'partial'): 1552 self.assertEqual(E.a.b(2), (2, 5)) 1553 1554 def test_enum_with_value_name(self): 1555 class Huh(Enum): 1556 name = 1 1557 value = 2 1558 self.assertEqual(list(Huh), [Huh.name, Huh.value]) 1559 self.assertIs(type(Huh.name), Huh) 1560 self.assertEqual(Huh.name.name, 'name') 1561 self.assertEqual(Huh.name.value, 1) 1562 1563 def test_contains_name_and_value_overlap(self): 1564 class IntEnum1(IntEnum): 1565 X = 1 1566 class IntEnum2(IntEnum): 1567 X = 1 1568 class IntEnum3(IntEnum): 1569 X = 2 1570 class IntEnum4(IntEnum): 1571 Y = 1 1572 self.assertIn(IntEnum1.X, IntEnum1) 1573 self.assertIn(IntEnum1.X, IntEnum2) 1574 self.assertNotIn(IntEnum1.X, IntEnum3) 1575 self.assertIn(IntEnum1.X, IntEnum4) 1576 1577 def test_contains_different_types_same_members(self): 1578 class IntEnum1(IntEnum): 1579 X = 1 1580 class IntFlag1(IntFlag): 1581 X = 1 1582 self.assertIn(IntEnum1.X, IntFlag1) 1583 self.assertIn(IntFlag1.X, IntEnum1) 1584 1585 def test_inherited_data_type(self): 1586 class HexInt(int): 1587 __qualname__ = 'HexInt' 1588 def __repr__(self): 1589 return hex(self) 1590 class MyEnum(HexInt, enum.Enum): 1591 __qualname__ = 'MyEnum' 1592 A = 1 1593 B = 2 1594 C = 3 1595 self.assertEqual(repr(MyEnum.A), '<MyEnum.A: 0x1>') 1596 globals()['HexInt'] = HexInt 1597 globals()['MyEnum'] = MyEnum 1598 test_pickle_dump_load(self.assertIs, MyEnum.A) 1599 test_pickle_dump_load(self.assertIs, MyEnum) 1600 # 1601 class SillyInt(HexInt): 1602 __qualname__ = 'SillyInt' 1603 class MyOtherEnum(SillyInt, enum.Enum): 1604 __qualname__ = 'MyOtherEnum' 1605 D = 4 1606 E = 5 1607 F = 6 1608 self.assertIs(MyOtherEnum._member_type_, SillyInt) 1609 globals()['SillyInt'] = SillyInt 1610 globals()['MyOtherEnum'] = MyOtherEnum 1611 test_pickle_dump_load(self.assertIs, MyOtherEnum.E) 1612 test_pickle_dump_load(self.assertIs, MyOtherEnum) 1613 # 1614 # This did not work in 3.10, but does now with pickling by name 1615 class UnBrokenInt(int): 1616 __qualname__ = 'UnBrokenInt' 1617 def __new__(cls, value): 1618 return int.__new__(cls, value) 1619 class MyUnBrokenEnum(UnBrokenInt, Enum): 1620 __qualname__ = 'MyUnBrokenEnum' 1621 G = 7 1622 H = 8 1623 I = 9 1624 self.assertIs(MyUnBrokenEnum._member_type_, UnBrokenInt) 1625 self.assertIs(MyUnBrokenEnum(7), MyUnBrokenEnum.G) 1626 globals()['UnBrokenInt'] = UnBrokenInt 1627 globals()['MyUnBrokenEnum'] = MyUnBrokenEnum 1628 test_pickle_dump_load(self.assertIs, MyUnBrokenEnum.I) 1629 test_pickle_dump_load(self.assertIs, MyUnBrokenEnum) 1630 1631 @reraise_if_not_enum(FloatStooges) 1632 def test_floatenum_fromhex(self): 1633 h = float.hex(FloatStooges.MOE.value) 1634 self.assertIs(FloatStooges.fromhex(h), FloatStooges.MOE) 1635 h = float.hex(FloatStooges.MOE.value + 0.01) 1636 with self.assertRaises(ValueError): 1637 FloatStooges.fromhex(h) 1638 1639 def test_programmatic_function_type(self): 1640 MinorEnum = Enum('MinorEnum', 'june july august', type=int) 1641 lst = list(MinorEnum) 1642 self.assertEqual(len(lst), len(MinorEnum)) 1643 self.assertEqual(len(MinorEnum), 3, MinorEnum) 1644 self.assertEqual( 1645 [MinorEnum.june, MinorEnum.july, MinorEnum.august], 1646 lst, 1647 ) 1648 for i, month in enumerate('june july august'.split(), 1): 1649 e = MinorEnum(i) 1650 self.assertEqual(e, i) 1651 self.assertEqual(e.name, month) 1652 self.assertIn(e, MinorEnum) 1653 self.assertIs(type(e), MinorEnum) 1654 1655 def test_programmatic_function_string_with_start(self): 1656 MinorEnum = Enum('MinorEnum', 'june july august', start=10) 1657 lst = list(MinorEnum) 1658 self.assertEqual(len(lst), len(MinorEnum)) 1659 self.assertEqual(len(MinorEnum), 3, MinorEnum) 1660 self.assertEqual( 1661 [MinorEnum.june, MinorEnum.july, MinorEnum.august], 1662 lst, 1663 ) 1664 for i, month in enumerate('june july august'.split(), 10): 1665 e = MinorEnum(i) 1666 self.assertEqual(int(e.value), i) 1667 self.assertNotEqual(e, i) 1668 self.assertEqual(e.name, month) 1669 self.assertIn(e, MinorEnum) 1670 self.assertIs(type(e), MinorEnum) 1671 1672 def test_programmatic_function_type_with_start(self): 1673 MinorEnum = Enum('MinorEnum', 'june july august', type=int, start=30) 1674 lst = list(MinorEnum) 1675 self.assertEqual(len(lst), len(MinorEnum)) 1676 self.assertEqual(len(MinorEnum), 3, MinorEnum) 1677 self.assertEqual( 1678 [MinorEnum.june, MinorEnum.july, MinorEnum.august], 1679 lst, 1680 ) 1681 for i, month in enumerate('june july august'.split(), 30): 1682 e = MinorEnum(i) 1683 self.assertEqual(e, i) 1684 self.assertEqual(e.name, month) 1685 self.assertIn(e, MinorEnum) 1686 self.assertIs(type(e), MinorEnum) 1687 1688 def test_programmatic_function_string_list_with_start(self): 1689 MinorEnum = Enum('MinorEnum', ['june', 'july', 'august'], start=20) 1690 lst = list(MinorEnum) 1691 self.assertEqual(len(lst), len(MinorEnum)) 1692 self.assertEqual(len(MinorEnum), 3, MinorEnum) 1693 self.assertEqual( 1694 [MinorEnum.june, MinorEnum.july, MinorEnum.august], 1695 lst, 1696 ) 1697 for i, month in enumerate('june july august'.split(), 20): 1698 e = MinorEnum(i) 1699 self.assertEqual(int(e.value), i) 1700 self.assertNotEqual(e, i) 1701 self.assertEqual(e.name, month) 1702 self.assertIn(e, MinorEnum) 1703 self.assertIs(type(e), MinorEnum) 1704 1705 def test_programmatic_function_type_from_subclass(self): 1706 MinorEnum = IntEnum('MinorEnum', 'june july august') 1707 lst = list(MinorEnum) 1708 self.assertEqual(len(lst), len(MinorEnum)) 1709 self.assertEqual(len(MinorEnum), 3, MinorEnum) 1710 self.assertEqual( 1711 [MinorEnum.june, MinorEnum.july, MinorEnum.august], 1712 lst, 1713 ) 1714 for i, month in enumerate('june july august'.split(), 1): 1715 e = MinorEnum(i) 1716 self.assertEqual(e, i) 1717 self.assertEqual(e.name, month) 1718 self.assertIn(e, MinorEnum) 1719 self.assertIs(type(e), MinorEnum) 1720 1721 def test_programmatic_function_type_from_subclass_with_start(self): 1722 MinorEnum = IntEnum('MinorEnum', 'june july august', start=40) 1723 lst = list(MinorEnum) 1724 self.assertEqual(len(lst), len(MinorEnum)) 1725 self.assertEqual(len(MinorEnum), 3, MinorEnum) 1726 self.assertEqual( 1727 [MinorEnum.june, MinorEnum.july, MinorEnum.august], 1728 lst, 1729 ) 1730 for i, month in enumerate('june july august'.split(), 40): 1731 e = MinorEnum(i) 1732 self.assertEqual(e, i) 1733 self.assertEqual(e.name, month) 1734 self.assertIn(e, MinorEnum) 1735 self.assertIs(type(e), MinorEnum) 1736 1737 def test_programmatic_function_is_value_call(self): 1738 class TwoPart(Enum): 1739 ONE = 1, 1.0 1740 TWO = 2, 2.0 1741 THREE = 3, 3.0 1742 self.assertRaisesRegex(ValueError, '1 is not a valid .*TwoPart', TwoPart, 1) 1743 self.assertIs(TwoPart((1, 1.0)), TwoPart.ONE) 1744 self.assertIs(TwoPart(1, 1.0), TwoPart.ONE) 1745 class ThreePart(Enum): 1746 ONE = 1, 1.0, 'one' 1747 TWO = 2, 2.0, 'two' 1748 THREE = 3, 3.0, 'three' 1749 self.assertIs(ThreePart((3, 3.0, 'three')), ThreePart.THREE) 1750 self.assertIs(ThreePart(3, 3.0, 'three'), ThreePart.THREE) 1751 1752 @reraise_if_not_enum(IntStooges) 1753 def test_intenum_from_bytes(self): 1754 self.assertIs(IntStooges.from_bytes(b'\x00\x03', 'big'), IntStooges.MOE) 1755 with self.assertRaises(ValueError): 1756 IntStooges.from_bytes(b'\x00\x05', 'big') 1757 1758 def test_reserved_sunder_error(self): 1759 with self.assertRaisesRegex( 1760 ValueError, 1761 '_sunder_ names, such as ._bad_., are reserved', 1762 ): 1763 class Bad(Enum): 1764 _bad_ = 1 1765 1766 def test_too_many_data_types(self): 1767 with self.assertRaisesRegex(TypeError, 'too many data types'): 1768 class Huh(str, int, Enum): 1769 One = 1 1770 1771 class MyStr(str): 1772 def hello(self): 1773 return 'hello, %s' % self 1774 class MyInt(int): 1775 def repr(self): 1776 return hex(self) 1777 with self.assertRaisesRegex(TypeError, 'too many data types'): 1778 class Huh(MyStr, MyInt, Enum): 1779 One = 1 1780 1781 @reraise_if_not_enum(Stooges) 1782 def test_pickle_enum(self): 1783 test_pickle_dump_load(self.assertIs, Stooges.CURLY) 1784 test_pickle_dump_load(self.assertIs, Stooges) 1785 1786 @reraise_if_not_enum(IntStooges) 1787 def test_pickle_int(self): 1788 test_pickle_dump_load(self.assertIs, IntStooges.CURLY) 1789 test_pickle_dump_load(self.assertIs, IntStooges) 1790 1791 @reraise_if_not_enum(FloatStooges) 1792 def test_pickle_float(self): 1793 test_pickle_dump_load(self.assertIs, FloatStooges.CURLY) 1794 test_pickle_dump_load(self.assertIs, FloatStooges) 1795 1796 @reraise_if_not_enum(Answer) 1797 def test_pickle_enum_function(self): 1798 test_pickle_dump_load(self.assertIs, Answer.him) 1799 test_pickle_dump_load(self.assertIs, Answer) 1800 1801 @reraise_if_not_enum(Question) 1802 def test_pickle_enum_function_with_module(self): 1803 test_pickle_dump_load(self.assertIs, Question.who) 1804 test_pickle_dump_load(self.assertIs, Question) 1805 1806 def test_pickle_nested_class(self): 1807 # would normally just have this directly in the class namespace 1808 class NestedEnum(Enum): 1809 twigs = 'common' 1810 shiny = 'rare' 1811 1812 self.__class__.NestedEnum = NestedEnum 1813 self.NestedEnum.__qualname__ = '%s.NestedEnum' % self.__class__.__name__ 1814 test_pickle_dump_load(self.assertIs, self.NestedEnum.twigs) 1815 1816 def test_pickle_by_name(self): 1817 class ReplaceGlobalInt(IntEnum): 1818 ONE = 1 1819 TWO = 2 1820 ReplaceGlobalInt.__reduce_ex__ = enum._reduce_ex_by_global_name 1821 for proto in range(HIGHEST_PROTOCOL): 1822 self.assertEqual(ReplaceGlobalInt.TWO.__reduce_ex__(proto), 'TWO') 1823 1824 def test_pickle_explodes(self): 1825 BadPickle = Enum( 1826 'BadPickle', 'dill sweet bread-n-butter', module=__name__) 1827 globals()['BadPickle'] = BadPickle 1828 # now break BadPickle to test exception raising 1829 enum._make_class_unpicklable(BadPickle) 1830 test_pickle_exception(self.assertRaises, TypeError, BadPickle.dill) 1831 test_pickle_exception(self.assertRaises, PicklingError, BadPickle) 1832 1833 def test_string_enum(self): 1834 class SkillLevel(str, Enum): 1835 master = 'what is the sound of one hand clapping?' 1836 journeyman = 'why did the chicken cross the road?' 1837 apprentice = 'knock, knock!' 1838 self.assertEqual(SkillLevel.apprentice, 'knock, knock!') 1839 1840 def test_getattr_getitem(self): 1841 class Period(Enum): 1842 morning = 1 1843 noon = 2 1844 evening = 3 1845 night = 4 1846 self.assertIs(Period(2), Period.noon) 1847 self.assertIs(getattr(Period, 'night'), Period.night) 1848 self.assertIs(Period['morning'], Period.morning) 1849 1850 def test_getattr_dunder(self): 1851 Season = self.Season 1852 self.assertTrue(getattr(Season, '__eq__')) 1853 1854 def test_iteration_order(self): 1855 class Season(Enum): 1856 SUMMER = 2 1857 WINTER = 4 1858 AUTUMN = 3 1859 SPRING = 1 1860 self.assertEqual( 1861 list(Season), 1862 [Season.SUMMER, Season.WINTER, Season.AUTUMN, Season.SPRING], 1863 ) 1864 1865 @reraise_if_not_enum(Name) 1866 def test_subclassing(self): 1867 self.assertEqual(Name.BDFL, 'Guido van Rossum') 1868 self.assertTrue(Name.BDFL, Name('Guido van Rossum')) 1869 self.assertIs(Name.BDFL, getattr(Name, 'BDFL')) 1870 test_pickle_dump_load(self.assertIs, Name.BDFL) 1871 1872 def test_extending(self): 1873 class Color(Enum): 1874 red = 1 1875 green = 2 1876 blue = 3 1877 # 1878 with self.assertRaises(TypeError): 1879 class MoreColor(Color): 1880 cyan = 4 1881 magenta = 5 1882 yellow = 6 1883 # 1884 with self.assertRaisesRegex(TypeError, "<enum .EvenMoreColor.> cannot extend <enum .Color.>"): 1885 class EvenMoreColor(Color, IntEnum): 1886 chartruese = 7 1887 # 1888 with self.assertRaisesRegex(ValueError, r"\(.Foo., \(.pink., .black.\)\) is not a valid .*Color"): 1889 Color('Foo', ('pink', 'black')) 1890 1891 def test_exclude_methods(self): 1892 class whatever(Enum): 1893 this = 'that' 1894 these = 'those' 1895 def really(self): 1896 return 'no, not %s' % self.value 1897 self.assertIsNot(type(whatever.really), whatever) 1898 self.assertEqual(whatever.this.really(), 'no, not that') 1899 1900 def test_wrong_inheritance_order(self): 1901 with self.assertRaises(TypeError): 1902 class Wrong(Enum, str): 1903 NotHere = 'error before this point' 1904 1905 def test_raise_custom_error_on_creation(self): 1906 class InvalidRgbColorError(ValueError): 1907 def __init__(self, r, g, b): 1908 self.r = r 1909 self.g = g 1910 self.b = b 1911 super().__init__(f'({r}, {g}, {b}) is not a valid RGB color') 1912 1913 with self.assertRaises(InvalidRgbColorError): 1914 class RgbColor(Enum): 1915 RED = (255, 0, 0) 1916 GREEN = (0, 255, 0) 1917 BLUE = (0, 0, 255) 1918 INVALID = (256, 0, 0) 1919 1920 def __init__(self, r, g, b): 1921 if not all(0 <= val <= 255 for val in (r, g, b)): 1922 raise InvalidRgbColorError(r, g, b) 1923 1924 def test_intenum_transitivity(self): 1925 class number(IntEnum): 1926 one = 1 1927 two = 2 1928 three = 3 1929 class numero(IntEnum): 1930 uno = 1 1931 dos = 2 1932 tres = 3 1933 self.assertEqual(number.one, numero.uno) 1934 self.assertEqual(number.two, numero.dos) 1935 self.assertEqual(number.three, numero.tres) 1936 1937 def test_wrong_enum_in_call(self): 1938 class Monochrome(Enum): 1939 black = 0 1940 white = 1 1941 class Gender(Enum): 1942 male = 0 1943 female = 1 1944 self.assertRaises(ValueError, Monochrome, Gender.male) 1945 1946 def test_wrong_enum_in_mixed_call(self): 1947 class Monochrome(IntEnum): 1948 black = 0 1949 white = 1 1950 class Gender(Enum): 1951 male = 0 1952 female = 1 1953 self.assertRaises(ValueError, Monochrome, Gender.male) 1954 1955 def test_mixed_enum_in_call_1(self): 1956 class Monochrome(IntEnum): 1957 black = 0 1958 white = 1 1959 class Gender(IntEnum): 1960 male = 0 1961 female = 1 1962 self.assertIs(Monochrome(Gender.female), Monochrome.white) 1963 1964 def test_mixed_enum_in_call_2(self): 1965 class Monochrome(Enum): 1966 black = 0 1967 white = 1 1968 class Gender(IntEnum): 1969 male = 0 1970 female = 1 1971 self.assertIs(Monochrome(Gender.male), Monochrome.black) 1972 1973 def test_flufl_enum(self): 1974 class Fluflnum(Enum): 1975 def __int__(self): 1976 return int(self.value) 1977 class MailManOptions(Fluflnum): 1978 option1 = 1 1979 option2 = 2 1980 option3 = 3 1981 self.assertEqual(int(MailManOptions.option1), 1) 1982 1983 def test_introspection(self): 1984 class Number(IntEnum): 1985 one = 100 1986 two = 200 1987 self.assertIs(Number.one._member_type_, int) 1988 self.assertIs(Number._member_type_, int) 1989 class String(str, Enum): 1990 yarn = 'soft' 1991 rope = 'rough' 1992 wire = 'hard' 1993 self.assertIs(String.yarn._member_type_, str) 1994 self.assertIs(String._member_type_, str) 1995 class Plain(Enum): 1996 vanilla = 'white' 1997 one = 1 1998 self.assertIs(Plain.vanilla._member_type_, object) 1999 self.assertIs(Plain._member_type_, object) 2000 2001 def test_no_such_enum_member(self): 2002 class Color(Enum): 2003 red = 1 2004 green = 2 2005 blue = 3 2006 with self.assertRaises(ValueError): 2007 Color(4) 2008 with self.assertRaises(KeyError): 2009 Color['chartreuse'] 2010 2011 # tests that need to be evalualted for moving 2012 2013 def test_multiple_mixin_mro(self): 2014 class auto_enum(type(Enum)): 2015 def __new__(metacls, cls, bases, classdict): 2016 temp = type(classdict)() 2017 temp._cls_name = cls 2018 names = set(classdict._member_names) 2019 i = 0 2020 for k in classdict._member_names: 2021 v = classdict[k] 2022 if v is Ellipsis: 2023 v = i 2024 else: 2025 i = v 2026 i += 1 2027 temp[k] = v 2028 for k, v in classdict.items(): 2029 if k not in names: 2030 temp[k] = v 2031 return super(auto_enum, metacls).__new__( 2032 metacls, cls, bases, temp) 2033 2034 class AutoNumberedEnum(Enum, metaclass=auto_enum): 2035 pass 2036 2037 class AutoIntEnum(IntEnum, metaclass=auto_enum): 2038 pass 2039 2040 class TestAutoNumber(AutoNumberedEnum): 2041 a = ... 2042 b = 3 2043 c = ... 2044 2045 class TestAutoInt(AutoIntEnum): 2046 a = ... 2047 b = 3 2048 c = ... 2049 2050 def test_subclasses_with_getnewargs(self): 2051 class NamedInt(int): 2052 __qualname__ = 'NamedInt' # needed for pickle protocol 4 2053 def __new__(cls, *args): 2054 _args = args 2055 name, *args = args 2056 if len(args) == 0: 2057 raise TypeError("name and value must be specified") 2058 self = int.__new__(cls, *args) 2059 self._intname = name 2060 self._args = _args 2061 return self 2062 def __getnewargs__(self): 2063 return self._args 2064 @bltns.property 2065 def __name__(self): 2066 return self._intname 2067 def __repr__(self): 2068 # repr() is updated to include the name and type info 2069 return "{}({!r}, {})".format( 2070 type(self).__name__, 2071 self.__name__, 2072 int.__repr__(self), 2073 ) 2074 def __str__(self): 2075 # str() is unchanged, even if it relies on the repr() fallback 2076 base = int 2077 base_str = base.__str__ 2078 if base_str.__objclass__ is object: 2079 return base.__repr__(self) 2080 return base_str(self) 2081 # for simplicity, we only define one operator that 2082 # propagates expressions 2083 def __add__(self, other): 2084 temp = int(self) + int( other) 2085 if isinstance(self, NamedInt) and isinstance(other, NamedInt): 2086 return NamedInt( 2087 '({0} + {1})'.format(self.__name__, other.__name__), 2088 temp, 2089 ) 2090 else: 2091 return temp 2092 2093 class NEI(NamedInt, Enum): 2094 __qualname__ = 'NEI' # needed for pickle protocol 4 2095 x = ('the-x', 1) 2096 y = ('the-y', 2) 2097 2098 2099 self.assertIs(NEI.__new__, Enum.__new__) 2100 self.assertEqual(repr(NEI.x + NEI.y), "NamedInt('(the-x + the-y)', 3)") 2101 globals()['NamedInt'] = NamedInt 2102 globals()['NEI'] = NEI 2103 NI5 = NamedInt('test', 5) 2104 self.assertEqual(NI5, 5) 2105 test_pickle_dump_load(self.assertEqual, NI5, 5) 2106 self.assertEqual(NEI.y.value, 2) 2107 test_pickle_dump_load(self.assertIs, NEI.y) 2108 test_pickle_dump_load(self.assertIs, NEI) 2109 2110 def test_subclasses_with_getnewargs_ex(self): 2111 class NamedInt(int): 2112 __qualname__ = 'NamedInt' # needed for pickle protocol 4 2113 def __new__(cls, *args): 2114 _args = args 2115 name, *args = args 2116 if len(args) == 0: 2117 raise TypeError("name and value must be specified") 2118 self = int.__new__(cls, *args) 2119 self._intname = name 2120 self._args = _args 2121 return self 2122 def __getnewargs_ex__(self): 2123 return self._args, {} 2124 @bltns.property 2125 def __name__(self): 2126 return self._intname 2127 def __repr__(self): 2128 # repr() is updated to include the name and type info 2129 return "{}({!r}, {})".format( 2130 type(self).__name__, 2131 self.__name__, 2132 int.__repr__(self), 2133 ) 2134 def __str__(self): 2135 # str() is unchanged, even if it relies on the repr() fallback 2136 base = int 2137 base_str = base.__str__ 2138 if base_str.__objclass__ is object: 2139 return base.__repr__(self) 2140 return base_str(self) 2141 # for simplicity, we only define one operator that 2142 # propagates expressions 2143 def __add__(self, other): 2144 temp = int(self) + int( other) 2145 if isinstance(self, NamedInt) and isinstance(other, NamedInt): 2146 return NamedInt( 2147 '({0} + {1})'.format(self.__name__, other.__name__), 2148 temp, 2149 ) 2150 else: 2151 return temp 2152 2153 class NEI(NamedInt, Enum): 2154 __qualname__ = 'NEI' # needed for pickle protocol 4 2155 x = ('the-x', 1) 2156 y = ('the-y', 2) 2157 2158 2159 self.assertIs(NEI.__new__, Enum.__new__) 2160 self.assertEqual(repr(NEI.x + NEI.y), "NamedInt('(the-x + the-y)', 3)") 2161 globals()['NamedInt'] = NamedInt 2162 globals()['NEI'] = NEI 2163 NI5 = NamedInt('test', 5) 2164 self.assertEqual(NI5, 5) 2165 test_pickle_dump_load(self.assertEqual, NI5, 5) 2166 self.assertEqual(NEI.y.value, 2) 2167 test_pickle_dump_load(self.assertIs, NEI.y) 2168 test_pickle_dump_load(self.assertIs, NEI) 2169 2170 def test_subclasses_with_reduce(self): 2171 class NamedInt(int): 2172 __qualname__ = 'NamedInt' # needed for pickle protocol 4 2173 def __new__(cls, *args): 2174 _args = args 2175 name, *args = args 2176 if len(args) == 0: 2177 raise TypeError("name and value must be specified") 2178 self = int.__new__(cls, *args) 2179 self._intname = name 2180 self._args = _args 2181 return self 2182 def __reduce__(self): 2183 return self.__class__, self._args 2184 @bltns.property 2185 def __name__(self): 2186 return self._intname 2187 def __repr__(self): 2188 # repr() is updated to include the name and type info 2189 return "{}({!r}, {})".format( 2190 type(self).__name__, 2191 self.__name__, 2192 int.__repr__(self), 2193 ) 2194 def __str__(self): 2195 # str() is unchanged, even if it relies on the repr() fallback 2196 base = int 2197 base_str = base.__str__ 2198 if base_str.__objclass__ is object: 2199 return base.__repr__(self) 2200 return base_str(self) 2201 # for simplicity, we only define one operator that 2202 # propagates expressions 2203 def __add__(self, other): 2204 temp = int(self) + int( other) 2205 if isinstance(self, NamedInt) and isinstance(other, NamedInt): 2206 return NamedInt( 2207 '({0} + {1})'.format(self.__name__, other.__name__), 2208 temp, 2209 ) 2210 else: 2211 return temp 2212 2213 class NEI(NamedInt, Enum): 2214 __qualname__ = 'NEI' # needed for pickle protocol 4 2215 x = ('the-x', 1) 2216 y = ('the-y', 2) 2217 2218 2219 self.assertIs(NEI.__new__, Enum.__new__) 2220 self.assertEqual(repr(NEI.x + NEI.y), "NamedInt('(the-x + the-y)', 3)") 2221 globals()['NamedInt'] = NamedInt 2222 globals()['NEI'] = NEI 2223 NI5 = NamedInt('test', 5) 2224 self.assertEqual(NI5, 5) 2225 test_pickle_dump_load(self.assertEqual, NI5, 5) 2226 self.assertEqual(NEI.y.value, 2) 2227 test_pickle_dump_load(self.assertIs, NEI.y) 2228 test_pickle_dump_load(self.assertIs, NEI) 2229 2230 def test_subclasses_with_reduce_ex(self): 2231 class NamedInt(int): 2232 __qualname__ = 'NamedInt' # needed for pickle protocol 4 2233 def __new__(cls, *args): 2234 _args = args 2235 name, *args = args 2236 if len(args) == 0: 2237 raise TypeError("name and value must be specified") 2238 self = int.__new__(cls, *args) 2239 self._intname = name 2240 self._args = _args 2241 return self 2242 def __reduce_ex__(self, proto): 2243 return self.__class__, self._args 2244 @bltns.property 2245 def __name__(self): 2246 return self._intname 2247 def __repr__(self): 2248 # repr() is updated to include the name and type info 2249 return "{}({!r}, {})".format( 2250 type(self).__name__, 2251 self.__name__, 2252 int.__repr__(self), 2253 ) 2254 def __str__(self): 2255 # str() is unchanged, even if it relies on the repr() fallback 2256 base = int 2257 base_str = base.__str__ 2258 if base_str.__objclass__ is object: 2259 return base.__repr__(self) 2260 return base_str(self) 2261 # for simplicity, we only define one operator that 2262 # propagates expressions 2263 def __add__(self, other): 2264 temp = int(self) + int( other) 2265 if isinstance(self, NamedInt) and isinstance(other, NamedInt): 2266 return NamedInt( 2267 '({0} + {1})'.format(self.__name__, other.__name__), 2268 temp, 2269 ) 2270 else: 2271 return temp 2272 2273 class NEI(NamedInt, Enum): 2274 __qualname__ = 'NEI' # needed for pickle protocol 4 2275 x = ('the-x', 1) 2276 y = ('the-y', 2) 2277 2278 self.assertIs(NEI.__new__, Enum.__new__) 2279 self.assertEqual(repr(NEI.x + NEI.y), "NamedInt('(the-x + the-y)', 3)") 2280 globals()['NamedInt'] = NamedInt 2281 globals()['NEI'] = NEI 2282 NI5 = NamedInt('test', 5) 2283 self.assertEqual(NI5, 5) 2284 test_pickle_dump_load(self.assertEqual, NI5, 5) 2285 self.assertEqual(NEI.y.value, 2) 2286 test_pickle_dump_load(self.assertIs, NEI.y) 2287 test_pickle_dump_load(self.assertIs, NEI) 2288 2289 def test_subclasses_without_direct_pickle_support(self): 2290 class NamedInt(int): 2291 __qualname__ = 'NamedInt' 2292 def __new__(cls, *args): 2293 _args = args 2294 name, *args = args 2295 if len(args) == 0: 2296 raise TypeError("name and value must be specified") 2297 self = int.__new__(cls, *args) 2298 self._intname = name 2299 self._args = _args 2300 return self 2301 @bltns.property 2302 def __name__(self): 2303 return self._intname 2304 def __repr__(self): 2305 # repr() is updated to include the name and type info 2306 return "{}({!r}, {})".format( 2307 type(self).__name__, 2308 self.__name__, 2309 int.__repr__(self), 2310 ) 2311 def __str__(self): 2312 # str() is unchanged, even if it relies on the repr() fallback 2313 base = int 2314 base_str = base.__str__ 2315 if base_str.__objclass__ is object: 2316 return base.__repr__(self) 2317 return base_str(self) 2318 # for simplicity, we only define one operator that 2319 # propagates expressions 2320 def __add__(self, other): 2321 temp = int(self) + int( other) 2322 if isinstance(self, NamedInt) and isinstance(other, NamedInt): 2323 return NamedInt( 2324 '({0} + {1})'.format(self.__name__, other.__name__), 2325 temp ) 2326 else: 2327 return temp 2328 2329 class NEI(NamedInt, Enum): 2330 __qualname__ = 'NEI' 2331 x = ('the-x', 1) 2332 y = ('the-y', 2) 2333 self.assertIs(NEI.__new__, Enum.__new__) 2334 self.assertEqual(repr(NEI.x + NEI.y), "NamedInt('(the-x + the-y)', 3)") 2335 globals()['NamedInt'] = NamedInt 2336 globals()['NEI'] = NEI 2337 NI5 = NamedInt('test', 5) 2338 self.assertEqual(NI5, 5) 2339 self.assertEqual(NEI.y.value, 2) 2340 with self.assertRaisesRegex(TypeError, "name and value must be specified"): 2341 test_pickle_dump_load(self.assertIs, NEI.y) 2342 # fix pickle support and try again 2343 NEI.__reduce_ex__ = enum.pickle_by_enum_name 2344 test_pickle_dump_load(self.assertIs, NEI.y) 2345 test_pickle_dump_load(self.assertIs, NEI) 2346 2347 def test_subclasses_with_direct_pickle_support(self): 2348 class NamedInt(int): 2349 __qualname__ = 'NamedInt' 2350 def __new__(cls, *args): 2351 _args = args 2352 name, *args = args 2353 if len(args) == 0: 2354 raise TypeError("name and value must be specified") 2355 self = int.__new__(cls, *args) 2356 self._intname = name 2357 self._args = _args 2358 return self 2359 @bltns.property 2360 def __name__(self): 2361 return self._intname 2362 def __repr__(self): 2363 # repr() is updated to include the name and type info 2364 return "{}({!r}, {})".format( 2365 type(self).__name__, 2366 self.__name__, 2367 int.__repr__(self), 2368 ) 2369 def __str__(self): 2370 # str() is unchanged, even if it relies on the repr() fallback 2371 base = int 2372 base_str = base.__str__ 2373 if base_str.__objclass__ is object: 2374 return base.__repr__(self) 2375 return base_str(self) 2376 # for simplicity, we only define one operator that 2377 # propagates expressions 2378 def __add__(self, other): 2379 temp = int(self) + int( other) 2380 if isinstance(self, NamedInt) and isinstance(other, NamedInt): 2381 return NamedInt( 2382 '({0} + {1})'.format(self.__name__, other.__name__), 2383 temp, 2384 ) 2385 else: 2386 return temp 2387 2388 class NEI(NamedInt, Enum): 2389 __qualname__ = 'NEI' 2390 x = ('the-x', 1) 2391 y = ('the-y', 2) 2392 def __reduce_ex__(self, proto): 2393 return getattr, (self.__class__, self._name_) 2394 2395 self.assertIs(NEI.__new__, Enum.__new__) 2396 self.assertEqual(repr(NEI.x + NEI.y), "NamedInt('(the-x + the-y)', 3)") 2397 globals()['NamedInt'] = NamedInt 2398 globals()['NEI'] = NEI 2399 NI5 = NamedInt('test', 5) 2400 self.assertEqual(NI5, 5) 2401 self.assertEqual(NEI.y.value, 2) 2402 test_pickle_dump_load(self.assertIs, NEI.y) 2403 test_pickle_dump_load(self.assertIs, NEI) 2404 2405 def test_tuple_subclass(self): 2406 class SomeTuple(tuple, Enum): 2407 __qualname__ = 'SomeTuple' # needed for pickle protocol 4 2408 first = (1, 'for the money') 2409 second = (2, 'for the show') 2410 third = (3, 'for the music') 2411 self.assertIs(type(SomeTuple.first), SomeTuple) 2412 self.assertIsInstance(SomeTuple.second, tuple) 2413 self.assertEqual(SomeTuple.third, (3, 'for the music')) 2414 globals()['SomeTuple'] = SomeTuple 2415 test_pickle_dump_load(self.assertIs, SomeTuple.first) 2416 2417 def test_tuple_subclass_with_auto_1(self): 2418 from collections import namedtuple 2419 T = namedtuple('T', 'index desc') 2420 class SomeEnum(T, Enum): 2421 __qualname__ = 'SomeEnum' # needed for pickle protocol 4 2422 first = auto(), 'for the money' 2423 second = auto(), 'for the show' 2424 third = auto(), 'for the music' 2425 self.assertIs(type(SomeEnum.first), SomeEnum) 2426 self.assertEqual(SomeEnum.third.value, (3, 'for the music')) 2427 self.assertIsInstance(SomeEnum.third.value, T) 2428 self.assertEqual(SomeEnum.first.index, 1) 2429 self.assertEqual(SomeEnum.second.desc, 'for the show') 2430 globals()['SomeEnum'] = SomeEnum 2431 globals()['T'] = T 2432 test_pickle_dump_load(self.assertIs, SomeEnum.first) 2433 2434 def test_tuple_subclass_with_auto_2(self): 2435 from collections import namedtuple 2436 T = namedtuple('T', 'index desc') 2437 class SomeEnum(Enum): 2438 __qualname__ = 'SomeEnum' # needed for pickle protocol 4 2439 first = T(auto(), 'for the money') 2440 second = T(auto(), 'for the show') 2441 third = T(auto(), 'for the music') 2442 self.assertIs(type(SomeEnum.first), SomeEnum) 2443 self.assertEqual(SomeEnum.third.value, (3, 'for the music')) 2444 self.assertIsInstance(SomeEnum.third.value, T) 2445 self.assertEqual(SomeEnum.first.value.index, 1) 2446 self.assertEqual(SomeEnum.second.value.desc, 'for the show') 2447 globals()['SomeEnum'] = SomeEnum 2448 globals()['T'] = T 2449 test_pickle_dump_load(self.assertIs, SomeEnum.first) 2450 2451 def test_duplicate_values_give_unique_enum_items(self): 2452 class AutoNumber(Enum): 2453 first = () 2454 second = () 2455 third = () 2456 def __new__(cls): 2457 value = len(cls.__members__) + 1 2458 obj = object.__new__(cls) 2459 obj._value_ = value 2460 return obj 2461 def __int__(self): 2462 return int(self._value_) 2463 self.assertEqual( 2464 list(AutoNumber), 2465 [AutoNumber.first, AutoNumber.second, AutoNumber.third], 2466 ) 2467 self.assertEqual(int(AutoNumber.second), 2) 2468 self.assertEqual(AutoNumber.third.value, 3) 2469 self.assertIs(AutoNumber(1), AutoNumber.first) 2470 2471 def test_inherited_new_from_enhanced_enum(self): 2472 class AutoNumber(Enum): 2473 def __new__(cls): 2474 value = len(cls.__members__) + 1 2475 obj = object.__new__(cls) 2476 obj._value_ = value 2477 return obj 2478 def __int__(self): 2479 return int(self._value_) 2480 class Color(AutoNumber): 2481 red = () 2482 green = () 2483 blue = () 2484 self.assertEqual(list(Color), [Color.red, Color.green, Color.blue]) 2485 self.assertEqual(list(map(int, Color)), [1, 2, 3]) 2486 2487 def test_inherited_new_from_mixed_enum(self): 2488 class AutoNumber(IntEnum): 2489 def __new__(cls): 2490 value = len(cls.__members__) + 1 2491 obj = int.__new__(cls, value) 2492 obj._value_ = value 2493 return obj 2494 class Color(AutoNumber): 2495 red = () 2496 green = () 2497 blue = () 2498 self.assertEqual(list(Color), [Color.red, Color.green, Color.blue]) 2499 self.assertEqual(list(map(int, Color)), [1, 2, 3]) 2500 2501 def test_equality(self): 2502 class OrdinaryEnum(Enum): 2503 a = 1 2504 self.assertEqual(ALWAYS_EQ, OrdinaryEnum.a) 2505 self.assertEqual(OrdinaryEnum.a, ALWAYS_EQ) 2506 2507 def test_ordered_mixin(self): 2508 class OrderedEnum(Enum): 2509 def __ge__(self, other): 2510 if self.__class__ is other.__class__: 2511 return self._value_ >= other._value_ 2512 return NotImplemented 2513 def __gt__(self, other): 2514 if self.__class__ is other.__class__: 2515 return self._value_ > other._value_ 2516 return NotImplemented 2517 def __le__(self, other): 2518 if self.__class__ is other.__class__: 2519 return self._value_ <= other._value_ 2520 return NotImplemented 2521 def __lt__(self, other): 2522 if self.__class__ is other.__class__: 2523 return self._value_ < other._value_ 2524 return NotImplemented 2525 class Grade(OrderedEnum): 2526 A = 5 2527 B = 4 2528 C = 3 2529 D = 2 2530 F = 1 2531 self.assertGreater(Grade.A, Grade.B) 2532 self.assertLessEqual(Grade.F, Grade.C) 2533 self.assertLess(Grade.D, Grade.A) 2534 self.assertGreaterEqual(Grade.B, Grade.B) 2535 self.assertEqual(Grade.B, Grade.B) 2536 self.assertNotEqual(Grade.C, Grade.D) 2537 2538 def test_extending2(self): 2539 class Shade(Enum): 2540 def shade(self): 2541 print(self.name) 2542 class Color(Shade): 2543 red = 1 2544 green = 2 2545 blue = 3 2546 with self.assertRaises(TypeError): 2547 class MoreColor(Color): 2548 cyan = 4 2549 magenta = 5 2550 yellow = 6 2551 2552 def test_extending3(self): 2553 class Shade(Enum): 2554 def shade(self): 2555 return self.name 2556 class Color(Shade): 2557 def hex(self): 2558 return '%s hexlified!' % self.value 2559 class MoreColor(Color): 2560 cyan = 4 2561 magenta = 5 2562 yellow = 6 2563 self.assertEqual(MoreColor.magenta.hex(), '5 hexlified!') 2564 2565 def test_subclass_duplicate_name(self): 2566 class Base(Enum): 2567 def test(self): 2568 pass 2569 class Test(Base): 2570 test = 1 2571 self.assertIs(type(Test.test), Test) 2572 2573 def test_subclass_duplicate_name_dynamic(self): 2574 from types import DynamicClassAttribute 2575 class Base(Enum): 2576 @DynamicClassAttribute 2577 def test(self): 2578 return 'dynamic' 2579 class Test(Base): 2580 test = 1 2581 self.assertEqual(Test.test.test, 'dynamic') 2582 self.assertEqual(Test.test.value, 1) 2583 class Base2(Enum): 2584 @enum.property 2585 def flash(self): 2586 return 'flashy dynamic' 2587 class Test(Base2): 2588 flash = 1 2589 self.assertEqual(Test.flash.flash, 'flashy dynamic') 2590 self.assertEqual(Test.flash.value, 1) 2591 2592 def test_no_duplicates(self): 2593 class UniqueEnum(Enum): 2594 def __init__(self, *args): 2595 cls = self.__class__ 2596 if any(self.value == e.value for e in cls): 2597 a = self.name 2598 e = cls(self.value).name 2599 raise ValueError( 2600 "aliases not allowed in UniqueEnum: %r --> %r" 2601 % (a, e) 2602 ) 2603 class Color(UniqueEnum): 2604 red = 1 2605 green = 2 2606 blue = 3 2607 with self.assertRaises(ValueError): 2608 class Color(UniqueEnum): 2609 red = 1 2610 green = 2 2611 blue = 3 2612 grene = 2 2613 2614 def test_init(self): 2615 class Planet(Enum): 2616 MERCURY = (3.303e+23, 2.4397e6) 2617 VENUS = (4.869e+24, 6.0518e6) 2618 EARTH = (5.976e+24, 6.37814e6) 2619 MARS = (6.421e+23, 3.3972e6) 2620 JUPITER = (1.9e+27, 7.1492e7) 2621 SATURN = (5.688e+26, 6.0268e7) 2622 URANUS = (8.686e+25, 2.5559e7) 2623 NEPTUNE = (1.024e+26, 2.4746e7) 2624 def __init__(self, mass, radius): 2625 self.mass = mass # in kilograms 2626 self.radius = radius # in meters 2627 @enum.property 2628 def surface_gravity(self): 2629 # universal gravitational constant (m3 kg-1 s-2) 2630 G = 6.67300E-11 2631 return G * self.mass / (self.radius * self.radius) 2632 self.assertEqual(round(Planet.EARTH.surface_gravity, 2), 9.80) 2633 self.assertEqual(Planet.EARTH.value, (5.976e+24, 6.37814e6)) 2634 2635 def test_ignore(self): 2636 class Period(timedelta, Enum): 2637 ''' 2638 different lengths of time 2639 ''' 2640 def __new__(cls, value, period): 2641 obj = timedelta.__new__(cls, value) 2642 obj._value_ = value 2643 obj.period = period 2644 return obj 2645 _ignore_ = 'Period i' 2646 Period = vars() 2647 for i in range(13): 2648 Period['month_%d' % i] = i*30, 'month' 2649 for i in range(53): 2650 Period['week_%d' % i] = i*7, 'week' 2651 for i in range(32): 2652 Period['day_%d' % i] = i, 'day' 2653 OneDay = day_1 2654 OneWeek = week_1 2655 OneMonth = month_1 2656 self.assertFalse(hasattr(Period, '_ignore_')) 2657 self.assertFalse(hasattr(Period, 'Period')) 2658 self.assertFalse(hasattr(Period, 'i')) 2659 self.assertTrue(isinstance(Period.day_1, timedelta)) 2660 self.assertTrue(Period.month_1 is Period.day_30) 2661 self.assertTrue(Period.week_4 is Period.day_28) 2662 2663 def test_nonhash_value(self): 2664 class AutoNumberInAList(Enum): 2665 def __new__(cls): 2666 value = [len(cls.__members__) + 1] 2667 obj = object.__new__(cls) 2668 obj._value_ = value 2669 return obj 2670 class ColorInAList(AutoNumberInAList): 2671 red = () 2672 green = () 2673 blue = () 2674 self.assertEqual(list(ColorInAList), [ColorInAList.red, ColorInAList.green, ColorInAList.blue]) 2675 for enum, value in zip(ColorInAList, range(3)): 2676 value += 1 2677 self.assertEqual(enum.value, [value]) 2678 self.assertIs(ColorInAList([value]), enum) 2679 2680 def test_conflicting_types_resolved_in_new(self): 2681 class LabelledIntEnum(int, Enum): 2682 def __new__(cls, *args): 2683 value, label = args 2684 obj = int.__new__(cls, value) 2685 obj.label = label 2686 obj._value_ = value 2687 return obj 2688 2689 class LabelledList(LabelledIntEnum): 2690 unprocessed = (1, "Unprocessed") 2691 payment_complete = (2, "Payment Complete") 2692 2693 self.assertEqual(list(LabelledList), [LabelledList.unprocessed, LabelledList.payment_complete]) 2694 self.assertEqual(LabelledList.unprocessed, 1) 2695 self.assertEqual(LabelledList(1), LabelledList.unprocessed) 2696 2697 def test_default_missing_no_chained_exception(self): 2698 class Color(Enum): 2699 RED = 1 2700 GREEN = 2 2701 BLUE = 3 2702 try: 2703 Color(7) 2704 except ValueError as exc: 2705 self.assertTrue(exc.__context__ is None) 2706 else: 2707 raise Exception('Exception not raised.') 2708 2709 def test_missing_override(self): 2710 class Color(Enum): 2711 red = 1 2712 green = 2 2713 blue = 3 2714 @classmethod 2715 def _missing_(cls, item): 2716 if item == 'three': 2717 return cls.blue 2718 elif item == 'bad return': 2719 # trigger internal error 2720 return 5 2721 elif item == 'error out': 2722 raise ZeroDivisionError 2723 else: 2724 # trigger not found 2725 return None 2726 self.assertIs(Color('three'), Color.blue) 2727 try: 2728 Color(7) 2729 except ValueError as exc: 2730 self.assertTrue(exc.__context__ is None) 2731 else: 2732 raise Exception('Exception not raised.') 2733 try: 2734 Color('bad return') 2735 except TypeError as exc: 2736 self.assertTrue(isinstance(exc.__context__, ValueError)) 2737 else: 2738 raise Exception('Exception not raised.') 2739 try: 2740 Color('error out') 2741 except ZeroDivisionError as exc: 2742 self.assertTrue(isinstance(exc.__context__, ValueError)) 2743 else: 2744 raise Exception('Exception not raised.') 2745 2746 def test_missing_exceptions_reset(self): 2747 import gc 2748 import weakref 2749 # 2750 class TestEnum(enum.Enum): 2751 VAL1 = 'val1' 2752 VAL2 = 'val2' 2753 # 2754 class Class1: 2755 def __init__(self): 2756 # Gracefully handle an exception of our own making 2757 try: 2758 raise ValueError() 2759 except ValueError: 2760 pass 2761 # 2762 class Class2: 2763 def __init__(self): 2764 # Gracefully handle an exception of Enum's making 2765 try: 2766 TestEnum('invalid_value') 2767 except ValueError: 2768 pass 2769 # No strong refs here so these are free to die. 2770 class_1_ref = weakref.ref(Class1()) 2771 class_2_ref = weakref.ref(Class2()) 2772 # 2773 # The exception raised by Enum used to create a reference loop and thus 2774 # Class2 instances would stick around until the next garbage collection 2775 # cycle, unlike Class1. Verify Class2 no longer does this. 2776 gc.collect() # For PyPy or other GCs. 2777 self.assertIs(class_1_ref(), None) 2778 self.assertIs(class_2_ref(), None) 2779 2780 def test_multiple_mixin(self): 2781 class MaxMixin: 2782 @classproperty 2783 def MAX(cls): 2784 max = len(cls) 2785 cls.MAX = max 2786 return max 2787 class StrMixin: 2788 def __str__(self): 2789 return self._name_.lower() 2790 class SomeEnum(Enum): 2791 def behavior(self): 2792 return 'booyah' 2793 class AnotherEnum(Enum): 2794 def behavior(self): 2795 return 'nuhuh!' 2796 def social(self): 2797 return "what's up?" 2798 class Color(MaxMixin, Enum): 2799 RED = auto() 2800 GREEN = auto() 2801 BLUE = auto() 2802 self.assertEqual(Color.RED.value, 1) 2803 self.assertEqual(Color.GREEN.value, 2) 2804 self.assertEqual(Color.BLUE.value, 3) 2805 self.assertEqual(Color.MAX, 3) 2806 self.assertEqual(str(Color.BLUE), 'Color.BLUE') 2807 class Color(MaxMixin, StrMixin, Enum): 2808 RED = auto() 2809 GREEN = auto() 2810 BLUE = auto() 2811 __str__ = StrMixin.__str__ # needed as of 3.11 2812 self.assertEqual(Color.RED.value, 1) 2813 self.assertEqual(Color.GREEN.value, 2) 2814 self.assertEqual(Color.BLUE.value, 3) 2815 self.assertEqual(Color.MAX, 3) 2816 self.assertEqual(str(Color.BLUE), 'blue') 2817 class Color(StrMixin, MaxMixin, Enum): 2818 RED = auto() 2819 GREEN = auto() 2820 BLUE = auto() 2821 __str__ = StrMixin.__str__ # needed as of 3.11 2822 self.assertEqual(Color.RED.value, 1) 2823 self.assertEqual(Color.GREEN.value, 2) 2824 self.assertEqual(Color.BLUE.value, 3) 2825 self.assertEqual(Color.MAX, 3) 2826 self.assertEqual(str(Color.BLUE), 'blue') 2827 class CoolColor(StrMixin, SomeEnum, Enum): 2828 RED = auto() 2829 GREEN = auto() 2830 BLUE = auto() 2831 __str__ = StrMixin.__str__ # needed as of 3.11 2832 self.assertEqual(CoolColor.RED.value, 1) 2833 self.assertEqual(CoolColor.GREEN.value, 2) 2834 self.assertEqual(CoolColor.BLUE.value, 3) 2835 self.assertEqual(str(CoolColor.BLUE), 'blue') 2836 self.assertEqual(CoolColor.RED.behavior(), 'booyah') 2837 class CoolerColor(StrMixin, AnotherEnum, Enum): 2838 RED = auto() 2839 GREEN = auto() 2840 BLUE = auto() 2841 __str__ = StrMixin.__str__ # needed as of 3.11 2842 self.assertEqual(CoolerColor.RED.value, 1) 2843 self.assertEqual(CoolerColor.GREEN.value, 2) 2844 self.assertEqual(CoolerColor.BLUE.value, 3) 2845 self.assertEqual(str(CoolerColor.BLUE), 'blue') 2846 self.assertEqual(CoolerColor.RED.behavior(), 'nuhuh!') 2847 self.assertEqual(CoolerColor.RED.social(), "what's up?") 2848 class CoolestColor(StrMixin, SomeEnum, AnotherEnum): 2849 RED = auto() 2850 GREEN = auto() 2851 BLUE = auto() 2852 __str__ = StrMixin.__str__ # needed as of 3.11 2853 self.assertEqual(CoolestColor.RED.value, 1) 2854 self.assertEqual(CoolestColor.GREEN.value, 2) 2855 self.assertEqual(CoolestColor.BLUE.value, 3) 2856 self.assertEqual(str(CoolestColor.BLUE), 'blue') 2857 self.assertEqual(CoolestColor.RED.behavior(), 'booyah') 2858 self.assertEqual(CoolestColor.RED.social(), "what's up?") 2859 class ConfusedColor(StrMixin, AnotherEnum, SomeEnum): 2860 RED = auto() 2861 GREEN = auto() 2862 BLUE = auto() 2863 __str__ = StrMixin.__str__ # needed as of 3.11 2864 self.assertEqual(ConfusedColor.RED.value, 1) 2865 self.assertEqual(ConfusedColor.GREEN.value, 2) 2866 self.assertEqual(ConfusedColor.BLUE.value, 3) 2867 self.assertEqual(str(ConfusedColor.BLUE), 'blue') 2868 self.assertEqual(ConfusedColor.RED.behavior(), 'nuhuh!') 2869 self.assertEqual(ConfusedColor.RED.social(), "what's up?") 2870 class ReformedColor(StrMixin, IntEnum, SomeEnum, AnotherEnum): 2871 RED = auto() 2872 GREEN = auto() 2873 BLUE = auto() 2874 __str__ = StrMixin.__str__ # needed as of 3.11 2875 self.assertEqual(ReformedColor.RED.value, 1) 2876 self.assertEqual(ReformedColor.GREEN.value, 2) 2877 self.assertEqual(ReformedColor.BLUE.value, 3) 2878 self.assertEqual(str(ReformedColor.BLUE), 'blue') 2879 self.assertEqual(ReformedColor.RED.behavior(), 'booyah') 2880 self.assertEqual(ConfusedColor.RED.social(), "what's up?") 2881 self.assertTrue(issubclass(ReformedColor, int)) 2882 2883 def test_multiple_inherited_mixin(self): 2884 @unique 2885 class Decision1(StrEnum): 2886 REVERT = "REVERT" 2887 REVERT_ALL = "REVERT_ALL" 2888 RETRY = "RETRY" 2889 class MyEnum(StrEnum): 2890 pass 2891 @unique 2892 class Decision2(MyEnum): 2893 REVERT = "REVERT" 2894 REVERT_ALL = "REVERT_ALL" 2895 RETRY = "RETRY" 2896 2897 def test_multiple_mixin_inherited(self): 2898 class MyInt(int): 2899 def __new__(cls, value): 2900 return super().__new__(cls, value) 2901 2902 class HexMixin: 2903 def __repr__(self): 2904 return hex(self) 2905 2906 class MyIntEnum(HexMixin, MyInt, enum.Enum): 2907 __repr__ = HexMixin.__repr__ 2908 2909 class Foo(MyIntEnum): 2910 TEST = 1 2911 self.assertTrue(isinstance(Foo.TEST, MyInt)) 2912 self.assertEqual(Foo._member_type_, MyInt) 2913 self.assertEqual(repr(Foo.TEST), "0x1") 2914 2915 class Fee(MyIntEnum): 2916 TEST = 1 2917 def __new__(cls, value): 2918 value += 1 2919 member = int.__new__(cls, value) 2920 member._value_ = value 2921 return member 2922 self.assertEqual(Fee.TEST, 2) 2923 2924 def test_multiple_mixin_with_common_data_type(self): 2925 class CaseInsensitiveStrEnum(str, Enum): 2926 @classmethod 2927 def _missing_(cls, value): 2928 for member in cls._member_map_.values(): 2929 if member._value_.lower() == value.lower(): 2930 return member 2931 return super()._missing_(value) 2932 # 2933 class LenientStrEnum(str, Enum): 2934 def __init__(self, *args): 2935 self._valid = True 2936 @classmethod 2937 def _missing_(cls, value): 2938 unknown = cls._member_type_.__new__(cls, value) 2939 unknown._valid = False 2940 unknown._name_ = value.upper() 2941 unknown._value_ = value 2942 cls._member_map_[value] = unknown 2943 return unknown 2944 @enum.property 2945 def valid(self): 2946 return self._valid 2947 # 2948 class JobStatus(CaseInsensitiveStrEnum, LenientStrEnum): 2949 ACTIVE = "active" 2950 PENDING = "pending" 2951 TERMINATED = "terminated" 2952 # 2953 JS = JobStatus 2954 self.assertEqual(list(JobStatus), [JS.ACTIVE, JS.PENDING, JS.TERMINATED]) 2955 self.assertEqual(JS.ACTIVE, 'active') 2956 self.assertEqual(JS.ACTIVE.value, 'active') 2957 self.assertIs(JS('Active'), JS.ACTIVE) 2958 self.assertTrue(JS.ACTIVE.valid) 2959 missing = JS('missing') 2960 self.assertEqual(list(JobStatus), [JS.ACTIVE, JS.PENDING, JS.TERMINATED]) 2961 self.assertEqual(JS.ACTIVE, 'active') 2962 self.assertEqual(JS.ACTIVE.value, 'active') 2963 self.assertIs(JS('Active'), JS.ACTIVE) 2964 self.assertTrue(JS.ACTIVE.valid) 2965 self.assertTrue(isinstance(missing, JS)) 2966 self.assertFalse(missing.valid) 2967 2968 def test_empty_globals(self): 2969 # bpo-35717: sys._getframe(2).f_globals['__name__'] fails with KeyError 2970 # when using compile and exec because f_globals is empty 2971 code = "from enum import Enum; Enum('Animal', 'ANT BEE CAT DOG')" 2972 code = compile(code, "<string>", "exec") 2973 global_ns = {} 2974 local_ls = {} 2975 exec(code, global_ns, local_ls) 2976 2977 def test_strenum(self): 2978 class GoodStrEnum(StrEnum): 2979 one = '1' 2980 two = '2' 2981 three = b'3', 'ascii' 2982 four = b'4', 'latin1', 'strict' 2983 self.assertEqual(GoodStrEnum.one, '1') 2984 self.assertEqual(str(GoodStrEnum.one), '1') 2985 self.assertEqual('{}'.format(GoodStrEnum.one), '1') 2986 self.assertEqual(GoodStrEnum.one, str(GoodStrEnum.one)) 2987 self.assertEqual(GoodStrEnum.one, '{}'.format(GoodStrEnum.one)) 2988 self.assertEqual(repr(GoodStrEnum.one), "<GoodStrEnum.one: '1'>") 2989 # 2990 class DumbMixin: 2991 def __str__(self): 2992 return "don't do this" 2993 class DumbStrEnum(DumbMixin, StrEnum): 2994 five = '5' 2995 six = '6' 2996 seven = '7' 2997 __str__ = DumbMixin.__str__ # needed as of 3.11 2998 self.assertEqual(DumbStrEnum.seven, '7') 2999 self.assertEqual(str(DumbStrEnum.seven), "don't do this") 3000 # 3001 class EnumMixin(Enum): 3002 def hello(self): 3003 print('hello from %s' % (self, )) 3004 class HelloEnum(EnumMixin, StrEnum): 3005 eight = '8' 3006 self.assertEqual(HelloEnum.eight, '8') 3007 self.assertEqual(HelloEnum.eight, str(HelloEnum.eight)) 3008 # 3009 class GoodbyeMixin: 3010 def goodbye(self): 3011 print('%s wishes you a fond farewell') 3012 class GoodbyeEnum(GoodbyeMixin, EnumMixin, StrEnum): 3013 nine = '9' 3014 self.assertEqual(GoodbyeEnum.nine, '9') 3015 self.assertEqual(GoodbyeEnum.nine, str(GoodbyeEnum.nine)) 3016 # 3017 with self.assertRaisesRegex(TypeError, '1 is not a string'): 3018 class FirstFailedStrEnum(StrEnum): 3019 one = 1 3020 two = '2' 3021 with self.assertRaisesRegex(TypeError, "2 is not a string"): 3022 class SecondFailedStrEnum(StrEnum): 3023 one = '1' 3024 two = 2, 3025 three = '3' 3026 with self.assertRaisesRegex(TypeError, '2 is not a string'): 3027 class ThirdFailedStrEnum(StrEnum): 3028 one = '1' 3029 two = 2 3030 with self.assertRaisesRegex(TypeError, 'encoding must be a string, not %r' % (sys.getdefaultencoding, )): 3031 class ThirdFailedStrEnum(StrEnum): 3032 one = '1' 3033 two = b'2', sys.getdefaultencoding 3034 with self.assertRaisesRegex(TypeError, 'errors must be a string, not 9'): 3035 class ThirdFailedStrEnum(StrEnum): 3036 one = '1' 3037 two = b'2', 'ascii', 9 3038 3039 def test_custom_strenum(self): 3040 class CustomStrEnum(str, Enum): 3041 pass 3042 class OkayEnum(CustomStrEnum): 3043 one = '1' 3044 two = '2' 3045 three = b'3', 'ascii' 3046 four = b'4', 'latin1', 'strict' 3047 self.assertEqual(OkayEnum.one, '1') 3048 self.assertEqual(str(OkayEnum.one), 'OkayEnum.one') 3049 self.assertEqual('{}'.format(OkayEnum.one), 'OkayEnum.one') 3050 self.assertEqual(repr(OkayEnum.one), "<OkayEnum.one: '1'>") 3051 # 3052 class DumbMixin: 3053 def __str__(self): 3054 return "don't do this" 3055 class DumbStrEnum(DumbMixin, CustomStrEnum): 3056 five = '5' 3057 six = '6' 3058 seven = '7' 3059 __str__ = DumbMixin.__str__ # needed as of 3.11 3060 self.assertEqual(DumbStrEnum.seven, '7') 3061 self.assertEqual(str(DumbStrEnum.seven), "don't do this") 3062 # 3063 class EnumMixin(Enum): 3064 def hello(self): 3065 print('hello from %s' % (self, )) 3066 class HelloEnum(EnumMixin, CustomStrEnum): 3067 eight = '8' 3068 self.assertEqual(HelloEnum.eight, '8') 3069 self.assertEqual(str(HelloEnum.eight), 'HelloEnum.eight') 3070 # 3071 class GoodbyeMixin: 3072 def goodbye(self): 3073 print('%s wishes you a fond farewell') 3074 class GoodbyeEnum(GoodbyeMixin, EnumMixin, CustomStrEnum): 3075 nine = '9' 3076 self.assertEqual(GoodbyeEnum.nine, '9') 3077 self.assertEqual(str(GoodbyeEnum.nine), 'GoodbyeEnum.nine') 3078 # 3079 class FirstFailedStrEnum(CustomStrEnum): 3080 one = 1 # this will become '1' 3081 two = '2' 3082 class SecondFailedStrEnum(CustomStrEnum): 3083 one = '1' 3084 two = 2, # this will become '2' 3085 three = '3' 3086 class ThirdFailedStrEnum(CustomStrEnum): 3087 one = '1' 3088 two = 2 # this will become '2' 3089 with self.assertRaisesRegex(TypeError, 3090 r"argument (2|'encoding') must be str, not "): 3091 class ThirdFailedStrEnum(CustomStrEnum): 3092 one = '1' 3093 two = b'2', sys.getdefaultencoding 3094 with self.assertRaisesRegex(TypeError, 3095 r"argument (3|'errors') must be str, not "): 3096 class ThirdFailedStrEnum(CustomStrEnum): 3097 one = '1' 3098 two = b'2', 'ascii', 9 3099 3100 def test_missing_value_error(self): 3101 with self.assertRaisesRegex(TypeError, "_value_ not set in __new__"): 3102 class Combined(str, Enum): 3103 # 3104 def __new__(cls, value, sequence): 3105 enum = str.__new__(cls, value) 3106 if '(' in value: 3107 fis_name, segment = value.split('(', 1) 3108 segment = segment.strip(' )') 3109 else: 3110 fis_name = value 3111 segment = None 3112 enum.fis_name = fis_name 3113 enum.segment = segment 3114 enum.sequence = sequence 3115 return enum 3116 # 3117 def __repr__(self): 3118 return "<%s.%s>" % (self.__class__.__name__, self._name_) 3119 # 3120 key_type = 'An$(1,2)', 0 3121 company_id = 'An$(3,2)', 1 3122 code = 'An$(5,1)', 2 3123 description = 'Bn$', 3 3124 3125 3126 def test_private_variable_is_normal_attribute(self): 3127 class Private(Enum): 3128 __corporal = 'Radar' 3129 __major_ = 'Hoolihan' 3130 self.assertEqual(Private._Private__corporal, 'Radar') 3131 self.assertEqual(Private._Private__major_, 'Hoolihan') 3132 3133 def test_member_from_member_access(self): 3134 class Di(Enum): 3135 YES = 1 3136 NO = 0 3137 name = 3 3138 warn = Di.YES.NO 3139 self.assertIs(warn, Di.NO) 3140 self.assertIs(Di.name, Di['name']) 3141 self.assertEqual(Di.name.name, 'name') 3142 3143 def test_dynamic_members_with_static_methods(self): 3144 # 3145 foo_defines = {'FOO_CAT': 'aloof', 'BAR_DOG': 'friendly', 'FOO_HORSE': 'big'} 3146 class Foo(Enum): 3147 vars().update({ 3148 k: v 3149 for k, v in foo_defines.items() 3150 if k.startswith('FOO_') 3151 }) 3152 def upper(self): 3153 return self.value.upper() 3154 self.assertEqual(list(Foo), [Foo.FOO_CAT, Foo.FOO_HORSE]) 3155 self.assertEqual(Foo.FOO_CAT.value, 'aloof') 3156 self.assertEqual(Foo.FOO_HORSE.upper(), 'BIG') 3157 # 3158 with self.assertRaisesRegex(TypeError, "'FOO_CAT' already defined as 'aloof'"): 3159 class FooBar(Enum): 3160 vars().update({ 3161 k: v 3162 for k, v in foo_defines.items() 3163 if k.startswith('FOO_') 3164 }, 3165 **{'FOO_CAT': 'small'}, 3166 ) 3167 def upper(self): 3168 return self.value.upper() 3169 3170 def test_repr_with_dataclass(self): 3171 "ensure dataclass-mixin has correct repr()" 3172 # 3173 # check overridden dataclass __repr__ is used 3174 # 3175 from dataclasses import dataclass, field 3176 @dataclass(repr=False) 3177 class Foo: 3178 __qualname__ = 'Foo' 3179 a: int 3180 def __repr__(self): 3181 return 'ha hah!' 3182 class Entries(Foo, Enum): 3183 ENTRY1 = 1 3184 self.assertEqual(repr(Entries.ENTRY1), '<Entries.ENTRY1: ha hah!>') 3185 self.assertTrue(Entries.ENTRY1.value == Foo(1), Entries.ENTRY1.value) 3186 self.assertTrue(isinstance(Entries.ENTRY1, Foo)) 3187 self.assertTrue(Entries._member_type_ is Foo, Entries._member_type_) 3188 # 3189 # check auto-generated dataclass __repr__ is not used 3190 # 3191 @dataclass 3192 class CreatureDataMixin: 3193 __qualname__ = 'CreatureDataMixin' 3194 size: str 3195 legs: int 3196 tail: bool = field(repr=False, default=True) 3197 class Creature(CreatureDataMixin, Enum): 3198 __qualname__ = 'Creature' 3199 BEETLE = ('small', 6) 3200 DOG = ('medium', 4) 3201 self.assertEqual(repr(Creature.DOG), "<Creature.DOG: size='medium', legs=4>") 3202 # 3203 # check inherited repr used 3204 # 3205 class Huh: 3206 def __repr__(self): 3207 return 'inherited' 3208 @dataclass(repr=False) 3209 class CreatureDataMixin(Huh): 3210 __qualname__ = 'CreatureDataMixin' 3211 size: str 3212 legs: int 3213 tail: bool = field(repr=False, default=True) 3214 class Creature(CreatureDataMixin, Enum): 3215 __qualname__ = 'Creature' 3216 BEETLE = ('small', 6) 3217 DOG = ('medium', 4) 3218 self.assertEqual(repr(Creature.DOG), "<Creature.DOG: inherited>") 3219 # 3220 # check default object.__repr__ used if nothing provided 3221 # 3222 @dataclass(repr=False) 3223 class CreatureDataMixin: 3224 __qualname__ = 'CreatureDataMixin' 3225 size: str 3226 legs: int 3227 tail: bool = field(repr=False, default=True) 3228 class Creature(CreatureDataMixin, Enum): 3229 __qualname__ = 'Creature' 3230 BEETLE = ('small', 6) 3231 DOG = ('medium', 4) 3232 self.assertRegex(repr(Creature.DOG), "<Creature.DOG: .*CreatureDataMixin object at .*>") 3233 3234 def test_repr_with_init_mixin(self): 3235 class Foo: 3236 def __init__(self, a): 3237 self.a = a 3238 def __repr__(self): 3239 return f'Foo(a={self.a!r})' 3240 class Entries(Foo, Enum): 3241 ENTRY1 = 1 3242 # 3243 self.assertEqual(repr(Entries.ENTRY1), 'Foo(a=1)') 3244 3245 def test_repr_and_str_with_no_init_mixin(self): 3246 # non-data_type is a mixin that doesn't define __new__ 3247 class Foo: 3248 def __repr__(self): 3249 return 'Foo' 3250 def __str__(self): 3251 return 'ooF' 3252 class Entries(Foo, Enum): 3253 ENTRY1 = 1 3254 # 3255 self.assertEqual(repr(Entries.ENTRY1), 'Foo') 3256 self.assertEqual(str(Entries.ENTRY1), 'ooF') 3257 3258 def test_value_backup_assign(self): 3259 # check that enum will add missing values when custom __new__ does not 3260 class Some(Enum): 3261 def __new__(cls, val): 3262 return object.__new__(cls) 3263 x = 1 3264 y = 2 3265 self.assertEqual(Some.x.value, 1) 3266 self.assertEqual(Some.y.value, 2) 3267 3268 def test_custom_flag_bitwise(self): 3269 class MyIntFlag(int, Flag): 3270 ONE = 1 3271 TWO = 2 3272 FOUR = 4 3273 self.assertTrue(isinstance(MyIntFlag.ONE | MyIntFlag.TWO, MyIntFlag), MyIntFlag.ONE | MyIntFlag.TWO) 3274 self.assertTrue(isinstance(MyIntFlag.ONE | 2, MyIntFlag)) 3275 3276 def test_int_flags_copy(self): 3277 class MyIntFlag(IntFlag): 3278 ONE = 1 3279 TWO = 2 3280 FOUR = 4 3281 3282 flags = MyIntFlag.ONE | MyIntFlag.TWO 3283 copied = copy.copy(flags) 3284 deep = copy.deepcopy(flags) 3285 self.assertEqual(copied, flags) 3286 self.assertEqual(deep, flags) 3287 3288 flags = MyIntFlag.ONE | MyIntFlag.TWO | 8 3289 copied = copy.copy(flags) 3290 deep = copy.deepcopy(flags) 3291 self.assertEqual(copied, flags) 3292 self.assertEqual(deep, flags) 3293 self.assertEqual(copied.value, 1 | 2 | 8) 3294 3295 def test_namedtuple_as_value(self): 3296 from collections import namedtuple 3297 TTuple = namedtuple('TTuple', 'id a blist') 3298 class NTEnum(Enum): 3299 NONE = TTuple(0, 0, []) 3300 A = TTuple(1, 2, [4]) 3301 B = TTuple(2, 4, [0, 1, 2]) 3302 self.assertEqual(repr(NTEnum.NONE), "<NTEnum.NONE: TTuple(id=0, a=0, blist=[])>") 3303 self.assertEqual(NTEnum.NONE.value, TTuple(id=0, a=0, blist=[])) 3304 self.assertEqual( 3305 [x.value for x in NTEnum], 3306 [TTuple(id=0, a=0, blist=[]), TTuple(id=1, a=2, blist=[4]), TTuple(id=2, a=4, blist=[0, 1, 2])], 3307 ) 3308 3309 self.assertRaises(AttributeError, getattr, NTEnum.NONE, 'id') 3310 # 3311 class NTCEnum(TTuple, Enum): 3312 NONE = 0, 0, [] 3313 A = 1, 2, [4] 3314 B = 2, 4, [0, 1, 2] 3315 self.assertEqual(repr(NTCEnum.NONE), "<NTCEnum.NONE: TTuple(id=0, a=0, blist=[])>") 3316 self.assertEqual(NTCEnum.NONE.value, TTuple(id=0, a=0, blist=[])) 3317 self.assertEqual(NTCEnum.NONE.id, 0) 3318 self.assertEqual(NTCEnum.A.a, 2) 3319 self.assertEqual(NTCEnum.B.blist, [0, 1 ,2]) 3320 self.assertEqual( 3321 [x.value for x in NTCEnum], 3322 [TTuple(id=0, a=0, blist=[]), TTuple(id=1, a=2, blist=[4]), TTuple(id=2, a=4, blist=[0, 1, 2])], 3323 ) 3324 # 3325 class NTDEnum(Enum): 3326 def __new__(cls, id, a, blist): 3327 member = object.__new__(cls) 3328 member.id = id 3329 member.a = a 3330 member.blist = blist 3331 return member 3332 NONE = TTuple(0, 0, []) 3333 A = TTuple(1, 2, [4]) 3334 B = TTuple(2, 4, [0, 1, 2]) 3335 self.assertEqual(repr(NTDEnum.NONE), "<NTDEnum.NONE: TTuple(id=0, a=0, blist=[])>") 3336 self.assertEqual(NTDEnum.NONE.id, 0) 3337 self.assertEqual(NTDEnum.A.a, 2) 3338 self.assertEqual(NTDEnum.B.blist, [0, 1 ,2]) 3339 3340 def test_flag_with_custom_new(self): 3341 class FlagFromChar(IntFlag): 3342 def __new__(cls, c): 3343 value = 1 << c 3344 self = int.__new__(cls, value) 3345 self._value_ = value 3346 return self 3347 # 3348 a = ord('a') 3349 # 3350 self.assertEqual(FlagFromChar._all_bits_, 316912650057057350374175801343) 3351 self.assertEqual(FlagFromChar._flag_mask_, 158456325028528675187087900672) 3352 self.assertEqual(FlagFromChar.a, 158456325028528675187087900672) 3353 self.assertEqual(FlagFromChar.a|1, 158456325028528675187087900673) 3354 # 3355 # 3356 class FlagFromChar(Flag): 3357 def __new__(cls, c): 3358 value = 1 << c 3359 self = object.__new__(cls) 3360 self._value_ = value 3361 return self 3362 # 3363 a = ord('a') 3364 z = 1 3365 # 3366 self.assertEqual(FlagFromChar._all_bits_, 316912650057057350374175801343) 3367 self.assertEqual(FlagFromChar._flag_mask_, 158456325028528675187087900674) 3368 self.assertEqual(FlagFromChar.a.value, 158456325028528675187087900672) 3369 self.assertEqual((FlagFromChar.a|FlagFromChar.z).value, 158456325028528675187087900674) 3370 # 3371 # 3372 class FlagFromChar(int, Flag, boundary=KEEP): 3373 def __new__(cls, c): 3374 value = 1 << c 3375 self = int.__new__(cls, value) 3376 self._value_ = value 3377 return self 3378 # 3379 a = ord('a') 3380 # 3381 self.assertEqual(FlagFromChar._all_bits_, 316912650057057350374175801343) 3382 self.assertEqual(FlagFromChar._flag_mask_, 158456325028528675187087900672) 3383 self.assertEqual(FlagFromChar.a, 158456325028528675187087900672) 3384 self.assertEqual(FlagFromChar.a|1, 158456325028528675187087900673) 3385 3386 def test_init_exception(self): 3387 class Base: 3388 def __new__(cls, *args): 3389 return object.__new__(cls) 3390 def __init__(self, x): 3391 raise ValueError("I don't like", x) 3392 with self.assertRaises(TypeError): 3393 class MyEnum(Base, enum.Enum): 3394 A = 'a' 3395 def __init__(self, y): 3396 self.y = y 3397 with self.assertRaises(ValueError): 3398 class MyEnum(Base, enum.Enum): 3399 A = 'a' 3400 def __init__(self, y): 3401 self.y = y 3402 def __new__(cls, value): 3403 member = Base.__new__(cls) 3404 member._value_ = Base(value) 3405 return member 3406 3407 def test_extra_member_creation(self): 3408 class IDEnumMeta(EnumMeta): 3409 def __new__(metacls, cls, bases, classdict, **kwds): 3410 # add new entries to classdict 3411 for name in classdict.member_names: 3412 classdict[f'{name}_DESC'] = f'-{classdict[name]}' 3413 return super().__new__(metacls, cls, bases, classdict, **kwds) 3414 class IDEnum(StrEnum, metaclass=IDEnumMeta): 3415 pass 3416 class MyEnum(IDEnum): 3417 ID = 'id' 3418 NAME = 'name' 3419 self.assertEqual(list(MyEnum), [MyEnum.ID, MyEnum.NAME, MyEnum.ID_DESC, MyEnum.NAME_DESC]) 3420 3421 def test_add_alias(self): 3422 class mixin: 3423 @property 3424 def ORG(self): 3425 return 'huh' 3426 class Color(mixin, Enum): 3427 RED = 1 3428 GREEN = 2 3429 BLUE = 3 3430 Color.RED._add_alias_('ROJO') 3431 self.assertIs(Color.RED, Color['ROJO']) 3432 self.assertIs(Color.RED, Color.ROJO) 3433 Color.BLUE._add_alias_('ORG') 3434 self.assertIs(Color.BLUE, Color['ORG']) 3435 self.assertIs(Color.BLUE, Color.ORG) 3436 self.assertEqual(Color.RED.ORG, 'huh') 3437 self.assertEqual(Color.GREEN.ORG, 'huh') 3438 self.assertEqual(Color.BLUE.ORG, 'huh') 3439 self.assertEqual(Color.ORG.ORG, 'huh') 3440 3441 def test_add_value_alias_after_creation(self): 3442 class Color(Enum): 3443 RED = 1 3444 GREEN = 2 3445 BLUE = 3 3446 Color.RED._add_value_alias_(5) 3447 self.assertIs(Color.RED, Color(5)) 3448 3449 def test_add_value_alias_during_creation(self): 3450 class Types(Enum): 3451 Unknown = 0, 3452 Source = 1, 'src' 3453 NetList = 2, 'nl' 3454 def __new__(cls, int_value, *value_aliases): 3455 member = object.__new__(cls) 3456 member._value_ = int_value 3457 for alias in value_aliases: 3458 member._add_value_alias_(alias) 3459 return member 3460 self.assertIs(Types(0), Types.Unknown) 3461 self.assertIs(Types(1), Types.Source) 3462 self.assertIs(Types('src'), Types.Source) 3463 self.assertIs(Types(2), Types.NetList) 3464 self.assertIs(Types('nl'), Types.NetList) 3465 3466 def test_second_tuple_item_is_falsey(self): 3467 class Cardinal(Enum): 3468 RIGHT = (1, 0) 3469 UP = (0, 1) 3470 LEFT = (-1, 0) 3471 DOWN = (0, -1) 3472 self.assertIs(Cardinal(1, 0), Cardinal.RIGHT) 3473 self.assertIs(Cardinal(-1, 0), Cardinal.LEFT) 3474 3475 def test_no_members(self): 3476 with self.assertRaisesRegex( 3477 TypeError, 3478 'has no members', 3479 ): 3480 Enum(7) 3481 with self.assertRaisesRegex( 3482 TypeError, 3483 'has no members', 3484 ): 3485 Flag(7) 3486 3487 def test_empty_names(self): 3488 for nothing in '', [], {}: 3489 for e_type in None, int: 3490 empty_enum = Enum('empty_enum', nothing, type=e_type) 3491 self.assertEqual(len(empty_enum), 0) 3492 self.assertRaisesRegex(TypeError, 'has no members', empty_enum, 0) 3493 self.assertRaisesRegex(TypeError, '.int. object is not iterable', Enum, 'bad_enum', names=0) 3494 self.assertRaisesRegex(TypeError, '.int. object is not iterable', Enum, 'bad_enum', 0, type=int) 3495 3496 def test_nonhashable_matches_hashable(self): # issue 125710 3497 class Directions(Enum): 3498 DOWN_ONLY = frozenset({"sc"}) 3499 UP_ONLY = frozenset({"cs"}) 3500 UNRESTRICTED = frozenset({"sc", "cs"}) 3501 self.assertIs(Directions({"sc"}), Directions.DOWN_ONLY) 3502 3503 3504class TestOrder(unittest.TestCase): 3505 "test usage of the `_order_` attribute" 3506 3507 def test_same_members(self): 3508 class Color(Enum): 3509 _order_ = 'red green blue' 3510 red = 1 3511 green = 2 3512 blue = 3 3513 3514 def test_same_members_with_aliases(self): 3515 class Color(Enum): 3516 _order_ = 'red green blue' 3517 red = 1 3518 green = 2 3519 blue = 3 3520 verde = green 3521 3522 def test_same_members_wrong_order(self): 3523 with self.assertRaisesRegex(TypeError, 'member order does not match _order_'): 3524 class Color(Enum): 3525 _order_ = 'red green blue' 3526 red = 1 3527 blue = 3 3528 green = 2 3529 3530 def test_order_has_extra_members(self): 3531 with self.assertRaisesRegex(TypeError, 'member order does not match _order_'): 3532 class Color(Enum): 3533 _order_ = 'red green blue purple' 3534 red = 1 3535 green = 2 3536 blue = 3 3537 3538 def test_order_has_extra_members_with_aliases(self): 3539 with self.assertRaisesRegex(TypeError, 'member order does not match _order_'): 3540 class Color(Enum): 3541 _order_ = 'red green blue purple' 3542 red = 1 3543 green = 2 3544 blue = 3 3545 verde = green 3546 3547 def test_enum_has_extra_members(self): 3548 with self.assertRaisesRegex(TypeError, 'member order does not match _order_'): 3549 class Color(Enum): 3550 _order_ = 'red green blue' 3551 red = 1 3552 green = 2 3553 blue = 3 3554 purple = 4 3555 3556 def test_enum_has_extra_members_with_aliases(self): 3557 with self.assertRaisesRegex(TypeError, 'member order does not match _order_'): 3558 class Color(Enum): 3559 _order_ = 'red green blue' 3560 red = 1 3561 green = 2 3562 blue = 3 3563 purple = 4 3564 verde = green 3565 3566 3567class OldTestFlag(unittest.TestCase): 3568 """Tests of the Flags.""" 3569 3570 class Perm(Flag): 3571 R, W, X = 4, 2, 1 3572 3573 class Open(Flag): 3574 RO = 0 3575 WO = 1 3576 RW = 2 3577 AC = 3 3578 CE = 1<<19 3579 3580 class Color(Flag): 3581 BLACK = 0 3582 RED = 1 3583 ROJO = 1 3584 GREEN = 2 3585 BLUE = 4 3586 PURPLE = RED|BLUE 3587 WHITE = RED|GREEN|BLUE 3588 BLANCO = RED|GREEN|BLUE 3589 3590 def test_or(self): 3591 Perm = self.Perm 3592 for i in Perm: 3593 for j in Perm: 3594 self.assertEqual((i | j), Perm(i.value | j.value)) 3595 self.assertEqual((i | j).value, i.value | j.value) 3596 self.assertIs(type(i | j), Perm) 3597 for i in Perm: 3598 self.assertIs(i | i, i) 3599 Open = self.Open 3600 self.assertIs(Open.RO | Open.CE, Open.CE) 3601 3602 def test_and(self): 3603 Perm = self.Perm 3604 RW = Perm.R | Perm.W 3605 RX = Perm.R | Perm.X 3606 WX = Perm.W | Perm.X 3607 RWX = Perm.R | Perm.W | Perm.X 3608 values = list(Perm) + [RW, RX, WX, RWX, Perm(0)] 3609 for i in values: 3610 for j in values: 3611 self.assertEqual((i & j).value, i.value & j.value) 3612 self.assertIs(type(i & j), Perm) 3613 for i in Perm: 3614 self.assertIs(i & i, i) 3615 self.assertIs(i & RWX, i) 3616 self.assertIs(RWX & i, i) 3617 Open = self.Open 3618 self.assertIs(Open.RO & Open.CE, Open.RO) 3619 3620 def test_xor(self): 3621 Perm = self.Perm 3622 for i in Perm: 3623 for j in Perm: 3624 self.assertEqual((i ^ j).value, i.value ^ j.value) 3625 self.assertIs(type(i ^ j), Perm) 3626 for i in Perm: 3627 self.assertIs(i ^ Perm(0), i) 3628 self.assertIs(Perm(0) ^ i, i) 3629 Open = self.Open 3630 self.assertIs(Open.RO ^ Open.CE, Open.CE) 3631 self.assertIs(Open.CE ^ Open.CE, Open.RO) 3632 3633 def test_bool(self): 3634 Perm = self.Perm 3635 for f in Perm: 3636 self.assertTrue(f) 3637 Open = self.Open 3638 for f in Open: 3639 self.assertEqual(bool(f.value), bool(f)) 3640 3641 def test_boundary(self): 3642 self.assertIs(enum.Flag._boundary_, STRICT) 3643 class Iron(Flag, boundary=CONFORM): 3644 ONE = 1 3645 TWO = 2 3646 EIGHT = 8 3647 self.assertIs(Iron._boundary_, CONFORM) 3648 # 3649 class Water(Flag, boundary=STRICT): 3650 ONE = 1 3651 TWO = 2 3652 EIGHT = 8 3653 self.assertIs(Water._boundary_, STRICT) 3654 # 3655 class Space(Flag, boundary=EJECT): 3656 ONE = 1 3657 TWO = 2 3658 EIGHT = 8 3659 self.assertIs(Space._boundary_, EJECT) 3660 # 3661 class Bizarre(Flag, boundary=KEEP): 3662 b = 3 3663 c = 4 3664 d = 6 3665 # 3666 self.assertRaisesRegex(ValueError, 'invalid value 7', Water, 7) 3667 # 3668 self.assertIs(Iron(7), Iron.ONE|Iron.TWO) 3669 self.assertIs(Iron(~9), Iron.TWO) 3670 # 3671 self.assertEqual(Space(7), 7) 3672 self.assertTrue(type(Space(7)) is int) 3673 # 3674 self.assertEqual(list(Bizarre), [Bizarre.c]) 3675 self.assertIs(Bizarre(3), Bizarre.b) 3676 self.assertIs(Bizarre(6), Bizarre.d) 3677 # 3678 class SkipFlag(enum.Flag): 3679 A = 1 3680 B = 2 3681 C = 4 | B 3682 # 3683 self.assertTrue(SkipFlag.C in (SkipFlag.A|SkipFlag.C)) 3684 self.assertRaisesRegex(ValueError, 'SkipFlag.. invalid value 42', SkipFlag, 42) 3685 # 3686 class SkipIntFlag(enum.IntFlag): 3687 A = 1 3688 B = 2 3689 C = 4 | B 3690 # 3691 self.assertTrue(SkipIntFlag.C in (SkipIntFlag.A|SkipIntFlag.C)) 3692 self.assertEqual(SkipIntFlag(42).value, 42) 3693 # 3694 class MethodHint(Flag): 3695 HiddenText = 0x10 3696 DigitsOnly = 0x01 3697 LettersOnly = 0x02 3698 OnlyMask = 0x0f 3699 # 3700 self.assertEqual(str(MethodHint.HiddenText|MethodHint.OnlyMask), 'MethodHint.HiddenText|DigitsOnly|LettersOnly|OnlyMask') 3701 3702 3703 def test_iter(self): 3704 Color = self.Color 3705 Open = self.Open 3706 self.assertEqual(list(Color), [Color.RED, Color.GREEN, Color.BLUE]) 3707 self.assertEqual(list(Open), [Open.WO, Open.RW, Open.CE]) 3708 3709 def test_programatic_function_string(self): 3710 Perm = Flag('Perm', 'R W X') 3711 lst = list(Perm) 3712 self.assertEqual(len(lst), len(Perm)) 3713 self.assertEqual(len(Perm), 3, Perm) 3714 self.assertEqual(lst, [Perm.R, Perm.W, Perm.X]) 3715 for i, n in enumerate('R W X'.split()): 3716 v = 1<<i 3717 e = Perm(v) 3718 self.assertEqual(e.value, v) 3719 self.assertEqual(type(e.value), int) 3720 self.assertEqual(e.name, n) 3721 self.assertIn(e, Perm) 3722 self.assertIs(type(e), Perm) 3723 3724 def test_programatic_function_string_with_start(self): 3725 Perm = Flag('Perm', 'R W X', start=8) 3726 lst = list(Perm) 3727 self.assertEqual(len(lst), len(Perm)) 3728 self.assertEqual(len(Perm), 3, Perm) 3729 self.assertEqual(lst, [Perm.R, Perm.W, Perm.X]) 3730 for i, n in enumerate('R W X'.split()): 3731 v = 8<<i 3732 e = Perm(v) 3733 self.assertEqual(e.value, v) 3734 self.assertEqual(type(e.value), int) 3735 self.assertEqual(e.name, n) 3736 self.assertIn(e, Perm) 3737 self.assertIs(type(e), Perm) 3738 3739 def test_programatic_function_string_list(self): 3740 Perm = Flag('Perm', ['R', 'W', 'X']) 3741 lst = list(Perm) 3742 self.assertEqual(len(lst), len(Perm)) 3743 self.assertEqual(len(Perm), 3, Perm) 3744 self.assertEqual(lst, [Perm.R, Perm.W, Perm.X]) 3745 for i, n in enumerate('R W X'.split()): 3746 v = 1<<i 3747 e = Perm(v) 3748 self.assertEqual(e.value, v) 3749 self.assertEqual(type(e.value), int) 3750 self.assertEqual(e.name, n) 3751 self.assertIn(e, Perm) 3752 self.assertIs(type(e), Perm) 3753 3754 def test_programatic_function_iterable(self): 3755 Perm = Flag('Perm', (('R', 2), ('W', 8), ('X', 32))) 3756 lst = list(Perm) 3757 self.assertEqual(len(lst), len(Perm)) 3758 self.assertEqual(len(Perm), 3, Perm) 3759 self.assertEqual(lst, [Perm.R, Perm.W, Perm.X]) 3760 for i, n in enumerate('R W X'.split()): 3761 v = 1<<(2*i+1) 3762 e = Perm(v) 3763 self.assertEqual(e.value, v) 3764 self.assertEqual(type(e.value), int) 3765 self.assertEqual(e.name, n) 3766 self.assertIn(e, Perm) 3767 self.assertIs(type(e), Perm) 3768 3769 def test_programatic_function_from_dict(self): 3770 Perm = Flag('Perm', OrderedDict((('R', 2), ('W', 8), ('X', 32)))) 3771 lst = list(Perm) 3772 self.assertEqual(len(lst), len(Perm)) 3773 self.assertEqual(len(Perm), 3, Perm) 3774 self.assertEqual(lst, [Perm.R, Perm.W, Perm.X]) 3775 for i, n in enumerate('R W X'.split()): 3776 v = 1<<(2*i+1) 3777 e = Perm(v) 3778 self.assertEqual(e.value, v) 3779 self.assertEqual(type(e.value), int) 3780 self.assertEqual(e.name, n) 3781 self.assertIn(e, Perm) 3782 self.assertIs(type(e), Perm) 3783 3784 @reraise_if_not_enum( 3785 FlagStooges, 3786 FlagStoogesWithZero, 3787 IntFlagStooges, 3788 IntFlagStoogesWithZero, 3789 ) 3790 def test_pickle(self): 3791 test_pickle_dump_load(self.assertIs, FlagStooges.CURLY) 3792 test_pickle_dump_load(self.assertEqual, 3793 FlagStooges.CURLY|FlagStooges.MOE) 3794 test_pickle_dump_load(self.assertEqual, 3795 FlagStooges.CURLY&~FlagStooges.CURLY) 3796 test_pickle_dump_load(self.assertIs, FlagStooges) 3797 test_pickle_dump_load(self.assertEqual, FlagStooges.BIG) 3798 test_pickle_dump_load(self.assertEqual, 3799 FlagStooges.CURLY|FlagStooges.BIG) 3800 3801 test_pickle_dump_load(self.assertIs, FlagStoogesWithZero.CURLY) 3802 test_pickle_dump_load(self.assertEqual, 3803 FlagStoogesWithZero.CURLY|FlagStoogesWithZero.MOE) 3804 test_pickle_dump_load(self.assertIs, FlagStoogesWithZero.NOFLAG) 3805 test_pickle_dump_load(self.assertEqual, FlagStoogesWithZero.BIG) 3806 test_pickle_dump_load(self.assertEqual, 3807 FlagStoogesWithZero.CURLY|FlagStoogesWithZero.BIG) 3808 3809 test_pickle_dump_load(self.assertIs, IntFlagStooges.CURLY) 3810 test_pickle_dump_load(self.assertEqual, 3811 IntFlagStooges.CURLY|IntFlagStooges.MOE) 3812 test_pickle_dump_load(self.assertEqual, 3813 IntFlagStooges.CURLY|IntFlagStooges.MOE|0x30) 3814 test_pickle_dump_load(self.assertEqual, IntFlagStooges(0)) 3815 test_pickle_dump_load(self.assertEqual, IntFlagStooges(0x30)) 3816 test_pickle_dump_load(self.assertIs, IntFlagStooges) 3817 test_pickle_dump_load(self.assertEqual, IntFlagStooges.BIG) 3818 test_pickle_dump_load(self.assertEqual, IntFlagStooges.BIG|1) 3819 test_pickle_dump_load(self.assertEqual, 3820 IntFlagStooges.CURLY|IntFlagStooges.BIG) 3821 3822 test_pickle_dump_load(self.assertIs, IntFlagStoogesWithZero.CURLY) 3823 test_pickle_dump_load(self.assertEqual, 3824 IntFlagStoogesWithZero.CURLY|IntFlagStoogesWithZero.MOE) 3825 test_pickle_dump_load(self.assertIs, IntFlagStoogesWithZero.NOFLAG) 3826 test_pickle_dump_load(self.assertEqual, IntFlagStoogesWithZero.BIG) 3827 test_pickle_dump_load(self.assertEqual, IntFlagStoogesWithZero.BIG|1) 3828 test_pickle_dump_load(self.assertEqual, 3829 IntFlagStoogesWithZero.CURLY|IntFlagStoogesWithZero.BIG) 3830 3831 def test_contains_tf(self): 3832 Open = self.Open 3833 Color = self.Color 3834 self.assertFalse(Color.BLACK in Open) 3835 self.assertFalse(Open.RO in Color) 3836 self.assertFalse('BLACK' in Color) 3837 self.assertFalse('RO' in Open) 3838 self.assertTrue(Color.BLACK in Color) 3839 self.assertTrue(Open.RO in Open) 3840 self.assertTrue(1 in Color) 3841 self.assertTrue(1 in Open) 3842 3843 def test_member_contains(self): 3844 Perm = self.Perm 3845 R, W, X = Perm 3846 RW = R | W 3847 RX = R | X 3848 WX = W | X 3849 RWX = R | W | X 3850 self.assertTrue(R in RW) 3851 self.assertTrue(R in RX) 3852 self.assertTrue(R in RWX) 3853 self.assertTrue(W in RW) 3854 self.assertTrue(W in WX) 3855 self.assertTrue(W in RWX) 3856 self.assertTrue(X in RX) 3857 self.assertTrue(X in WX) 3858 self.assertTrue(X in RWX) 3859 self.assertFalse(R in WX) 3860 self.assertFalse(W in RX) 3861 self.assertFalse(X in RW) 3862 3863 def test_member_iter(self): 3864 Color = self.Color 3865 self.assertEqual(list(Color.BLACK), []) 3866 self.assertEqual(list(Color.PURPLE), [Color.RED, Color.BLUE]) 3867 self.assertEqual(list(Color.BLUE), [Color.BLUE]) 3868 self.assertEqual(list(Color.GREEN), [Color.GREEN]) 3869 self.assertEqual(list(Color.WHITE), [Color.RED, Color.GREEN, Color.BLUE]) 3870 self.assertEqual(list(Color.WHITE), [Color.RED, Color.GREEN, Color.BLUE]) 3871 3872 def test_member_length(self): 3873 self.assertEqual(self.Color.__len__(self.Color.BLACK), 0) 3874 self.assertEqual(self.Color.__len__(self.Color.GREEN), 1) 3875 self.assertEqual(self.Color.__len__(self.Color.PURPLE), 2) 3876 self.assertEqual(self.Color.__len__(self.Color.BLANCO), 3) 3877 3878 def test_number_reset_and_order_cleanup(self): 3879 class Confused(Flag): 3880 _order_ = 'ONE TWO FOUR DOS EIGHT SIXTEEN' 3881 ONE = auto() 3882 TWO = auto() 3883 FOUR = auto() 3884 DOS = 2 3885 EIGHT = auto() 3886 SIXTEEN = auto() 3887 self.assertEqual( 3888 list(Confused), 3889 [Confused.ONE, Confused.TWO, Confused.FOUR, Confused.EIGHT, Confused.SIXTEEN]) 3890 self.assertIs(Confused.TWO, Confused.DOS) 3891 self.assertEqual(Confused.DOS._value_, 2) 3892 self.assertEqual(Confused.EIGHT._value_, 8) 3893 self.assertEqual(Confused.SIXTEEN._value_, 16) 3894 3895 def test_aliases(self): 3896 Color = self.Color 3897 self.assertEqual(Color(1).name, 'RED') 3898 self.assertEqual(Color['ROJO'].name, 'RED') 3899 self.assertEqual(Color(7).name, 'WHITE') 3900 self.assertEqual(Color['BLANCO'].name, 'WHITE') 3901 self.assertIs(Color.BLANCO, Color.WHITE) 3902 Open = self.Open 3903 self.assertIs(Open['AC'], Open.AC) 3904 3905 def test_auto_number(self): 3906 class Color(Flag): 3907 red = auto() 3908 blue = auto() 3909 green = auto() 3910 3911 self.assertEqual(list(Color), [Color.red, Color.blue, Color.green]) 3912 self.assertEqual(Color.red.value, 1) 3913 self.assertEqual(Color.blue.value, 2) 3914 self.assertEqual(Color.green.value, 4) 3915 3916 def test_auto_number_garbage(self): 3917 with self.assertRaisesRegex(TypeError, 'invalid flag value .not an int.'): 3918 class Color(Flag): 3919 red = 'not an int' 3920 blue = auto() 3921 3922 def test_duplicate_auto(self): 3923 class Dupes(Enum): 3924 first = primero = auto() 3925 second = auto() 3926 third = auto() 3927 self.assertEqual([Dupes.first, Dupes.second, Dupes.third], list(Dupes)) 3928 3929 def test_multiple_mixin(self): 3930 class AllMixin: 3931 @classproperty 3932 def ALL(cls): 3933 members = list(cls) 3934 all_value = None 3935 if members: 3936 all_value = members[0] 3937 for member in members[1:]: 3938 all_value |= member 3939 cls.ALL = all_value 3940 return all_value 3941 class StrMixin: 3942 def __str__(self): 3943 return self._name_.lower() 3944 class Color(AllMixin, Flag): 3945 RED = auto() 3946 GREEN = auto() 3947 BLUE = auto() 3948 self.assertEqual(Color.RED.value, 1) 3949 self.assertEqual(Color.GREEN.value, 2) 3950 self.assertEqual(Color.BLUE.value, 4) 3951 self.assertEqual(Color.ALL.value, 7) 3952 self.assertEqual(str(Color.BLUE), 'Color.BLUE') 3953 class Color(AllMixin, StrMixin, Flag): 3954 RED = auto() 3955 GREEN = auto() 3956 BLUE = auto() 3957 __str__ = StrMixin.__str__ 3958 self.assertEqual(Color.RED.value, 1) 3959 self.assertEqual(Color.GREEN.value, 2) 3960 self.assertEqual(Color.BLUE.value, 4) 3961 self.assertEqual(Color.ALL.value, 7) 3962 self.assertEqual(str(Color.BLUE), 'blue') 3963 class Color(StrMixin, AllMixin, Flag): 3964 RED = auto() 3965 GREEN = auto() 3966 BLUE = auto() 3967 __str__ = StrMixin.__str__ 3968 self.assertEqual(Color.RED.value, 1) 3969 self.assertEqual(Color.GREEN.value, 2) 3970 self.assertEqual(Color.BLUE.value, 4) 3971 self.assertEqual(Color.ALL.value, 7) 3972 self.assertEqual(str(Color.BLUE), 'blue') 3973 3974 @threading_helper.reap_threads 3975 @threading_helper.requires_working_threading() 3976 def test_unique_composite(self): 3977 # override __eq__ to be identity only 3978 class TestFlag(Flag): 3979 one = auto() 3980 two = auto() 3981 three = auto() 3982 four = auto() 3983 five = auto() 3984 six = auto() 3985 seven = auto() 3986 eight = auto() 3987 def __eq__(self, other): 3988 return self is other 3989 def __hash__(self): 3990 return hash(self._value_) 3991 # have multiple threads competing to complete the composite members 3992 seen = set() 3993 failed = False 3994 def cycle_enum(): 3995 nonlocal failed 3996 try: 3997 for i in range(256): 3998 seen.add(TestFlag(i)) 3999 except Exception: 4000 failed = True 4001 threads = [ 4002 threading.Thread(target=cycle_enum) 4003 for _ in range(8) 4004 ] 4005 with threading_helper.start_threads(threads): 4006 pass 4007 # check that only 248 members were created 4008 self.assertFalse( 4009 failed, 4010 'at least one thread failed while creating composite members') 4011 self.assertEqual(256, len(seen), 'too many composite members created') 4012 4013 def test_init_subclass(self): 4014 class MyEnum(Flag): 4015 def __init_subclass__(cls, **kwds): 4016 super().__init_subclass__(**kwds) 4017 self.assertFalse(cls.__dict__.get('_test', False)) 4018 cls._test1 = 'MyEnum' 4019 # 4020 class TheirEnum(MyEnum): 4021 def __init_subclass__(cls, **kwds): 4022 super(TheirEnum, cls).__init_subclass__(**kwds) 4023 cls._test2 = 'TheirEnum' 4024 class WhoseEnum(TheirEnum): 4025 def __init_subclass__(cls, **kwds): 4026 pass 4027 class NoEnum(WhoseEnum): 4028 ONE = 1 4029 self.assertEqual(TheirEnum.__dict__['_test1'], 'MyEnum') 4030 self.assertEqual(WhoseEnum.__dict__['_test1'], 'MyEnum') 4031 self.assertEqual(WhoseEnum.__dict__['_test2'], 'TheirEnum') 4032 self.assertFalse(NoEnum.__dict__.get('_test1', False)) 4033 self.assertFalse(NoEnum.__dict__.get('_test2', False)) 4034 # 4035 class OurEnum(MyEnum): 4036 def __init_subclass__(cls, **kwds): 4037 cls._test2 = 'OurEnum' 4038 class WhereEnum(OurEnum): 4039 def __init_subclass__(cls, **kwds): 4040 pass 4041 class NeverEnum(WhereEnum): 4042 ONE = 1 4043 self.assertEqual(OurEnum.__dict__['_test1'], 'MyEnum') 4044 self.assertFalse(WhereEnum.__dict__.get('_test1', False)) 4045 self.assertEqual(WhereEnum.__dict__['_test2'], 'OurEnum') 4046 self.assertFalse(NeverEnum.__dict__.get('_test1', False)) 4047 self.assertFalse(NeverEnum.__dict__.get('_test2', False)) 4048 4049 4050class OldTestIntFlag(unittest.TestCase): 4051 """Tests of the IntFlags.""" 4052 4053 class Perm(IntFlag): 4054 R = 1 << 2 4055 W = 1 << 1 4056 X = 1 << 0 4057 4058 class Open(IntFlag): 4059 RO = 0 4060 WO = 1 4061 RW = 2 4062 AC = 3 4063 CE = 1<<19 4064 4065 class Color(IntFlag): 4066 BLACK = 0 4067 RED = 1 4068 ROJO = 1 4069 GREEN = 2 4070 BLUE = 4 4071 PURPLE = RED|BLUE 4072 WHITE = RED|GREEN|BLUE 4073 BLANCO = RED|GREEN|BLUE 4074 4075 class Skip(IntFlag): 4076 FIRST = 1 4077 SECOND = 2 4078 EIGHTH = 8 4079 4080 def test_type(self): 4081 Perm = self.Perm 4082 self.assertTrue(Perm._member_type_ is int) 4083 Open = self.Open 4084 for f in Perm: 4085 self.assertTrue(isinstance(f, Perm)) 4086 self.assertEqual(f, f.value) 4087 self.assertTrue(isinstance(Perm.W | Perm.X, Perm)) 4088 self.assertEqual(Perm.W | Perm.X, 3) 4089 for f in Open: 4090 self.assertTrue(isinstance(f, Open)) 4091 self.assertEqual(f, f.value) 4092 self.assertTrue(isinstance(Open.WO | Open.RW, Open)) 4093 self.assertEqual(Open.WO | Open.RW, 3) 4094 4095 @reraise_if_not_enum(HeadlightsK) 4096 def test_global_repr_keep(self): 4097 self.assertEqual( 4098 repr(HeadlightsK(0)), 4099 '%s.OFF_K' % SHORT_MODULE, 4100 ) 4101 self.assertEqual( 4102 repr(HeadlightsK(2**0 + 2**2 + 2**3)), 4103 '%(m)s.LOW_BEAM_K|%(m)s.FOG_K|8' % {'m': SHORT_MODULE}, 4104 ) 4105 self.assertEqual( 4106 repr(HeadlightsK(2**3)), 4107 '%(m)s.HeadlightsK(8)' % {'m': SHORT_MODULE}, 4108 ) 4109 4110 @reraise_if_not_enum(HeadlightsC) 4111 def test_global_repr_conform1(self): 4112 self.assertEqual( 4113 repr(HeadlightsC(0)), 4114 '%s.OFF_C' % SHORT_MODULE, 4115 ) 4116 self.assertEqual( 4117 repr(HeadlightsC(2**0 + 2**2 + 2**3)), 4118 '%(m)s.LOW_BEAM_C|%(m)s.FOG_C' % {'m': SHORT_MODULE}, 4119 ) 4120 self.assertEqual( 4121 repr(HeadlightsC(2**3)), 4122 '%(m)s.OFF_C' % {'m': SHORT_MODULE}, 4123 ) 4124 4125 @reraise_if_not_enum(NoName) 4126 def test_global_enum_str(self): 4127 self.assertEqual(repr(NoName.ONE), 'test_enum.ONE') 4128 self.assertEqual(repr(NoName(0)), 'test_enum.NoName(0)') 4129 self.assertEqual(str(NoName.ONE & NoName.TWO), 'NoName(0)') 4130 self.assertEqual(str(NoName(0)), 'NoName(0)') 4131 4132 def test_format(self): 4133 Perm = self.Perm 4134 self.assertEqual(format(Perm.R, ''), '4') 4135 self.assertEqual(format(Perm.R | Perm.X, ''), '5') 4136 # 4137 class NewPerm(IntFlag): 4138 R = 1 << 2 4139 W = 1 << 1 4140 X = 1 << 0 4141 def __str__(self): 4142 return self._name_ 4143 self.assertEqual(format(NewPerm.R, ''), 'R') 4144 self.assertEqual(format(NewPerm.R | Perm.X, ''), 'R|X') 4145 4146 def test_or(self): 4147 Perm = self.Perm 4148 for i in Perm: 4149 for j in Perm: 4150 self.assertEqual(i | j, i.value | j.value) 4151 self.assertEqual((i | j).value, i.value | j.value) 4152 self.assertIs(type(i | j), Perm) 4153 for j in range(8): 4154 self.assertEqual(i | j, i.value | j) 4155 self.assertEqual((i | j).value, i.value | j) 4156 self.assertIs(type(i | j), Perm) 4157 self.assertEqual(j | i, j | i.value) 4158 self.assertEqual((j | i).value, j | i.value) 4159 self.assertIs(type(j | i), Perm) 4160 for i in Perm: 4161 self.assertIs(i | i, i) 4162 self.assertIs(i | 0, i) 4163 self.assertIs(0 | i, i) 4164 Open = self.Open 4165 self.assertIs(Open.RO | Open.CE, Open.CE) 4166 4167 def test_and(self): 4168 Perm = self.Perm 4169 RW = Perm.R | Perm.W 4170 RX = Perm.R | Perm.X 4171 WX = Perm.W | Perm.X 4172 RWX = Perm.R | Perm.W | Perm.X 4173 values = list(Perm) + [RW, RX, WX, RWX, Perm(0)] 4174 for i in values: 4175 for j in values: 4176 self.assertEqual(i & j, i.value & j.value, 'i is %r, j is %r' % (i, j)) 4177 self.assertEqual((i & j).value, i.value & j.value, 'i is %r, j is %r' % (i, j)) 4178 self.assertIs(type(i & j), Perm, 'i is %r, j is %r' % (i, j)) 4179 for j in range(8): 4180 self.assertEqual(i & j, i.value & j) 4181 self.assertEqual((i & j).value, i.value & j) 4182 self.assertIs(type(i & j), Perm) 4183 self.assertEqual(j & i, j & i.value) 4184 self.assertEqual((j & i).value, j & i.value) 4185 self.assertIs(type(j & i), Perm) 4186 for i in Perm: 4187 self.assertIs(i & i, i) 4188 self.assertIs(i & 7, i) 4189 self.assertIs(7 & i, i) 4190 Open = self.Open 4191 self.assertIs(Open.RO & Open.CE, Open.RO) 4192 4193 def test_xor(self): 4194 Perm = self.Perm 4195 for i in Perm: 4196 for j in Perm: 4197 self.assertEqual(i ^ j, i.value ^ j.value) 4198 self.assertEqual((i ^ j).value, i.value ^ j.value) 4199 self.assertIs(type(i ^ j), Perm) 4200 for j in range(8): 4201 self.assertEqual(i ^ j, i.value ^ j) 4202 self.assertEqual((i ^ j).value, i.value ^ j) 4203 self.assertIs(type(i ^ j), Perm) 4204 self.assertEqual(j ^ i, j ^ i.value) 4205 self.assertEqual((j ^ i).value, j ^ i.value) 4206 self.assertIs(type(j ^ i), Perm) 4207 for i in Perm: 4208 self.assertIs(i ^ 0, i) 4209 self.assertIs(0 ^ i, i) 4210 Open = self.Open 4211 self.assertIs(Open.RO ^ Open.CE, Open.CE) 4212 self.assertIs(Open.CE ^ Open.CE, Open.RO) 4213 4214 def test_invert(self): 4215 Perm = self.Perm 4216 RW = Perm.R | Perm.W 4217 RX = Perm.R | Perm.X 4218 WX = Perm.W | Perm.X 4219 RWX = Perm.R | Perm.W | Perm.X 4220 values = list(Perm) + [RW, RX, WX, RWX, Perm(0)] 4221 for i in values: 4222 self.assertEqual(~i, (~i).value) 4223 self.assertIs(type(~i), Perm) 4224 self.assertEqual(~~i, i) 4225 for i in Perm: 4226 self.assertIs(~~i, i) 4227 Open = self.Open 4228 self.assertIs(Open.WO & ~Open.WO, Open.RO) 4229 self.assertIs((Open.WO|Open.CE) & ~Open.WO, Open.CE) 4230 4231 def test_boundary(self): 4232 self.assertIs(enum.IntFlag._boundary_, KEEP) 4233 class Simple(IntFlag, boundary=KEEP): 4234 SINGLE = 1 4235 # 4236 class Iron(IntFlag, boundary=STRICT): 4237 ONE = 1 4238 TWO = 2 4239 EIGHT = 8 4240 self.assertIs(Iron._boundary_, STRICT) 4241 # 4242 class Water(IntFlag, boundary=CONFORM): 4243 ONE = 1 4244 TWO = 2 4245 EIGHT = 8 4246 self.assertIs(Water._boundary_, CONFORM) 4247 # 4248 class Space(IntFlag, boundary=EJECT): 4249 ONE = 1 4250 TWO = 2 4251 EIGHT = 8 4252 self.assertIs(Space._boundary_, EJECT) 4253 # 4254 class Bizarre(IntFlag, boundary=KEEP): 4255 b = 3 4256 c = 4 4257 d = 6 4258 # 4259 self.assertRaisesRegex(ValueError, 'invalid value 5', Iron, 5) 4260 # 4261 self.assertIs(Water(7), Water.ONE|Water.TWO) 4262 self.assertIs(Water(~9), Water.TWO) 4263 # 4264 self.assertEqual(Space(7), 7) 4265 self.assertTrue(type(Space(7)) is int) 4266 # 4267 self.assertEqual(list(Bizarre), [Bizarre.c]) 4268 self.assertIs(Bizarre(3), Bizarre.b) 4269 self.assertIs(Bizarre(6), Bizarre.d) 4270 # 4271 simple = Simple.SINGLE | Iron.TWO 4272 self.assertEqual(simple, 3) 4273 self.assertIsInstance(simple, Simple) 4274 self.assertEqual(repr(simple), '<Simple.SINGLE|<Iron.TWO: 2>: 3>') 4275 self.assertEqual(str(simple), '3') 4276 4277 def test_iter(self): 4278 Color = self.Color 4279 Open = self.Open 4280 self.assertEqual(list(Color), [Color.RED, Color.GREEN, Color.BLUE]) 4281 self.assertEqual(list(Open), [Open.WO, Open.RW, Open.CE]) 4282 4283 def test_programatic_function_string(self): 4284 Perm = IntFlag('Perm', 'R W X') 4285 lst = list(Perm) 4286 self.assertEqual(len(lst), len(Perm)) 4287 self.assertEqual(len(Perm), 3, Perm) 4288 self.assertEqual(lst, [Perm.R, Perm.W, Perm.X]) 4289 for i, n in enumerate('R W X'.split()): 4290 v = 1<<i 4291 e = Perm(v) 4292 self.assertEqual(e.value, v) 4293 self.assertEqual(type(e.value), int) 4294 self.assertEqual(e, v) 4295 self.assertEqual(e.name, n) 4296 self.assertIn(e, Perm) 4297 self.assertIs(type(e), Perm) 4298 4299 def test_programatic_function_string_with_start(self): 4300 Perm = IntFlag('Perm', 'R W X', start=8) 4301 lst = list(Perm) 4302 self.assertEqual(len(lst), len(Perm)) 4303 self.assertEqual(len(Perm), 3, Perm) 4304 self.assertEqual(lst, [Perm.R, Perm.W, Perm.X]) 4305 for i, n in enumerate('R W X'.split()): 4306 v = 8<<i 4307 e = Perm(v) 4308 self.assertEqual(e.value, v) 4309 self.assertEqual(type(e.value), int) 4310 self.assertEqual(e, v) 4311 self.assertEqual(e.name, n) 4312 self.assertIn(e, Perm) 4313 self.assertIs(type(e), Perm) 4314 4315 def test_programatic_function_string_list(self): 4316 Perm = IntFlag('Perm', ['R', 'W', 'X']) 4317 lst = list(Perm) 4318 self.assertEqual(len(lst), len(Perm)) 4319 self.assertEqual(len(Perm), 3, Perm) 4320 self.assertEqual(lst, [Perm.R, Perm.W, Perm.X]) 4321 for i, n in enumerate('R W X'.split()): 4322 v = 1<<i 4323 e = Perm(v) 4324 self.assertEqual(e.value, v) 4325 self.assertEqual(type(e.value), int) 4326 self.assertEqual(e, v) 4327 self.assertEqual(e.name, n) 4328 self.assertIn(e, Perm) 4329 self.assertIs(type(e), Perm) 4330 4331 def test_programatic_function_iterable(self): 4332 Perm = IntFlag('Perm', (('R', 2), ('W', 8), ('X', 32))) 4333 lst = list(Perm) 4334 self.assertEqual(len(lst), len(Perm)) 4335 self.assertEqual(len(Perm), 3, Perm) 4336 self.assertEqual(lst, [Perm.R, Perm.W, Perm.X]) 4337 for i, n in enumerate('R W X'.split()): 4338 v = 1<<(2*i+1) 4339 e = Perm(v) 4340 self.assertEqual(e.value, v) 4341 self.assertEqual(type(e.value), int) 4342 self.assertEqual(e, v) 4343 self.assertEqual(e.name, n) 4344 self.assertIn(e, Perm) 4345 self.assertIs(type(e), Perm) 4346 4347 def test_programatic_function_from_dict(self): 4348 Perm = IntFlag('Perm', OrderedDict((('R', 2), ('W', 8), ('X', 32)))) 4349 lst = list(Perm) 4350 self.assertEqual(len(lst), len(Perm)) 4351 self.assertEqual(len(Perm), 3, Perm) 4352 self.assertEqual(lst, [Perm.R, Perm.W, Perm.X]) 4353 for i, n in enumerate('R W X'.split()): 4354 v = 1<<(2*i+1) 4355 e = Perm(v) 4356 self.assertEqual(e.value, v) 4357 self.assertEqual(type(e.value), int) 4358 self.assertEqual(e, v) 4359 self.assertEqual(e.name, n) 4360 self.assertIn(e, Perm) 4361 self.assertIs(type(e), Perm) 4362 4363 4364 def test_programatic_function_from_empty_list(self): 4365 Perm = enum.IntFlag('Perm', []) 4366 lst = list(Perm) 4367 self.assertEqual(len(lst), len(Perm)) 4368 self.assertEqual(len(Perm), 0, Perm) 4369 Thing = enum.Enum('Thing', []) 4370 lst = list(Thing) 4371 self.assertEqual(len(lst), len(Thing)) 4372 self.assertEqual(len(Thing), 0, Thing) 4373 4374 4375 def test_programatic_function_from_empty_tuple(self): 4376 Perm = enum.IntFlag('Perm', ()) 4377 lst = list(Perm) 4378 self.assertEqual(len(lst), len(Perm)) 4379 self.assertEqual(len(Perm), 0, Perm) 4380 Thing = enum.Enum('Thing', ()) 4381 self.assertEqual(len(lst), len(Thing)) 4382 self.assertEqual(len(Thing), 0, Thing) 4383 4384 def test_contains_tf(self): 4385 Open = self.Open 4386 Color = self.Color 4387 self.assertTrue(Color.GREEN in Color) 4388 self.assertTrue(Open.RW in Open) 4389 self.assertFalse('GREEN' in Color) 4390 self.assertFalse('RW' in Open) 4391 self.assertTrue(2 in Color) 4392 self.assertTrue(2 in Open) 4393 4394 def test_member_contains(self): 4395 Perm = self.Perm 4396 R, W, X = Perm 4397 RW = R | W 4398 RX = R | X 4399 WX = W | X 4400 RWX = R | W | X 4401 self.assertTrue(R in RW) 4402 self.assertTrue(R in RX) 4403 self.assertTrue(R in RWX) 4404 self.assertTrue(W in RW) 4405 self.assertTrue(W in WX) 4406 self.assertTrue(W in RWX) 4407 self.assertTrue(X in RX) 4408 self.assertTrue(X in WX) 4409 self.assertTrue(X in RWX) 4410 self.assertFalse(R in WX) 4411 self.assertFalse(W in RX) 4412 self.assertFalse(X in RW) 4413 with self.assertRaises(TypeError): 4414 self.assertFalse('test' in RW) 4415 4416 def test_member_iter(self): 4417 Color = self.Color 4418 self.assertEqual(list(Color.BLACK), []) 4419 self.assertEqual(list(Color.PURPLE), [Color.RED, Color.BLUE]) 4420 self.assertEqual(list(Color.BLUE), [Color.BLUE]) 4421 self.assertEqual(list(Color.GREEN), [Color.GREEN]) 4422 self.assertEqual(list(Color.WHITE), [Color.RED, Color.GREEN, Color.BLUE]) 4423 4424 def test_member_length(self): 4425 self.assertEqual(self.Color.__len__(self.Color.BLACK), 0) 4426 self.assertEqual(self.Color.__len__(self.Color.GREEN), 1) 4427 self.assertEqual(self.Color.__len__(self.Color.PURPLE), 2) 4428 self.assertEqual(self.Color.__len__(self.Color.BLANCO), 3) 4429 4430 def test_aliases(self): 4431 Color = self.Color 4432 self.assertEqual(Color(1).name, 'RED') 4433 self.assertEqual(Color['ROJO'].name, 'RED') 4434 self.assertEqual(Color(7).name, 'WHITE') 4435 self.assertEqual(Color['BLANCO'].name, 'WHITE') 4436 self.assertIs(Color.BLANCO, Color.WHITE) 4437 Open = self.Open 4438 self.assertIs(Open['AC'], Open.AC) 4439 4440 def test_bool(self): 4441 Perm = self.Perm 4442 for f in Perm: 4443 self.assertTrue(f) 4444 Open = self.Open 4445 for f in Open: 4446 self.assertEqual(bool(f.value), bool(f)) 4447 4448 4449 def test_multiple_mixin(self): 4450 class AllMixin: 4451 @classproperty 4452 def ALL(cls): 4453 members = list(cls) 4454 all_value = None 4455 if members: 4456 all_value = members[0] 4457 for member in members[1:]: 4458 all_value |= member 4459 cls.ALL = all_value 4460 return all_value 4461 class StrMixin: 4462 def __str__(self): 4463 return self._name_.lower() 4464 class Color(AllMixin, IntFlag): 4465 RED = auto() 4466 GREEN = auto() 4467 BLUE = auto() 4468 self.assertEqual(Color.RED.value, 1) 4469 self.assertEqual(Color.GREEN.value, 2) 4470 self.assertEqual(Color.BLUE.value, 4) 4471 self.assertEqual(Color.ALL.value, 7) 4472 self.assertEqual(str(Color.BLUE), '4') 4473 class Color(AllMixin, StrMixin, IntFlag): 4474 RED = auto() 4475 GREEN = auto() 4476 BLUE = auto() 4477 __str__ = StrMixin.__str__ 4478 self.assertEqual(Color.RED.value, 1) 4479 self.assertEqual(Color.GREEN.value, 2) 4480 self.assertEqual(Color.BLUE.value, 4) 4481 self.assertEqual(Color.ALL.value, 7) 4482 self.assertEqual(str(Color.BLUE), 'blue') 4483 class Color(StrMixin, AllMixin, IntFlag): 4484 RED = auto() 4485 GREEN = auto() 4486 BLUE = auto() 4487 __str__ = StrMixin.__str__ 4488 self.assertEqual(Color.RED.value, 1) 4489 self.assertEqual(Color.GREEN.value, 2) 4490 self.assertEqual(Color.BLUE.value, 4) 4491 self.assertEqual(Color.ALL.value, 7) 4492 self.assertEqual(str(Color.BLUE), 'blue') 4493 4494 @threading_helper.reap_threads 4495 @threading_helper.requires_working_threading() 4496 def test_unique_composite(self): 4497 # override __eq__ to be identity only 4498 class TestFlag(IntFlag): 4499 one = auto() 4500 two = auto() 4501 three = auto() 4502 four = auto() 4503 five = auto() 4504 six = auto() 4505 seven = auto() 4506 eight = auto() 4507 def __eq__(self, other): 4508 return self is other 4509 def __hash__(self): 4510 return hash(self._value_) 4511 # have multiple threads competing to complete the composite members 4512 seen = set() 4513 failed = False 4514 def cycle_enum(): 4515 nonlocal failed 4516 try: 4517 for i in range(256): 4518 seen.add(TestFlag(i)) 4519 except Exception: 4520 failed = True 4521 threads = [ 4522 threading.Thread(target=cycle_enum) 4523 for _ in range(8) 4524 ] 4525 with threading_helper.start_threads(threads): 4526 pass 4527 # check that only 248 members were created 4528 self.assertFalse( 4529 failed, 4530 'at least one thread failed while creating composite members') 4531 self.assertEqual(256, len(seen), 'too many composite members created') 4532 4533 4534class TestEmptyAndNonLatinStrings(unittest.TestCase): 4535 4536 def test_empty_string(self): 4537 with self.assertRaises(ValueError): 4538 empty_abc = Enum('empty_abc', ('', 'B', 'C')) 4539 4540 def test_non_latin_character_string(self): 4541 greek_abc = Enum('greek_abc', ('\u03B1', 'B', 'C')) 4542 item = getattr(greek_abc, '\u03B1') 4543 self.assertEqual(item.value, 1) 4544 4545 def test_non_latin_number_string(self): 4546 hebrew_123 = Enum('hebrew_123', ('\u05D0', '2', '3')) 4547 item = getattr(hebrew_123, '\u05D0') 4548 self.assertEqual(item.value, 1) 4549 4550 4551class TestUnique(unittest.TestCase): 4552 4553 def test_unique_clean(self): 4554 @unique 4555 class Clean(Enum): 4556 one = 1 4557 two = 'dos' 4558 tres = 4.0 4559 # 4560 @unique 4561 class Cleaner(IntEnum): 4562 single = 1 4563 double = 2 4564 triple = 3 4565 4566 def test_unique_dirty(self): 4567 with self.assertRaisesRegex(ValueError, 'tres.*one'): 4568 @unique 4569 class Dirty(Enum): 4570 one = 1 4571 two = 'dos' 4572 tres = 1 4573 with self.assertRaisesRegex( 4574 ValueError, 4575 'double.*single.*turkey.*triple', 4576 ): 4577 @unique 4578 class Dirtier(IntEnum): 4579 single = 1 4580 double = 1 4581 triple = 3 4582 turkey = 3 4583 4584 def test_unique_with_name(self): 4585 @verify(UNIQUE) 4586 class Silly(Enum): 4587 one = 1 4588 two = 'dos' 4589 name = 3 4590 # 4591 @verify(UNIQUE) 4592 class Sillier(IntEnum): 4593 single = 1 4594 name = 2 4595 triple = 3 4596 value = 4 4597 4598class TestVerify(unittest.TestCase): 4599 4600 def test_continuous(self): 4601 @verify(CONTINUOUS) 4602 class Auto(Enum): 4603 FIRST = auto() 4604 SECOND = auto() 4605 THIRD = auto() 4606 FORTH = auto() 4607 # 4608 @verify(CONTINUOUS) 4609 class Manual(Enum): 4610 FIRST = 3 4611 SECOND = 4 4612 THIRD = 5 4613 FORTH = 6 4614 # 4615 with self.assertRaisesRegex(ValueError, 'invalid enum .Missing.: missing values 5, 6, 7, 8, 9, 10, 12'): 4616 @verify(CONTINUOUS) 4617 class Missing(Enum): 4618 FIRST = 3 4619 SECOND = 4 4620 THIRD = 11 4621 FORTH = 13 4622 # 4623 with self.assertRaisesRegex(ValueError, 'invalid flag .Incomplete.: missing values 32'): 4624 @verify(CONTINUOUS) 4625 class Incomplete(Flag): 4626 FIRST = 4 4627 SECOND = 8 4628 THIRD = 16 4629 FORTH = 64 4630 # 4631 with self.assertRaisesRegex(ValueError, 'invalid flag .StillIncomplete.: missing values 16'): 4632 @verify(CONTINUOUS) 4633 class StillIncomplete(Flag): 4634 FIRST = 4 4635 SECOND = 8 4636 THIRD = 11 4637 FORTH = 32 4638 4639 4640 def test_composite(self): 4641 class Bizarre(Flag): 4642 b = 3 4643 c = 4 4644 d = 6 4645 self.assertEqual(list(Bizarre), [Bizarre.c]) 4646 self.assertEqual(Bizarre.b.value, 3) 4647 self.assertEqual(Bizarre.c.value, 4) 4648 self.assertEqual(Bizarre.d.value, 6) 4649 with self.assertRaisesRegex( 4650 ValueError, 4651 "invalid Flag 'Bizarre': aliases b and d are missing combined values of 0x3 .use enum.show_flag_values.value. for details.", 4652 ): 4653 @verify(NAMED_FLAGS) 4654 class Bizarre(Flag): 4655 b = 3 4656 c = 4 4657 d = 6 4658 # 4659 self.assertEqual(enum.show_flag_values(3), [1, 2]) 4660 class Bizarre(IntFlag): 4661 b = 3 4662 c = 4 4663 d = 6 4664 self.assertEqual(list(Bizarre), [Bizarre.c]) 4665 self.assertEqual(Bizarre.b.value, 3) 4666 self.assertEqual(Bizarre.c.value, 4) 4667 self.assertEqual(Bizarre.d.value, 6) 4668 with self.assertRaisesRegex( 4669 ValueError, 4670 "invalid Flag 'Bizarre': alias d is missing value 0x2 .use enum.show_flag_values.value. for details.", 4671 ): 4672 @verify(NAMED_FLAGS) 4673 class Bizarre(IntFlag): 4674 c = 4 4675 d = 6 4676 self.assertEqual(enum.show_flag_values(2), [2]) 4677 4678 def test_unique_clean(self): 4679 @verify(UNIQUE) 4680 class Clean(Enum): 4681 one = 1 4682 two = 'dos' 4683 tres = 4.0 4684 # 4685 @verify(UNIQUE) 4686 class Cleaner(IntEnum): 4687 single = 1 4688 double = 2 4689 triple = 3 4690 4691 def test_unique_dirty(self): 4692 with self.assertRaisesRegex(ValueError, 'tres.*one'): 4693 @verify(UNIQUE) 4694 class Dirty(Enum): 4695 one = 1 4696 two = 'dos' 4697 tres = 1 4698 with self.assertRaisesRegex( 4699 ValueError, 4700 'double.*single.*turkey.*triple', 4701 ): 4702 @verify(UNIQUE) 4703 class Dirtier(IntEnum): 4704 single = 1 4705 double = 1 4706 triple = 3 4707 turkey = 3 4708 4709 def test_unique_with_name(self): 4710 @verify(UNIQUE) 4711 class Silly(Enum): 4712 one = 1 4713 two = 'dos' 4714 name = 3 4715 # 4716 @verify(UNIQUE) 4717 class Sillier(IntEnum): 4718 single = 1 4719 name = 2 4720 triple = 3 4721 value = 4 4722 4723 def test_negative_alias(self): 4724 @verify(NAMED_FLAGS) 4725 class Color(Flag): 4726 RED = 1 4727 GREEN = 2 4728 BLUE = 4 4729 WHITE = -1 4730 # no error means success 4731 4732 4733class TestInternals(unittest.TestCase): 4734 4735 sunder_names = '_bad_', '_good_', '_what_ho_' 4736 dunder_names = '__mal__', '__bien__', '__que_que__' 4737 private_names = '_MyEnum__private', '_MyEnum__still_private' 4738 private_and_sunder_names = '_MyEnum__private_', '_MyEnum__also_private_' 4739 random_names = 'okay', '_semi_private', '_weird__', '_MyEnum__' 4740 4741 def test_sunder(self): 4742 for name in self.sunder_names + self.private_and_sunder_names: 4743 self.assertTrue(enum._is_sunder(name), '%r is a not sunder name?' % name) 4744 for name in self.dunder_names + self.private_names + self.random_names: 4745 self.assertFalse(enum._is_sunder(name), '%r is a sunder name?' % name) 4746 4747 def test_dunder(self): 4748 for name in self.dunder_names: 4749 self.assertTrue(enum._is_dunder(name), '%r is a not dunder name?' % name) 4750 for name in self.sunder_names + self.private_names + self.private_and_sunder_names + self.random_names: 4751 self.assertFalse(enum._is_dunder(name), '%r is a dunder name?' % name) 4752 4753 def test_is_private(self): 4754 for name in self.private_names + self.private_and_sunder_names: 4755 self.assertTrue(enum._is_private('MyEnum', name), '%r is a not private name?') 4756 for name in self.sunder_names + self.dunder_names + self.random_names: 4757 self.assertFalse(enum._is_private('MyEnum', name), '%r is a private name?') 4758 4759 def test_auto_number(self): 4760 class Color(Enum): 4761 red = auto() 4762 blue = auto() 4763 green = auto() 4764 4765 self.assertEqual(list(Color), [Color.red, Color.blue, Color.green]) 4766 self.assertEqual(Color.red.value, 1) 4767 self.assertEqual(Color.blue.value, 2) 4768 self.assertEqual(Color.green.value, 3) 4769 4770 def test_auto_name(self): 4771 class Color(Enum): 4772 def _generate_next_value_(name, start, count, last): 4773 return name 4774 red = auto() 4775 blue = auto() 4776 green = auto() 4777 4778 self.assertEqual(list(Color), [Color.red, Color.blue, Color.green]) 4779 self.assertEqual(Color.red.value, 'red') 4780 self.assertEqual(Color.blue.value, 'blue') 4781 self.assertEqual(Color.green.value, 'green') 4782 4783 def test_auto_name_inherit(self): 4784 class AutoNameEnum(Enum): 4785 def _generate_next_value_(name, start, count, last): 4786 return name 4787 class Color(AutoNameEnum): 4788 red = auto() 4789 blue = auto() 4790 green = auto() 4791 4792 self.assertEqual(list(Color), [Color.red, Color.blue, Color.green]) 4793 self.assertEqual(Color.red.value, 'red') 4794 self.assertEqual(Color.blue.value, 'blue') 4795 self.assertEqual(Color.green.value, 'green') 4796 4797 @unittest.skipIf( 4798 python_version >= (3, 13), 4799 'mixed types with auto() no longer supported', 4800 ) 4801 def test_auto_garbage_ok(self): 4802 with self.assertWarnsRegex(DeprecationWarning, 'will require all values to be sortable'): 4803 class Color(Enum): 4804 red = 'red' 4805 blue = auto() 4806 self.assertEqual(Color.blue.value, 1) 4807 4808 @unittest.skipIf( 4809 python_version >= (3, 13), 4810 'mixed types with auto() no longer supported', 4811 ) 4812 def test_auto_garbage_corrected_ok(self): 4813 with self.assertWarnsRegex(DeprecationWarning, 'will require all values to be sortable'): 4814 class Color(Enum): 4815 red = 'red' 4816 blue = 2 4817 green = auto() 4818 4819 self.assertEqual(list(Color), [Color.red, Color.blue, Color.green]) 4820 self.assertEqual(Color.red.value, 'red') 4821 self.assertEqual(Color.blue.value, 2) 4822 self.assertEqual(Color.green.value, 3) 4823 4824 @unittest.skipIf( 4825 python_version < (3, 13), 4826 'mixed types with auto() will raise in 3.13', 4827 ) 4828 def test_auto_garbage_fail(self): 4829 with self.assertRaisesRegex(TypeError, "unable to increment 'red'"): 4830 class Color(Enum): 4831 red = 'red' 4832 blue = auto() 4833 4834 @unittest.skipIf( 4835 python_version < (3, 13), 4836 'mixed types with auto() will raise in 3.13', 4837 ) 4838 def test_auto_garbage_corrected_fail(self): 4839 with self.assertRaisesRegex(TypeError, 'unable to sort non-numeric values'): 4840 class Color(Enum): 4841 red = 'red' 4842 blue = 2 4843 green = auto() 4844 4845 def test_auto_order(self): 4846 with self.assertRaises(TypeError): 4847 class Color(Enum): 4848 red = auto() 4849 green = auto() 4850 blue = auto() 4851 def _generate_next_value_(name, start, count, last): 4852 return name 4853 4854 def test_auto_order_wierd(self): 4855 weird_auto = auto() 4856 weird_auto.value = 'pathological case' 4857 class Color(Enum): 4858 red = weird_auto 4859 def _generate_next_value_(name, start, count, last): 4860 return name 4861 blue = auto() 4862 self.assertEqual(list(Color), [Color.red, Color.blue]) 4863 self.assertEqual(Color.red.value, 'pathological case') 4864 self.assertEqual(Color.blue.value, 'blue') 4865 4866 @unittest.skipIf( 4867 python_version < (3, 13), 4868 'auto() will return highest value + 1 in 3.13', 4869 ) 4870 def test_auto_with_aliases(self): 4871 class Color(Enum): 4872 red = auto() 4873 blue = auto() 4874 oxford = blue 4875 crimson = red 4876 green = auto() 4877 self.assertIs(Color.crimson, Color.red) 4878 self.assertIs(Color.oxford, Color.blue) 4879 self.assertIsNot(Color.green, Color.red) 4880 self.assertIsNot(Color.green, Color.blue) 4881 4882 def test_duplicate_auto(self): 4883 class Dupes(Enum): 4884 first = primero = auto() 4885 second = auto() 4886 third = auto() 4887 self.assertEqual([Dupes.first, Dupes.second, Dupes.third], list(Dupes)) 4888 4889 def test_multiple_auto_on_line(self): 4890 class Huh(Enum): 4891 ONE = auto() 4892 TWO = auto(), auto() 4893 THREE = auto(), auto(), auto() 4894 self.assertEqual(Huh.ONE.value, 1) 4895 self.assertEqual(Huh.TWO.value, (2, 3)) 4896 self.assertEqual(Huh.THREE.value, (4, 5, 6)) 4897 # 4898 class Hah(Enum): 4899 def __new__(cls, value, abbr=None): 4900 member = object.__new__(cls) 4901 member._value_ = value 4902 member.abbr = abbr or value[:3].lower() 4903 return member 4904 def _generate_next_value_(name, start, count, last): 4905 return name 4906 # 4907 MONDAY = auto() 4908 TUESDAY = auto() 4909 WEDNESDAY = auto(), 'WED' 4910 THURSDAY = auto(), 'Thu' 4911 FRIDAY = auto() 4912 self.assertEqual(Hah.MONDAY.value, 'MONDAY') 4913 self.assertEqual(Hah.MONDAY.abbr, 'mon') 4914 self.assertEqual(Hah.TUESDAY.value, 'TUESDAY') 4915 self.assertEqual(Hah.TUESDAY.abbr, 'tue') 4916 self.assertEqual(Hah.WEDNESDAY.value, 'WEDNESDAY') 4917 self.assertEqual(Hah.WEDNESDAY.abbr, 'WED') 4918 self.assertEqual(Hah.THURSDAY.value, 'THURSDAY') 4919 self.assertEqual(Hah.THURSDAY.abbr, 'Thu') 4920 self.assertEqual(Hah.FRIDAY.value, 'FRIDAY') 4921 self.assertEqual(Hah.FRIDAY.abbr, 'fri') 4922 # 4923 class Huh(Enum): 4924 def _generate_next_value_(name, start, count, last): 4925 return count+1 4926 ONE = auto() 4927 TWO = auto(), auto() 4928 THREE = auto(), auto(), auto() 4929 self.assertEqual(Huh.ONE.value, 1) 4930 self.assertEqual(Huh.TWO.value, (2, 2)) 4931 self.assertEqual(Huh.THREE.value, (3, 3, 3)) 4932 4933 4934expected_help_output_with_docs = """\ 4935Help on class Color in module %s: 4936 4937class Color(enum.Enum) 4938 | Color(*values) 4939 | 4940 | Method resolution order: 4941 | Color 4942 | enum.Enum 4943 | builtins.object 4944 | 4945 | Data and other attributes defined here: 4946 | 4947 | CYAN = <Color.CYAN: 1> 4948 | 4949 | MAGENTA = <Color.MAGENTA: 2> 4950 | 4951 | YELLOW = <Color.YELLOW: 3> 4952 | 4953 | ---------------------------------------------------------------------- 4954 | Data descriptors inherited from enum.Enum: 4955 | 4956 | name 4957 | The name of the Enum member. 4958 | 4959 | value 4960 | The value of the Enum member. 4961 | 4962 | ---------------------------------------------------------------------- 4963 | Static methods inherited from enum.EnumType: 4964 | 4965 | __contains__(value) 4966 | Return True if `value` is in `cls`. 4967 | 4968 | `value` is in `cls` if: 4969 | 1) `value` is a member of `cls`, or 4970 | 2) `value` is the value of one of the `cls`'s members. 4971 | 4972 | __getitem__(name) 4973 | Return the member matching `name`. 4974 | 4975 | __iter__() 4976 | Return members in definition order. 4977 | 4978 | __len__() 4979 | Return the number of members (no aliases) 4980 | 4981 | ---------------------------------------------------------------------- 4982 | Readonly properties inherited from enum.EnumType: 4983 | 4984 | __members__ 4985 | Returns a mapping of member name->value. 4986 | 4987 | This mapping lists all enum members, including aliases. Note that this 4988 | is a read-only view of the internal mapping.""" 4989 4990expected_help_output_without_docs = """\ 4991Help on class Color in module %s: 4992 4993class Color(enum.Enum) 4994 | Color(*values) 4995 | 4996 | Method resolution order: 4997 | Color 4998 | enum.Enum 4999 | builtins.object 5000 | 5001 | Data and other attributes defined here: 5002 | 5003 | CYAN = <Color.CYAN: 1> 5004 | 5005 | MAGENTA = <Color.MAGENTA: 2> 5006 | 5007 | YELLOW = <Color.YELLOW: 3> 5008 | 5009 | ---------------------------------------------------------------------- 5010 | Data descriptors inherited from enum.Enum: 5011 | 5012 | name 5013 | 5014 | value 5015 | 5016 | ---------------------------------------------------------------------- 5017 | Static methods inherited from enum.EnumType: 5018 | 5019 | __contains__(value) 5020 | 5021 | __getitem__(name) 5022 | 5023 | __iter__() 5024 | 5025 | __len__() 5026 | 5027 | ---------------------------------------------------------------------- 5028 | Readonly properties inherited from enum.EnumType: 5029 | 5030 | __members__""" 5031 5032class TestStdLib(unittest.TestCase): 5033 5034 maxDiff = None 5035 5036 class Color(Enum): 5037 CYAN = 1 5038 MAGENTA = 2 5039 YELLOW = 3 5040 5041 def test_pydoc(self): 5042 # indirectly test __objclass__ 5043 if StrEnum.__doc__ is None: 5044 expected_text = expected_help_output_without_docs % __name__ 5045 else: 5046 expected_text = expected_help_output_with_docs % __name__ 5047 output = StringIO() 5048 helper = pydoc.Helper(output=output) 5049 helper(self.Color) 5050 result = output.getvalue().strip() 5051 self.assertEqual(result, expected_text, result) 5052 5053 def test_inspect_getmembers(self): 5054 values = dict(( 5055 ('__class__', EnumType), 5056 ('__doc__', '...'), 5057 ('__members__', self.Color.__members__), 5058 ('__module__', __name__), 5059 ('YELLOW', self.Color.YELLOW), 5060 ('MAGENTA', self.Color.MAGENTA), 5061 ('CYAN', self.Color.CYAN), 5062 ('name', Enum.__dict__['name']), 5063 ('value', Enum.__dict__['value']), 5064 ('__len__', self.Color.__len__), 5065 ('__contains__', self.Color.__contains__), 5066 ('__name__', 'Color'), 5067 ('__getitem__', self.Color.__getitem__), 5068 ('__qualname__', 'TestStdLib.Color'), 5069 ('__init_subclass__', getattr(self.Color, '__init_subclass__')), 5070 ('__iter__', self.Color.__iter__), 5071 )) 5072 result = dict(inspect.getmembers(self.Color)) 5073 self.assertEqual(set(values.keys()), set(result.keys())) 5074 failed = False 5075 for k in values.keys(): 5076 if k == '__doc__': 5077 # __doc__ is huge, not comparing 5078 continue 5079 if result[k] != values[k]: 5080 print() 5081 print('\n%s\n key: %s\n result: %s\nexpected: %s\n%s\n' % 5082 ('=' * 75, k, result[k], values[k], '=' * 75), sep='') 5083 failed = True 5084 if failed: 5085 self.fail("result does not equal expected, see print above") 5086 5087 def test_inspect_classify_class_attrs(self): 5088 # indirectly test __objclass__ 5089 from inspect import Attribute 5090 values = [ 5091 Attribute(name='__class__', kind='data', 5092 defining_class=object, object=EnumType), 5093 Attribute(name='__contains__', kind='method', 5094 defining_class=EnumType, object=self.Color.__contains__), 5095 Attribute(name='__doc__', kind='data', 5096 defining_class=self.Color, object='...'), 5097 Attribute(name='__getitem__', kind='method', 5098 defining_class=EnumType, object=self.Color.__getitem__), 5099 Attribute(name='__iter__', kind='method', 5100 defining_class=EnumType, object=self.Color.__iter__), 5101 Attribute(name='__init_subclass__', kind='class method', 5102 defining_class=object, object=getattr(self.Color, '__init_subclass__')), 5103 Attribute(name='__len__', kind='method', 5104 defining_class=EnumType, object=self.Color.__len__), 5105 Attribute(name='__members__', kind='property', 5106 defining_class=EnumType, object=EnumType.__members__), 5107 Attribute(name='__module__', kind='data', 5108 defining_class=self.Color, object=__name__), 5109 Attribute(name='__name__', kind='data', 5110 defining_class=self.Color, object='Color'), 5111 Attribute(name='__qualname__', kind='data', 5112 defining_class=self.Color, object='TestStdLib.Color'), 5113 Attribute(name='YELLOW', kind='data', 5114 defining_class=self.Color, object=self.Color.YELLOW), 5115 Attribute(name='MAGENTA', kind='data', 5116 defining_class=self.Color, object=self.Color.MAGENTA), 5117 Attribute(name='CYAN', kind='data', 5118 defining_class=self.Color, object=self.Color.CYAN), 5119 Attribute(name='name', kind='data', 5120 defining_class=Enum, object=Enum.__dict__['name']), 5121 Attribute(name='value', kind='data', 5122 defining_class=Enum, object=Enum.__dict__['value']), 5123 ] 5124 for v in values: 5125 try: 5126 v.name 5127 except AttributeError: 5128 print(v) 5129 values.sort(key=lambda item: item.name) 5130 result = list(inspect.classify_class_attrs(self.Color)) 5131 result.sort(key=lambda item: item.name) 5132 self.assertEqual( 5133 len(values), len(result), 5134 "%s != %s" % ([a.name for a in values], [a.name for a in result]) 5135 ) 5136 failed = False 5137 for v, r in zip(values, result): 5138 if r.name in ('__init_subclass__', '__doc__'): 5139 # not sure how to make the __init_subclass_ Attributes match 5140 # so as long as there is one, call it good 5141 # __doc__ is too big to check exactly, so treat the same as __init_subclass__ 5142 for name in ('name','kind','defining_class'): 5143 if getattr(v, name) != getattr(r, name): 5144 print('\n%s\n%s\n%s\n%s\n' % ('=' * 75, r, v, '=' * 75), sep='') 5145 failed = True 5146 elif r != v: 5147 print('\n%s\n%s\n%s\n%s\n' % ('=' * 75, r, v, '=' * 75), sep='') 5148 failed = True 5149 if failed: 5150 self.fail("result does not equal expected, see print above") 5151 5152 def test_inspect_signatures(self): 5153 from inspect import signature, Signature, Parameter 5154 self.assertEqual( 5155 signature(Enum), 5156 Signature([ 5157 Parameter('new_class_name', Parameter.POSITIONAL_ONLY), 5158 Parameter('names', Parameter.POSITIONAL_OR_KEYWORD), 5159 Parameter('module', Parameter.KEYWORD_ONLY, default=None), 5160 Parameter('qualname', Parameter.KEYWORD_ONLY, default=None), 5161 Parameter('type', Parameter.KEYWORD_ONLY, default=None), 5162 Parameter('start', Parameter.KEYWORD_ONLY, default=1), 5163 Parameter('boundary', Parameter.KEYWORD_ONLY, default=None), 5164 ]), 5165 ) 5166 self.assertEqual( 5167 signature(enum.FlagBoundary), 5168 Signature([ 5169 Parameter('values', Parameter.VAR_POSITIONAL), 5170 ]), 5171 ) 5172 5173 def test_test_simple_enum(self): 5174 @_simple_enum(Enum) 5175 class SimpleColor: 5176 CYAN = 1 5177 MAGENTA = 2 5178 YELLOW = 3 5179 @bltns.property 5180 def zeroth(self): 5181 return 'zeroed %s' % self.name 5182 class CheckedColor(Enum): 5183 CYAN = 1 5184 MAGENTA = 2 5185 YELLOW = 3 5186 @bltns.property 5187 def zeroth(self): 5188 return 'zeroed %s' % self.name 5189 _test_simple_enum(CheckedColor, SimpleColor) 5190 SimpleColor.MAGENTA._value_ = 9 5191 self.assertRaisesRegex( 5192 TypeError, "enum mismatch", 5193 _test_simple_enum, CheckedColor, SimpleColor, 5194 ) 5195 # 5196 # 5197 class CheckedMissing(IntFlag, boundary=KEEP): 5198 SIXTY_FOUR = 64 5199 ONE_TWENTY_EIGHT = 128 5200 TWENTY_FORTY_EIGHT = 2048 5201 ALL = 2048 + 128 + 64 + 12 5202 CM = CheckedMissing 5203 self.assertEqual(list(CheckedMissing), [CM.SIXTY_FOUR, CM.ONE_TWENTY_EIGHT, CM.TWENTY_FORTY_EIGHT]) 5204 # 5205 @_simple_enum(IntFlag, boundary=KEEP) 5206 class Missing: 5207 SIXTY_FOUR = 64 5208 ONE_TWENTY_EIGHT = 128 5209 TWENTY_FORTY_EIGHT = 2048 5210 ALL = 2048 + 128 + 64 + 12 5211 M = Missing 5212 self.assertEqual(list(CheckedMissing), [M.SIXTY_FOUR, M.ONE_TWENTY_EIGHT, M.TWENTY_FORTY_EIGHT]) 5213 _test_simple_enum(CheckedMissing, Missing) 5214 # 5215 # 5216 class CheckedUnhashable(Enum): 5217 ONE = dict() 5218 TWO = set() 5219 name = 'python' 5220 self.assertIn(dict(), CheckedUnhashable) 5221 self.assertIn('python', CheckedUnhashable) 5222 self.assertEqual(CheckedUnhashable.name.value, 'python') 5223 self.assertEqual(CheckedUnhashable.name.name, 'name') 5224 # 5225 @_simple_enum() 5226 class Unhashable: 5227 ONE = dict() 5228 TWO = set() 5229 name = 'python' 5230 self.assertIn(dict(), Unhashable) 5231 self.assertIn('python', Unhashable) 5232 self.assertEqual(Unhashable.name.value, 'python') 5233 self.assertEqual(Unhashable.name.name, 'name') 5234 _test_simple_enum(CheckedUnhashable, Unhashable) 5235 ## 5236 class CheckedComplexStatus(IntEnum): 5237 def __new__(cls, value, phrase, description=''): 5238 obj = int.__new__(cls, value) 5239 obj._value_ = value 5240 obj.phrase = phrase 5241 obj.description = description 5242 return obj 5243 CONTINUE = 100, 'Continue', 'Request received, please continue' 5244 PROCESSING = 102, 'Processing' 5245 EARLY_HINTS = 103, 'Early Hints' 5246 SOME_HINTS = 103, 'Some Early Hints' 5247 # 5248 @_simple_enum(IntEnum) 5249 class ComplexStatus: 5250 def __new__(cls, value, phrase, description=''): 5251 obj = int.__new__(cls, value) 5252 obj._value_ = value 5253 obj.phrase = phrase 5254 obj.description = description 5255 return obj 5256 CONTINUE = 100, 'Continue', 'Request received, please continue' 5257 PROCESSING = 102, 'Processing' 5258 EARLY_HINTS = 103, 'Early Hints' 5259 SOME_HINTS = 103, 'Some Early Hints' 5260 _test_simple_enum(CheckedComplexStatus, ComplexStatus) 5261 # 5262 # 5263 class CheckedComplexFlag(IntFlag): 5264 def __new__(cls, value, label): 5265 obj = int.__new__(cls, value) 5266 obj._value_ = value 5267 obj.label = label 5268 return obj 5269 SHIRT = 1, 'upper half' 5270 VEST = 1, 'outer upper half' 5271 PANTS = 2, 'lower half' 5272 self.assertIs(CheckedComplexFlag.SHIRT, CheckedComplexFlag.VEST) 5273 # 5274 @_simple_enum(IntFlag) 5275 class ComplexFlag: 5276 def __new__(cls, value, label): 5277 obj = int.__new__(cls, value) 5278 obj._value_ = value 5279 obj.label = label 5280 return obj 5281 SHIRT = 1, 'upper half' 5282 VEST = 1, 'uppert half' 5283 PANTS = 2, 'lower half' 5284 _test_simple_enum(CheckedComplexFlag, ComplexFlag) 5285 5286 5287class MiscTestCase(unittest.TestCase): 5288 5289 def test__all__(self): 5290 support.check__all__(self, enum, not_exported={'bin', 'show_flag_values'}) 5291 5292 def test_doc_1(self): 5293 class Single(Enum): 5294 ONE = 1 5295 self.assertEqual(Single.__doc__, None) 5296 5297 def test_doc_2(self): 5298 class Double(Enum): 5299 ONE = 1 5300 TWO = 2 5301 self.assertEqual(Double.__doc__, None) 5302 5303 def test_doc_3(self): 5304 class Triple(Enum): 5305 ONE = 1 5306 TWO = 2 5307 THREE = 3 5308 self.assertEqual(Triple.__doc__, None) 5309 5310 def test_doc_4(self): 5311 class Quadruple(Enum): 5312 ONE = 1 5313 TWO = 2 5314 THREE = 3 5315 FOUR = 4 5316 self.assertEqual(Quadruple.__doc__, None) 5317 5318 5319# These are unordered here on purpose to ensure that declaration order 5320# makes no difference. 5321CONVERT_TEST_NAME_D = 5 5322CONVERT_TEST_NAME_C = 5 5323CONVERT_TEST_NAME_B = 5 5324CONVERT_TEST_NAME_A = 5 # This one should sort first. 5325CONVERT_TEST_NAME_E = 5 5326CONVERT_TEST_NAME_F = 5 5327 5328CONVERT_STRING_TEST_NAME_D = 5 5329CONVERT_STRING_TEST_NAME_C = 5 5330CONVERT_STRING_TEST_NAME_B = 5 5331CONVERT_STRING_TEST_NAME_A = 5 # This one should sort first. 5332CONVERT_STRING_TEST_NAME_E = 5 5333CONVERT_STRING_TEST_NAME_F = 5 5334 5335# global names for StrEnum._convert_ test 5336CONVERT_STR_TEST_2 = 'goodbye' 5337CONVERT_STR_TEST_1 = 'hello' 5338 5339# We also need values that cannot be compared: 5340UNCOMPARABLE_A = 5 5341UNCOMPARABLE_C = (9, 1) # naming order is broken on purpose 5342UNCOMPARABLE_B = 'value' 5343 5344COMPLEX_C = 1j 5345COMPLEX_A = 2j 5346COMPLEX_B = 3j 5347 5348class TestConvert(unittest.TestCase): 5349 def tearDown(self): 5350 # Reset the module-level test variables to their original integer 5351 # values, otherwise the already created enum values get converted 5352 # instead. 5353 g = globals() 5354 for suffix in ['A', 'B', 'C', 'D', 'E', 'F']: 5355 g['CONVERT_TEST_NAME_%s' % suffix] = 5 5356 g['CONVERT_STRING_TEST_NAME_%s' % suffix] = 5 5357 for suffix, value in (('A', 5), ('B', (9, 1)), ('C', 'value')): 5358 g['UNCOMPARABLE_%s' % suffix] = value 5359 for suffix, value in (('A', 2j), ('B', 3j), ('C', 1j)): 5360 g['COMPLEX_%s' % suffix] = value 5361 for suffix, value in (('1', 'hello'), ('2', 'goodbye')): 5362 g['CONVERT_STR_TEST_%s' % suffix] = value 5363 5364 def test_convert_value_lookup_priority(self): 5365 test_type = enum.IntEnum._convert_( 5366 'UnittestConvert', 5367 MODULE, 5368 filter=lambda x: x.startswith('CONVERT_TEST_')) 5369 # We don't want the reverse lookup value to vary when there are 5370 # multiple possible names for a given value. It should always 5371 # report the first lexicographical name in that case. 5372 self.assertEqual(test_type(5).name, 'CONVERT_TEST_NAME_A') 5373 5374 def test_convert_int(self): 5375 test_type = enum.IntEnum._convert_( 5376 'UnittestConvert', 5377 MODULE, 5378 filter=lambda x: x.startswith('CONVERT_TEST_')) 5379 # Ensure that test_type has all of the desired names and values. 5380 self.assertEqual(test_type.CONVERT_TEST_NAME_F, 5381 test_type.CONVERT_TEST_NAME_A) 5382 self.assertEqual(test_type.CONVERT_TEST_NAME_B, 5) 5383 self.assertEqual(test_type.CONVERT_TEST_NAME_C, 5) 5384 self.assertEqual(test_type.CONVERT_TEST_NAME_D, 5) 5385 self.assertEqual(test_type.CONVERT_TEST_NAME_E, 5) 5386 # Ensure that test_type only picked up names matching the filter. 5387 extra = [name for name in dir(test_type) if name not in enum_dir(test_type)] 5388 missing = [name for name in enum_dir(test_type) if name not in dir(test_type)] 5389 self.assertEqual( 5390 extra + missing, 5391 [], 5392 msg='extra names: %r; missing names: %r' % (extra, missing), 5393 ) 5394 5395 5396 def test_convert_uncomparable(self): 5397 uncomp = enum.Enum._convert_( 5398 'Uncomparable', 5399 MODULE, 5400 filter=lambda x: x.startswith('UNCOMPARABLE_')) 5401 # Should be ordered by `name` only: 5402 self.assertEqual( 5403 list(uncomp), 5404 [uncomp.UNCOMPARABLE_A, uncomp.UNCOMPARABLE_B, uncomp.UNCOMPARABLE_C], 5405 ) 5406 5407 def test_convert_complex(self): 5408 uncomp = enum.Enum._convert_( 5409 'Uncomparable', 5410 MODULE, 5411 filter=lambda x: x.startswith('COMPLEX_')) 5412 # Should be ordered by `name` only: 5413 self.assertEqual( 5414 list(uncomp), 5415 [uncomp.COMPLEX_A, uncomp.COMPLEX_B, uncomp.COMPLEX_C], 5416 ) 5417 5418 def test_convert_str(self): 5419 test_type = enum.StrEnum._convert_( 5420 'UnittestConvert', 5421 MODULE, 5422 filter=lambda x: x.startswith('CONVERT_STR_'), 5423 as_global=True) 5424 # Ensure that test_type has all of the desired names and values. 5425 self.assertEqual(test_type.CONVERT_STR_TEST_1, 'hello') 5426 self.assertEqual(test_type.CONVERT_STR_TEST_2, 'goodbye') 5427 # Ensure that test_type only picked up names matching the filter. 5428 extra = [name for name in dir(test_type) if name not in enum_dir(test_type)] 5429 missing = [name for name in enum_dir(test_type) if name not in dir(test_type)] 5430 self.assertEqual( 5431 extra + missing, 5432 [], 5433 msg='extra names: %r; missing names: %r' % (extra, missing), 5434 ) 5435 self.assertEqual(repr(test_type.CONVERT_STR_TEST_1), '%s.CONVERT_STR_TEST_1' % SHORT_MODULE) 5436 self.assertEqual(str(test_type.CONVERT_STR_TEST_2), 'goodbye') 5437 self.assertEqual(format(test_type.CONVERT_STR_TEST_1), 'hello') 5438 5439 def test_convert_raise(self): 5440 with self.assertRaises(AttributeError): 5441 enum.IntEnum._convert( 5442 'UnittestConvert', 5443 MODULE, 5444 filter=lambda x: x.startswith('CONVERT_TEST_')) 5445 5446 def test_convert_repr_and_str(self): 5447 test_type = enum.IntEnum._convert_( 5448 'UnittestConvert', 5449 MODULE, 5450 filter=lambda x: x.startswith('CONVERT_STRING_TEST_'), 5451 as_global=True) 5452 self.assertEqual(repr(test_type.CONVERT_STRING_TEST_NAME_A), '%s.CONVERT_STRING_TEST_NAME_A' % SHORT_MODULE) 5453 self.assertEqual(str(test_type.CONVERT_STRING_TEST_NAME_A), '5') 5454 self.assertEqual(format(test_type.CONVERT_STRING_TEST_NAME_A), '5') 5455 5456 5457# helpers 5458 5459def enum_dir(cls): 5460 interesting = set([ 5461 '__class__', '__contains__', '__doc__', '__getitem__', 5462 '__iter__', '__len__', '__members__', '__module__', 5463 '__name__', '__qualname__', 5464 ] 5465 + cls._member_names_ 5466 ) 5467 if cls._new_member_ is not object.__new__: 5468 interesting.add('__new__') 5469 if cls.__init_subclass__ is not object.__init_subclass__: 5470 interesting.add('__init_subclass__') 5471 if cls._member_type_ is object: 5472 return sorted(interesting) 5473 else: 5474 # return whatever mixed-in data type has 5475 return sorted(set(dir(cls._member_type_)) | interesting) 5476 5477def member_dir(member): 5478 if member.__class__._member_type_ is object: 5479 allowed = set(['__class__', '__doc__', '__eq__', '__hash__', '__module__', 'name', 'value']) 5480 else: 5481 allowed = set(dir(member)) 5482 for cls in member.__class__.mro(): 5483 for name, obj in cls.__dict__.items(): 5484 if name[0] == '_': 5485 continue 5486 if isinstance(obj, enum.property): 5487 if obj.fget is not None or name not in member._member_map_: 5488 allowed.add(name) 5489 else: 5490 allowed.discard(name) 5491 elif name not in member._member_map_: 5492 allowed.add(name) 5493 return sorted(allowed) 5494 5495 5496if __name__ == '__main__': 5497 unittest.main() 5498