1import sys 2from types import MappingProxyType, DynamicClassAttribute 3 4 5__all__ = [ 6 'EnumMeta', 7 'Enum', 'IntEnum', 'Flag', 'IntFlag', 8 'auto', 'unique', 9 ] 10 11 12def _is_descriptor(obj): 13 """Returns True if obj is a descriptor, False otherwise.""" 14 return ( 15 hasattr(obj, '__get__') or 16 hasattr(obj, '__set__') or 17 hasattr(obj, '__delete__')) 18 19 20def _is_dunder(name): 21 """Returns True if a __dunder__ name, False otherwise.""" 22 return (len(name) > 4 and 23 name[:2] == name[-2:] == '__' and 24 name[2] != '_' and 25 name[-3] != '_') 26 27 28def _is_sunder(name): 29 """Returns True if a _sunder_ name, False otherwise.""" 30 return (len(name) > 2 and 31 name[0] == name[-1] == '_' and 32 name[1:2] != '_' and 33 name[-2:-1] != '_') 34 35 36def _make_class_unpicklable(cls): 37 """Make the given class un-picklable.""" 38 def _break_on_call_reduce(self, proto): 39 raise TypeError('%r cannot be pickled' % self) 40 cls.__reduce_ex__ = _break_on_call_reduce 41 cls.__module__ = '<unknown>' 42 43_auto_null = object() 44class auto: 45 """ 46 Instances are replaced with an appropriate value in Enum class suites. 47 """ 48 value = _auto_null 49 50 51class _EnumDict(dict): 52 """Track enum member order and ensure member names are not reused. 53 54 EnumMeta will use the names found in self._member_names as the 55 enumeration member names. 56 57 """ 58 def __init__(self): 59 super().__init__() 60 self._member_names = [] 61 self._last_values = [] 62 self._ignore = [] 63 self._auto_called = False 64 65 def __setitem__(self, key, value): 66 """Changes anything not dundered or not a descriptor. 67 68 If an enum member name is used twice, an error is raised; duplicate 69 values are not checked for. 70 71 Single underscore (sunder) names are reserved. 72 73 """ 74 if _is_sunder(key): 75 if key not in ( 76 '_order_', '_create_pseudo_member_', 77 '_generate_next_value_', '_missing_', '_ignore_', 78 ): 79 raise ValueError('_names_ are reserved for future Enum use') 80 if key == '_generate_next_value_': 81 # check if members already defined as auto() 82 if self._auto_called: 83 raise TypeError("_generate_next_value_ must be defined before members") 84 setattr(self, '_generate_next_value', value) 85 elif key == '_ignore_': 86 if isinstance(value, str): 87 value = value.replace(',',' ').split() 88 else: 89 value = list(value) 90 self._ignore = value 91 already = set(value) & set(self._member_names) 92 if already: 93 raise ValueError('_ignore_ cannot specify already set names: %r' % (already, )) 94 elif _is_dunder(key): 95 if key == '__order__': 96 key = '_order_' 97 elif key in self._member_names: 98 # descriptor overwriting an enum? 99 raise TypeError('Attempted to reuse key: %r' % key) 100 elif key in self._ignore: 101 pass 102 elif not _is_descriptor(value): 103 if key in self: 104 # enum overwriting a descriptor? 105 raise TypeError('%r already defined as: %r' % (key, self[key])) 106 if isinstance(value, auto): 107 if value.value == _auto_null: 108 value.value = self._generate_next_value(key, 1, len(self._member_names), self._last_values[:]) 109 self._auto_called = True 110 value = value.value 111 self._member_names.append(key) 112 self._last_values.append(value) 113 super().__setitem__(key, value) 114 115 116# Dummy value for Enum as EnumMeta explicitly checks for it, but of course 117# until EnumMeta finishes running the first time the Enum class doesn't exist. 118# This is also why there are checks in EnumMeta like `if Enum is not None` 119Enum = None 120 121 122class EnumMeta(type): 123 """Metaclass for Enum""" 124 @classmethod 125 def __prepare__(metacls, cls, bases): 126 # check that previous enum members do not exist 127 metacls._check_for_existing_members(cls, bases) 128 # create the namespace dict 129 enum_dict = _EnumDict() 130 # inherit previous flags and _generate_next_value_ function 131 member_type, first_enum = metacls._get_mixins_(cls, bases) 132 if first_enum is not None: 133 enum_dict['_generate_next_value_'] = getattr(first_enum, '_generate_next_value_', None) 134 return enum_dict 135 136 def __new__(metacls, cls, bases, classdict): 137 # an Enum class is final once enumeration items have been defined; it 138 # cannot be mixed with other types (int, float, etc.) if it has an 139 # inherited __new__ unless a new __new__ is defined (or the resulting 140 # class will fail). 141 # 142 # remove any keys listed in _ignore_ 143 classdict.setdefault('_ignore_', []).append('_ignore_') 144 ignore = classdict['_ignore_'] 145 for key in ignore: 146 classdict.pop(key, None) 147 member_type, first_enum = metacls._get_mixins_(cls, bases) 148 __new__, save_new, use_args = metacls._find_new_(classdict, member_type, 149 first_enum) 150 151 # save enum items into separate mapping so they don't get baked into 152 # the new class 153 enum_members = {k: classdict[k] for k in classdict._member_names} 154 for name in classdict._member_names: 155 del classdict[name] 156 157 # adjust the sunders 158 _order_ = classdict.pop('_order_', None) 159 160 # check for illegal enum names (any others?) 161 invalid_names = set(enum_members) & {'mro', ''} 162 if invalid_names: 163 raise ValueError('Invalid enum member name: {0}'.format( 164 ','.join(invalid_names))) 165 166 # create a default docstring if one has not been provided 167 if '__doc__' not in classdict: 168 classdict['__doc__'] = 'An enumeration.' 169 170 # create our new Enum type 171 enum_class = super().__new__(metacls, cls, bases, classdict) 172 enum_class._member_names_ = [] # names in definition order 173 enum_class._member_map_ = {} # name->value map 174 enum_class._member_type_ = member_type 175 176 # save DynamicClassAttribute attributes from super classes so we know 177 # if we can take the shortcut of storing members in the class dict 178 dynamic_attributes = {k for c in enum_class.mro() 179 for k, v in c.__dict__.items() 180 if isinstance(v, DynamicClassAttribute)} 181 182 # Reverse value->name map for hashable values. 183 enum_class._value2member_map_ = {} 184 185 # If a custom type is mixed into the Enum, and it does not know how 186 # to pickle itself, pickle.dumps will succeed but pickle.loads will 187 # fail. Rather than have the error show up later and possibly far 188 # from the source, sabotage the pickle protocol for this class so 189 # that pickle.dumps also fails. 190 # 191 # However, if the new class implements its own __reduce_ex__, do not 192 # sabotage -- it's on them to make sure it works correctly. We use 193 # __reduce_ex__ instead of any of the others as it is preferred by 194 # pickle over __reduce__, and it handles all pickle protocols. 195 if '__reduce_ex__' not in classdict: 196 if member_type is not object: 197 methods = ('__getnewargs_ex__', '__getnewargs__', 198 '__reduce_ex__', '__reduce__') 199 if not any(m in member_type.__dict__ for m in methods): 200 _make_class_unpicklable(enum_class) 201 202 # instantiate them, checking for duplicates as we go 203 # we instantiate first instead of checking for duplicates first in case 204 # a custom __new__ is doing something funky with the values -- such as 205 # auto-numbering ;) 206 for member_name in classdict._member_names: 207 value = enum_members[member_name] 208 if not isinstance(value, tuple): 209 args = (value, ) 210 else: 211 args = value 212 if member_type is tuple: # special case for tuple enums 213 args = (args, ) # wrap it one more time 214 if not use_args: 215 enum_member = __new__(enum_class) 216 if not hasattr(enum_member, '_value_'): 217 enum_member._value_ = value 218 else: 219 enum_member = __new__(enum_class, *args) 220 if not hasattr(enum_member, '_value_'): 221 if member_type is object: 222 enum_member._value_ = value 223 else: 224 enum_member._value_ = member_type(*args) 225 value = enum_member._value_ 226 enum_member._name_ = member_name 227 enum_member.__objclass__ = enum_class 228 enum_member.__init__(*args) 229 # If another member with the same value was already defined, the 230 # new member becomes an alias to the existing one. 231 for name, canonical_member in enum_class._member_map_.items(): 232 if canonical_member._value_ == enum_member._value_: 233 enum_member = canonical_member 234 break 235 else: 236 # Aliases don't appear in member names (only in __members__). 237 enum_class._member_names_.append(member_name) 238 # performance boost for any member that would not shadow 239 # a DynamicClassAttribute 240 if member_name not in dynamic_attributes: 241 setattr(enum_class, member_name, enum_member) 242 # now add to _member_map_ 243 enum_class._member_map_[member_name] = enum_member 244 try: 245 # This may fail if value is not hashable. We can't add the value 246 # to the map, and by-value lookups for this value will be 247 # linear. 248 enum_class._value2member_map_[value] = enum_member 249 except TypeError: 250 pass 251 252 # double check that repr and friends are not the mixin's or various 253 # things break (such as pickle) 254 # however, if the method is defined in the Enum itself, don't replace 255 # it 256 for name in ('__repr__', '__str__', '__format__', '__reduce_ex__'): 257 if name in classdict: 258 continue 259 class_method = getattr(enum_class, name) 260 obj_method = getattr(member_type, name, None) 261 enum_method = getattr(first_enum, name, None) 262 if obj_method is not None and obj_method is class_method: 263 setattr(enum_class, name, enum_method) 264 265 # replace any other __new__ with our own (as long as Enum is not None, 266 # anyway) -- again, this is to support pickle 267 if Enum is not None: 268 # if the user defined their own __new__, save it before it gets 269 # clobbered in case they subclass later 270 if save_new: 271 enum_class.__new_member__ = __new__ 272 enum_class.__new__ = Enum.__new__ 273 274 # py3 support for definition order (helps keep py2/py3 code in sync) 275 if _order_ is not None: 276 if isinstance(_order_, str): 277 _order_ = _order_.replace(',', ' ').split() 278 if _order_ != enum_class._member_names_: 279 raise TypeError('member order does not match _order_') 280 281 return enum_class 282 283 def __bool__(self): 284 """ 285 classes/types should always be True. 286 """ 287 return True 288 289 def __call__(cls, value, names=None, *, module=None, qualname=None, type=None, start=1): 290 """Either returns an existing member, or creates a new enum class. 291 292 This method is used both when an enum class is given a value to match 293 to an enumeration member (i.e. Color(3)) and for the functional API 294 (i.e. Color = Enum('Color', names='RED GREEN BLUE')). 295 296 When used for the functional API: 297 298 `value` will be the name of the new class. 299 300 `names` should be either a string of white-space/comma delimited names 301 (values will start at `start`), or an iterator/mapping of name, value pairs. 302 303 `module` should be set to the module this class is being created in; 304 if it is not set, an attempt to find that module will be made, but if 305 it fails the class will not be picklable. 306 307 `qualname` should be set to the actual location this class can be found 308 at in its module; by default it is set to the global scope. If this is 309 not correct, unpickling will fail in some circumstances. 310 311 `type`, if set, will be mixed in as the first base class. 312 313 """ 314 if names is None: # simple value lookup 315 return cls.__new__(cls, value) 316 # otherwise, functional API: we're creating a new Enum type 317 return cls._create_(value, names, module=module, qualname=qualname, type=type, start=start) 318 319 def __contains__(cls, member): 320 if not isinstance(member, Enum): 321 raise TypeError( 322 "unsupported operand type(s) for 'in': '%s' and '%s'" % ( 323 type(member).__qualname__, cls.__class__.__qualname__)) 324 return isinstance(member, cls) and member._name_ in cls._member_map_ 325 326 def __delattr__(cls, attr): 327 # nicer error message when someone tries to delete an attribute 328 # (see issue19025). 329 if attr in cls._member_map_: 330 raise AttributeError( 331 "%s: cannot delete Enum member." % cls.__name__) 332 super().__delattr__(attr) 333 334 def __dir__(self): 335 return (['__class__', '__doc__', '__members__', '__module__'] + 336 self._member_names_) 337 338 def __getattr__(cls, name): 339 """Return the enum member matching `name` 340 341 We use __getattr__ instead of descriptors or inserting into the enum 342 class' __dict__ in order to support `name` and `value` being both 343 properties for enum members (which live in the class' __dict__) and 344 enum members themselves. 345 346 """ 347 if _is_dunder(name): 348 raise AttributeError(name) 349 try: 350 return cls._member_map_[name] 351 except KeyError: 352 raise AttributeError(name) from None 353 354 def __getitem__(cls, name): 355 return cls._member_map_[name] 356 357 def __iter__(cls): 358 return (cls._member_map_[name] for name in cls._member_names_) 359 360 def __len__(cls): 361 return len(cls._member_names_) 362 363 @property 364 def __members__(cls): 365 """Returns a mapping of member name->value. 366 367 This mapping lists all enum members, including aliases. Note that this 368 is a read-only view of the internal mapping. 369 370 """ 371 return MappingProxyType(cls._member_map_) 372 373 def __repr__(cls): 374 return "<enum %r>" % cls.__name__ 375 376 def __reversed__(cls): 377 return (cls._member_map_[name] for name in reversed(cls._member_names_)) 378 379 def __setattr__(cls, name, value): 380 """Block attempts to reassign Enum members. 381 382 A simple assignment to the class namespace only changes one of the 383 several possible ways to get an Enum member from the Enum class, 384 resulting in an inconsistent Enumeration. 385 386 """ 387 member_map = cls.__dict__.get('_member_map_', {}) 388 if name in member_map: 389 raise AttributeError('Cannot reassign members.') 390 super().__setattr__(name, value) 391 392 def _create_(cls, class_name, names, *, module=None, qualname=None, type=None, start=1): 393 """Convenience method to create a new Enum class. 394 395 `names` can be: 396 397 * A string containing member names, separated either with spaces or 398 commas. Values are incremented by 1 from `start`. 399 * An iterable of member names. Values are incremented by 1 from `start`. 400 * An iterable of (member name, value) pairs. 401 * A mapping of member name -> value pairs. 402 403 """ 404 metacls = cls.__class__ 405 bases = (cls, ) if type is None else (type, cls) 406 _, first_enum = cls._get_mixins_(cls, bases) 407 classdict = metacls.__prepare__(class_name, bases) 408 409 # special processing needed for names? 410 if isinstance(names, str): 411 names = names.replace(',', ' ').split() 412 if isinstance(names, (tuple, list)) and names and isinstance(names[0], str): 413 original_names, names = names, [] 414 last_values = [] 415 for count, name in enumerate(original_names): 416 value = first_enum._generate_next_value_(name, start, count, last_values[:]) 417 last_values.append(value) 418 names.append((name, value)) 419 420 # Here, names is either an iterable of (name, value) or a mapping. 421 for item in names: 422 if isinstance(item, str): 423 member_name, member_value = item, names[item] 424 else: 425 member_name, member_value = item 426 classdict[member_name] = member_value 427 enum_class = metacls.__new__(metacls, class_name, bases, classdict) 428 429 # TODO: replace the frame hack if a blessed way to know the calling 430 # module is ever developed 431 if module is None: 432 try: 433 module = sys._getframe(2).f_globals['__name__'] 434 except (AttributeError, ValueError, KeyError): 435 pass 436 if module is None: 437 _make_class_unpicklable(enum_class) 438 else: 439 enum_class.__module__ = module 440 if qualname is not None: 441 enum_class.__qualname__ = qualname 442 443 return enum_class 444 445 def _convert_(cls, name, module, filter, source=None): 446 """ 447 Create a new Enum subclass that replaces a collection of global constants 448 """ 449 # convert all constants from source (or module) that pass filter() to 450 # a new Enum called name, and export the enum and its members back to 451 # module; 452 # also, replace the __reduce_ex__ method so unpickling works in 453 # previous Python versions 454 module_globals = vars(sys.modules[module]) 455 if source: 456 source = vars(source) 457 else: 458 source = module_globals 459 # _value2member_map_ is populated in the same order every time 460 # for a consistent reverse mapping of number to name when there 461 # are multiple names for the same number. 462 members = [ 463 (name, value) 464 for name, value in source.items() 465 if filter(name)] 466 try: 467 # sort by value 468 members.sort(key=lambda t: (t[1], t[0])) 469 except TypeError: 470 # unless some values aren't comparable, in which case sort by name 471 members.sort(key=lambda t: t[0]) 472 cls = cls(name, members, module=module) 473 cls.__reduce_ex__ = _reduce_ex_by_name 474 module_globals.update(cls.__members__) 475 module_globals[name] = cls 476 return cls 477 478 @staticmethod 479 def _check_for_existing_members(class_name, bases): 480 for chain in bases: 481 for base in chain.__mro__: 482 if issubclass(base, Enum) and base._member_names_: 483 raise TypeError("%s: cannot extend enumeration %r" % (class_name, base.__name__)) 484 485 @staticmethod 486 def _get_mixins_(class_name, bases): 487 """Returns the type for creating enum members, and the first inherited 488 enum class. 489 490 bases: the tuple of bases that was given to __new__ 491 492 """ 493 if not bases: 494 return object, Enum 495 496 def _find_data_type(bases): 497 data_types = [] 498 for chain in bases: 499 candidate = None 500 for base in chain.__mro__: 501 if base is object: 502 continue 503 elif '__new__' in base.__dict__: 504 if issubclass(base, Enum): 505 continue 506 data_types.append(candidate or base) 507 break 508 elif not issubclass(base, Enum): 509 candidate = base 510 if len(data_types) > 1: 511 raise TypeError('%r: too many data types: %r' % (class_name, data_types)) 512 elif data_types: 513 return data_types[0] 514 else: 515 return None 516 517 # ensure final parent class is an Enum derivative, find any concrete 518 # data type, and check that Enum has no members 519 first_enum = bases[-1] 520 if not issubclass(first_enum, Enum): 521 raise TypeError("new enumerations should be created as " 522 "`EnumName([mixin_type, ...] [data_type,] enum_type)`") 523 member_type = _find_data_type(bases) or object 524 if first_enum._member_names_: 525 raise TypeError("Cannot extend enumerations") 526 return member_type, first_enum 527 528 @staticmethod 529 def _find_new_(classdict, member_type, first_enum): 530 """Returns the __new__ to be used for creating the enum members. 531 532 classdict: the class dictionary given to __new__ 533 member_type: the data type whose __new__ will be used by default 534 first_enum: enumeration to check for an overriding __new__ 535 536 """ 537 # now find the correct __new__, checking to see of one was defined 538 # by the user; also check earlier enum classes in case a __new__ was 539 # saved as __new_member__ 540 __new__ = classdict.get('__new__', None) 541 542 # should __new__ be saved as __new_member__ later? 543 save_new = __new__ is not None 544 545 if __new__ is None: 546 # check all possibles for __new_member__ before falling back to 547 # __new__ 548 for method in ('__new_member__', '__new__'): 549 for possible in (member_type, first_enum): 550 target = getattr(possible, method, None) 551 if target not in { 552 None, 553 None.__new__, 554 object.__new__, 555 Enum.__new__, 556 }: 557 __new__ = target 558 break 559 if __new__ is not None: 560 break 561 else: 562 __new__ = object.__new__ 563 564 # if a non-object.__new__ is used then whatever value/tuple was 565 # assigned to the enum member name will be passed to __new__ and to the 566 # new enum member's __init__ 567 if __new__ is object.__new__: 568 use_args = False 569 else: 570 use_args = True 571 return __new__, save_new, use_args 572 573 574class Enum(metaclass=EnumMeta): 575 """Generic enumeration. 576 577 Derive from this class to define new enumerations. 578 579 """ 580 def __new__(cls, value): 581 # all enum instances are actually created during class construction 582 # without calling this method; this method is called by the metaclass' 583 # __call__ (i.e. Color(3) ), and by pickle 584 if type(value) is cls: 585 # For lookups like Color(Color.RED) 586 return value 587 # by-value search for a matching enum member 588 # see if it's in the reverse mapping (for hashable values) 589 try: 590 return cls._value2member_map_[value] 591 except KeyError: 592 # Not found, no need to do long O(n) search 593 pass 594 except TypeError: 595 # not there, now do long search -- O(n) behavior 596 for member in cls._member_map_.values(): 597 if member._value_ == value: 598 return member 599 # still not found -- try _missing_ hook 600 try: 601 exc = None 602 result = cls._missing_(value) 603 except Exception as e: 604 exc = e 605 result = None 606 if isinstance(result, cls): 607 return result 608 else: 609 ve_exc = ValueError("%r is not a valid %s" % (value, cls.__qualname__)) 610 if result is None and exc is None: 611 raise ve_exc 612 elif exc is None: 613 exc = TypeError( 614 'error in %s._missing_: returned %r instead of None or a valid member' 615 % (cls.__name__, result) 616 ) 617 exc.__context__ = ve_exc 618 raise exc 619 620 def _generate_next_value_(name, start, count, last_values): 621 for last_value in reversed(last_values): 622 try: 623 return last_value + 1 624 except TypeError: 625 pass 626 else: 627 return start 628 629 @classmethod 630 def _missing_(cls, value): 631 return None 632 633 def __repr__(self): 634 return "<%s.%s: %r>" % ( 635 self.__class__.__name__, self._name_, self._value_) 636 637 def __str__(self): 638 return "%s.%s" % (self.__class__.__name__, self._name_) 639 640 def __dir__(self): 641 added_behavior = [ 642 m 643 for cls in self.__class__.mro() 644 for m in cls.__dict__ 645 if m[0] != '_' and m not in self._member_map_ 646 ] 647 return (['__class__', '__doc__', '__module__'] + added_behavior) 648 649 def __format__(self, format_spec): 650 # mixed-in Enums should use the mixed-in type's __format__, otherwise 651 # we can get strange results with the Enum name showing up instead of 652 # the value 653 654 # pure Enum branch, or branch with __str__ explicitly overridden 655 str_overridden = type(self).__str__ != Enum.__str__ 656 if self._member_type_ is object or str_overridden: 657 cls = str 658 val = str(self) 659 # mix-in branch 660 else: 661 cls = self._member_type_ 662 val = self._value_ 663 return cls.__format__(val, format_spec) 664 665 def __hash__(self): 666 return hash(self._name_) 667 668 def __reduce_ex__(self, proto): 669 return self.__class__, (self._value_, ) 670 671 # DynamicClassAttribute is used to provide access to the `name` and 672 # `value` properties of enum members while keeping some measure of 673 # protection from modification, while still allowing for an enumeration 674 # to have members named `name` and `value`. This works because enumeration 675 # members are not set directly on the enum class -- __getattr__ is 676 # used to look them up. 677 678 @DynamicClassAttribute 679 def name(self): 680 """The name of the Enum member.""" 681 return self._name_ 682 683 @DynamicClassAttribute 684 def value(self): 685 """The value of the Enum member.""" 686 return self._value_ 687 688 689class IntEnum(int, Enum): 690 """Enum where members are also (and must be) ints""" 691 692 693def _reduce_ex_by_name(self, proto): 694 return self.name 695 696class Flag(Enum): 697 """Support for flags""" 698 699 def _generate_next_value_(name, start, count, last_values): 700 """ 701 Generate the next value when not given. 702 703 name: the name of the member 704 start: the initial start value or None 705 count: the number of existing members 706 last_value: the last value assigned or None 707 """ 708 if not count: 709 return start if start is not None else 1 710 for last_value in reversed(last_values): 711 try: 712 high_bit = _high_bit(last_value) 713 break 714 except Exception: 715 raise TypeError('Invalid Flag value: %r' % last_value) from None 716 return 2 ** (high_bit+1) 717 718 @classmethod 719 def _missing_(cls, value): 720 original_value = value 721 if value < 0: 722 value = ~value 723 possible_member = cls._create_pseudo_member_(value) 724 if original_value < 0: 725 possible_member = ~possible_member 726 return possible_member 727 728 @classmethod 729 def _create_pseudo_member_(cls, value): 730 """ 731 Create a composite member iff value contains only members. 732 """ 733 pseudo_member = cls._value2member_map_.get(value, None) 734 if pseudo_member is None: 735 # verify all bits are accounted for 736 _, extra_flags = _decompose(cls, value) 737 if extra_flags: 738 raise ValueError("%r is not a valid %s" % (value, cls.__qualname__)) 739 # construct a singleton enum pseudo-member 740 pseudo_member = object.__new__(cls) 741 pseudo_member._name_ = None 742 pseudo_member._value_ = value 743 # use setdefault in case another thread already created a composite 744 # with this value 745 pseudo_member = cls._value2member_map_.setdefault(value, pseudo_member) 746 return pseudo_member 747 748 def __contains__(self, other): 749 if not isinstance(other, self.__class__): 750 raise TypeError( 751 "unsupported operand type(s) for 'in': '%s' and '%s'" % ( 752 type(other).__qualname__, self.__class__.__qualname__)) 753 return other._value_ & self._value_ == other._value_ 754 755 def __repr__(self): 756 cls = self.__class__ 757 if self._name_ is not None: 758 return '<%s.%s: %r>' % (cls.__name__, self._name_, self._value_) 759 members, uncovered = _decompose(cls, self._value_) 760 return '<%s.%s: %r>' % ( 761 cls.__name__, 762 '|'.join([str(m._name_ or m._value_) for m in members]), 763 self._value_, 764 ) 765 766 def __str__(self): 767 cls = self.__class__ 768 if self._name_ is not None: 769 return '%s.%s' % (cls.__name__, self._name_) 770 members, uncovered = _decompose(cls, self._value_) 771 if len(members) == 1 and members[0]._name_ is None: 772 return '%s.%r' % (cls.__name__, members[0]._value_) 773 else: 774 return '%s.%s' % ( 775 cls.__name__, 776 '|'.join([str(m._name_ or m._value_) for m in members]), 777 ) 778 779 def __bool__(self): 780 return bool(self._value_) 781 782 def __or__(self, other): 783 if not isinstance(other, self.__class__): 784 return NotImplemented 785 return self.__class__(self._value_ | other._value_) 786 787 def __and__(self, other): 788 if not isinstance(other, self.__class__): 789 return NotImplemented 790 return self.__class__(self._value_ & other._value_) 791 792 def __xor__(self, other): 793 if not isinstance(other, self.__class__): 794 return NotImplemented 795 return self.__class__(self._value_ ^ other._value_) 796 797 def __invert__(self): 798 members, uncovered = _decompose(self.__class__, self._value_) 799 inverted = self.__class__(0) 800 for m in self.__class__: 801 if m not in members and not (m._value_ & self._value_): 802 inverted = inverted | m 803 return self.__class__(inverted) 804 805 806class IntFlag(int, Flag): 807 """Support for integer-based Flags""" 808 809 @classmethod 810 def _missing_(cls, value): 811 if not isinstance(value, int): 812 raise ValueError("%r is not a valid %s" % (value, cls.__qualname__)) 813 new_member = cls._create_pseudo_member_(value) 814 return new_member 815 816 @classmethod 817 def _create_pseudo_member_(cls, value): 818 pseudo_member = cls._value2member_map_.get(value, None) 819 if pseudo_member is None: 820 need_to_create = [value] 821 # get unaccounted for bits 822 _, extra_flags = _decompose(cls, value) 823 # timer = 10 824 while extra_flags: 825 # timer -= 1 826 bit = _high_bit(extra_flags) 827 flag_value = 2 ** bit 828 if (flag_value not in cls._value2member_map_ and 829 flag_value not in need_to_create 830 ): 831 need_to_create.append(flag_value) 832 if extra_flags == -flag_value: 833 extra_flags = 0 834 else: 835 extra_flags ^= flag_value 836 for value in reversed(need_to_create): 837 # construct singleton pseudo-members 838 pseudo_member = int.__new__(cls, value) 839 pseudo_member._name_ = None 840 pseudo_member._value_ = value 841 # use setdefault in case another thread already created a composite 842 # with this value 843 pseudo_member = cls._value2member_map_.setdefault(value, pseudo_member) 844 return pseudo_member 845 846 def __or__(self, other): 847 if not isinstance(other, (self.__class__, int)): 848 return NotImplemented 849 result = self.__class__(self._value_ | self.__class__(other)._value_) 850 return result 851 852 def __and__(self, other): 853 if not isinstance(other, (self.__class__, int)): 854 return NotImplemented 855 return self.__class__(self._value_ & self.__class__(other)._value_) 856 857 def __xor__(self, other): 858 if not isinstance(other, (self.__class__, int)): 859 return NotImplemented 860 return self.__class__(self._value_ ^ self.__class__(other)._value_) 861 862 __ror__ = __or__ 863 __rand__ = __and__ 864 __rxor__ = __xor__ 865 866 def __invert__(self): 867 result = self.__class__(~self._value_) 868 return result 869 870 871def _high_bit(value): 872 """returns index of highest bit, or -1 if value is zero or negative""" 873 return value.bit_length() - 1 874 875def unique(enumeration): 876 """Class decorator for enumerations ensuring unique member values.""" 877 duplicates = [] 878 for name, member in enumeration.__members__.items(): 879 if name != member.name: 880 duplicates.append((name, member.name)) 881 if duplicates: 882 alias_details = ', '.join( 883 ["%s -> %s" % (alias, name) for (alias, name) in duplicates]) 884 raise ValueError('duplicate values found in %r: %s' % 885 (enumeration, alias_details)) 886 return enumeration 887 888def _decompose(flag, value): 889 """Extract all members from the value.""" 890 # _decompose is only called if the value is not named 891 not_covered = value 892 negative = value < 0 893 members = [] 894 for member in flag: 895 member_value = member.value 896 if member_value and member_value & value == member_value: 897 members.append(member) 898 not_covered &= ~member_value 899 if not negative: 900 tmp = not_covered 901 while tmp: 902 flag_value = 2 ** _high_bit(tmp) 903 if flag_value in flag._value2member_map_: 904 members.append(flag._value2member_map_[flag_value]) 905 not_covered &= ~flag_value 906 tmp &= ~flag_value 907 if not members and value in flag._value2member_map_: 908 members.append(flag._value2member_map_[value]) 909 members.sort(key=lambda m: m._value_, reverse=True) 910 if len(members) > 1 and members[0].value == value: 911 # we have the breakdown, don't need the value member itself 912 members.pop(0) 913 return members, not_covered 914