codekingpro/portable-devtools
114k
1import sys2import builtins as bltns3from types import MappingProxyType, DynamicClassAttribute4 5 6__all__ = [7 'EnumType', 'EnumMeta', 'EnumDict',8 'Enum', 'IntEnum', 'StrEnum', 'Flag', 'IntFlag', 'ReprEnum',9 'auto', 'unique', 'property', 'verify', 'member', 'nonmember',10 'FlagBoundary', 'STRICT', 'CONFORM', 'EJECT', 'KEEP',11 'global_flag_repr', 'global_enum_repr', 'global_str', 'global_enum',12 'EnumCheck', 'CONTINUOUS', 'NAMED_FLAGS', 'UNIQUE',13 'pickle_by_global_name', 'pickle_by_enum_name',14 ]15 16 17# Dummy value for Enum and Flag as there are explicit checks for them18# before they have been created.19# This is also why there are checks in EnumType like `if Enum is not None`20Enum = Flag = EJECT = _stdlib_enums = ReprEnum = None21 22class nonmember(object):23 """24 Protects item from becoming an Enum member during class creation.25 """26 def __init__(self, value):27 self.value = value28 29class member(object):30 """31 Forces item to become an Enum member during class creation.32 """33 def __init__(self, value):34 self.value = value35 36def _is_descriptor(obj):37 """38 Returns True if obj is a descriptor, False otherwise.39 """40 return (41 hasattr(obj, '__get__') or42 hasattr(obj, '__set__') or43 hasattr(obj, '__delete__')44 )45 46def _is_dunder(name):47 """48 Returns True if a __dunder__ name, False otherwise.49 """50 return (51 len(name) > 4 and52 name[:2] == name[-2:] == '__' and53 name[2] != '_' and54 name[-3] != '_'55 )56 57def _is_sunder(name):58 """59 Returns True if a _sunder_ name, False otherwise.60 """61 return (62 len(name) > 2 and63 name[0] == name[-1] == '_' and64 name[1] != '_' and65 name[-2] != '_'66 )67 68def _is_internal_class(cls_name, obj):69 # do not use `re` as `re` imports `enum`70 if not isinstance(obj, type):71 return False72 qualname = getattr(obj, '__qualname__', '')73 s_pattern = cls_name + '.' + getattr(obj, '__name__', '')74 e_pattern = '.' + s_pattern75 return qualname == s_pattern or qualname.endswith(e_pattern)76 77def _is_private(cls_name, name):78 # do not use `re` as `re` imports `enum`79 pattern = '_%s__' % (cls_name, )80 pat_len = len(pattern)81 if (82 len(name) > pat_len83 and name.startswith(pattern)84 and (name[-1] != '_' or name[-2] != '_')85 ):86 return True87 else:88 return False89 90def _is_single_bit(num):91 """92 True if only one bit set in num (should be an int)93 """94 if num == 0:95 return False96 num &= num - 197 return num == 098 99def _make_class_unpicklable(obj):100 """101 Make the given obj un-picklable.102 103 obj should be either a dictionary, or an Enum104 """105 def _break_on_call_reduce(self, proto):106 raise TypeError('%r cannot be pickled' % self)107 if isinstance(obj, dict):108 obj['__reduce_ex__'] = _break_on_call_reduce109 obj['__module__'] = '<unknown>'110 else:111 setattr(obj, '__reduce_ex__', _break_on_call_reduce)112 setattr(obj, '__module__', '<unknown>')113 114def _iter_bits_lsb(num):115 # num must be a positive integer116 original = num117 if isinstance(num, Enum):118 num = num.value119 if num < 0:120 raise ValueError('%r is not a positive integer' % original)121 while num:122 b = num & (~num + 1)123 yield b124 num ^= b125 126def show_flag_values(value):127 return list(_iter_bits_lsb(value))128 129def bin(num, max_bits=None):130 """131 Like built-in bin(), except negative values are represented in132 twos-complement, and the leading bit always indicates sign133 (0=positive, 1=negative).134 135 >>> bin(10)136 '0b0 1010'137 >>> bin(~10) # ~10 is -11138 '0b1 0101'139 """140 141 num = num.__index__()142 ceiling = 2 ** (num).bit_length()143 if num >= 0:144 s = bltns.bin(num + ceiling).replace('1', '0', 1)145 else:146 s = bltns.bin(~num ^ (ceiling - 1) + ceiling)147 sign = s[:3]148 digits = s[3:]149 if max_bits is not None:150 if len(digits) < max_bits:151 digits = (sign[-1] * max_bits + digits)[-max_bits:]152 return "%s %s" % (sign, digits)153 154class _not_given:155 def __repr__(self):156 return('<not given>')157_not_given = _not_given()158 159class _auto_null:160 def __repr__(self):161 return '_auto_null'162_auto_null = _auto_null()163 164class auto:165 """166 Instances are replaced with an appropriate value in Enum class suites.167 """168 def __init__(self, value=_auto_null):169 self.value = value170 171 def __repr__(self):172 return "auto(%r)" % self.value173 174class property(DynamicClassAttribute):175 """176 This is a descriptor, used to define attributes that act differently177 when accessed through an enum member and through an enum class.178 Instance access is the same as property(), but access to an attribute179 through the enum class will instead look in the class' _member_map_ for180 a corresponding enum member.181 """182 183 member = None184 _attr_type = None185 _cls_type = None186 187 def __get__(self, instance, ownerclass=None):188 if instance is None:189 if self.member is not None:190 return self.member191 else:192 raise AttributeError(193 '%r has no attribute %r' % (ownerclass, self.name)194 )195 if self.fget is not None:196 # use previous enum.property197 return self.fget(instance)198 elif self._attr_type == 'attr':199 # look up previous attribute200 return getattr(self._cls_type, self.name)201 elif self._attr_type == 'desc':202 # use previous descriptor203 return getattr(instance._value_, self.name)204 # look for a member by this name.205 try:206 return ownerclass._member_map_[self.name]207 except KeyError:208 raise AttributeError(209 '%r has no attribute %r' % (ownerclass, self.name)210 ) from None211 212 def __set__(self, instance, value):213 if self.fset is not None:214 return self.fset(instance, value)215 raise AttributeError(216 "<enum %r> cannot set attribute %r" % (self.clsname, self.name)217 )218 219 def __delete__(self, instance):220 if self.fdel is not None:221 return self.fdel(instance)222 raise AttributeError(223 "<enum %r> cannot delete attribute %r" % (self.clsname, self.name)224 )225 226 def __set_name__(self, ownerclass, name):227 self.name = name228 self.clsname = ownerclass.__name__229 230 231class _proto_member:232 """233 intermediate step for enum members between class execution and final creation234 """235 236 def __init__(self, value):237 self.value = value238 239 def __set_name__(self, enum_class, member_name):240 """241 convert each quasi-member into an instance of the new enum class242 """243 # first step: remove ourself from enum_class244 delattr(enum_class, member_name)245 # second step: create member based on enum_class246 value = self.value247 if not isinstance(value, tuple):248 args = (value, )249 else:250 args = value251 if enum_class._member_type_ is tuple: # special case for tuple enums252 args = (args, ) # wrap it one more time253 if not enum_class._use_args_:254 enum_member = enum_class._new_member_(enum_class)255 else:256 enum_member = enum_class._new_member_(enum_class, *args)257 if not hasattr(enum_member, '_value_'):258 if enum_class._member_type_ is object:259 enum_member._value_ = value260 else:261 try:262 enum_member._value_ = enum_class._member_type_(*args)263 except Exception as exc:264 new_exc = TypeError(265 '_value_ not set in __new__, unable to create it'266 )267 new_exc.__cause__ = exc268 raise new_exc269 value = enum_member._value_270 enum_member._name_ = member_name271 enum_member.__objclass__ = enum_class272 enum_member.__init__(*args)273 enum_member._sort_order_ = len(enum_class._member_names_)274 275 if Flag is not None and issubclass(enum_class, Flag):276 if isinstance(value, int):277 enum_class._flag_mask_ |= value278 if _is_single_bit(value):279 enum_class._singles_mask_ |= value280 enum_class._all_bits_ = 2 ** ((enum_class._flag_mask_).bit_length()) - 1281 282 # If another member with the same value was already defined, the283 # new member becomes an alias to the existing one.284 try:285 try:286 # try to do a fast lookup to avoid the quadratic loop287 enum_member = enum_class._value2member_map_[value]288 except TypeError:289 for name, canonical_member in enum_class._member_map_.items():290 if canonical_member._value_ == value:291 enum_member = canonical_member292 break293 else:294 raise KeyError295 except KeyError:296 # this could still be an alias if the value is multi-bit and the297 # class is a flag class298 if (299 Flag is None300 or not issubclass(enum_class, Flag)301 ):302 # no other instances found, record this member in _member_names_303 enum_class._member_names_.append(member_name)304 elif (305 Flag is not None306 and issubclass(enum_class, Flag)307 and isinstance(value, int)308 and _is_single_bit(value)309 ):310 # no other instances found, record this member in _member_names_311 enum_class._member_names_.append(member_name)312 313 enum_class._add_member_(member_name, enum_member)314 try:315 # This may fail if value is not hashable. We can't add the value316 # to the map, and by-value lookups for this value will be317 # linear.318 enum_class._value2member_map_.setdefault(value, enum_member)319 if value not in enum_class._hashable_values_:320 enum_class._hashable_values_.append(value)321 except TypeError:322 # keep track of the value in a list so containment checks are quick323 enum_class._unhashable_values_.append(value)324 enum_class._unhashable_values_map_.setdefault(member_name, []).append(value)325 326 327class EnumDict(dict):328 """329 Track enum member order and ensure member names are not reused.330 331 EnumType will use the names found in self._member_names as the332 enumeration member names.333 """334 def __init__(self, cls_name=None):335 super().__init__()336 self._member_names = {} # use a dict -- faster look-up than a list, and keeps insertion order since 3.7337 self._last_values = []338 self._ignore = []339 self._auto_called = False340 self._cls_name = cls_name341 342 def __setitem__(self, key, value):343 """344 Changes anything not dundered or not a descriptor.345 346 If an enum member name is used twice, an error is raised; duplicate347 values are not checked for.348 349 Single underscore (sunder) names are reserved.350 """351 if self._cls_name is not None and _is_private(self._cls_name, key):352 # do nothing, name will be a normal attribute353 pass354 elif _is_sunder(key):355 if key not in (356 '_order_',357 '_generate_next_value_', '_numeric_repr_', '_missing_', '_ignore_',358 '_iter_member_', '_iter_member_by_value_', '_iter_member_by_def_',359 '_add_alias_', '_add_value_alias_',360 # While not in use internally, those are common for pretty361 # printing and thus excluded from Enum's reservation of362 # _sunder_ names363 ) and not key.startswith('_repr_'):364 raise ValueError(365 '_sunder_ names, such as %r, are reserved for future Enum use'366 % (key, )367 )368 if key == '_generate_next_value_':369 # check if members already defined as auto()370 if self._auto_called:371 raise TypeError("_generate_next_value_ must be defined before members")372 _gnv = value.__func__ if isinstance(value, staticmethod) else value373 setattr(self, '_generate_next_value', _gnv)374 elif key == '_ignore_':375 if isinstance(value, str):376 value = value.replace(',',' ').split()377 else:378 value = list(value)379 self._ignore = value380 already = set(value) & set(self._member_names)381 if already:382 raise ValueError(383 '_ignore_ cannot specify already set names: %r'384 % (already, )385 )386 elif _is_dunder(key):387 if key == '__order__':388 key = '_order_'389 elif key in self._member_names:390 # descriptor overwriting an enum?391 raise TypeError('%r already defined as %r' % (key, self[key]))392 elif key in self._ignore:393 pass394 elif isinstance(value, nonmember):395 # unwrap value here; it won't be processed by the below `else`396 value = value.value397 elif _is_descriptor(value):398 pass399 elif self._cls_name is not None and _is_internal_class(self._cls_name, value):400 # do nothing, name will be a normal attribute401 pass402 else:403 if key in self:404 # enum overwriting a descriptor?405 raise TypeError('%r already defined as %r' % (key, self[key]))406 elif isinstance(value, member):407 # unwrap value here -- it will become a member408 value = value.value409 non_auto_store = True410 single = False411 if isinstance(value, auto):412 single = True413 value = (value, )414 if isinstance(value, tuple) and any(isinstance(v, auto) for v in value):415 # insist on an actual tuple, no subclasses, in keeping with only supporting416 # top-level auto() usage (not contained in any other data structure)417 auto_valued = []418 t = type(value)419 for v in value:420 if isinstance(v, auto):421 non_auto_store = False422 if v.value == _auto_null:423 v.value = self._generate_next_value(424 key, 1, len(self._member_names), self._last_values[:],425 )426 self._auto_called = True427 v = v.value428 self._last_values.append(v)429 auto_valued.append(v)430 if single:431 value = auto_valued[0]432 else:433 try:434 # accepts iterable as multiple arguments?435 value = t(auto_valued)436 except TypeError:437 # then pass them in singly438 value = t(*auto_valued)439 self._member_names[key] = None440 if non_auto_store:441 self._last_values.append(value)442 super().__setitem__(key, value)443 444 @property445 def member_names(self):446 return list(self._member_names)447 448 def update(self, members, **more_members):449 try:450 for name in members.keys():451 self[name] = members[name]452 except AttributeError:453 for name, value in members:454 self[name] = value455 for name, value in more_members.items():456 self[name] = value457 458_EnumDict = EnumDict # keep private name for backwards compatibility459 460 461class EnumType(type):462 """463 Metaclass for Enum464 """465 466 @classmethod467 def __prepare__(metacls, cls, bases, **kwds):468 # check that previous enum members do not exist469 metacls._check_for_existing_members_(cls, bases)470 # create the namespace dict471 enum_dict = EnumDict(cls)472 # inherit previous flags and _generate_next_value_ function473 member_type, first_enum = metacls._get_mixins_(cls, bases)474 if first_enum is not None:475 enum_dict['_generate_next_value_'] = getattr(476 first_enum, '_generate_next_value_', None,477 )478 return enum_dict479 480 def __new__(metacls, cls, bases, classdict, *, boundary=None, _simple=False, **kwds):481 # an Enum class is final once enumeration items have been defined; it482 # cannot be mixed with other types (int, float, etc.) if it has an483 # inherited __new__ unless a new __new__ is defined (or the resulting484 # class will fail).485 #486 if _simple:487 return super().__new__(metacls, cls, bases, classdict, **kwds)488 #489 # remove any keys listed in _ignore_490 classdict.setdefault('_ignore_', []).append('_ignore_')491 ignore = classdict['_ignore_']492 for key in ignore:493 classdict.pop(key, None)494 #495 # grab member names496 member_names = classdict._member_names497 #498 # check for illegal enum names (any others?)499 invalid_names = set(member_names) & {'mro', ''}500 if invalid_names:501 raise ValueError('invalid enum member name(s) %s' % (502 ','.join(repr(n) for n in invalid_names)503 ))504 #505 # adjust the sunders506 _order_ = classdict.pop('_order_', None)507 _gnv = classdict.get('_generate_next_value_')508 if _gnv is not None and type(_gnv) is not staticmethod:509 _gnv = staticmethod(_gnv)510 # convert to normal dict511 classdict = dict(classdict.items())512 if _gnv is not None:513 classdict['_generate_next_value_'] = _gnv514 #515 # data type of member and the controlling Enum class516 member_type, first_enum = metacls._get_mixins_(cls, bases)517 __new__, save_new, use_args = metacls._find_new_(518 classdict, member_type, first_enum,519 )520 classdict['_new_member_'] = __new__521 classdict['_use_args_'] = use_args522 #523 # convert future enum members into temporary _proto_members524 for name in member_names:525 value = classdict[name]526 classdict[name] = _proto_member(value)527 #528 # house-keeping structures529 classdict['_member_names_'] = []530 classdict['_member_map_'] = {}531 classdict['_value2member_map_'] = {}532 classdict['_hashable_values_'] = [] # for comparing with non-hashable types533 classdict['_unhashable_values_'] = [] # e.g. frozenset() with set()534 classdict['_unhashable_values_map_'] = {}535 classdict['_member_type_'] = member_type536 # now set the __repr__ for the value537 classdict['_value_repr_'] = metacls._find_data_repr_(cls, bases)538 #539 # Flag structures (will be removed if final class is not a Flag540 classdict['_boundary_'] = (541 boundary542 or getattr(first_enum, '_boundary_', None)543 )544 classdict['_flag_mask_'] = 0545 classdict['_singles_mask_'] = 0546 classdict['_all_bits_'] = 0547 classdict['_inverted_'] = None548 try:549 classdict['_%s__in_progress' % cls] = True550 enum_class = super().__new__(metacls, cls, bases, classdict, **kwds)551 classdict['_%s__in_progress' % cls] = False552 delattr(enum_class, '_%s__in_progress' % cls)553 except Exception as e:554 # since 3.12 the note "Error calling __set_name__ on '_proto_member' instance ..."555 # is tacked on to the error instead of raising a RuntimeError, so discard it556 if hasattr(e, '__notes__'):557 del e.__notes__558 raise559 # update classdict with any changes made by __init_subclass__560 classdict.update(enum_class.__dict__)561 #562 # double check that repr and friends are not the mixin's or various563 # things break (such as pickle)564 # however, if the method is defined in the Enum itself, don't replace565 # it566 #567 # Also, special handling for ReprEnum568 if ReprEnum is not None and ReprEnum in bases:569 if member_type is object:570 raise TypeError(571 'ReprEnum subclasses must be mixed with a data type (i.e.'572 ' int, str, float, etc.)'573 )574 if '__format__' not in classdict:575 enum_class.__format__ = member_type.__format__576 classdict['__format__'] = enum_class.__format__577 if '__str__' not in classdict:578 method = member_type.__str__579 if method is object.__str__:580 # if member_type does not define __str__, object.__str__ will use581 # its __repr__ instead, so we'll also use its __repr__582 method = member_type.__repr__583 enum_class.__str__ = method584 classdict['__str__'] = enum_class.__str__585 for name in ('__repr__', '__str__', '__format__', '__reduce_ex__'):586 if name not in classdict:587 # check for mixin overrides before replacing588 enum_method = getattr(first_enum, name)589 found_method = getattr(enum_class, name)590 object_method = getattr(object, name)591 data_type_method = getattr(member_type, name)592 if found_method in (data_type_method, object_method):593 setattr(enum_class, name, enum_method)594 #595 # for Flag, add __or__, __and__, __xor__, and __invert__596 if Flag is not None and issubclass(enum_class, Flag):597 for name in (598 '__or__', '__and__', '__xor__',599 '__ror__', '__rand__', '__rxor__',600 '__invert__'601 ):602 if name not in classdict:603 enum_method = getattr(Flag, name)604 setattr(enum_class, name, enum_method)605 classdict[name] = enum_method606 #607 # replace any other __new__ with our own (as long as Enum is not None,608 # anyway) -- again, this is to support pickle609 if Enum is not None:610 # if the user defined their own __new__, save it before it gets611 # clobbered in case they subclass later612 if save_new:613 enum_class.__new_member__ = __new__614 enum_class.__new__ = Enum.__new__615 #616 # py3 support for definition order (helps keep py2/py3 code in sync)617 #618 # _order_ checking is spread out into three/four steps619 # - if enum_class is a Flag:620 # - remove any non-single-bit flags from _order_621 # - remove any aliases from _order_622 # - check that _order_ and _member_names_ match623 #624 # step 1: ensure we have a list625 if _order_ is not None:626 if isinstance(_order_, str):627 _order_ = _order_.replace(',', ' ').split()628 #629 # remove Flag structures if final class is not a Flag630 if (631 Flag is None and cls != 'Flag'632 or Flag is not None and not issubclass(enum_class, Flag)633 ):634 delattr(enum_class, '_boundary_')635 delattr(enum_class, '_flag_mask_')636 delattr(enum_class, '_singles_mask_')637 delattr(enum_class, '_all_bits_')638 delattr(enum_class, '_inverted_')639 elif Flag is not None and issubclass(enum_class, Flag):640 # set correct __iter__641 member_list = [m._value_ for m in enum_class]642 if member_list != sorted(member_list):643 enum_class._iter_member_ = enum_class._iter_member_by_def_644 if _order_:645 # _order_ step 2: remove any items from _order_ that are not single-bit646 _order_ = [647 o648 for o in _order_649 if o not in enum_class._member_map_ or _is_single_bit(enum_class[o]._value_)650 ]651 #652 if _order_:653 # _order_ step 3: remove aliases from _order_654 _order_ = [655 o656 for o in _order_657 if (658 o not in enum_class._member_map_659 or660 (o in enum_class._member_map_ and o in enum_class._member_names_)661 )]662 # _order_ step 4: verify that _order_ and _member_names_ match663 if _order_ != enum_class._member_names_:664 raise TypeError(665 'member order does not match _order_:\n %r\n %r'666 % (enum_class._member_names_, _order_)667 )668 #669 return enum_class670 671 def __bool__(cls):672 """673 classes/types should always be True.674 """675 return True676 677 def __call__(cls, value, names=_not_given, *values, module=None, qualname=None, type=None, start=1, boundary=None):678 """679 Either returns an existing member, or creates a new enum class.680 681 This method is used both when an enum class is given a value to match682 to an enumeration member (i.e. Color(3)) and for the functional API683 (i.e. Color = Enum('Color', names='RED GREEN BLUE')).684 685 The value lookup branch is chosen if the enum is final.686 687 When used for the functional API:688 689 `value` will be the name of the new class.690 691 `names` should be either a string of white-space/comma delimited names692 (values will start at `start`), or an iterator/mapping of name, value pairs.693 694 `module` should be set to the module this class is being created in;695 if it is not set, an attempt to find that module will be made, but if696 it fails the class will not be picklable.697 698 `qualname` should be set to the actual location this class can be found699 at in its module; by default it is set to the global scope. If this is700 not correct, unpickling will fail in some circumstances.701 702 `type`, if set, will be mixed in as the first base class.703 """704 if cls._member_map_:705 # simple value lookup if members exist706 if names is not _not_given:707 value = (value, names) + values708 return cls.__new__(cls, value)709 # otherwise, functional API: we're creating a new Enum type710 if names is _not_given and type is None:711 # no body? no data-type? possibly wrong usage712 raise TypeError(713 f"{cls} has no members; specify `names=()` if you meant to create a new, empty, enum"714 )715 return cls._create_(716 class_name=value,717 names=None if names is _not_given else names,718 module=module,719 qualname=qualname,720 type=type,721 start=start,722 boundary=boundary,723 )724 725 def __contains__(cls, value):726 """Return True if `value` is in `cls`.727 728 `value` is in `cls` if:729 1) `value` is a member of `cls`, or730 2) `value` is the value of one of the `cls`'s members.731 3) `value` is a pseudo-member (flags)732 """733 if isinstance(value, cls):734 return True735 if issubclass(cls, Flag):736 try:737 result = cls._missing_(value)738 return isinstance(result, cls)739 except ValueError:740 pass741 return (742 value in cls._unhashable_values_ # both structures are lists743 or value in cls._hashable_values_744 )745 746 def __delattr__(cls, attr):747 # nicer error message when someone tries to delete an attribute748 # (see issue19025).749 if attr in cls._member_map_:750 raise AttributeError("%r cannot delete member %r." % (cls.__name__, attr))751 super().__delattr__(attr)752 753 def __dir__(cls):754 interesting = set([755 '__class__', '__contains__', '__doc__', '__getitem__',756 '__iter__', '__len__', '__members__', '__module__',757 '__name__', '__qualname__',758 ]759 + cls._member_names_760 )761 if cls._new_member_ is not object.__new__:762 interesting.add('__new__')763 if cls.__init_subclass__ is not object.__init_subclass__:764 interesting.add('__init_subclass__')765 if cls._member_type_ is object:766 return sorted(interesting)767 else:768 # return whatever mixed-in data type has769 return sorted(set(dir(cls._member_type_)) | interesting)770 771 def __getitem__(cls, name):772 """773 Return the member matching `name`.774 """775 return cls._member_map_[name]776 777 def __iter__(cls):778 """779 Return members in definition order.780 """781 return (cls._member_map_[name] for name in cls._member_names_)782 783 def __len__(cls):784 """785 Return the number of members (no aliases)786 """787 return len(cls._member_names_)788 789 @bltns.property790 def __members__(cls):791 """792 Returns a mapping of member name->value.793 794 This mapping lists all enum members, including aliases. Note that this795 is a read-only view of the internal mapping.796 """797 return MappingProxyType(cls._member_map_)798 799 def __repr__(cls):800 if Flag is not None and issubclass(cls, Flag):801 return "<flag %r>" % cls.__name__802 else:803 return "<enum %r>" % cls.__name__804 805 def __reversed__(cls):806 """807 Return members in reverse definition order.808 """809 return (cls._member_map_[name] for name in reversed(cls._member_names_))810 811 def __setattr__(cls, name, value):812 """813 Block attempts to reassign Enum members.814 815 A simple assignment to the class namespace only changes one of the816 several possible ways to get an Enum member from the Enum class,817 resulting in an inconsistent Enumeration.818 """819 member_map = cls.__dict__.get('_member_map_', {})820 if name in member_map:821 raise AttributeError('cannot reassign member %r' % (name, ))822 super().__setattr__(name, value)823 824 def _create_(cls, class_name, names, *, module=None, qualname=None, type=None, start=1, boundary=None):825 """826 Convenience method to create a new Enum class.827 828 `names` can be:829 830 * A string containing member names, separated either with spaces or831 commas. Values are incremented by 1 from `start`.832 * An iterable of member names. Values are incremented by 1 from `start`.833 * An iterable of (member name, value) pairs.834 * A mapping of member name -> value pairs.835 """836 metacls = cls.__class__837 bases = (cls, ) if type is None else (type, cls)838 _, first_enum = cls._get_mixins_(class_name, bases)839 classdict = metacls.__prepare__(class_name, bases)840 841 # special processing needed for names?842 if isinstance(names, str):843 names = names.replace(',', ' ').split()844 if isinstance(names, (tuple, list)) and names and isinstance(names[0], str):845 original_names, names = names, []846 last_values = []847 for count, name in enumerate(original_names):848 value = first_enum._generate_next_value_(name, start, count, last_values[:])849 last_values.append(value)850 names.append((name, value))851 if names is None:852 names = ()853 854 # Here, names is either an iterable of (name, value) or a mapping.855 for item in names:856 if isinstance(item, str):857 member_name, member_value = item, names[item]858 else:859 member_name, member_value = item860 classdict[member_name] = member_value861 862 if module is None:863 try:864 module = sys._getframemodulename(2)865 except AttributeError:866 # Fall back on _getframe if _getframemodulename is missing867 try:868 module = sys._getframe(2).f_globals['__name__']869 except (AttributeError, ValueError, KeyError):870 pass871 if module is None:872 _make_class_unpicklable(classdict)873 else:874 classdict['__module__'] = module875 if qualname is not None:876 classdict['__qualname__'] = qualname877 878 return metacls.__new__(metacls, class_name, bases, classdict, boundary=boundary)879 880 def _convert_(cls, name, module, filter, source=None, *, boundary=None, as_global=False):881 """882 Create a new Enum subclass that replaces a collection of global constants883 """884 # convert all constants from source (or module) that pass filter() to885 # a new Enum called name, and export the enum and its members back to886 # module;887 # also, replace the __reduce_ex__ method so unpickling works in888 # previous Python versions889 module_globals = sys.modules[module].__dict__890 if source:891 source = source.__dict__892 else:893 source = module_globals894 # _value2member_map_ is populated in the same order every time895 # for a consistent reverse mapping of number to name when there896 # are multiple names for the same number.897 members = [898 (name, value)899 for name, value in source.items()900 if filter(name)]901 try:902 # sort by value903 members.sort(key=lambda t: (t[1], t[0]))904 except TypeError:905 # unless some values aren't comparable, in which case sort by name906 members.sort(key=lambda t: t[0])907 body = {t[0]: t[1] for t in members}908 body['__module__'] = module909 tmp_cls = type(name, (object, ), body)910 cls = _simple_enum(etype=cls, boundary=boundary or KEEP)(tmp_cls)911 if as_global:912 global_enum(cls)913 else:914 sys.modules[cls.__module__].__dict__.update(cls.__members__)915 module_globals[name] = cls916 return cls917 918 @classmethod919 def _check_for_existing_members_(mcls, class_name, bases):920 for chain in bases:921 for base in chain.__mro__:922 if isinstance(base, EnumType) and base._member_names_:923 raise TypeError(924 "<enum %r> cannot extend %r"925 % (class_name, base)926 )927 928 @classmethod929 def _get_mixins_(mcls, class_name, bases):930 """931 Returns the type for creating enum members, and the first inherited932 enum class.933 934 bases: the tuple of bases that was given to __new__935 """936 if not bases:937 return object, Enum938 # ensure final parent class is an Enum derivative, find any concrete939 # data type, and check that Enum has no members940 first_enum = bases[-1]941 if not isinstance(first_enum, EnumType):942 raise TypeError("new enumerations should be created as "943 "`EnumName([mixin_type, ...] [data_type,] enum_type)`")944 member_type = mcls._find_data_type_(class_name, bases) or object945 return member_type, first_enum946 947 @classmethod948 def _find_data_repr_(mcls, class_name, bases):949 for chain in bases:950 for base in chain.__mro__:951 if base is object:952 continue953 elif isinstance(base, EnumType):954 # if we hit an Enum, use it's _value_repr_955 return base._value_repr_956 elif '__repr__' in base.__dict__:957 # this is our data repr958 # double-check if a dataclass with a default __repr__959 if (960 '__dataclass_fields__' in base.__dict__961 and '__dataclass_params__' in base.__dict__962 and base.__dict__['__dataclass_params__'].repr963 ):964 return _dataclass_repr965 else:966 return base.__dict__['__repr__']967 return None968 969 @classmethod970 def _find_data_type_(mcls, class_name, bases):971 # a datatype has a __new__ method, or a __dataclass_fields__ attribute972 data_types = set()973 base_chain = set()974 for chain in bases:975 candidate = None976 for base in chain.__mro__:977 base_chain.add(base)978 if base is object:979 continue980 elif isinstance(base, EnumType):981 if base._member_type_ is not object:982 data_types.add(base._member_type_)983 break984 elif '__new__' in base.__dict__ or '__dataclass_fields__' in base.__dict__:985 data_types.add(candidate or base)986 break987 else:988 candidate = candidate or base989 if len(data_types) > 1:990 raise TypeError('too many data types for %r: %r' % (class_name, data_types))991 elif data_types:992 return data_types.pop()993 else:994 return None995 996 @classmethod997 def _find_new_(mcls, classdict, member_type, first_enum):998 """999 Returns the __new__ to be used for creating the enum members.1000 1001 classdict: the class dictionary given to __new__1002 member_type: the data type whose __new__ will be used by default1003 first_enum: enumeration to check for an overriding __new__1004 """1005 # now find the correct __new__, checking to see of one was defined1006 # by the user; also check earlier enum classes in case a __new__ was1007 # saved as __new_member__1008 __new__ = classdict.get('__new__', None)1009 1010 # should __new__ be saved as __new_member__ later?1011 save_new = first_enum is not None and __new__ is not None1012 1013 if __new__ is None:1014 # check all possibles for __new_member__ before falling back to1015 # __new__1016 for method in ('__new_member__', '__new__'):1017 for possible in (member_type, first_enum):1018 target = getattr(possible, method, None)1019 if target not in {1020 None,1021 None.__new__,1022 object.__new__,1023 Enum.__new__,1024 }:1025 __new__ = target1026 break1027 if __new__ is not None:1028 break1029 else:1030 __new__ = object.__new__1031 1032 # if a non-object.__new__ is used then whatever value/tuple was1033 # assigned to the enum member name will be passed to __new__ and to the1034 # new enum member's __init__1035 if first_enum is None or __new__ in (Enum.__new__, object.__new__):1036 use_args = False1037 else:1038 use_args = True1039 return __new__, save_new, use_args1040 1041 def _add_member_(cls, name, member):1042 # _value_ structures are not updated1043 if name in cls._member_map_:1044 if cls._member_map_[name] is not member:1045 raise NameError('%r is already bound: %r' % (name, cls._member_map_[name]))1046 return1047 #1048 # if necessary, get redirect in place and then add it to _member_map_1049 found_descriptor = None1050 descriptor_type = None1051 class_type = None1052 for base in cls.__mro__[1:]:1053 attr = base.__dict__.get(name)1054 if attr is not None:1055 if isinstance(attr, (property, DynamicClassAttribute)):1056 found_descriptor = attr1057 class_type = base1058 descriptor_type = 'enum'1059 break1060 elif _is_descriptor(attr):1061 found_descriptor = attr1062 descriptor_type = descriptor_type or 'desc'1063 class_type = class_type or base1064 continue1065 else:1066 descriptor_type = 'attr'1067 class_type = base1068 if found_descriptor:1069 redirect = property()1070 redirect.member = member1071 redirect.__set_name__(cls, name)1072 if descriptor_type in ('enum', 'desc'):1073 # earlier descriptor found; copy fget, fset, fdel to this one.1074 redirect.fget = getattr(found_descriptor, 'fget', None)1075 redirect._get = getattr(found_descriptor, '__get__', None)1076 redirect.fset = getattr(found_descriptor, 'fset', None)1077 redirect._set = getattr(found_descriptor, '__set__', None)1078 redirect.fdel = getattr(found_descriptor, 'fdel', None)1079 redirect._del = getattr(found_descriptor, '__delete__', None)1080 redirect._attr_type = descriptor_type1081 redirect._cls_type = class_type1082 setattr(cls, name, redirect)1083 else:1084 setattr(cls, name, member)1085 # now add to _member_map_ (even aliases)1086 cls._member_map_[name] = member1087 1088 @property1089 def __signature__(cls):1090 from inspect import Parameter, Signature1091 if cls._member_names_:1092 return Signature([Parameter('values', Parameter.VAR_POSITIONAL)])1093 else:1094 return Signature([Parameter('new_class_name', Parameter.POSITIONAL_ONLY),1095 Parameter('names', Parameter.POSITIONAL_OR_KEYWORD),1096 Parameter('module', Parameter.KEYWORD_ONLY, default=None),1097 Parameter('qualname', Parameter.KEYWORD_ONLY, default=None),1098 Parameter('type', Parameter.KEYWORD_ONLY, default=None),1099 Parameter('start', Parameter.KEYWORD_ONLY, default=1),1100 Parameter('boundary', Parameter.KEYWORD_ONLY, default=None)])1101 1102 1103EnumMeta = EnumType # keep EnumMeta name for backwards compatibility1104 1105 1106class Enum(metaclass=EnumType):1107 """1108 Create a collection of name/value pairs.1109 1110 Example enumeration:1111 1112 >>> class Color(Enum):1113 ... RED = 11114 ... BLUE = 21115 ... GREEN = 31116 1117 Access them by:1118 1119 - attribute access:1120 1121 >>> Color.RED1122 <Color.RED: 1>1123 1124 - value lookup:1125 1126 >>> Color(1)1127 <Color.RED: 1>1128 1129 - name lookup:1130 1131 >>> Color['RED']1132 <Color.RED: 1>1133 1134 Enumerations can be iterated over, and know how many members they have:1135 1136 >>> len(Color)1137 31138 1139 >>> list(Color)1140 [<Color.RED: 1>, <Color.BLUE: 2>, <Color.GREEN: 3>]1141 1142 Methods can be added to enumerations, and members can have their own1143 attributes -- see the documentation for details.1144 """1145 1146 def __new__(cls, value):1147 # all enum instances are actually created during class construction1148 # without calling this method; this method is called by the metaclass'1149 # __call__ (i.e. Color(3) ), and by pickle1150 if type(value) is cls:1151 # For lookups like Color(Color.RED)1152 return value1153 # by-value search for a matching enum member1154 # see if it's in the reverse mapping (for hashable values)1155 try:1156 return cls._value2member_map_[value]1157 except KeyError:1158 # Not found, no need to do long O(n) search1159 pass1160 except TypeError:1161 # not there, now do long search -- O(n) behavior1162 for name, unhashable_values in cls._unhashable_values_map_.items():1163 if value in unhashable_values:1164 return cls[name]1165 for name, member in cls._member_map_.items():1166 if value == member._value_:1167 return cls[name]1168 # still not found -- verify that members exist, in-case somebody got here mistakenly1169 # (such as via super when trying to override __new__)1170 if not cls._member_map_:1171 if getattr(cls, '_%s__in_progress' % cls.__name__, False):1172 raise TypeError('do not use `super().__new__; call the appropriate __new__ directly') from None1173 raise TypeError("%r has no members defined" % cls)1174 #1175 # still not found -- try _missing_ hook1176 try:1177 exc = None1178 result = cls._missing_(value)1179 except Exception as e:1180 exc = e1181 result = None1182 try:1183 if isinstance(result, cls):1184 return result1185 elif (1186 Flag is not None and issubclass(cls, Flag)1187 and cls._boundary_ is EJECT and isinstance(result, int)1188 ):1189 return result1190 else:1191 ve_exc = ValueError("%r is not a valid %s" % (value, cls.__qualname__))1192 if result is None and exc is None:1193 raise ve_exc1194 elif exc is None:1195 exc = TypeError(1196 'error in %s._missing_: returned %r instead of None or a valid member'1197 % (cls.__name__, result)1198 )1199 if not isinstance(exc, ValueError):1200 exc.__context__ = ve_exc