Coverage for /root/GitHubProjects/impacket/impacket/dcerpc/v5/enum.py : 53%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""Python Enumerations"""
3import sys as _sys
5__all__ = ['Enum', 'IntEnum', 'unique']
7pyver = float('%s.%s' % _sys.version_info[:2])
9try:
10 any
11except NameError:
12 def any(iterable):
13 for element in iterable:
14 if element:
15 return True
16 return False
19class _RouteClassAttributeToGetattr(object):
20 """Route attribute access on a class to __getattr__.
22 This is a descriptor, used to define attributes that act differently when
23 accessed through an instance and through a class. Instance access remains
24 normal, but access to an attribute through a class will be routed to the
25 class's __getattr__ method; this is done by raising AttributeError.
27 """
28 def __init__(self, fget=None):
29 self.fget = fget
31 def __get__(self, instance, ownerclass=None):
32 if instance is None: 32 ↛ 33line 32 didn't jump to line 33, because the condition on line 32 was never true
33 raise AttributeError()
34 return self.fget(instance)
36 def __set__(self, instance, value):
37 raise AttributeError("can't set attribute")
39 def __delete__(self, instance):
40 raise AttributeError("can't delete attribute")
43def _is_descriptor(obj):
44 """Returns True if obj is a descriptor, False otherwise."""
45 return (
46 hasattr(obj, '__get__') or
47 hasattr(obj, '__set__') or
48 hasattr(obj, '__delete__'))
51def _is_dunder(name):
52 """Returns True if a __dunder__ name, False otherwise."""
53 return (name[:2] == name[-2:] == '__' and
54 name[2:3] != '_' and
55 name[-3:-2] != '_' and
56 len(name) > 4)
59def _is_sunder(name):
60 """Returns True if a _sunder_ name, False otherwise."""
61 return (name[0] == name[-1] == '_' and
62 name[1:2] != '_' and
63 name[-2:-1] != '_' and
64 len(name) > 2)
67def _make_class_unpicklable(cls):
68 """Make the given class un-picklable."""
69 def _break_on_call_reduce(self):
70 raise TypeError('%r cannot be pickled' % self)
71 cls.__reduce__ = _break_on_call_reduce
72 cls.__module__ = '<unknown>'
75class _EnumDict(dict):
76 """Track enum member order and ensure member names are not reused.
78 EnumMeta will use the names found in self._member_names as the
79 enumeration member names.
81 """
82 def __init__(self):
83 super(_EnumDict, self).__init__()
84 self._member_names = []
86 def __setitem__(self, key, value):
87 """Changes anything not dundered or not a descriptor.
89 If a descriptor is added with the same name as an enum member, the name
90 is removed from _member_names (this may leave a hole in the numerical
91 sequence of values).
93 If an enum member name is used twice, an error is raised; duplicate
94 values are not checked for.
96 Single underscore (sunder) names are reserved.
98 Note: in 3.x __order__ is simply discarded as a not necessary piece
99 leftover from 2.x
101 """
102 if pyver >= 3.0 and key == '__order__': 102 ↛ 103line 102 didn't jump to line 103, because the condition on line 102 was never true
103 return
104 if _is_sunder(key): 104 ↛ 105line 104 didn't jump to line 105, because the condition on line 104 was never true
105 raise ValueError('_names_ are reserved for future Enum use')
106 elif _is_dunder(key):
107 pass
108 elif key in self._member_names: 108 ↛ 110line 108 didn't jump to line 110, because the condition on line 108 was never true
109 # descriptor overwriting an enum?
110 raise TypeError('Attempted to reuse key: %r' % key)
111 elif not _is_descriptor(value):
112 if key in self: 112 ↛ 114line 112 didn't jump to line 114, because the condition on line 112 was never true
113 # enum overwriting a descriptor?
114 raise TypeError('Key already defined as: %r' % self[key])
115 self._member_names.append(key)
116 super(_EnumDict, self).__setitem__(key, value)
119# Dummy value for Enum as EnumMeta explicitly checks for it, but of course until
120# EnumMeta finishes running the first time the Enum class doesn't exist. This
121# is also why there are checks in EnumMeta like `if Enum is not None`
122Enum = None
125class EnumMeta(type):
126 """Metaclass for Enum"""
127 @classmethod
128 def __prepare__(metacls, cls, bases):
129 return _EnumDict()
131 def __new__(metacls, cls, bases, classdict):
132 # an Enum class is final once enumeration items have been defined; it
133 # cannot be mixed with other types (int, float, etc.) if it has an
134 # inherited __new__ unless a new __new__ is defined (or the resulting
135 # class will fail).
136 if type(classdict) is dict:
137 original_dict = classdict
138 classdict = _EnumDict()
139 for k, v in original_dict.items():
140 classdict[k] = v
142 member_type, first_enum = metacls._get_mixins_(bases)
143 #if member_type is object:
144 # use_args = False
145 #else:
146 # use_args = True
147 __new__, save_new, use_args = metacls._find_new_(classdict, member_type,
148 first_enum)
149 # save enum items into separate mapping so they don't get baked into
150 # the new class
151 members = dict((k, classdict[k]) for k in classdict._member_names)
152 for name in classdict._member_names:
153 del classdict[name]
155 # py2 support for definition order
156 __order__ = classdict.get('__order__')
157 if __order__ is None: 157 ↛ 164line 157 didn't jump to line 164, because the condition on line 157 was never false
158 __order__ = classdict._member_names
159 if pyver < 3.0: 159 ↛ 160line 159 didn't jump to line 160, because the condition on line 159 was never true
160 order_specified = False
161 else:
162 order_specified = True
163 else:
164 del classdict['__order__']
165 order_specified = True
166 if pyver < 3.0:
167 __order__ = __order__.replace(',', ' ').split()
168 aliases = [name for name in members if name not in __order__]
169 __order__ += aliases
171 # check for illegal enum names (any others?)
172 invalid_names = set(members) & set(['mro'])
173 if invalid_names: 173 ↛ 174line 173 didn't jump to line 174, because the condition on line 173 was never true
174 raise ValueError('Invalid enum member name(s): %s' % (
175 ', '.join(invalid_names), ))
177 # create our new Enum type
178 enum_class = super(EnumMeta, metacls).__new__(metacls, cls, bases, classdict)
179 enum_class._member_names_ = [] # names in random order
180 enum_class._member_map_ = {} # name->value map
181 enum_class._member_type_ = member_type
183 # Reverse value->name map for hashable values.
184 enum_class._value2member_map_ = {}
186 # check for a __getnewargs__, and if not present sabotage
187 # pickling, since it won't work anyway
188 if (member_type is not object and 188 ↛ 191line 188 didn't jump to line 191, because the condition on line 188 was never true
189 member_type.__dict__.get('__getnewargs__') is None
190 ):
191 _make_class_unpicklable(enum_class)
193 # instantiate them, checking for duplicates as we go
194 # we instantiate first instead of checking for duplicates first in case
195 # a custom __new__ is doing something funky with the values -- such as
196 # auto-numbering ;)
197 if __new__ is None: 197 ↛ 198line 197 didn't jump to line 198, because the condition on line 197 was never true
198 __new__ = enum_class.__new__
199 for member_name in __order__:
200 value = members[member_name]
201 if not isinstance(value, tuple): 201 ↛ 204line 201 didn't jump to line 204, because the condition on line 201 was never false
202 args = (value, )
203 else:
204 args = value
205 if member_type is tuple: # special case for tuple enums 205 ↛ 206line 205 didn't jump to line 206, because the condition on line 205 was never true
206 args = (args, ) # wrap it one more time
207 if not use_args or not args: 207 ↛ 212line 207 didn't jump to line 212, because the condition on line 207 was never false
208 enum_member = __new__(enum_class)
209 if not hasattr(enum_member, '_value_'): 209 ↛ 215line 209 didn't jump to line 215, because the condition on line 209 was never false
210 enum_member._value_ = value
211 else:
212 enum_member = __new__(enum_class, *args)
213 if not hasattr(enum_member, '_value_'):
214 enum_member._value_ = member_type(*args)
215 value = enum_member._value_
216 enum_member._name_ = member_name
217 enum_member.__objclass__ = enum_class
218 enum_member.__init__(*args)
219 # If another member with the same value was already defined, the
220 # new member becomes an alias to the existing one.
221 for name, canonical_member in enum_class._member_map_.items():
222 if canonical_member.value == enum_member._value_:
223 enum_member = canonical_member
224 break
225 else:
226 # Aliases don't appear in member names (only in __members__).
227 enum_class._member_names_.append(member_name)
228 enum_class._member_map_[member_name] = enum_member
229 try:
230 # This may fail if value is not hashable. We can't add the value
231 # to the map, and by-value lookups for this value will be
232 # linear.
233 enum_class._value2member_map_[value] = enum_member
234 except TypeError:
235 pass
237 # in Python2.x we cannot know definition order, so go with value order
238 # unless __order__ was specified in the class definition
239 if not order_specified: 239 ↛ 240line 239 didn't jump to line 240, because the condition on line 239 was never true
240 enum_class._member_names_ = [
241 e[0] for e in sorted(
242 [(name, enum_class._member_map_[name]) for name in enum_class._member_names_],
243 key=lambda t: t[1]._value_
244 )]
246 # double check that repr and friends are not the mixin's or various
247 # things break (such as pickle)
248 if Enum is not None:
249 setattr(enum_class, '__getnewargs__', Enum.__getnewargs__)
250 for name in ('__repr__', '__str__', '__format__'):
251 class_method = getattr(enum_class, name)
252 obj_method = getattr(member_type, name, None)
253 enum_method = getattr(first_enum, name, None)
254 if obj_method is not None and obj_method is class_method:
255 setattr(enum_class, name, enum_method)
257 # method resolution and int's are not playing nice
258 # Python's less than 2.6 use __cmp__
260 if pyver < 2.6: 260 ↛ 262line 260 didn't jump to line 262, because the condition on line 260 was never true
262 if issubclass(enum_class, int):
263 setattr(enum_class, '__cmp__', getattr(int, '__cmp__'))
265 elif pyver < 3.0: 265 ↛ 267line 265 didn't jump to line 267, because the condition on line 265 was never true
267 if issubclass(enum_class, int):
268 for method in (
269 '__le__',
270 '__lt__',
271 '__gt__',
272 '__ge__',
273 '__eq__',
274 '__ne__',
275 '__hash__',
276 ):
277 setattr(enum_class, method, getattr(int, method))
279 # replace any other __new__ with our own (as long as Enum is not None,
280 # anyway) -- again, this is to support pickle
281 if Enum is not None:
282 # if the user defined their own __new__, save it before it gets
283 # clobbered in case they subclass later
284 if save_new: 284 ↛ 285line 284 didn't jump to line 285, because the condition on line 284 was never true
285 setattr(enum_class, '__member_new__', enum_class.__dict__['__new__'])
286 setattr(enum_class, '__new__', Enum.__dict__['__new__'])
287 return enum_class
289 def __call__(cls, value, names=None, module=None, type=None):
290 """Either returns an existing member, or creates a new enum class.
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')).
296 When used for the functional API: `module`, if set, will be stored in
297 the new class' __module__ attribute; `type`, if set, will be mixed in
298 as the first base class.
300 Note: if `module` is not set this routine will attempt to discover the
301 calling module by walking the frame stack; if this is unsuccessful
302 the resulting class will not be pickleable.
304 """
305 if names is None: # simple value lookup 305 ↛ 308line 305 didn't jump to line 308, because the condition on line 305 was never false
306 return cls.__new__(cls, value)
307 # otherwise, functional API: we're creating a new Enum type
308 return cls._create_(value, names, module=module, type=type)
310 def __contains__(cls, member):
311 return isinstance(member, cls) and member.name in cls._member_map_
313 def __delattr__(cls, attr):
314 # nicer error message when someone tries to delete an attribute
315 # (see issue19025).
316 if attr in cls._member_map_:
317 raise AttributeError(
318 "%s: cannot delete Enum member." % cls.__name__)
319 super(EnumMeta, cls).__delattr__(attr)
321 def __dir__(self):
322 return (['__class__', '__doc__', '__members__', '__module__'] +
323 self._member_names_)
325 @property
326 def __members__(cls):
327 """Returns a mapping of member name->value.
329 This mapping lists all enum members, including aliases. Note that this
330 is a copy of the internal mapping.
332 """
333 return cls._member_map_.copy()
335 def __getattr__(cls, name):
336 """Return the enum member matching `name`
338 We use __getattr__ instead of descriptors or inserting into the enum
339 class' __dict__ in order to support `name` and `value` being both
340 properties for enum members (which live in the class' __dict__) and
341 enum members themselves.
343 """
344 if _is_dunder(name):
345 raise AttributeError(name)
346 try:
347 return cls._member_map_[name]
348 except KeyError:
349 raise AttributeError(name)
351 def __getitem__(cls, name):
352 return cls._member_map_[name]
354 def __iter__(cls):
355 return (cls._member_map_[name] for name in cls._member_names_)
357 def __reversed__(cls):
358 return (cls._member_map_[name] for name in reversed(cls._member_names_))
360 def __len__(cls):
361 return len(cls._member_names_)
363 def __repr__(cls):
364 return "<enum %r>" % cls.__name__
366 def __setattr__(cls, name, value):
367 """Block attempts to reassign Enum members.
369 A simple assignment to the class namespace only changes one of the
370 several possible ways to get an Enum member from the Enum class,
371 resulting in an inconsistent Enumeration.
373 """
374 member_map = cls.__dict__.get('_member_map_', {})
375 if name in member_map: 375 ↛ 376line 375 didn't jump to line 376, because the condition on line 375 was never true
376 raise AttributeError('Cannot reassign members.')
377 super(EnumMeta, cls).__setattr__(name, value)
379 def _create_(cls, class_name, names=None, module=None, type=None):
380 """Convenience method to create a new Enum class.
382 `names` can be:
384 * A string containing member names, separated either with spaces or
385 commas. Values are auto-numbered from 1.
386 * An iterable of member names. Values are auto-numbered from 1.
387 * An iterable of (member name, value) pairs.
388 * A mapping of member name -> value.
390 """
391 metacls = cls.__class__
392 if type is None:
393 bases = (cls, )
394 else:
395 bases = (type, cls)
396 classdict = metacls.__prepare__(class_name, bases)
397 __order__ = []
399 # special processing needed for names?
400 if isinstance(names, str):
401 names = names.replace(',', ' ').split()
402 if isinstance(names, (tuple, list)) and isinstance(names[0], str):
403 names = [(e, i+1) for (i, e) in enumerate(names)]
405 # Here, names is either an iterable of (name, value) or a mapping.
406 for item in names:
407 if isinstance(item, str):
408 member_name, member_value = item, names[item]
409 else:
410 member_name, member_value = item
411 classdict[member_name] = member_value
412 __order__.append(member_name)
413 # only set __order__ in classdict if name/value was not from a mapping
414 if not isinstance(item, str):
415 classdict['__order__'] = ' '.join(__order__)
416 enum_class = metacls.__new__(metacls, class_name, bases, classdict)
418 # TODO: replace the frame hack if a blessed way to know the calling
419 # module is ever developed
420 if module is None:
421 try:
422 module = _sys._getframe(2).f_globals['__name__']
423 except (AttributeError, ValueError):
424 pass
425 if module is None:
426 _make_class_unpicklable(enum_class)
427 else:
428 enum_class.__module__ = module
430 return enum_class
432 @staticmethod
433 def _get_mixins_(bases):
434 """Returns the type for creating enum members, and the first inherited
435 enum class.
437 bases: the tuple of bases that was given to __new__
439 """
440 if not bases or Enum is None:
441 return object, Enum
444 # double check that we are not subclassing a class with existing
445 # enumeration members; while we're at it, see if any other data
446 # type has been mixed in so we can use the correct __new__
447 member_type = first_enum = None
448 for base in bases:
449 if (base is not Enum and 449 ↛ 452line 449 didn't jump to line 452, because the condition on line 449 was never true
450 issubclass(base, Enum) and
451 base._member_names_):
452 raise TypeError("Cannot extend enumerations")
453 # base is now the last base in bases
454 if not issubclass(base, Enum): 454 ↛ 455line 454 didn't jump to line 455, because the condition on line 454 was never true
455 raise TypeError("new enumerations must be created as "
456 "`ClassName([mixin_type,] enum_type)`")
458 # get correct mix-in type (either mix-in type of Enum subclass, or
459 # first base if last base is Enum)
460 if not issubclass(bases[0], Enum):
461 member_type = bases[0] # first data type
462 first_enum = bases[-1] # enum type
463 else:
464 for base in bases[0].__mro__:
465 # most common: (IntEnum, int, Enum, object)
466 # possible: (<Enum 'AutoIntEnum'>, <Enum 'IntEnum'>,
467 # <class 'int'>, <Enum 'Enum'>,
468 # <class 'object'>)
469 if issubclass(base, Enum):
470 if first_enum is None: 470 ↛ 464line 470 didn't jump to line 464, because the condition on line 470 was never false
471 first_enum = base
472 else:
473 if member_type is None: 473 ↛ 464line 473 didn't jump to line 464, because the condition on line 473 was never false
474 member_type = base
476 return member_type, first_enum
478 if pyver < 3.0: 478 ↛ 479line 478 didn't jump to line 479, because the condition on line 478 was never true
479 @staticmethod
480 def _find_new_(classdict, member_type, first_enum):
481 """Returns the __new__ to be used for creating the enum members.
483 classdict: the class dictionary given to __new__
484 member_type: the data type whose __new__ will be used by default
485 first_enum: enumeration to check for an overriding __new__
487 """
488 # now find the correct __new__, checking to see of one was defined
489 # by the user; also check earlier enum classes in case a __new__ was
490 # saved as __member_new__
491 __new__ = classdict.get('__new__', None)
492 if __new__:
493 return None, True, True # __new__, save_new, use_args
495 N__new__ = getattr(None, '__new__')
496 O__new__ = getattr(object, '__new__')
497 if Enum is None:
498 E__new__ = N__new__
499 else:
500 E__new__ = Enum.__dict__['__new__']
501 # check all possibles for __member_new__ before falling back to
502 # __new__
503 for method in ('__member_new__', '__new__'):
504 for possible in (member_type, first_enum):
505 try:
506 target = possible.__dict__[method]
507 except (AttributeError, KeyError):
508 target = getattr(possible, method, None)
509 if target not in [
510 None,
511 N__new__,
512 O__new__,
513 E__new__,
514 ]:
515 if method == '__member_new__':
516 classdict['__new__'] = target
517 return None, False, True
518 if isinstance(target, staticmethod):
519 target = target.__get__(member_type)
520 __new__ = target
521 break
522 if __new__ is not None:
523 break
524 else:
525 __new__ = object.__new__
527 # if a non-object.__new__ is used then whatever value/tuple was
528 # assigned to the enum member name will be passed to __new__ and to the
529 # new enum member's __init__
530 if __new__ is object.__new__:
531 use_args = False
532 else:
533 use_args = True
535 return __new__, False, use_args
536 else:
537 @staticmethod
538 def _find_new_(classdict, member_type, first_enum):
539 """Returns the __new__ to be used for creating the enum members.
541 classdict: the class dictionary given to __new__
542 member_type: the data type whose __new__ will be used by default
543 first_enum: enumeration to check for an overriding __new__
545 """
546 # now find the correct __new__, checking to see of one was defined
547 # by the user; also check earlier enum classes in case a __new__ was
548 # saved as __member_new__
549 __new__ = classdict.get('__new__', None)
551 # should __new__ be saved as __member_new__ later?
552 save_new = __new__ is not None
554 if __new__ is None:
555 # check all possibles for __member_new__ before falling back to
556 # __new__
557 for method in ('__member_new__', '__new__'):
558 for possible in (member_type, first_enum):
559 target = getattr(possible, method, None)
560 if target not in (
561 None,
562 None.__new__,
563 object.__new__,
564 Enum.__new__,
565 ):
566 __new__ = target
567 break
568 if __new__ is not None:
569 break
570 else:
571 __new__ = object.__new__
573 # if a non-object.__new__ is used then whatever value/tuple was
574 # assigned to the enum member name will be passed to __new__ and to the
575 # new enum member's __init__
576 if __new__ is object.__new__:
577 use_args = False
578 else:
579 use_args = True
581 return __new__, save_new, use_args
584########################################################
585# In order to support Python 2 and 3 with a single
586# codebase we have to create the Enum methods separately
587# and then use the `type(name, bases, dict)` method to
588# create the class.
589########################################################
590temp_enum_dict = {}
591temp_enum_dict['__doc__'] = "Generic enumeration.\n\n Derive from this class to define new enumerations.\n\n"
593def __new__(cls, value):
594 # all enum instances are actually created during class construction
595 # without calling this method; this method is called by the metaclass'
596 # __call__ (i.e. Color(3) ), and by pickle
597 if type(value) is cls: 597 ↛ 599line 597 didn't jump to line 599, because the condition on line 597 was never true
598 # For lookups like Color(Color.red)
599 value = value.value
600 #return value
601 # by-value search for a matching enum member
602 # see if it's in the reverse mapping (for hashable values)
603 try:
604 if value in cls._value2member_map_:
605 return cls._value2member_map_[value]
606 except TypeError:
607 # not there, now do long search -- O(n) behavior
608 for member in cls._member_map_.values():
609 if member.value == value:
610 return member
611 raise ValueError("%s is not a valid %s" % (value, cls.__name__))
612temp_enum_dict['__new__'] = __new__
613del __new__
615def __repr__(self):
616 return "<%s.%s: %r>" % (
617 self.__class__.__name__, self._name_, self._value_)
618temp_enum_dict['__repr__'] = __repr__
619del __repr__
621def __str__(self):
622 return "%s.%s" % (self.__class__.__name__, self._name_)
623temp_enum_dict['__str__'] = __str__
624del __str__
626def __dir__(self):
627 added_behavior = [m for m in self.__class__.__dict__ if m[0] != '_']
628 return (['__class__', '__doc__', '__module__', 'name', 'value'] + added_behavior)
629temp_enum_dict['__dir__'] = __dir__
630del __dir__
632def __format__(self, format_spec):
633 # mixed-in Enums should use the mixed-in type's __format__, otherwise
634 # we can get strange results with the Enum name showing up instead of
635 # the value
637 # pure Enum branch
638 if self._member_type_ is object:
639 cls = str
640 val = str(self)
641 # mix-in branch
642 else:
643 cls = self._member_type_
644 val = self.value
645 return cls.__format__(val, format_spec)
646temp_enum_dict['__format__'] = __format__
647del __format__
650####################################
651# Python's less than 2.6 use __cmp__
653if pyver < 2.6: 653 ↛ 655line 653 didn't jump to line 655, because the condition on line 653 was never true
655 def __cmp__(self, other):
656 if type(other) is self.__class__:
657 if self is other:
658 return 0
659 return -1
660 return NotImplemented
661 raise TypeError("unorderable types: %s() and %s()" % (self.__class__.__name__, other.__class__.__name__))
662 temp_enum_dict['__cmp__'] = __cmp__
663 del __cmp__
665else:
667 def __le__(self, other):
668 raise TypeError("unorderable types: %s() <= %s()" % (self.__class__.__name__, other.__class__.__name__))
669 temp_enum_dict['__le__'] = __le__
670 del __le__
672 def __lt__(self, other):
673 raise TypeError("unorderable types: %s() < %s()" % (self.__class__.__name__, other.__class__.__name__))
674 temp_enum_dict['__lt__'] = __lt__
675 del __lt__
677 def __ge__(self, other):
678 raise TypeError("unorderable types: %s() >= %s()" % (self.__class__.__name__, other.__class__.__name__))
679 temp_enum_dict['__ge__'] = __ge__
680 del __ge__
682 def __gt__(self, other):
683 raise TypeError("unorderable types: %s() > %s()" % (self.__class__.__name__, other.__class__.__name__))
684 temp_enum_dict['__gt__'] = __gt__
685 del __gt__
688def __eq__(self, other):
689 if type(other) is self.__class__:
690 return self is other
691 return NotImplemented
692temp_enum_dict['__eq__'] = __eq__
693del __eq__
695def __ne__(self, other):
696 if type(other) is self.__class__:
697 return self is not other
698 return NotImplemented
699temp_enum_dict['__ne__'] = __ne__
700del __ne__
702def __getnewargs__(self):
703 return (self._value_, )
704temp_enum_dict['__getnewargs__'] = __getnewargs__
705del __getnewargs__
707def __hash__(self):
708 return hash(self._name_)
709temp_enum_dict['__hash__'] = __hash__
710del __hash__
712# _RouteClassAttributeToGetattr is used to provide access to the `name`
713# and `value` properties of enum members while keeping some measure of
714# protection from modification, while still allowing for an enumeration
715# to have members named `name` and `value`. This works because enumeration
716# members are not set directly on the enum class -- __getattr__ is
717# used to look them up.
719@_RouteClassAttributeToGetattr
720def name(self):
721 return self._name_
722temp_enum_dict['name'] = name
723del name
725@_RouteClassAttributeToGetattr
726def value(self):
727 return self._value_
728temp_enum_dict['value'] = value
729del value
731Enum = EnumMeta('Enum', (object, ), temp_enum_dict)
732del temp_enum_dict
734# Enum has now been created
735###########################
737class IntEnum(int, Enum):
738 """Enum where members are also (and must be) ints"""
741def unique(enumeration):
742 """Class decorator that ensures only unique members exist in an enumeration."""
743 duplicates = []
744 for name, member in enumeration.__members__.items():
745 if name != member.name:
746 duplicates.append((name, member.name))
747 if duplicates:
748 duplicate_names = ', '.join(
749 ["%s -> %s" % (alias, name) for (alias, name) in duplicates]
750 )
751 raise ValueError('duplicate names found in %r: %s' %
752 (enumeration, duplicate_names)
753 )
754 return enumeration