~jocave/checkbox/hybrid-amd-gpu-mods

« back to all changes in this revision

Viewing changes to plainbox/plainbox/vendor/enum.py

  • Committer: Tarmac
  • Author(s): Brendan Donegan
  • Date: 2013-06-03 11:12:58 UTC
  • mfrom: (2154.2.1 bug1185759)
  • Revision ID: tarmac-20130603111258-1b3m5ydvkf1accts
"[r=zkrynicki][bug=1185759][author=brendan-donegan] automatic merge by tarmac"

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# This file is part of Checkbox.
2
 
#
3
 
# Copyright 2015 Canonical Ltd.
4
 
# Written by:
5
 
#   Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
6
 
#
7
 
# Checkbox is free software: you can redistribute it and/or modify
8
 
# it under the terms of the GNU General Public License version 3,
9
 
# as published by the Free Software Foundation.
10
 
#
11
 
# Checkbox is distributed in the hope that it will be useful,
12
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 
# GNU General Public License for more details.
15
 
#
16
 
# You should have received a copy of the GNU General Public License
17
 
# along with Checkbox.  If not, see <http://www.gnu.org/licenses/>.
18
 
#
19
 
# The enum module was copied from Python trunk, originally written by Ethan
20
 
# Furman <ethan@stoneleaf.us>.
21
 
#
22
 
# The DynamicClassAttribute class was copied from Python trunk, originally
23
 
# written by Ethan Furman <ethan@stoneleaf.us>, as committed in git mirror
24
 
# commit c621f59146d0f3cd9bd78daa9dbf7784f5f7bcab "Close #19030: improvements
25
 
# to inspect and Enum."
26
 
#
27
 
# The back-ported MappingProxyType was written by
28
 
# Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
29
 
#
30
 
# PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
31
 
# --------------------------------------------
32
 
#
33
 
# 1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"),
34
 
#    and the Individual or Organization ("Licensee") accessing and otherwise
35
 
#    using this software ("Python") in source or binary form and its associated
36
 
#    documentation.
37
 
#
38
 
# 2. Subject to the terms and conditions of this License Agreement, PSF hereby
39
 
#    grants Licensee a nonexclusive, royalty-free, world-wide license to
40
 
#    reproduce, analyze, test, perform and/or display publicly, prepare
41
 
#    derivative works, distribute, and otherwise use Python alone or in any
42
 
#    derivative version, provided, however, that PSF's License Agreement and
43
 
#    PSF's notice of copyright, i.e., "Copyright (c) 2001, 2002, 2003, 2004,
44
 
#    2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Python Software
45
 
#    Foundation; All Rights Reserved" are retained in Python alone or in any
46
 
#    derivative version prepared by Licensee.
47
 
#
48
 
# 3. In the event Licensee prepares a derivative work that is based on or
49
 
#    incorporates Python or any part thereof, and wants to make the derivative
50
 
#    work available to others as provided herein, then Licensee hereby agrees
51
 
#    to include in any such work a brief summary of the changes made to Python.
52
 
#
53
 
# 4. PSF is making Python available to Licensee on an "AS IS" basis.  PSF MAKES
54
 
#    NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED.  BY WAY OF EXAMPLE,
55
 
#    BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION OR
56
 
#    WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT
57
 
#    THE USE OF PYTHON WILL NOT INFRINGE ANY THIRD PARTY RIGHTS.
58
 
#
59
 
# 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON FOR ANY
60
 
#    INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF
61
 
#    MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, OR ANY DERIVATIVE
62
 
#    THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
63
 
#
64
 
# 6. This License Agreement will automatically terminate upon a material breach
65
 
#    of its terms and conditions.
66
 
#
67
 
# 7. Nothing in this License Agreement shall be deemed to create any
68
 
#    relationship of agency, partnership, or joint venture between PSF and
69
 
#    Licensee.  This License Agreement does not grant permission to use PSF
70
 
#    trademarks or trade name in a trademark sense to endorse or promote
71
 
#    products or services of Licensee, or any third party.
72
 
#
73
 
# 8. By copying, installing or otherwise using Python, Licensee agrees to be
74
 
#    bound by the terms and conditions of this License Agreement.
75
 
 
76
 
import sys
77
 
from collections import OrderedDict
78
 
try:
79
 
    from types import MappingProxyType
80
 
except ImportError:
81
 
    # NOTE: this code is specific to Plainbox
82
 
    import ctypes
83
 
    PyDictProxy_New = ctypes.pythonapi.PyDictProxy_New
84
 
    PyDictProxy_New.argtypes = (ctypes.py_object,)
85
 
    PyDictProxy_New.restype = ctypes.py_object
86
 
 
87
 
    class MappingProxyType:
88
 
        def __new__(cls, dict):
89
 
            return PyDictProxy_New(dict)
90
 
try:
91
 
    from types import DynamicClassAttribute
92
 
except ImportError:
93
 
    # NOTE: this code is copied from Python trunk
94
 
    class DynamicClassAttribute:
95
 
        """Route attribute access on a class to __getattr__.
96
 
 
97
 
        This is a descriptor, used to define attributes that act differently when
98
 
        accessed through an instance and through a class.  Instance access remains
99
 
        normal, but access to an attribute through a class will be routed to the
100
 
        class's __getattr__ method; this is done by raising AttributeError.
101
 
 
102
 
        This allows one to have properties active on an instance, and have virtual
103
 
        attributes on the class with the same name (see Enum for an example).
104
 
 
105
 
        """
106
 
        def __init__(self, fget=None, fset=None, fdel=None, doc=None):
107
 
            self.fget = fget
108
 
            self.fset = fset
109
 
            self.fdel = fdel
110
 
            # next two lines make DynamicClassAttribute act the same as property
111
 
            self.__doc__ = doc or fget.__doc__
112
 
            self.overwrite_doc = doc is None
113
 
            # support for abstract methods
114
 
            self.__isabstractmethod__ = bool(getattr(fget, '__isabstractmethod__', False))
115
 
 
116
 
        def __get__(self, instance, ownerclass=None):
117
 
            if instance is None:
118
 
                if self.__isabstractmethod__:
119
 
                    return self
120
 
                raise AttributeError()
121
 
            elif self.fget is None:
122
 
                raise AttributeError("unreadable attribute")
123
 
            return self.fget(instance)
124
 
 
125
 
        def __set__(self, instance, value):
126
 
            if self.fset is None:
127
 
                raise AttributeError("can't set attribute")
128
 
            self.fset(instance, value)
129
 
 
130
 
        def __delete__(self, instance):
131
 
            if self.fdel is None:
132
 
                raise AttributeError("can't delete attribute")
133
 
            self.fdel(instance)
134
 
 
135
 
        def getter(self, fget):
136
 
            fdoc = fget.__doc__ if self.overwrite_doc else None
137
 
            result = type(self)(fget, self.fset, self.fdel, fdoc or self.__doc__)
138
 
            result.overwrite_doc = self.overwrite_doc
139
 
            return result
140
 
 
141
 
        def setter(self, fset):
142
 
            result = type(self)(self.fget, fset, self.fdel, self.__doc__)
143
 
            result.overwrite_doc = self.overwrite_doc
144
 
            return result
145
 
 
146
 
        def deleter(self, fdel):
147
 
            result = type(self)(self.fget, self.fset, fdel, self.__doc__)
148
 
            result.overwrite_doc = self.overwrite_doc
149
 
            return result
150
 
 
151
 
__all__ = ['Enum', 'IntEnum', 'unique']
152
 
 
153
 
 
154
 
def _is_descriptor(obj):
155
 
    """Returns True if obj is a descriptor, False otherwise."""
156
 
    return (
157
 
            hasattr(obj, '__get__') or
158
 
            hasattr(obj, '__set__') or
159
 
            hasattr(obj, '__delete__'))
160
 
 
161
 
 
162
 
def _is_dunder(name):
163
 
    """Returns True if a __dunder__ name, False otherwise."""
164
 
    return (name[:2] == name[-2:] == '__' and
165
 
            name[2:3] != '_' and
166
 
            name[-3:-2] != '_' and
167
 
            len(name) > 4)
168
 
 
169
 
 
170
 
def _is_sunder(name):
171
 
    """Returns True if a _sunder_ name, False otherwise."""
172
 
    return (name[0] == name[-1] == '_' and
173
 
            name[1:2] != '_' and
174
 
            name[-2:-1] != '_' and
175
 
            len(name) > 2)
176
 
 
177
 
 
178
 
def _make_class_unpicklable(cls):
179
 
    """Make the given class un-picklable."""
180
 
    def _break_on_call_reduce(self, proto):
181
 
        raise TypeError('%r cannot be pickled' % self)
182
 
    cls.__reduce_ex__ = _break_on_call_reduce
183
 
    cls.__module__ = '<unknown>'
184
 
 
185
 
 
186
 
class _EnumDict(dict):
187
 
    """Track enum member order and ensure member names are not reused.
188
 
 
189
 
    EnumMeta will use the names found in self._member_names as the
190
 
    enumeration member names.
191
 
 
192
 
    """
193
 
    def __init__(self):
194
 
        super().__init__()
195
 
        self._member_names = []
196
 
 
197
 
    def __setitem__(self, key, value):
198
 
        """Changes anything not dundered or not a descriptor.
199
 
 
200
 
        If an enum member name is used twice, an error is raised; duplicate
201
 
        values are not checked for.
202
 
 
203
 
        Single underscore (sunder) names are reserved.
204
 
 
205
 
        """
206
 
        if _is_sunder(key):
207
 
            raise ValueError('_names_ are reserved for future Enum use')
208
 
        elif _is_dunder(key):
209
 
            pass
210
 
        elif key in self._member_names:
211
 
            # descriptor overwriting an enum?
212
 
            raise TypeError('Attempted to reuse key: %r' % key)
213
 
        elif not _is_descriptor(value):
214
 
            if key in self:
215
 
                # enum overwriting a descriptor?
216
 
                raise TypeError('Key already defined as: %r' % self[key])
217
 
            self._member_names.append(key)
218
 
        super().__setitem__(key, value)
219
 
 
220
 
 
221
 
 
222
 
# Dummy value for Enum as EnumMeta explicitly checks for it, but of course
223
 
# until EnumMeta finishes running the first time the Enum class doesn't exist.
224
 
# This is also why there are checks in EnumMeta like `if Enum is not None`
225
 
Enum = None
226
 
 
227
 
 
228
 
class EnumMeta(type):
229
 
    """Metaclass for Enum"""
230
 
    @classmethod
231
 
    def __prepare__(metacls, cls, bases):
232
 
        return _EnumDict()
233
 
 
234
 
    def __new__(metacls, cls, bases, classdict):
235
 
        # an Enum class is final once enumeration items have been defined; it
236
 
        # cannot be mixed with other types (int, float, etc.) if it has an
237
 
        # inherited __new__ unless a new __new__ is defined (or the resulting
238
 
        # class will fail).
239
 
        member_type, first_enum = metacls._get_mixins_(bases)
240
 
        __new__, save_new, use_args = metacls._find_new_(classdict, member_type,
241
 
                                                        first_enum)
242
 
 
243
 
        # save enum items into separate mapping so they don't get baked into
244
 
        # the new class
245
 
        members = {k: classdict[k] for k in classdict._member_names}
246
 
        for name in classdict._member_names:
247
 
            del classdict[name]
248
 
 
249
 
        # check for illegal enum names (any others?)
250
 
        invalid_names = set(members) & {'mro', }
251
 
        if invalid_names:
252
 
            raise ValueError('Invalid enum member name: {0}'.format(
253
 
                ','.join(invalid_names)))
254
 
 
255
 
        # create our new Enum type
256
 
        enum_class = super().__new__(metacls, cls, bases, classdict)
257
 
        enum_class._member_names_ = []               # names in definition order
258
 
        enum_class._member_map_ = OrderedDict()      # name->value map
259
 
        enum_class._member_type_ = member_type
260
 
 
261
 
        # Reverse value->name map for hashable values.
262
 
        enum_class._value2member_map_ = {}
263
 
 
264
 
        # If a custom type is mixed into the Enum, and it does not know how
265
 
        # to pickle itself, pickle.dumps will succeed but pickle.loads will
266
 
        # fail.  Rather than have the error show up later and possibly far
267
 
        # from the source, sabotage the pickle protocol for this class so
268
 
        # that pickle.dumps also fails.
269
 
        #
270
 
        # However, if the new class implements its own __reduce_ex__, do not
271
 
        # sabotage -- it's on them to make sure it works correctly.  We use
272
 
        # __reduce_ex__ instead of any of the others as it is preferred by
273
 
        # pickle over __reduce__, and it handles all pickle protocols.
274
 
        if '__reduce_ex__' not in classdict:
275
 
            if member_type is not object:
276
 
                methods = ('__getnewargs_ex__', '__getnewargs__',
277
 
                        '__reduce_ex__', '__reduce__')
278
 
                if not any(m in member_type.__dict__ for m in methods):
279
 
                    _make_class_unpicklable(enum_class)
280
 
 
281
 
        # instantiate them, checking for duplicates as we go
282
 
        # we instantiate first instead of checking for duplicates first in case
283
 
        # a custom __new__ is doing something funky with the values -- such as
284
 
        # auto-numbering ;)
285
 
        for member_name in classdict._member_names:
286
 
            value = members[member_name]
287
 
            if not isinstance(value, tuple):
288
 
                args = (value, )
289
 
            else:
290
 
                args = value
291
 
            if member_type is tuple:   # special case for tuple enums
292
 
                args = (args, )     # wrap it one more time
293
 
            if not use_args:
294
 
                enum_member = __new__(enum_class)
295
 
                if not hasattr(enum_member, '_value_'):
296
 
                    enum_member._value_ = value
297
 
            else:
298
 
                enum_member = __new__(enum_class, *args)
299
 
                if not hasattr(enum_member, '_value_'):
300
 
                    enum_member._value_ = member_type(*args)
301
 
            value = enum_member._value_
302
 
            enum_member._name_ = member_name
303
 
            enum_member.__objclass__ = enum_class
304
 
            enum_member.__init__(*args)
305
 
            # If another member with the same value was already defined, the
306
 
            # new member becomes an alias to the existing one.
307
 
            for name, canonical_member in enum_class._member_map_.items():
308
 
                if canonical_member._value_ == enum_member._value_:
309
 
                    enum_member = canonical_member
310
 
                    break
311
 
            else:
312
 
                # Aliases don't appear in member names (only in __members__).
313
 
                enum_class._member_names_.append(member_name)
314
 
            enum_class._member_map_[member_name] = enum_member
315
 
            try:
316
 
                # This may fail if value is not hashable. We can't add the value
317
 
                # to the map, and by-value lookups for this value will be
318
 
                # linear.
319
 
                enum_class._value2member_map_[value] = enum_member
320
 
            except TypeError:
321
 
                pass
322
 
 
323
 
        # double check that repr and friends are not the mixin's or various
324
 
        # things break (such as pickle)
325
 
        for name in ('__repr__', '__str__', '__format__', '__reduce_ex__'):
326
 
            class_method = getattr(enum_class, name)
327
 
            obj_method = getattr(member_type, name, None)
328
 
            enum_method = getattr(first_enum, name, None)
329
 
            if obj_method is not None and obj_method is class_method:
330
 
                setattr(enum_class, name, enum_method)
331
 
 
332
 
        # replace any other __new__ with our own (as long as Enum is not None,
333
 
        # anyway) -- again, this is to support pickle
334
 
        if Enum is not None:
335
 
            # if the user defined their own __new__, save it before it gets
336
 
            # clobbered in case they subclass later
337
 
            if save_new:
338
 
                enum_class.__new_member__ = __new__
339
 
            enum_class.__new__ = Enum.__new__
340
 
        return enum_class
341
 
 
342
 
    def __call__(cls, value, names=None, *, module=None, qualname=None, type=None, start=1):
343
 
        """Either returns an existing member, or creates a new enum class.
344
 
 
345
 
        This method is used both when an enum class is given a value to match
346
 
        to an enumeration member (i.e. Color(3)) and for the functional API
347
 
        (i.e. Color = Enum('Color', names='red green blue')).
348
 
 
349
 
        When used for the functional API:
350
 
 
351
 
        `value` will be the name of the new class.
352
 
 
353
 
        `names` should be either a string of white-space/comma delimited names
354
 
        (values will start at `start`), or an iterator/mapping of name, value pairs.
355
 
 
356
 
        `module` should be set to the module this class is being created in;
357
 
        if it is not set, an attempt to find that module will be made, but if
358
 
        it fails the class will not be picklable.
359
 
 
360
 
        `qualname` should be set to the actual location this class can be found
361
 
        at in its module; by default it is set to the global scope.  If this is
362
 
        not correct, unpickling will fail in some circumstances.
363
 
 
364
 
        `type`, if set, will be mixed in as the first base class.
365
 
 
366
 
        """
367
 
        if names is None:  # simple value lookup
368
 
            return cls.__new__(cls, value)
369
 
        # otherwise, functional API: we're creating a new Enum type
370
 
        return cls._create_(value, names, module=module, qualname=qualname, type=type, start=start)
371
 
 
372
 
    def __contains__(cls, member):
373
 
        return isinstance(member, cls) and member._name_ in cls._member_map_
374
 
 
375
 
    def __delattr__(cls, attr):
376
 
        # nicer error message when someone tries to delete an attribute
377
 
        # (see issue19025).
378
 
        if attr in cls._member_map_:
379
 
            raise AttributeError(
380
 
                    "%s: cannot delete Enum member." % cls.__name__)
381
 
        super().__delattr__(attr)
382
 
 
383
 
    def __dir__(self):
384
 
        return (['__class__', '__doc__', '__members__', '__module__'] +
385
 
                self._member_names_)
386
 
 
387
 
    def __getattr__(cls, name):
388
 
        """Return the enum member matching `name`
389
 
 
390
 
        We use __getattr__ instead of descriptors or inserting into the enum
391
 
        class' __dict__ in order to support `name` and `value` being both
392
 
        properties for enum members (which live in the class' __dict__) and
393
 
        enum members themselves.
394
 
 
395
 
        """
396
 
        if _is_dunder(name):
397
 
            raise AttributeError(name)
398
 
        try:
399
 
            return cls._member_map_[name]
400
 
        except KeyError:
401
 
            # NOTE: patched on python 3.2
402
 
            raise AttributeError(name)
403
 
 
404
 
    def __getitem__(cls, name):
405
 
        return cls._member_map_[name]
406
 
 
407
 
    def __iter__(cls):
408
 
        return (cls._member_map_[name] for name in cls._member_names_)
409
 
 
410
 
    def __len__(cls):
411
 
        return len(cls._member_names_)
412
 
 
413
 
    @property
414
 
    def __members__(cls):
415
 
        """Returns a mapping of member name->value.
416
 
 
417
 
        This mapping lists all enum members, including aliases. Note that this
418
 
        is a read-only view of the internal mapping.
419
 
 
420
 
        """
421
 
        return MappingProxyType(cls._member_map_)
422
 
 
423
 
    def __repr__(cls):
424
 
        return "<enum %r>" % cls.__name__
425
 
 
426
 
    def __reversed__(cls):
427
 
        return (cls._member_map_[name] for name in reversed(cls._member_names_))
428
 
 
429
 
    def __setattr__(cls, name, value):
430
 
        """Block attempts to reassign Enum members.
431
 
 
432
 
        A simple assignment to the class namespace only changes one of the
433
 
        several possible ways to get an Enum member from the Enum class,
434
 
        resulting in an inconsistent Enumeration.
435
 
 
436
 
        """
437
 
        member_map = cls.__dict__.get('_member_map_', {})
438
 
        if name in member_map:
439
 
            raise AttributeError('Cannot reassign members.')
440
 
        super().__setattr__(name, value)
441
 
 
442
 
    def _create_(cls, class_name, names=None, *, module=None, qualname=None, type=None, start=1):
443
 
        """Convenience method to create a new Enum class.
444
 
 
445
 
        `names` can be:
446
 
 
447
 
        * A string containing member names, separated either with spaces or
448
 
          commas.  Values are incremented by 1 from `start`.
449
 
        * An iterable of member names.  Values are incremented by 1 from `start`.
450
 
        * An iterable of (member name, value) pairs.
451
 
        * A mapping of member name -> value pairs.
452
 
 
453
 
        """
454
 
        metacls = cls.__class__
455
 
        bases = (cls, ) if type is None else (type, cls)
456
 
        classdict = metacls.__prepare__(class_name, bases)
457
 
 
458
 
        # special processing needed for names?
459
 
        if isinstance(names, str):
460
 
            names = names.replace(',', ' ').split()
461
 
        if isinstance(names, (tuple, list)) and isinstance(names[0], str):
462
 
            names = [(e, i) for (i, e) in enumerate(names, start)]
463
 
 
464
 
        # Here, names is either an iterable of (name, value) or a mapping.
465
 
        for item in names:
466
 
            if isinstance(item, str):
467
 
                member_name, member_value = item, names[item]
468
 
            else:
469
 
                member_name, member_value = item
470
 
            classdict[member_name] = member_value
471
 
        enum_class = metacls.__new__(metacls, class_name, bases, classdict)
472
 
 
473
 
        # TODO: replace the frame hack if a blessed way to know the calling
474
 
        # module is ever developed
475
 
        if module is None:
476
 
            try:
477
 
                module = sys._getframe(2).f_globals['__name__']
478
 
            except (AttributeError, ValueError) as exc:
479
 
                pass
480
 
        if module is None:
481
 
            _make_class_unpicklable(enum_class)
482
 
        else:
483
 
            enum_class.__module__ = module
484
 
        if qualname is not None:
485
 
            enum_class.__qualname__ = qualname
486
 
 
487
 
        return enum_class
488
 
 
489
 
    @staticmethod
490
 
    def _get_mixins_(bases):
491
 
        """Returns the type for creating enum members, and the first inherited
492
 
        enum class.
493
 
 
494
 
        bases: the tuple of bases that was given to __new__
495
 
 
496
 
        """
497
 
        if not bases:
498
 
            return object, Enum
499
 
 
500
 
        # double check that we are not subclassing a class with existing
501
 
        # enumeration members; while we're at it, see if any other data
502
 
        # type has been mixed in so we can use the correct __new__
503
 
        member_type = first_enum = None
504
 
        for base in bases:
505
 
            if  (base is not Enum and
506
 
                    issubclass(base, Enum) and
507
 
                    base._member_names_):
508
 
                raise TypeError("Cannot extend enumerations")
509
 
        # base is now the last base in bases
510
 
        if not issubclass(base, Enum):
511
 
            raise TypeError("new enumerations must be created as "
512
 
                    "`ClassName([mixin_type,] enum_type)`")
513
 
 
514
 
        # get correct mix-in type (either mix-in type of Enum subclass, or
515
 
        # first base if last base is Enum)
516
 
        if not issubclass(bases[0], Enum):
517
 
            member_type = bases[0]     # first data type
518
 
            first_enum = bases[-1]  # enum type
519
 
        else:
520
 
            for base in bases[0].__mro__:
521
 
                # most common: (IntEnum, int, Enum, object)
522
 
                # possible:    (<Enum 'AutoIntEnum'>, <Enum 'IntEnum'>,
523
 
                #               <class 'int'>, <Enum 'Enum'>,
524
 
                #               <class 'object'>)
525
 
                if issubclass(base, Enum):
526
 
                    if first_enum is None:
527
 
                        first_enum = base
528
 
                else:
529
 
                    if member_type is None:
530
 
                        member_type = base
531
 
 
532
 
        return member_type, first_enum
533
 
 
534
 
    @staticmethod
535
 
    def _find_new_(classdict, member_type, first_enum):
536
 
        """Returns the __new__ to be used for creating the enum members.
537
 
 
538
 
        classdict: the class dictionary given to __new__
539
 
        member_type: the data type whose __new__ will be used by default
540
 
        first_enum: enumeration to check for an overriding __new__
541
 
 
542
 
        """
543
 
        # now find the correct __new__, checking to see of one was defined
544
 
        # by the user; also check earlier enum classes in case a __new__ was
545
 
        # saved as __new_member__
546
 
        __new__ = classdict.get('__new__', None)
547
 
 
548
 
        # should __new__ be saved as __new_member__ later?
549
 
        save_new = __new__ is not None
550
 
 
551
 
        if __new__ is None:
552
 
            # check all possibles for __new_member__ before falling back to
553
 
            # __new__
554
 
            for method in ('__new_member__', '__new__'):
555
 
                for possible in (member_type, first_enum):
556
 
                    target = getattr(possible, method, None)
557
 
                    if target not in {
558
 
                            None,
559
 
                            None.__new__,
560
 
                            object.__new__,
561
 
                            Enum.__new__,
562
 
                            }:
563
 
                        __new__ = target
564
 
                        break
565
 
                if __new__ is not None:
566
 
                    break
567
 
            else:
568
 
                __new__ = object.__new__
569
 
 
570
 
        # if a non-object.__new__ is used then whatever value/tuple was
571
 
        # assigned to the enum member name will be passed to __new__ and to the
572
 
        # new enum member's __init__
573
 
        if __new__ is object.__new__:
574
 
            use_args = False
575
 
        else:
576
 
            use_args = True
577
 
 
578
 
        return __new__, save_new, use_args
579
 
 
580
 
 
581
 
class Enum(metaclass=EnumMeta):
582
 
    """Generic enumeration.
583
 
 
584
 
    Derive from this class to define new enumerations.
585
 
 
586
 
    """
587
 
    def __new__(cls, value):
588
 
        # all enum instances are actually created during class construction
589
 
        # without calling this method; this method is called by the metaclass'
590
 
        # __call__ (i.e. Color(3) ), and by pickle
591
 
        if type(value) is cls:
592
 
            # For lookups like Color(Color.red)
593
 
            return value
594
 
        # by-value search for a matching enum member
595
 
        # see if it's in the reverse mapping (for hashable values)
596
 
        try:
597
 
            if value in cls._value2member_map_:
598
 
                return cls._value2member_map_[value]
599
 
        except TypeError:
600
 
            # not there, now do long search -- O(n) behavior
601
 
            for member in cls._member_map_.values():
602
 
                if member._value_ == value:
603
 
                    return member
604
 
        raise ValueError("%r is not a valid %s" % (value, cls.__name__))
605
 
 
606
 
    def __repr__(self):
607
 
        return "<%s.%s: %r>" % (
608
 
                self.__class__.__name__, self._name_, self._value_)
609
 
 
610
 
    def __str__(self):
611
 
        return "%s.%s" % (self.__class__.__name__, self._name_)
612
 
 
613
 
    def __dir__(self):
614
 
        added_behavior = [
615
 
                m
616
 
                for cls in self.__class__.mro()
617
 
                for m in cls.__dict__
618
 
                if m[0] != '_'
619
 
                ]
620
 
        return (['__class__', '__doc__', '__module__'] + added_behavior)
621
 
 
622
 
    def __format__(self, format_spec):
623
 
        # mixed-in Enums should use the mixed-in type's __format__, otherwise
624
 
        # we can get strange results with the Enum name showing up instead of
625
 
        # the value
626
 
 
627
 
        # pure Enum branch
628
 
        if self._member_type_ is object:
629
 
            cls = str
630
 
            val = str(self)
631
 
        # mix-in branch
632
 
        else:
633
 
            cls = self._member_type_
634
 
            val = self._value_
635
 
        return cls.__format__(val, format_spec)
636
 
 
637
 
    def __hash__(self):
638
 
        return hash(self._name_)
639
 
 
640
 
    def __reduce_ex__(self, proto):
641
 
        return self.__class__, (self._value_, )
642
 
 
643
 
    # DynamicClassAttribute is used to provide access to the `name` and
644
 
    # `value` properties of enum members while keeping some measure of
645
 
    # protection from modification, while still allowing for an enumeration
646
 
    # to have members named `name` and `value`.  This works because enumeration
647
 
    # members are not set directly on the enum class -- __getattr__ is
648
 
    # used to look them up.
649
 
 
650
 
    @DynamicClassAttribute
651
 
    def name(self):
652
 
        """The name of the Enum member."""
653
 
        return self._name_
654
 
 
655
 
    @DynamicClassAttribute
656
 
    def value(self):
657
 
        """The value of the Enum member."""
658
 
        return self._value_
659
 
 
660
 
 
661
 
class IntEnum(int, Enum):
662
 
    """Enum where members are also (and must be) ints"""
663
 
 
664
 
 
665
 
def unique(enumeration):
666
 
    """Class decorator for enumerations ensuring unique member values."""
667
 
    duplicates = []
668
 
    for name, member in enumeration.__members__.items():
669
 
        if name != member.name:
670
 
            duplicates.append((name, member.name))
671
 
    if duplicates:
672
 
        alias_details = ', '.join(
673
 
                ["%s -> %s" % (alias, name) for (alias, name) in duplicates])
674
 
        raise ValueError('duplicate values found in %r: %s' %
675
 
                (enumeration, alias_details))
676
 
    return enumeration