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