1 | n/a | import sys |
---|
2 | n/a | from types import MappingProxyType, DynamicClassAttribute |
---|
3 | n/a | from functools import reduce |
---|
4 | n/a | from operator import or_ as _or_ |
---|
5 | n/a | |
---|
6 | n/a | # try _collections first to reduce startup cost |
---|
7 | n/a | try: |
---|
8 | n/a | from _collections import OrderedDict |
---|
9 | n/a | except ImportError: |
---|
10 | n/a | from collections import OrderedDict |
---|
11 | n/a | |
---|
12 | n/a | |
---|
13 | n/a | __all__ = [ |
---|
14 | n/a | 'EnumMeta', |
---|
15 | n/a | 'Enum', 'IntEnum', 'Flag', 'IntFlag', |
---|
16 | n/a | 'auto', 'unique', |
---|
17 | n/a | ] |
---|
18 | n/a | |
---|
19 | n/a | |
---|
20 | n/a | def _is_descriptor(obj): |
---|
21 | n/a | """Returns True if obj is a descriptor, False otherwise.""" |
---|
22 | n/a | return ( |
---|
23 | n/a | hasattr(obj, '__get__') or |
---|
24 | n/a | hasattr(obj, '__set__') or |
---|
25 | n/a | hasattr(obj, '__delete__')) |
---|
26 | n/a | |
---|
27 | n/a | |
---|
28 | n/a | def _is_dunder(name): |
---|
29 | n/a | """Returns True if a __dunder__ name, False otherwise.""" |
---|
30 | n/a | return (name[:2] == name[-2:] == '__' and |
---|
31 | n/a | name[2:3] != '_' and |
---|
32 | n/a | name[-3:-2] != '_' and |
---|
33 | n/a | len(name) > 4) |
---|
34 | n/a | |
---|
35 | n/a | |
---|
36 | n/a | def _is_sunder(name): |
---|
37 | n/a | """Returns True if a _sunder_ name, False otherwise.""" |
---|
38 | n/a | return (name[0] == name[-1] == '_' and |
---|
39 | n/a | name[1:2] != '_' and |
---|
40 | n/a | name[-2:-1] != '_' and |
---|
41 | n/a | len(name) > 2) |
---|
42 | n/a | |
---|
43 | n/a | def _make_class_unpicklable(cls): |
---|
44 | n/a | """Make the given class un-picklable.""" |
---|
45 | n/a | def _break_on_call_reduce(self, proto): |
---|
46 | n/a | raise TypeError('%r cannot be pickled' % self) |
---|
47 | n/a | cls.__reduce_ex__ = _break_on_call_reduce |
---|
48 | n/a | cls.__module__ = '<unknown>' |
---|
49 | n/a | |
---|
50 | n/a | _auto_null = object() |
---|
51 | n/a | class auto: |
---|
52 | n/a | """ |
---|
53 | n/a | Instances are replaced with an appropriate value in Enum class suites. |
---|
54 | n/a | """ |
---|
55 | n/a | value = _auto_null |
---|
56 | n/a | |
---|
57 | n/a | |
---|
58 | n/a | class _EnumDict(dict): |
---|
59 | n/a | """Track enum member order and ensure member names are not reused. |
---|
60 | n/a | |
---|
61 | n/a | EnumMeta will use the names found in self._member_names as the |
---|
62 | n/a | enumeration member names. |
---|
63 | n/a | |
---|
64 | n/a | """ |
---|
65 | n/a | def __init__(self): |
---|
66 | n/a | super().__init__() |
---|
67 | n/a | self._member_names = [] |
---|
68 | n/a | self._last_values = [] |
---|
69 | n/a | |
---|
70 | n/a | def __setitem__(self, key, value): |
---|
71 | n/a | """Changes anything not dundered or not a descriptor. |
---|
72 | n/a | |
---|
73 | n/a | If an enum member name is used twice, an error is raised; duplicate |
---|
74 | n/a | values are not checked for. |
---|
75 | n/a | |
---|
76 | n/a | Single underscore (sunder) names are reserved. |
---|
77 | n/a | |
---|
78 | n/a | """ |
---|
79 | n/a | if _is_sunder(key): |
---|
80 | n/a | if key not in ( |
---|
81 | n/a | '_order_', '_create_pseudo_member_', |
---|
82 | n/a | '_generate_next_value_', '_missing_', |
---|
83 | n/a | ): |
---|
84 | n/a | raise ValueError('_names_ are reserved for future Enum use') |
---|
85 | n/a | if key == '_generate_next_value_': |
---|
86 | n/a | setattr(self, '_generate_next_value', value) |
---|
87 | n/a | elif _is_dunder(key): |
---|
88 | n/a | if key == '__order__': |
---|
89 | n/a | key = '_order_' |
---|
90 | n/a | elif key in self._member_names: |
---|
91 | n/a | # descriptor overwriting an enum? |
---|
92 | n/a | raise TypeError('Attempted to reuse key: %r' % key) |
---|
93 | n/a | elif not _is_descriptor(value): |
---|
94 | n/a | if key in self: |
---|
95 | n/a | # enum overwriting a descriptor? |
---|
96 | n/a | raise TypeError('%r already defined as: %r' % (key, self[key])) |
---|
97 | n/a | if isinstance(value, auto): |
---|
98 | n/a | if value.value == _auto_null: |
---|
99 | n/a | value.value = self._generate_next_value(key, 1, len(self._member_names), self._last_values[:]) |
---|
100 | n/a | value = value.value |
---|
101 | n/a | self._member_names.append(key) |
---|
102 | n/a | self._last_values.append(value) |
---|
103 | n/a | super().__setitem__(key, value) |
---|
104 | n/a | |
---|
105 | n/a | |
---|
106 | n/a | # Dummy value for Enum as EnumMeta explicitly checks for it, but of course |
---|
107 | n/a | # until EnumMeta finishes running the first time the Enum class doesn't exist. |
---|
108 | n/a | # This is also why there are checks in EnumMeta like `if Enum is not None` |
---|
109 | n/a | Enum = None |
---|
110 | n/a | |
---|
111 | n/a | |
---|
112 | n/a | class EnumMeta(type): |
---|
113 | n/a | """Metaclass for Enum""" |
---|
114 | n/a | @classmethod |
---|
115 | n/a | def __prepare__(metacls, cls, bases): |
---|
116 | n/a | # create the namespace dict |
---|
117 | n/a | enum_dict = _EnumDict() |
---|
118 | n/a | # inherit previous flags and _generate_next_value_ function |
---|
119 | n/a | member_type, first_enum = metacls._get_mixins_(bases) |
---|
120 | n/a | if first_enum is not None: |
---|
121 | n/a | enum_dict['_generate_next_value_'] = getattr(first_enum, '_generate_next_value_', None) |
---|
122 | n/a | return enum_dict |
---|
123 | n/a | |
---|
124 | n/a | def __new__(metacls, cls, bases, classdict): |
---|
125 | n/a | # an Enum class is final once enumeration items have been defined; it |
---|
126 | n/a | # cannot be mixed with other types (int, float, etc.) if it has an |
---|
127 | n/a | # inherited __new__ unless a new __new__ is defined (or the resulting |
---|
128 | n/a | # class will fail). |
---|
129 | n/a | member_type, first_enum = metacls._get_mixins_(bases) |
---|
130 | n/a | __new__, save_new, use_args = metacls._find_new_(classdict, member_type, |
---|
131 | n/a | first_enum) |
---|
132 | n/a | |
---|
133 | n/a | # save enum items into separate mapping so they don't get baked into |
---|
134 | n/a | # the new class |
---|
135 | n/a | enum_members = {k: classdict[k] for k in classdict._member_names} |
---|
136 | n/a | for name in classdict._member_names: |
---|
137 | n/a | del classdict[name] |
---|
138 | n/a | |
---|
139 | n/a | # adjust the sunders |
---|
140 | n/a | _order_ = classdict.pop('_order_', None) |
---|
141 | n/a | |
---|
142 | n/a | # check for illegal enum names (any others?) |
---|
143 | n/a | invalid_names = set(enum_members) & {'mro', } |
---|
144 | n/a | if invalid_names: |
---|
145 | n/a | raise ValueError('Invalid enum member name: {0}'.format( |
---|
146 | n/a | ','.join(invalid_names))) |
---|
147 | n/a | |
---|
148 | n/a | # create a default docstring if one has not been provided |
---|
149 | n/a | if '__doc__' not in classdict: |
---|
150 | n/a | classdict['__doc__'] = 'An enumeration.' |
---|
151 | n/a | |
---|
152 | n/a | # create our new Enum type |
---|
153 | n/a | enum_class = super().__new__(metacls, cls, bases, classdict) |
---|
154 | n/a | enum_class._member_names_ = [] # names in definition order |
---|
155 | n/a | enum_class._member_map_ = OrderedDict() # name->value map |
---|
156 | n/a | enum_class._member_type_ = member_type |
---|
157 | n/a | |
---|
158 | n/a | # save attributes from super classes so we know if we can take |
---|
159 | n/a | # the shortcut of storing members in the class dict |
---|
160 | n/a | base_attributes = {a for b in enum_class.mro() for a in b.__dict__} |
---|
161 | n/a | |
---|
162 | n/a | # Reverse value->name map for hashable values. |
---|
163 | n/a | enum_class._value2member_map_ = {} |
---|
164 | n/a | |
---|
165 | n/a | # If a custom type is mixed into the Enum, and it does not know how |
---|
166 | n/a | # to pickle itself, pickle.dumps will succeed but pickle.loads will |
---|
167 | n/a | # fail. Rather than have the error show up later and possibly far |
---|
168 | n/a | # from the source, sabotage the pickle protocol for this class so |
---|
169 | n/a | # that pickle.dumps also fails. |
---|
170 | n/a | # |
---|
171 | n/a | # However, if the new class implements its own __reduce_ex__, do not |
---|
172 | n/a | # sabotage -- it's on them to make sure it works correctly. We use |
---|
173 | n/a | # __reduce_ex__ instead of any of the others as it is preferred by |
---|
174 | n/a | # pickle over __reduce__, and it handles all pickle protocols. |
---|
175 | n/a | if '__reduce_ex__' not in classdict: |
---|
176 | n/a | if member_type is not object: |
---|
177 | n/a | methods = ('__getnewargs_ex__', '__getnewargs__', |
---|
178 | n/a | '__reduce_ex__', '__reduce__') |
---|
179 | n/a | if not any(m in member_type.__dict__ for m in methods): |
---|
180 | n/a | _make_class_unpicklable(enum_class) |
---|
181 | n/a | |
---|
182 | n/a | # instantiate them, checking for duplicates as we go |
---|
183 | n/a | # we instantiate first instead of checking for duplicates first in case |
---|
184 | n/a | # a custom __new__ is doing something funky with the values -- such as |
---|
185 | n/a | # auto-numbering ;) |
---|
186 | n/a | for member_name in classdict._member_names: |
---|
187 | n/a | value = enum_members[member_name] |
---|
188 | n/a | if not isinstance(value, tuple): |
---|
189 | n/a | args = (value, ) |
---|
190 | n/a | else: |
---|
191 | n/a | args = value |
---|
192 | n/a | if member_type is tuple: # special case for tuple enums |
---|
193 | n/a | args = (args, ) # wrap it one more time |
---|
194 | n/a | if not use_args: |
---|
195 | n/a | enum_member = __new__(enum_class) |
---|
196 | n/a | if not hasattr(enum_member, '_value_'): |
---|
197 | n/a | enum_member._value_ = value |
---|
198 | n/a | else: |
---|
199 | n/a | enum_member = __new__(enum_class, *args) |
---|
200 | n/a | if not hasattr(enum_member, '_value_'): |
---|
201 | n/a | if member_type is object: |
---|
202 | n/a | enum_member._value_ = value |
---|
203 | n/a | else: |
---|
204 | n/a | enum_member._value_ = member_type(*args) |
---|
205 | n/a | value = enum_member._value_ |
---|
206 | n/a | enum_member._name_ = member_name |
---|
207 | n/a | enum_member.__objclass__ = enum_class |
---|
208 | n/a | enum_member.__init__(*args) |
---|
209 | n/a | # If another member with the same value was already defined, the |
---|
210 | n/a | # new member becomes an alias to the existing one. |
---|
211 | n/a | for name, canonical_member in enum_class._member_map_.items(): |
---|
212 | n/a | if canonical_member._value_ == enum_member._value_: |
---|
213 | n/a | enum_member = canonical_member |
---|
214 | n/a | break |
---|
215 | n/a | else: |
---|
216 | n/a | # Aliases don't appear in member names (only in __members__). |
---|
217 | n/a | enum_class._member_names_.append(member_name) |
---|
218 | n/a | # performance boost for any member that would not shadow |
---|
219 | n/a | # a DynamicClassAttribute |
---|
220 | n/a | if member_name not in base_attributes: |
---|
221 | n/a | setattr(enum_class, member_name, enum_member) |
---|
222 | n/a | # now add to _member_map_ |
---|
223 | n/a | enum_class._member_map_[member_name] = enum_member |
---|
224 | n/a | try: |
---|
225 | n/a | # This may fail if value is not hashable. We can't add the value |
---|
226 | n/a | # to the map, and by-value lookups for this value will be |
---|
227 | n/a | # linear. |
---|
228 | n/a | enum_class._value2member_map_[value] = enum_member |
---|
229 | n/a | except TypeError: |
---|
230 | n/a | pass |
---|
231 | n/a | |
---|
232 | n/a | # double check that repr and friends are not the mixin's or various |
---|
233 | n/a | # things break (such as pickle) |
---|
234 | n/a | for name in ('__repr__', '__str__', '__format__', '__reduce_ex__'): |
---|
235 | n/a | class_method = getattr(enum_class, name) |
---|
236 | n/a | obj_method = getattr(member_type, name, None) |
---|
237 | n/a | enum_method = getattr(first_enum, name, None) |
---|
238 | n/a | if obj_method is not None and obj_method is class_method: |
---|
239 | n/a | setattr(enum_class, name, enum_method) |
---|
240 | n/a | |
---|
241 | n/a | # replace any other __new__ with our own (as long as Enum is not None, |
---|
242 | n/a | # anyway) -- again, this is to support pickle |
---|
243 | n/a | if Enum is not None: |
---|
244 | n/a | # if the user defined their own __new__, save it before it gets |
---|
245 | n/a | # clobbered in case they subclass later |
---|
246 | n/a | if save_new: |
---|
247 | n/a | enum_class.__new_member__ = __new__ |
---|
248 | n/a | enum_class.__new__ = Enum.__new__ |
---|
249 | n/a | |
---|
250 | n/a | # py3 support for definition order (helps keep py2/py3 code in sync) |
---|
251 | n/a | if _order_ is not None: |
---|
252 | n/a | if isinstance(_order_, str): |
---|
253 | n/a | _order_ = _order_.replace(',', ' ').split() |
---|
254 | n/a | if _order_ != enum_class._member_names_: |
---|
255 | n/a | raise TypeError('member order does not match _order_') |
---|
256 | n/a | |
---|
257 | n/a | return enum_class |
---|
258 | n/a | |
---|
259 | n/a | def __bool__(self): |
---|
260 | n/a | """ |
---|
261 | n/a | classes/types should always be True. |
---|
262 | n/a | """ |
---|
263 | n/a | return True |
---|
264 | n/a | |
---|
265 | n/a | def __call__(cls, value, names=None, *, module=None, qualname=None, type=None, start=1): |
---|
266 | n/a | """Either returns an existing member, or creates a new enum class. |
---|
267 | n/a | |
---|
268 | n/a | This method is used both when an enum class is given a value to match |
---|
269 | n/a | to an enumeration member (i.e. Color(3)) and for the functional API |
---|
270 | n/a | (i.e. Color = Enum('Color', names='RED GREEN BLUE')). |
---|
271 | n/a | |
---|
272 | n/a | When used for the functional API: |
---|
273 | n/a | |
---|
274 | n/a | `value` will be the name of the new class. |
---|
275 | n/a | |
---|
276 | n/a | `names` should be either a string of white-space/comma delimited names |
---|
277 | n/a | (values will start at `start`), or an iterator/mapping of name, value pairs. |
---|
278 | n/a | |
---|
279 | n/a | `module` should be set to the module this class is being created in; |
---|
280 | n/a | if it is not set, an attempt to find that module will be made, but if |
---|
281 | n/a | it fails the class will not be picklable. |
---|
282 | n/a | |
---|
283 | n/a | `qualname` should be set to the actual location this class can be found |
---|
284 | n/a | at in its module; by default it is set to the global scope. If this is |
---|
285 | n/a | not correct, unpickling will fail in some circumstances. |
---|
286 | n/a | |
---|
287 | n/a | `type`, if set, will be mixed in as the first base class. |
---|
288 | n/a | |
---|
289 | n/a | """ |
---|
290 | n/a | if names is None: # simple value lookup |
---|
291 | n/a | return cls.__new__(cls, value) |
---|
292 | n/a | # otherwise, functional API: we're creating a new Enum type |
---|
293 | n/a | return cls._create_(value, names, module=module, qualname=qualname, type=type, start=start) |
---|
294 | n/a | |
---|
295 | n/a | def __contains__(cls, member): |
---|
296 | n/a | return isinstance(member, cls) and member._name_ in cls._member_map_ |
---|
297 | n/a | |
---|
298 | n/a | def __delattr__(cls, attr): |
---|
299 | n/a | # nicer error message when someone tries to delete an attribute |
---|
300 | n/a | # (see issue19025). |
---|
301 | n/a | if attr in cls._member_map_: |
---|
302 | n/a | raise AttributeError( |
---|
303 | n/a | "%s: cannot delete Enum member." % cls.__name__) |
---|
304 | n/a | super().__delattr__(attr) |
---|
305 | n/a | |
---|
306 | n/a | def __dir__(self): |
---|
307 | n/a | return (['__class__', '__doc__', '__members__', '__module__'] + |
---|
308 | n/a | self._member_names_) |
---|
309 | n/a | |
---|
310 | n/a | def __getattr__(cls, name): |
---|
311 | n/a | """Return the enum member matching `name` |
---|
312 | n/a | |
---|
313 | n/a | We use __getattr__ instead of descriptors or inserting into the enum |
---|
314 | n/a | class' __dict__ in order to support `name` and `value` being both |
---|
315 | n/a | properties for enum members (which live in the class' __dict__) and |
---|
316 | n/a | enum members themselves. |
---|
317 | n/a | |
---|
318 | n/a | """ |
---|
319 | n/a | if _is_dunder(name): |
---|
320 | n/a | raise AttributeError(name) |
---|
321 | n/a | try: |
---|
322 | n/a | return cls._member_map_[name] |
---|
323 | n/a | except KeyError: |
---|
324 | n/a | raise AttributeError(name) from None |
---|
325 | n/a | |
---|
326 | n/a | def __getitem__(cls, name): |
---|
327 | n/a | return cls._member_map_[name] |
---|
328 | n/a | |
---|
329 | n/a | def __iter__(cls): |
---|
330 | n/a | return (cls._member_map_[name] for name in cls._member_names_) |
---|
331 | n/a | |
---|
332 | n/a | def __len__(cls): |
---|
333 | n/a | return len(cls._member_names_) |
---|
334 | n/a | |
---|
335 | n/a | @property |
---|
336 | n/a | def __members__(cls): |
---|
337 | n/a | """Returns a mapping of member name->value. |
---|
338 | n/a | |
---|
339 | n/a | This mapping lists all enum members, including aliases. Note that this |
---|
340 | n/a | is a read-only view of the internal mapping. |
---|
341 | n/a | |
---|
342 | n/a | """ |
---|
343 | n/a | return MappingProxyType(cls._member_map_) |
---|
344 | n/a | |
---|
345 | n/a | def __repr__(cls): |
---|
346 | n/a | return "<enum %r>" % cls.__name__ |
---|
347 | n/a | |
---|
348 | n/a | def __reversed__(cls): |
---|
349 | n/a | return (cls._member_map_[name] for name in reversed(cls._member_names_)) |
---|
350 | n/a | |
---|
351 | n/a | def __setattr__(cls, name, value): |
---|
352 | n/a | """Block attempts to reassign Enum members. |
---|
353 | n/a | |
---|
354 | n/a | A simple assignment to the class namespace only changes one of the |
---|
355 | n/a | several possible ways to get an Enum member from the Enum class, |
---|
356 | n/a | resulting in an inconsistent Enumeration. |
---|
357 | n/a | |
---|
358 | n/a | """ |
---|
359 | n/a | member_map = cls.__dict__.get('_member_map_', {}) |
---|
360 | n/a | if name in member_map: |
---|
361 | n/a | raise AttributeError('Cannot reassign members.') |
---|
362 | n/a | super().__setattr__(name, value) |
---|
363 | n/a | |
---|
364 | n/a | def _create_(cls, class_name, names=None, *, module=None, qualname=None, type=None, start=1): |
---|
365 | n/a | """Convenience method to create a new Enum class. |
---|
366 | n/a | |
---|
367 | n/a | `names` can be: |
---|
368 | n/a | |
---|
369 | n/a | * A string containing member names, separated either with spaces or |
---|
370 | n/a | commas. Values are incremented by 1 from `start`. |
---|
371 | n/a | * An iterable of member names. Values are incremented by 1 from `start`. |
---|
372 | n/a | * An iterable of (member name, value) pairs. |
---|
373 | n/a | * A mapping of member name -> value pairs. |
---|
374 | n/a | |
---|
375 | n/a | """ |
---|
376 | n/a | metacls = cls.__class__ |
---|
377 | n/a | bases = (cls, ) if type is None else (type, cls) |
---|
378 | n/a | _, first_enum = cls._get_mixins_(bases) |
---|
379 | n/a | classdict = metacls.__prepare__(class_name, bases) |
---|
380 | n/a | |
---|
381 | n/a | # special processing needed for names? |
---|
382 | n/a | if isinstance(names, str): |
---|
383 | n/a | names = names.replace(',', ' ').split() |
---|
384 | n/a | if isinstance(names, (tuple, list)) and isinstance(names[0], str): |
---|
385 | n/a | original_names, names = names, [] |
---|
386 | n/a | last_values = [] |
---|
387 | n/a | for count, name in enumerate(original_names): |
---|
388 | n/a | value = first_enum._generate_next_value_(name, start, count, last_values[:]) |
---|
389 | n/a | last_values.append(value) |
---|
390 | n/a | names.append((name, value)) |
---|
391 | n/a | |
---|
392 | n/a | # Here, names is either an iterable of (name, value) or a mapping. |
---|
393 | n/a | for item in names: |
---|
394 | n/a | if isinstance(item, str): |
---|
395 | n/a | member_name, member_value = item, names[item] |
---|
396 | n/a | else: |
---|
397 | n/a | member_name, member_value = item |
---|
398 | n/a | classdict[member_name] = member_value |
---|
399 | n/a | enum_class = metacls.__new__(metacls, class_name, bases, classdict) |
---|
400 | n/a | |
---|
401 | n/a | # TODO: replace the frame hack if a blessed way to know the calling |
---|
402 | n/a | # module is ever developed |
---|
403 | n/a | if module is None: |
---|
404 | n/a | try: |
---|
405 | n/a | module = sys._getframe(2).f_globals['__name__'] |
---|
406 | n/a | except (AttributeError, ValueError) as exc: |
---|
407 | n/a | pass |
---|
408 | n/a | if module is None: |
---|
409 | n/a | _make_class_unpicklable(enum_class) |
---|
410 | n/a | else: |
---|
411 | n/a | enum_class.__module__ = module |
---|
412 | n/a | if qualname is not None: |
---|
413 | n/a | enum_class.__qualname__ = qualname |
---|
414 | n/a | |
---|
415 | n/a | return enum_class |
---|
416 | n/a | |
---|
417 | n/a | @staticmethod |
---|
418 | n/a | def _get_mixins_(bases): |
---|
419 | n/a | """Returns the type for creating enum members, and the first inherited |
---|
420 | n/a | enum class. |
---|
421 | n/a | |
---|
422 | n/a | bases: the tuple of bases that was given to __new__ |
---|
423 | n/a | |
---|
424 | n/a | """ |
---|
425 | n/a | if not bases: |
---|
426 | n/a | return object, Enum |
---|
427 | n/a | |
---|
428 | n/a | # double check that we are not subclassing a class with existing |
---|
429 | n/a | # enumeration members; while we're at it, see if any other data |
---|
430 | n/a | # type has been mixed in so we can use the correct __new__ |
---|
431 | n/a | member_type = first_enum = None |
---|
432 | n/a | for base in bases: |
---|
433 | n/a | if (base is not Enum and |
---|
434 | n/a | issubclass(base, Enum) and |
---|
435 | n/a | base._member_names_): |
---|
436 | n/a | raise TypeError("Cannot extend enumerations") |
---|
437 | n/a | # base is now the last base in bases |
---|
438 | n/a | if not issubclass(base, Enum): |
---|
439 | n/a | raise TypeError("new enumerations must be created as " |
---|
440 | n/a | "`ClassName([mixin_type,] enum_type)`") |
---|
441 | n/a | |
---|
442 | n/a | # get correct mix-in type (either mix-in type of Enum subclass, or |
---|
443 | n/a | # first base if last base is Enum) |
---|
444 | n/a | if not issubclass(bases[0], Enum): |
---|
445 | n/a | member_type = bases[0] # first data type |
---|
446 | n/a | first_enum = bases[-1] # enum type |
---|
447 | n/a | else: |
---|
448 | n/a | for base in bases[0].__mro__: |
---|
449 | n/a | # most common: (IntEnum, int, Enum, object) |
---|
450 | n/a | # possible: (<Enum 'AutoIntEnum'>, <Enum 'IntEnum'>, |
---|
451 | n/a | # <class 'int'>, <Enum 'Enum'>, |
---|
452 | n/a | # <class 'object'>) |
---|
453 | n/a | if issubclass(base, Enum): |
---|
454 | n/a | if first_enum is None: |
---|
455 | n/a | first_enum = base |
---|
456 | n/a | else: |
---|
457 | n/a | if member_type is None: |
---|
458 | n/a | member_type = base |
---|
459 | n/a | |
---|
460 | n/a | return member_type, first_enum |
---|
461 | n/a | |
---|
462 | n/a | @staticmethod |
---|
463 | n/a | def _find_new_(classdict, member_type, first_enum): |
---|
464 | n/a | """Returns the __new__ to be used for creating the enum members. |
---|
465 | n/a | |
---|
466 | n/a | classdict: the class dictionary given to __new__ |
---|
467 | n/a | member_type: the data type whose __new__ will be used by default |
---|
468 | n/a | first_enum: enumeration to check for an overriding __new__ |
---|
469 | n/a | |
---|
470 | n/a | """ |
---|
471 | n/a | # now find the correct __new__, checking to see of one was defined |
---|
472 | n/a | # by the user; also check earlier enum classes in case a __new__ was |
---|
473 | n/a | # saved as __new_member__ |
---|
474 | n/a | __new__ = classdict.get('__new__', None) |
---|
475 | n/a | |
---|
476 | n/a | # should __new__ be saved as __new_member__ later? |
---|
477 | n/a | save_new = __new__ is not None |
---|
478 | n/a | |
---|
479 | n/a | if __new__ is None: |
---|
480 | n/a | # check all possibles for __new_member__ before falling back to |
---|
481 | n/a | # __new__ |
---|
482 | n/a | for method in ('__new_member__', '__new__'): |
---|
483 | n/a | for possible in (member_type, first_enum): |
---|
484 | n/a | target = getattr(possible, method, None) |
---|
485 | n/a | if target not in { |
---|
486 | n/a | None, |
---|
487 | n/a | None.__new__, |
---|
488 | n/a | object.__new__, |
---|
489 | n/a | Enum.__new__, |
---|
490 | n/a | }: |
---|
491 | n/a | __new__ = target |
---|
492 | n/a | break |
---|
493 | n/a | if __new__ is not None: |
---|
494 | n/a | break |
---|
495 | n/a | else: |
---|
496 | n/a | __new__ = object.__new__ |
---|
497 | n/a | |
---|
498 | n/a | # if a non-object.__new__ is used then whatever value/tuple was |
---|
499 | n/a | # assigned to the enum member name will be passed to __new__ and to the |
---|
500 | n/a | # new enum member's __init__ |
---|
501 | n/a | if __new__ is object.__new__: |
---|
502 | n/a | use_args = False |
---|
503 | n/a | else: |
---|
504 | n/a | use_args = True |
---|
505 | n/a | |
---|
506 | n/a | return __new__, save_new, use_args |
---|
507 | n/a | |
---|
508 | n/a | |
---|
509 | n/a | class Enum(metaclass=EnumMeta): |
---|
510 | n/a | """Generic enumeration. |
---|
511 | n/a | |
---|
512 | n/a | Derive from this class to define new enumerations. |
---|
513 | n/a | |
---|
514 | n/a | """ |
---|
515 | n/a | def __new__(cls, value): |
---|
516 | n/a | # all enum instances are actually created during class construction |
---|
517 | n/a | # without calling this method; this method is called by the metaclass' |
---|
518 | n/a | # __call__ (i.e. Color(3) ), and by pickle |
---|
519 | n/a | if type(value) is cls: |
---|
520 | n/a | # For lookups like Color(Color.RED) |
---|
521 | n/a | return value |
---|
522 | n/a | # by-value search for a matching enum member |
---|
523 | n/a | # see if it's in the reverse mapping (for hashable values) |
---|
524 | n/a | try: |
---|
525 | n/a | if value in cls._value2member_map_: |
---|
526 | n/a | return cls._value2member_map_[value] |
---|
527 | n/a | except TypeError: |
---|
528 | n/a | # not there, now do long search -- O(n) behavior |
---|
529 | n/a | for member in cls._member_map_.values(): |
---|
530 | n/a | if member._value_ == value: |
---|
531 | n/a | return member |
---|
532 | n/a | # still not found -- try _missing_ hook |
---|
533 | n/a | return cls._missing_(value) |
---|
534 | n/a | |
---|
535 | n/a | def _generate_next_value_(name, start, count, last_values): |
---|
536 | n/a | for last_value in reversed(last_values): |
---|
537 | n/a | try: |
---|
538 | n/a | return last_value + 1 |
---|
539 | n/a | except TypeError: |
---|
540 | n/a | pass |
---|
541 | n/a | else: |
---|
542 | n/a | return start |
---|
543 | n/a | |
---|
544 | n/a | @classmethod |
---|
545 | n/a | def _missing_(cls, value): |
---|
546 | n/a | raise ValueError("%r is not a valid %s" % (value, cls.__name__)) |
---|
547 | n/a | |
---|
548 | n/a | def __repr__(self): |
---|
549 | n/a | return "<%s.%s: %r>" % ( |
---|
550 | n/a | self.__class__.__name__, self._name_, self._value_) |
---|
551 | n/a | |
---|
552 | n/a | def __str__(self): |
---|
553 | n/a | return "%s.%s" % (self.__class__.__name__, self._name_) |
---|
554 | n/a | |
---|
555 | n/a | def __dir__(self): |
---|
556 | n/a | added_behavior = [ |
---|
557 | n/a | m |
---|
558 | n/a | for cls in self.__class__.mro() |
---|
559 | n/a | for m in cls.__dict__ |
---|
560 | n/a | if m[0] != '_' and m not in self._member_map_ |
---|
561 | n/a | ] |
---|
562 | n/a | return (['__class__', '__doc__', '__module__'] + added_behavior) |
---|
563 | n/a | |
---|
564 | n/a | def __format__(self, format_spec): |
---|
565 | n/a | # mixed-in Enums should use the mixed-in type's __format__, otherwise |
---|
566 | n/a | # we can get strange results with the Enum name showing up instead of |
---|
567 | n/a | # the value |
---|
568 | n/a | |
---|
569 | n/a | # pure Enum branch |
---|
570 | n/a | if self._member_type_ is object: |
---|
571 | n/a | cls = str |
---|
572 | n/a | val = str(self) |
---|
573 | n/a | # mix-in branch |
---|
574 | n/a | else: |
---|
575 | n/a | cls = self._member_type_ |
---|
576 | n/a | val = self._value_ |
---|
577 | n/a | return cls.__format__(val, format_spec) |
---|
578 | n/a | |
---|
579 | n/a | def __hash__(self): |
---|
580 | n/a | return hash(self._name_) |
---|
581 | n/a | |
---|
582 | n/a | def __reduce_ex__(self, proto): |
---|
583 | n/a | return self.__class__, (self._value_, ) |
---|
584 | n/a | |
---|
585 | n/a | # DynamicClassAttribute is used to provide access to the `name` and |
---|
586 | n/a | # `value` properties of enum members while keeping some measure of |
---|
587 | n/a | # protection from modification, while still allowing for an enumeration |
---|
588 | n/a | # to have members named `name` and `value`. This works because enumeration |
---|
589 | n/a | # members are not set directly on the enum class -- __getattr__ is |
---|
590 | n/a | # used to look them up. |
---|
591 | n/a | |
---|
592 | n/a | @DynamicClassAttribute |
---|
593 | n/a | def name(self): |
---|
594 | n/a | """The name of the Enum member.""" |
---|
595 | n/a | return self._name_ |
---|
596 | n/a | |
---|
597 | n/a | @DynamicClassAttribute |
---|
598 | n/a | def value(self): |
---|
599 | n/a | """The value of the Enum member.""" |
---|
600 | n/a | return self._value_ |
---|
601 | n/a | |
---|
602 | n/a | @classmethod |
---|
603 | n/a | def _convert(cls, name, module, filter, source=None): |
---|
604 | n/a | """ |
---|
605 | n/a | Create a new Enum subclass that replaces a collection of global constants |
---|
606 | n/a | """ |
---|
607 | n/a | # convert all constants from source (or module) that pass filter() to |
---|
608 | n/a | # a new Enum called name, and export the enum and its members back to |
---|
609 | n/a | # module; |
---|
610 | n/a | # also, replace the __reduce_ex__ method so unpickling works in |
---|
611 | n/a | # previous Python versions |
---|
612 | n/a | module_globals = vars(sys.modules[module]) |
---|
613 | n/a | if source: |
---|
614 | n/a | source = vars(source) |
---|
615 | n/a | else: |
---|
616 | n/a | source = module_globals |
---|
617 | n/a | # We use an OrderedDict of sorted source keys so that the |
---|
618 | n/a | # _value2member_map is populated in the same order every time |
---|
619 | n/a | # for a consistent reverse mapping of number to name when there |
---|
620 | n/a | # are multiple names for the same number rather than varying |
---|
621 | n/a | # between runs due to hash randomization of the module dictionary. |
---|
622 | n/a | members = [ |
---|
623 | n/a | (name, source[name]) |
---|
624 | n/a | for name in source.keys() |
---|
625 | n/a | if filter(name)] |
---|
626 | n/a | try: |
---|
627 | n/a | # sort by value |
---|
628 | n/a | members.sort(key=lambda t: (t[1], t[0])) |
---|
629 | n/a | except TypeError: |
---|
630 | n/a | # unless some values aren't comparable, in which case sort by name |
---|
631 | n/a | members.sort(key=lambda t: t[0]) |
---|
632 | n/a | cls = cls(name, members, module=module) |
---|
633 | n/a | cls.__reduce_ex__ = _reduce_ex_by_name |
---|
634 | n/a | module_globals.update(cls.__members__) |
---|
635 | n/a | module_globals[name] = cls |
---|
636 | n/a | return cls |
---|
637 | n/a | |
---|
638 | n/a | |
---|
639 | n/a | class IntEnum(int, Enum): |
---|
640 | n/a | """Enum where members are also (and must be) ints""" |
---|
641 | n/a | |
---|
642 | n/a | |
---|
643 | n/a | def _reduce_ex_by_name(self, proto): |
---|
644 | n/a | return self.name |
---|
645 | n/a | |
---|
646 | n/a | class Flag(Enum): |
---|
647 | n/a | """Support for flags""" |
---|
648 | n/a | |
---|
649 | n/a | def _generate_next_value_(name, start, count, last_values): |
---|
650 | n/a | """ |
---|
651 | n/a | Generate the next value when not given. |
---|
652 | n/a | |
---|
653 | n/a | name: the name of the member |
---|
654 | n/a | start: the initital start value or None |
---|
655 | n/a | count: the number of existing members |
---|
656 | n/a | last_value: the last value assigned or None |
---|
657 | n/a | """ |
---|
658 | n/a | if not count: |
---|
659 | n/a | return start if start is not None else 1 |
---|
660 | n/a | for last_value in reversed(last_values): |
---|
661 | n/a | try: |
---|
662 | n/a | high_bit = _high_bit(last_value) |
---|
663 | n/a | break |
---|
664 | n/a | except Exception: |
---|
665 | n/a | raise TypeError('Invalid Flag value: %r' % last_value) from None |
---|
666 | n/a | return 2 ** (high_bit+1) |
---|
667 | n/a | |
---|
668 | n/a | @classmethod |
---|
669 | n/a | def _missing_(cls, value): |
---|
670 | n/a | original_value = value |
---|
671 | n/a | if value < 0: |
---|
672 | n/a | value = ~value |
---|
673 | n/a | possible_member = cls._create_pseudo_member_(value) |
---|
674 | n/a | if original_value < 0: |
---|
675 | n/a | possible_member = ~possible_member |
---|
676 | n/a | return possible_member |
---|
677 | n/a | |
---|
678 | n/a | @classmethod |
---|
679 | n/a | def _create_pseudo_member_(cls, value): |
---|
680 | n/a | """ |
---|
681 | n/a | Create a composite member iff value contains only members. |
---|
682 | n/a | """ |
---|
683 | n/a | pseudo_member = cls._value2member_map_.get(value, None) |
---|
684 | n/a | if pseudo_member is None: |
---|
685 | n/a | # verify all bits are accounted for |
---|
686 | n/a | _, extra_flags = _decompose(cls, value) |
---|
687 | n/a | if extra_flags: |
---|
688 | n/a | raise ValueError("%r is not a valid %s" % (value, cls.__name__)) |
---|
689 | n/a | # construct a singleton enum pseudo-member |
---|
690 | n/a | pseudo_member = object.__new__(cls) |
---|
691 | n/a | pseudo_member._name_ = None |
---|
692 | n/a | pseudo_member._value_ = value |
---|
693 | n/a | # use setdefault in case another thread already created a composite |
---|
694 | n/a | # with this value |
---|
695 | n/a | pseudo_member = cls._value2member_map_.setdefault(value, pseudo_member) |
---|
696 | n/a | return pseudo_member |
---|
697 | n/a | |
---|
698 | n/a | def __contains__(self, other): |
---|
699 | n/a | if not isinstance(other, self.__class__): |
---|
700 | n/a | return NotImplemented |
---|
701 | n/a | return other._value_ & self._value_ == other._value_ |
---|
702 | n/a | |
---|
703 | n/a | def __repr__(self): |
---|
704 | n/a | cls = self.__class__ |
---|
705 | n/a | if self._name_ is not None: |
---|
706 | n/a | return '<%s.%s: %r>' % (cls.__name__, self._name_, self._value_) |
---|
707 | n/a | members, uncovered = _decompose(cls, self._value_) |
---|
708 | n/a | return '<%s.%s: %r>' % ( |
---|
709 | n/a | cls.__name__, |
---|
710 | n/a | '|'.join([str(m._name_ or m._value_) for m in members]), |
---|
711 | n/a | self._value_, |
---|
712 | n/a | ) |
---|
713 | n/a | |
---|
714 | n/a | def __str__(self): |
---|
715 | n/a | cls = self.__class__ |
---|
716 | n/a | if self._name_ is not None: |
---|
717 | n/a | return '%s.%s' % (cls.__name__, self._name_) |
---|
718 | n/a | members, uncovered = _decompose(cls, self._value_) |
---|
719 | n/a | if len(members) == 1 and members[0]._name_ is None: |
---|
720 | n/a | return '%s.%r' % (cls.__name__, members[0]._value_) |
---|
721 | n/a | else: |
---|
722 | n/a | return '%s.%s' % ( |
---|
723 | n/a | cls.__name__, |
---|
724 | n/a | '|'.join([str(m._name_ or m._value_) for m in members]), |
---|
725 | n/a | ) |
---|
726 | n/a | |
---|
727 | n/a | def __bool__(self): |
---|
728 | n/a | return bool(self._value_) |
---|
729 | n/a | |
---|
730 | n/a | def __or__(self, other): |
---|
731 | n/a | if not isinstance(other, self.__class__): |
---|
732 | n/a | return NotImplemented |
---|
733 | n/a | return self.__class__(self._value_ | other._value_) |
---|
734 | n/a | |
---|
735 | n/a | def __and__(self, other): |
---|
736 | n/a | if not isinstance(other, self.__class__): |
---|
737 | n/a | return NotImplemented |
---|
738 | n/a | return self.__class__(self._value_ & other._value_) |
---|
739 | n/a | |
---|
740 | n/a | def __xor__(self, other): |
---|
741 | n/a | if not isinstance(other, self.__class__): |
---|
742 | n/a | return NotImplemented |
---|
743 | n/a | return self.__class__(self._value_ ^ other._value_) |
---|
744 | n/a | |
---|
745 | n/a | def __invert__(self): |
---|
746 | n/a | members, uncovered = _decompose(self.__class__, self._value_) |
---|
747 | n/a | inverted_members = [ |
---|
748 | n/a | m for m in self.__class__ |
---|
749 | n/a | if m not in members and not m._value_ & self._value_ |
---|
750 | n/a | ] |
---|
751 | n/a | inverted = reduce(_or_, inverted_members, self.__class__(0)) |
---|
752 | n/a | return self.__class__(inverted) |
---|
753 | n/a | |
---|
754 | n/a | |
---|
755 | n/a | class IntFlag(int, Flag): |
---|
756 | n/a | """Support for integer-based Flags""" |
---|
757 | n/a | |
---|
758 | n/a | @classmethod |
---|
759 | n/a | def _missing_(cls, value): |
---|
760 | n/a | if not isinstance(value, int): |
---|
761 | n/a | raise ValueError("%r is not a valid %s" % (value, cls.__name__)) |
---|
762 | n/a | new_member = cls._create_pseudo_member_(value) |
---|
763 | n/a | return new_member |
---|
764 | n/a | |
---|
765 | n/a | @classmethod |
---|
766 | n/a | def _create_pseudo_member_(cls, value): |
---|
767 | n/a | pseudo_member = cls._value2member_map_.get(value, None) |
---|
768 | n/a | if pseudo_member is None: |
---|
769 | n/a | need_to_create = [value] |
---|
770 | n/a | # get unaccounted for bits |
---|
771 | n/a | _, extra_flags = _decompose(cls, value) |
---|
772 | n/a | # timer = 10 |
---|
773 | n/a | while extra_flags: |
---|
774 | n/a | # timer -= 1 |
---|
775 | n/a | bit = _high_bit(extra_flags) |
---|
776 | n/a | flag_value = 2 ** bit |
---|
777 | n/a | if (flag_value not in cls._value2member_map_ and |
---|
778 | n/a | flag_value not in need_to_create |
---|
779 | n/a | ): |
---|
780 | n/a | need_to_create.append(flag_value) |
---|
781 | n/a | if extra_flags == -flag_value: |
---|
782 | n/a | extra_flags = 0 |
---|
783 | n/a | else: |
---|
784 | n/a | extra_flags ^= flag_value |
---|
785 | n/a | for value in reversed(need_to_create): |
---|
786 | n/a | # construct singleton pseudo-members |
---|
787 | n/a | pseudo_member = int.__new__(cls, value) |
---|
788 | n/a | pseudo_member._name_ = None |
---|
789 | n/a | pseudo_member._value_ = value |
---|
790 | n/a | # use setdefault in case another thread already created a composite |
---|
791 | n/a | # with this value |
---|
792 | n/a | pseudo_member = cls._value2member_map_.setdefault(value, pseudo_member) |
---|
793 | n/a | return pseudo_member |
---|
794 | n/a | |
---|
795 | n/a | def __or__(self, other): |
---|
796 | n/a | if not isinstance(other, (self.__class__, int)): |
---|
797 | n/a | return NotImplemented |
---|
798 | n/a | result = self.__class__(self._value_ | self.__class__(other)._value_) |
---|
799 | n/a | return result |
---|
800 | n/a | |
---|
801 | n/a | def __and__(self, other): |
---|
802 | n/a | if not isinstance(other, (self.__class__, int)): |
---|
803 | n/a | return NotImplemented |
---|
804 | n/a | return self.__class__(self._value_ & self.__class__(other)._value_) |
---|
805 | n/a | |
---|
806 | n/a | def __xor__(self, other): |
---|
807 | n/a | if not isinstance(other, (self.__class__, int)): |
---|
808 | n/a | return NotImplemented |
---|
809 | n/a | return self.__class__(self._value_ ^ self.__class__(other)._value_) |
---|
810 | n/a | |
---|
811 | n/a | __ror__ = __or__ |
---|
812 | n/a | __rand__ = __and__ |
---|
813 | n/a | __rxor__ = __xor__ |
---|
814 | n/a | |
---|
815 | n/a | def __invert__(self): |
---|
816 | n/a | result = self.__class__(~self._value_) |
---|
817 | n/a | return result |
---|
818 | n/a | |
---|
819 | n/a | |
---|
820 | n/a | def _high_bit(value): |
---|
821 | n/a | """returns index of highest bit, or -1 if value is zero or negative""" |
---|
822 | n/a | return value.bit_length() - 1 |
---|
823 | n/a | |
---|
824 | n/a | def unique(enumeration): |
---|
825 | n/a | """Class decorator for enumerations ensuring unique member values.""" |
---|
826 | n/a | duplicates = [] |
---|
827 | n/a | for name, member in enumeration.__members__.items(): |
---|
828 | n/a | if name != member.name: |
---|
829 | n/a | duplicates.append((name, member.name)) |
---|
830 | n/a | if duplicates: |
---|
831 | n/a | alias_details = ', '.join( |
---|
832 | n/a | ["%s -> %s" % (alias, name) for (alias, name) in duplicates]) |
---|
833 | n/a | raise ValueError('duplicate values found in %r: %s' % |
---|
834 | n/a | (enumeration, alias_details)) |
---|
835 | n/a | return enumeration |
---|
836 | n/a | |
---|
837 | n/a | def _decompose(flag, value): |
---|
838 | n/a | """Extract all members from the value.""" |
---|
839 | n/a | # _decompose is only called if the value is not named |
---|
840 | n/a | not_covered = value |
---|
841 | n/a | negative = value < 0 |
---|
842 | n/a | # issue29167: wrap accesses to _value2member_map_ in a list to avoid race |
---|
843 | n/a | # conditions between iterating over it and having more psuedo- |
---|
844 | n/a | # members added to it |
---|
845 | n/a | if negative: |
---|
846 | n/a | # only check for named flags |
---|
847 | n/a | flags_to_check = [ |
---|
848 | n/a | (m, v) |
---|
849 | n/a | for v, m in list(flag._value2member_map_.items()) |
---|
850 | n/a | if m.name is not None |
---|
851 | n/a | ] |
---|
852 | n/a | else: |
---|
853 | n/a | # check for named flags and powers-of-two flags |
---|
854 | n/a | flags_to_check = [ |
---|
855 | n/a | (m, v) |
---|
856 | n/a | for v, m in list(flag._value2member_map_.items()) |
---|
857 | n/a | if m.name is not None or _power_of_two(v) |
---|
858 | n/a | ] |
---|
859 | n/a | members = [] |
---|
860 | n/a | for member, member_value in flags_to_check: |
---|
861 | n/a | if member_value and member_value & value == member_value: |
---|
862 | n/a | members.append(member) |
---|
863 | n/a | not_covered &= ~member_value |
---|
864 | n/a | if not members and value in flag._value2member_map_: |
---|
865 | n/a | members.append(flag._value2member_map_[value]) |
---|
866 | n/a | members.sort(key=lambda m: m._value_, reverse=True) |
---|
867 | n/a | if len(members) > 1 and members[0].value == value: |
---|
868 | n/a | # we have the breakdown, don't need the value member itself |
---|
869 | n/a | members.pop(0) |
---|
870 | n/a | return members, not_covered |
---|
871 | n/a | |
---|
872 | n/a | def _power_of_two(value): |
---|
873 | n/a | if value < 1: |
---|
874 | n/a | return False |
---|
875 | n/a | return value == 2 ** _high_bit(value) |
---|