1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
|
# Copyright (C) 2004-2007 by the Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
# USA.
"""Enumeration meta class.
To define your own enumeration, do something like:
>>> class Colors(Enum):
... red = 1
... green = 2
... blue = 3
Enum subclasses cannot be instantiated, but you can convert them to integers
and from integers. Returned enumeration attributes are singletons and can be
compared by identity only.
"""
COMMASPACE = ', '
# Based on example by Jeremy Hylton
# Modified and extended by Barry Warsaw
class EnumMetaclass(type):
def __init__(cls, name, bases, dict):
# cls == the class being defined
# name == the name of the class
# bases == the class's bases
# dict == the class attributes
super(EnumMetaclass, cls).__init__(name, bases, dict)
# Store EnumValues here for easy access.
cls._enums = {}
# Figure out the set of enum values on the base classes, to ensure
# that we don't get any duplicate values (which would screw up
# conversion from integer).
for basecls in cls.__mro__:
if hasattr(basecls, '_enums'):
cls._enums.update(basecls._enums)
# For each class attribute, create an EnumValue and store that back on
# the class instead of the int. Skip Python reserved names. Also add
# a mapping from the integer to the instance so we can return the same
# object on conversion.
for attr in dict:
if not (attr.startswith('__') and attr.endswith('__')):
intval = dict[attr]
enumval = EnumValue(name, intval, attr)
if intval in cls._enums:
raise TypeError('Multiple enum values: %s' % enumval)
# Store as an attribute on the class, and save the attr name
setattr(cls, attr, enumval)
cls._enums[intval] = attr
def __getattr__(cls, name):
if name == '__members__':
return cls._enums.values()
raise AttributeError(name)
def __repr__(cls):
enums = ['%s: %d' % (cls._enums[k], k) for k in sorted(cls._enums)]
return '<%s {%s}>' % (cls.__name__, COMMASPACE.join(enums))
def __iter__(cls):
for i in sorted(cls._enums):
yield cls._enums[i]
def __getitem__(cls, i):
# i can be an integer or a string
attr = cls._enums.get(i)
if attr is None:
# It wasn't an integer -- try attribute name
try:
return getattr(cls, i)
except (AttributeError, TypeError):
raise ValueError(i)
return getattr(cls, attr)
# Support both MyEnum[i] and MyEnum(i)
__call__ = __getitem__
class EnumValue(object):
"""Class to represent an enumeration value.
EnumValue('Color', 'red', 12) prints as 'Color.red' and can be converted
to the integer 12.
"""
def __init__(self, classname, value, enumname):
self._classname = classname
self._value = value
self._enumname = enumname
def __repr__(self):
return 'EnumValue(%s, %s, %d)' % (
self._classname, self._enumname, self._value)
def __str__(self):
return self._enumname
def __int__(self):
return self._value
# Support only comparison by identity. Yes, really raise
# NotImplementedError instead of returning NotImplemented.
def __eq__(self, other):
raise NotImplementedError
__ne__ = __eq__
__lt__ = __eq__
__gt__ = __eq__
__le__ = __eq__
__ge__ = __eq__
class Enum:
__metaclass__ = EnumMetaclass
|