blob: 5db40403f3931e141f9805488697baeb19a321e8 [file] [log] [blame]
Ethan Furman6b3d64a2013-06-14 16:55:46 -07001import enum
Ethan Furman5875d742013-10-21 20:45:55 -07002import inspect
3import pydoc
Ethan Furman6b3d64a2013-06-14 16:55:46 -07004import unittest
5from collections import OrderedDict
Ethan Furman5875d742013-10-21 20:45:55 -07006from enum import Enum, IntEnum, EnumMeta, unique
7from io import StringIO
Ethan Furman2ddb39a2014-02-06 17:28:50 -08008from pickle import dumps, loads, PicklingError, HIGHEST_PROTOCOL
Ethan Furman6b3d64a2013-06-14 16:55:46 -07009
10# for pickle tests
11try:
12 class Stooges(Enum):
13 LARRY = 1
14 CURLY = 2
15 MOE = 3
16except Exception as exc:
17 Stooges = exc
18
19try:
20 class IntStooges(int, Enum):
21 LARRY = 1
22 CURLY = 2
23 MOE = 3
24except Exception as exc:
25 IntStooges = exc
26
27try:
28 class FloatStooges(float, Enum):
29 LARRY = 1.39
30 CURLY = 2.72
31 MOE = 3.142596
32except Exception as exc:
33 FloatStooges = exc
34
35# for pickle test and subclass tests
36try:
37 class StrEnum(str, Enum):
38 'accepts only string values'
39 class Name(StrEnum):
40 BDFL = 'Guido van Rossum'
41 FLUFL = 'Barry Warsaw'
42except Exception as exc:
43 Name = exc
44
45try:
46 Question = Enum('Question', 'who what when where why', module=__name__)
47except Exception as exc:
48 Question = exc
49
50try:
51 Answer = Enum('Answer', 'him this then there because')
52except Exception as exc:
53 Answer = exc
54
Ethan Furmanca1b7942014-02-08 11:36:27 -080055try:
56 Theory = Enum('Theory', 'rule law supposition', qualname='spanish_inquisition')
57except Exception as exc:
58 Theory = exc
59
Ethan Furman6b3d64a2013-06-14 16:55:46 -070060# for doctests
61try:
62 class Fruit(Enum):
63 tomato = 1
64 banana = 2
65 cherry = 3
66except Exception:
67 pass
68
Ethan Furmanca1b7942014-02-08 11:36:27 -080069def test_pickle_dump_load(assertion, source, target=None,
70 *, protocol=(0, HIGHEST_PROTOCOL)):
71 start, stop = protocol
Ethan Furman2ddb39a2014-02-06 17:28:50 -080072 if target is None:
73 target = source
Ethan Furmanca1b7942014-02-08 11:36:27 -080074 for protocol in range(start, stop+1):
Ethan Furman2ddb39a2014-02-06 17:28:50 -080075 assertion(loads(dumps(source, protocol=protocol)), target)
76
Ethan Furmanca1b7942014-02-08 11:36:27 -080077def test_pickle_exception(assertion, exception, obj,
78 *, protocol=(0, HIGHEST_PROTOCOL)):
79 start, stop = protocol
80 for protocol in range(start, stop+1):
Ethan Furman2ddb39a2014-02-06 17:28:50 -080081 with assertion(exception):
82 dumps(obj, protocol=protocol)
Ethan Furman648f8602013-10-06 17:19:54 -070083
84class TestHelpers(unittest.TestCase):
85 # _is_descriptor, _is_sunder, _is_dunder
86
87 def test_is_descriptor(self):
88 class foo:
89 pass
90 for attr in ('__get__','__set__','__delete__'):
91 obj = foo()
92 self.assertFalse(enum._is_descriptor(obj))
93 setattr(obj, attr, 1)
94 self.assertTrue(enum._is_descriptor(obj))
95
96 def test_is_sunder(self):
97 for s in ('_a_', '_aa_'):
98 self.assertTrue(enum._is_sunder(s))
99
100 for s in ('a', 'a_', '_a', '__a', 'a__', '__a__', '_a__', '__a_', '_',
101 '__', '___', '____', '_____',):
102 self.assertFalse(enum._is_sunder(s))
103
104 def test_is_dunder(self):
105 for s in ('__a__', '__aa__'):
106 self.assertTrue(enum._is_dunder(s))
107 for s in ('a', 'a_', '_a', '__a', 'a__', '_a_', '_a__', '__a_', '_',
108 '__', '___', '____', '_____',):
109 self.assertFalse(enum._is_dunder(s))
110
111
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700112class TestEnum(unittest.TestCase):
Ethan Furmanca1b7942014-02-08 11:36:27 -0800113
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700114 def setUp(self):
115 class Season(Enum):
116 SPRING = 1
117 SUMMER = 2
118 AUTUMN = 3
119 WINTER = 4
120 self.Season = Season
121
Ethan Furmanec15a822013-08-31 19:17:41 -0700122 class Konstants(float, Enum):
123 E = 2.7182818
124 PI = 3.1415926
125 TAU = 2 * PI
126 self.Konstants = Konstants
127
128 class Grades(IntEnum):
129 A = 5
130 B = 4
131 C = 3
132 D = 2
133 F = 0
134 self.Grades = Grades
135
136 class Directional(str, Enum):
137 EAST = 'east'
138 WEST = 'west'
139 NORTH = 'north'
140 SOUTH = 'south'
141 self.Directional = Directional
142
143 from datetime import date
144 class Holiday(date, Enum):
145 NEW_YEAR = 2013, 1, 1
146 IDES_OF_MARCH = 2013, 3, 15
147 self.Holiday = Holiday
148
Ethan Furman388a3922013-08-12 06:51:41 -0700149 def test_dir_on_class(self):
150 Season = self.Season
151 self.assertEqual(
152 set(dir(Season)),
Ethan Furmanc850f342013-09-15 16:59:35 -0700153 set(['__class__', '__doc__', '__members__', '__module__',
Ethan Furman388a3922013-08-12 06:51:41 -0700154 'SPRING', 'SUMMER', 'AUTUMN', 'WINTER']),
155 )
156
157 def test_dir_on_item(self):
158 Season = self.Season
159 self.assertEqual(
160 set(dir(Season.WINTER)),
Ethan Furmanc850f342013-09-15 16:59:35 -0700161 set(['__class__', '__doc__', '__module__', 'name', 'value']),
Ethan Furman388a3922013-08-12 06:51:41 -0700162 )
163
Ethan Furmanc850f342013-09-15 16:59:35 -0700164 def test_dir_with_added_behavior(self):
165 class Test(Enum):
166 this = 'that'
167 these = 'those'
168 def wowser(self):
169 return ("Wowser! I'm %s!" % self.name)
170 self.assertEqual(
171 set(dir(Test)),
172 set(['__class__', '__doc__', '__members__', '__module__', 'this', 'these']),
173 )
174 self.assertEqual(
175 set(dir(Test.this)),
176 set(['__class__', '__doc__', '__module__', 'name', 'value', 'wowser']),
177 )
178
Ethan Furman0ae550b2014-10-14 08:58:32 -0700179 def test_dir_on_sub_with_behavior_on_super(self):
180 # see issue22506
181 class SuperEnum(Enum):
182 def invisible(self):
183 return "did you see me?"
184 class SubEnum(SuperEnum):
185 sample = 5
186 self.assertEqual(
187 set(dir(SubEnum.sample)),
188 set(['__class__', '__doc__', '__module__', 'name', 'value', 'invisible']),
189 )
190
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700191 def test_enum_in_enum_out(self):
192 Season = self.Season
193 self.assertIs(Season(Season.WINTER), Season.WINTER)
194
195 def test_enum_value(self):
196 Season = self.Season
197 self.assertEqual(Season.SPRING.value, 1)
198
199 def test_intenum_value(self):
200 self.assertEqual(IntStooges.CURLY.value, 2)
201
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700202 def test_enum(self):
203 Season = self.Season
204 lst = list(Season)
205 self.assertEqual(len(lst), len(Season))
206 self.assertEqual(len(Season), 4, Season)
207 self.assertEqual(
208 [Season.SPRING, Season.SUMMER, Season.AUTUMN, Season.WINTER], lst)
209
210 for i, season in enumerate('SPRING SUMMER AUTUMN WINTER'.split(), 1):
211 e = Season(i)
212 self.assertEqual(e, getattr(Season, season))
213 self.assertEqual(e.value, i)
214 self.assertNotEqual(e, i)
215 self.assertEqual(e.name, season)
216 self.assertIn(e, Season)
217 self.assertIs(type(e), Season)
218 self.assertIsInstance(e, Season)
219 self.assertEqual(str(e), 'Season.' + season)
220 self.assertEqual(
221 repr(e),
222 '<Season.{0}: {1}>'.format(season, i),
223 )
224
225 def test_value_name(self):
226 Season = self.Season
227 self.assertEqual(Season.SPRING.name, 'SPRING')
228 self.assertEqual(Season.SPRING.value, 1)
229 with self.assertRaises(AttributeError):
230 Season.SPRING.name = 'invierno'
231 with self.assertRaises(AttributeError):
232 Season.SPRING.value = 2
233
Ethan Furmanf203f2d2013-09-06 07:16:48 -0700234 def test_changing_member(self):
235 Season = self.Season
236 with self.assertRaises(AttributeError):
237 Season.WINTER = 'really cold'
238
Ethan Furman64a99722013-09-22 16:18:19 -0700239 def test_attribute_deletion(self):
240 class Season(Enum):
241 SPRING = 1
242 SUMMER = 2
243 AUTUMN = 3
244 WINTER = 4
245
246 def spam(cls):
247 pass
248
249 self.assertTrue(hasattr(Season, 'spam'))
250 del Season.spam
251 self.assertFalse(hasattr(Season, 'spam'))
252
253 with self.assertRaises(AttributeError):
254 del Season.SPRING
255 with self.assertRaises(AttributeError):
256 del Season.DRY
257 with self.assertRaises(AttributeError):
258 del Season.SPRING.name
259
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700260 def test_invalid_names(self):
261 with self.assertRaises(ValueError):
262 class Wrong(Enum):
263 mro = 9
264 with self.assertRaises(ValueError):
265 class Wrong(Enum):
266 _create_= 11
267 with self.assertRaises(ValueError):
268 class Wrong(Enum):
269 _get_mixins_ = 9
270 with self.assertRaises(ValueError):
271 class Wrong(Enum):
272 _find_new_ = 1
273 with self.assertRaises(ValueError):
274 class Wrong(Enum):
275 _any_name_ = 9
276
277 def test_contains(self):
278 Season = self.Season
279 self.assertIn(Season.AUTUMN, Season)
280 self.assertNotIn(3, Season)
281
282 val = Season(3)
283 self.assertIn(val, Season)
284
285 class OtherEnum(Enum):
286 one = 1; two = 2
287 self.assertNotIn(OtherEnum.two, Season)
288
289 def test_comparisons(self):
290 Season = self.Season
291 with self.assertRaises(TypeError):
292 Season.SPRING < Season.WINTER
293 with self.assertRaises(TypeError):
294 Season.SPRING > 4
295
296 self.assertNotEqual(Season.SPRING, 1)
297
298 class Part(Enum):
299 SPRING = 1
300 CLIP = 2
301 BARREL = 3
302
303 self.assertNotEqual(Season.SPRING, Part.SPRING)
304 with self.assertRaises(TypeError):
305 Season.SPRING < Part.CLIP
306
307 def test_enum_duplicates(self):
308 class Season(Enum):
309 SPRING = 1
310 SUMMER = 2
311 AUTUMN = FALL = 3
312 WINTER = 4
313 ANOTHER_SPRING = 1
314 lst = list(Season)
315 self.assertEqual(
316 lst,
317 [Season.SPRING, Season.SUMMER,
318 Season.AUTUMN, Season.WINTER,
319 ])
320 self.assertIs(Season.FALL, Season.AUTUMN)
321 self.assertEqual(Season.FALL.value, 3)
322 self.assertEqual(Season.AUTUMN.value, 3)
323 self.assertIs(Season(3), Season.AUTUMN)
324 self.assertIs(Season(1), Season.SPRING)
325 self.assertEqual(Season.FALL.name, 'AUTUMN')
326 self.assertEqual(
327 [k for k,v in Season.__members__.items() if v.name != k],
328 ['FALL', 'ANOTHER_SPRING'],
329 )
330
Ethan Furman101e0742013-09-15 12:34:36 -0700331 def test_duplicate_name(self):
332 with self.assertRaises(TypeError):
333 class Color(Enum):
334 red = 1
335 green = 2
336 blue = 3
337 red = 4
338
339 with self.assertRaises(TypeError):
340 class Color(Enum):
341 red = 1
342 green = 2
343 blue = 3
344 def red(self):
345 return 'red'
346
347 with self.assertRaises(TypeError):
348 class Color(Enum):
349 @property
350 def red(self):
351 return 'redder'
352 red = 1
353 green = 2
354 blue = 3
355
356
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700357 def test_enum_with_value_name(self):
358 class Huh(Enum):
359 name = 1
360 value = 2
361 self.assertEqual(
362 list(Huh),
363 [Huh.name, Huh.value],
364 )
365 self.assertIs(type(Huh.name), Huh)
366 self.assertEqual(Huh.name.name, 'name')
367 self.assertEqual(Huh.name.value, 1)
Ethan Furmanec15a822013-08-31 19:17:41 -0700368
369 def test_format_enum(self):
370 Season = self.Season
371 self.assertEqual('{}'.format(Season.SPRING),
372 '{}'.format(str(Season.SPRING)))
373 self.assertEqual( '{:}'.format(Season.SPRING),
374 '{:}'.format(str(Season.SPRING)))
375 self.assertEqual('{:20}'.format(Season.SPRING),
376 '{:20}'.format(str(Season.SPRING)))
377 self.assertEqual('{:^20}'.format(Season.SPRING),
378 '{:^20}'.format(str(Season.SPRING)))
379 self.assertEqual('{:>20}'.format(Season.SPRING),
380 '{:>20}'.format(str(Season.SPRING)))
381 self.assertEqual('{:<20}'.format(Season.SPRING),
382 '{:<20}'.format(str(Season.SPRING)))
383
384 def test_format_enum_custom(self):
385 class TestFloat(float, Enum):
386 one = 1.0
387 two = 2.0
388 def __format__(self, spec):
389 return 'TestFloat success!'
390 self.assertEqual('{}'.format(TestFloat.one), 'TestFloat success!')
391
392 def assertFormatIsValue(self, spec, member):
393 self.assertEqual(spec.format(member), spec.format(member.value))
394
395 def test_format_enum_date(self):
396 Holiday = self.Holiday
397 self.assertFormatIsValue('{}', Holiday.IDES_OF_MARCH)
398 self.assertFormatIsValue('{:}', Holiday.IDES_OF_MARCH)
399 self.assertFormatIsValue('{:20}', Holiday.IDES_OF_MARCH)
400 self.assertFormatIsValue('{:^20}', Holiday.IDES_OF_MARCH)
401 self.assertFormatIsValue('{:>20}', Holiday.IDES_OF_MARCH)
402 self.assertFormatIsValue('{:<20}', Holiday.IDES_OF_MARCH)
403 self.assertFormatIsValue('{:%Y %m}', Holiday.IDES_OF_MARCH)
404 self.assertFormatIsValue('{:%Y %m %M:00}', Holiday.IDES_OF_MARCH)
405
406 def test_format_enum_float(self):
407 Konstants = self.Konstants
408 self.assertFormatIsValue('{}', Konstants.TAU)
409 self.assertFormatIsValue('{:}', Konstants.TAU)
410 self.assertFormatIsValue('{:20}', Konstants.TAU)
411 self.assertFormatIsValue('{:^20}', Konstants.TAU)
412 self.assertFormatIsValue('{:>20}', Konstants.TAU)
413 self.assertFormatIsValue('{:<20}', Konstants.TAU)
414 self.assertFormatIsValue('{:n}', Konstants.TAU)
415 self.assertFormatIsValue('{:5.2}', Konstants.TAU)
416 self.assertFormatIsValue('{:f}', Konstants.TAU)
417
418 def test_format_enum_int(self):
419 Grades = self.Grades
420 self.assertFormatIsValue('{}', Grades.C)
421 self.assertFormatIsValue('{:}', Grades.C)
422 self.assertFormatIsValue('{:20}', Grades.C)
423 self.assertFormatIsValue('{:^20}', Grades.C)
424 self.assertFormatIsValue('{:>20}', Grades.C)
425 self.assertFormatIsValue('{:<20}', Grades.C)
426 self.assertFormatIsValue('{:+}', Grades.C)
427 self.assertFormatIsValue('{:08X}', Grades.C)
428 self.assertFormatIsValue('{:b}', Grades.C)
429
430 def test_format_enum_str(self):
431 Directional = self.Directional
432 self.assertFormatIsValue('{}', Directional.WEST)
433 self.assertFormatIsValue('{:}', Directional.WEST)
434 self.assertFormatIsValue('{:20}', Directional.WEST)
435 self.assertFormatIsValue('{:^20}', Directional.WEST)
436 self.assertFormatIsValue('{:>20}', Directional.WEST)
437 self.assertFormatIsValue('{:<20}', Directional.WEST)
438
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700439 def test_hash(self):
440 Season = self.Season
441 dates = {}
442 dates[Season.WINTER] = '1225'
443 dates[Season.SPRING] = '0315'
444 dates[Season.SUMMER] = '0704'
445 dates[Season.AUTUMN] = '1031'
446 self.assertEqual(dates[Season.AUTUMN], '1031')
447
448 def test_intenum_from_scratch(self):
449 class phy(int, Enum):
450 pi = 3
451 tau = 2 * pi
452 self.assertTrue(phy.pi < phy.tau)
453
454 def test_intenum_inherited(self):
455 class IntEnum(int, Enum):
456 pass
457 class phy(IntEnum):
458 pi = 3
459 tau = 2 * pi
460 self.assertTrue(phy.pi < phy.tau)
461
462 def test_floatenum_from_scratch(self):
463 class phy(float, Enum):
Ethan Furmanec15a822013-08-31 19:17:41 -0700464 pi = 3.1415926
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700465 tau = 2 * pi
466 self.assertTrue(phy.pi < phy.tau)
467
468 def test_floatenum_inherited(self):
469 class FloatEnum(float, Enum):
470 pass
471 class phy(FloatEnum):
Ethan Furmanec15a822013-08-31 19:17:41 -0700472 pi = 3.1415926
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700473 tau = 2 * pi
474 self.assertTrue(phy.pi < phy.tau)
475
476 def test_strenum_from_scratch(self):
477 class phy(str, Enum):
478 pi = 'Pi'
479 tau = 'Tau'
480 self.assertTrue(phy.pi < phy.tau)
481
482 def test_strenum_inherited(self):
483 class StrEnum(str, Enum):
484 pass
485 class phy(StrEnum):
486 pi = 'Pi'
487 tau = 'Tau'
488 self.assertTrue(phy.pi < phy.tau)
489
490
491 def test_intenum(self):
492 class WeekDay(IntEnum):
493 SUNDAY = 1
494 MONDAY = 2
495 TUESDAY = 3
496 WEDNESDAY = 4
497 THURSDAY = 5
498 FRIDAY = 6
499 SATURDAY = 7
500
501 self.assertEqual(['a', 'b', 'c'][WeekDay.MONDAY], 'c')
502 self.assertEqual([i for i in range(WeekDay.TUESDAY)], [0, 1, 2])
503
504 lst = list(WeekDay)
505 self.assertEqual(len(lst), len(WeekDay))
506 self.assertEqual(len(WeekDay), 7)
507 target = 'SUNDAY MONDAY TUESDAY WEDNESDAY THURSDAY FRIDAY SATURDAY'
508 target = target.split()
509 for i, weekday in enumerate(target, 1):
510 e = WeekDay(i)
511 self.assertEqual(e, i)
512 self.assertEqual(int(e), i)
513 self.assertEqual(e.name, weekday)
514 self.assertIn(e, WeekDay)
515 self.assertEqual(lst.index(e)+1, i)
516 self.assertTrue(0 < e < 8)
517 self.assertIs(type(e), WeekDay)
518 self.assertIsInstance(e, int)
519 self.assertIsInstance(e, Enum)
520
521 def test_intenum_duplicates(self):
522 class WeekDay(IntEnum):
523 SUNDAY = 1
524 MONDAY = 2
525 TUESDAY = TEUSDAY = 3
526 WEDNESDAY = 4
527 THURSDAY = 5
528 FRIDAY = 6
529 SATURDAY = 7
530 self.assertIs(WeekDay.TEUSDAY, WeekDay.TUESDAY)
531 self.assertEqual(WeekDay(3).name, 'TUESDAY')
532 self.assertEqual([k for k,v in WeekDay.__members__.items()
533 if v.name != k], ['TEUSDAY', ])
534
535 def test_pickle_enum(self):
536 if isinstance(Stooges, Exception):
537 raise Stooges
Ethan Furman2ddb39a2014-02-06 17:28:50 -0800538 test_pickle_dump_load(self.assertIs, Stooges.CURLY)
539 test_pickle_dump_load(self.assertIs, Stooges)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700540
541 def test_pickle_int(self):
542 if isinstance(IntStooges, Exception):
543 raise IntStooges
Ethan Furman2ddb39a2014-02-06 17:28:50 -0800544 test_pickle_dump_load(self.assertIs, IntStooges.CURLY)
545 test_pickle_dump_load(self.assertIs, IntStooges)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700546
547 def test_pickle_float(self):
548 if isinstance(FloatStooges, Exception):
549 raise FloatStooges
Ethan Furman2ddb39a2014-02-06 17:28:50 -0800550 test_pickle_dump_load(self.assertIs, FloatStooges.CURLY)
551 test_pickle_dump_load(self.assertIs, FloatStooges)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700552
553 def test_pickle_enum_function(self):
554 if isinstance(Answer, Exception):
555 raise Answer
Ethan Furman2ddb39a2014-02-06 17:28:50 -0800556 test_pickle_dump_load(self.assertIs, Answer.him)
557 test_pickle_dump_load(self.assertIs, Answer)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700558
559 def test_pickle_enum_function_with_module(self):
560 if isinstance(Question, Exception):
561 raise Question
Ethan Furman2ddb39a2014-02-06 17:28:50 -0800562 test_pickle_dump_load(self.assertIs, Question.who)
563 test_pickle_dump_load(self.assertIs, Question)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700564
Ethan Furmanca1b7942014-02-08 11:36:27 -0800565 def test_enum_function_with_qualname(self):
566 if isinstance(Theory, Exception):
567 raise Theory
568 self.assertEqual(Theory.__qualname__, 'spanish_inquisition')
569
570 def test_class_nested_enum_and_pickle_protocol_four(self):
571 # would normally just have this directly in the class namespace
572 class NestedEnum(Enum):
573 twigs = 'common'
574 shiny = 'rare'
575
576 self.__class__.NestedEnum = NestedEnum
577 self.NestedEnum.__qualname__ = '%s.NestedEnum' % self.__class__.__name__
578 test_pickle_exception(
579 self.assertRaises, PicklingError, self.NestedEnum.twigs,
580 protocol=(0, 3))
581 test_pickle_dump_load(self.assertIs, self.NestedEnum.twigs,
582 protocol=(4, HIGHEST_PROTOCOL))
583
Ethan Furman482fe042015-03-18 18:19:30 -0700584 def test_pickle_by_name(self):
585 class ReplaceGlobalInt(IntEnum):
586 ONE = 1
587 TWO = 2
588 ReplaceGlobalInt.__reduce_ex__ = enum._reduce_ex_by_name
589 for proto in range(HIGHEST_PROTOCOL):
590 self.assertEqual(ReplaceGlobalInt.TWO.__reduce_ex__(proto), 'TWO')
591
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700592 def test_exploding_pickle(self):
Ethan Furmanca1b7942014-02-08 11:36:27 -0800593 BadPickle = Enum(
594 'BadPickle', 'dill sweet bread-n-butter', module=__name__)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700595 globals()['BadPickle'] = BadPickle
Ethan Furmanca1b7942014-02-08 11:36:27 -0800596 # now break BadPickle to test exception raising
597 enum._make_class_unpicklable(BadPickle)
Ethan Furman2ddb39a2014-02-06 17:28:50 -0800598 test_pickle_exception(self.assertRaises, TypeError, BadPickle.dill)
599 test_pickle_exception(self.assertRaises, PicklingError, BadPickle)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700600
601 def test_string_enum(self):
602 class SkillLevel(str, Enum):
603 master = 'what is the sound of one hand clapping?'
604 journeyman = 'why did the chicken cross the road?'
605 apprentice = 'knock, knock!'
606 self.assertEqual(SkillLevel.apprentice, 'knock, knock!')
607
608 def test_getattr_getitem(self):
609 class Period(Enum):
610 morning = 1
611 noon = 2
612 evening = 3
613 night = 4
614 self.assertIs(Period(2), Period.noon)
615 self.assertIs(getattr(Period, 'night'), Period.night)
616 self.assertIs(Period['morning'], Period.morning)
617
618 def test_getattr_dunder(self):
619 Season = self.Season
620 self.assertTrue(getattr(Season, '__eq__'))
621
622 def test_iteration_order(self):
623 class Season(Enum):
624 SUMMER = 2
625 WINTER = 4
626 AUTUMN = 3
627 SPRING = 1
628 self.assertEqual(
629 list(Season),
630 [Season.SUMMER, Season.WINTER, Season.AUTUMN, Season.SPRING],
631 )
632
Ethan Furman2131a4a2013-09-14 18:11:24 -0700633 def test_reversed_iteration_order(self):
634 self.assertEqual(
635 list(reversed(self.Season)),
636 [self.Season.WINTER, self.Season.AUTUMN, self.Season.SUMMER,
637 self.Season.SPRING]
638 )
639
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700640 def test_programatic_function_string(self):
641 SummerMonth = Enum('SummerMonth', 'june july august')
642 lst = list(SummerMonth)
643 self.assertEqual(len(lst), len(SummerMonth))
644 self.assertEqual(len(SummerMonth), 3, SummerMonth)
645 self.assertEqual(
646 [SummerMonth.june, SummerMonth.july, SummerMonth.august],
647 lst,
648 )
649 for i, month in enumerate('june july august'.split(), 1):
650 e = SummerMonth(i)
651 self.assertEqual(int(e.value), i)
652 self.assertNotEqual(e, i)
653 self.assertEqual(e.name, month)
654 self.assertIn(e, SummerMonth)
655 self.assertIs(type(e), SummerMonth)
656
657 def test_programatic_function_string_list(self):
658 SummerMonth = Enum('SummerMonth', ['june', 'july', 'august'])
659 lst = list(SummerMonth)
660 self.assertEqual(len(lst), len(SummerMonth))
661 self.assertEqual(len(SummerMonth), 3, SummerMonth)
662 self.assertEqual(
663 [SummerMonth.june, SummerMonth.july, SummerMonth.august],
664 lst,
665 )
666 for i, month in enumerate('june july august'.split(), 1):
667 e = SummerMonth(i)
668 self.assertEqual(int(e.value), i)
669 self.assertNotEqual(e, i)
670 self.assertEqual(e.name, month)
671 self.assertIn(e, SummerMonth)
672 self.assertIs(type(e), SummerMonth)
673
674 def test_programatic_function_iterable(self):
675 SummerMonth = Enum(
676 'SummerMonth',
677 (('june', 1), ('july', 2), ('august', 3))
678 )
679 lst = list(SummerMonth)
680 self.assertEqual(len(lst), len(SummerMonth))
681 self.assertEqual(len(SummerMonth), 3, SummerMonth)
682 self.assertEqual(
683 [SummerMonth.june, SummerMonth.july, SummerMonth.august],
684 lst,
685 )
686 for i, month in enumerate('june july august'.split(), 1):
687 e = SummerMonth(i)
688 self.assertEqual(int(e.value), i)
689 self.assertNotEqual(e, i)
690 self.assertEqual(e.name, month)
691 self.assertIn(e, SummerMonth)
692 self.assertIs(type(e), SummerMonth)
693
694 def test_programatic_function_from_dict(self):
695 SummerMonth = Enum(
696 'SummerMonth',
697 OrderedDict((('june', 1), ('july', 2), ('august', 3)))
698 )
699 lst = list(SummerMonth)
700 self.assertEqual(len(lst), len(SummerMonth))
701 self.assertEqual(len(SummerMonth), 3, SummerMonth)
702 self.assertEqual(
703 [SummerMonth.june, SummerMonth.july, SummerMonth.august],
704 lst,
705 )
706 for i, month in enumerate('june july august'.split(), 1):
707 e = SummerMonth(i)
708 self.assertEqual(int(e.value), i)
709 self.assertNotEqual(e, i)
710 self.assertEqual(e.name, month)
711 self.assertIn(e, SummerMonth)
712 self.assertIs(type(e), SummerMonth)
713
714 def test_programatic_function_type(self):
715 SummerMonth = Enum('SummerMonth', 'june july august', type=int)
716 lst = list(SummerMonth)
717 self.assertEqual(len(lst), len(SummerMonth))
718 self.assertEqual(len(SummerMonth), 3, SummerMonth)
719 self.assertEqual(
720 [SummerMonth.june, SummerMonth.july, SummerMonth.august],
721 lst,
722 )
723 for i, month in enumerate('june july august'.split(), 1):
724 e = SummerMonth(i)
725 self.assertEqual(e, i)
726 self.assertEqual(e.name, month)
727 self.assertIn(e, SummerMonth)
728 self.assertIs(type(e), SummerMonth)
729
730 def test_programatic_function_type_from_subclass(self):
731 SummerMonth = IntEnum('SummerMonth', 'june july august')
732 lst = list(SummerMonth)
733 self.assertEqual(len(lst), len(SummerMonth))
734 self.assertEqual(len(SummerMonth), 3, SummerMonth)
735 self.assertEqual(
736 [SummerMonth.june, SummerMonth.july, SummerMonth.august],
737 lst,
738 )
739 for i, month in enumerate('june july august'.split(), 1):
740 e = SummerMonth(i)
741 self.assertEqual(e, i)
742 self.assertEqual(e.name, month)
743 self.assertIn(e, SummerMonth)
744 self.assertIs(type(e), SummerMonth)
745
746 def test_subclassing(self):
747 if isinstance(Name, Exception):
748 raise Name
749 self.assertEqual(Name.BDFL, 'Guido van Rossum')
750 self.assertTrue(Name.BDFL, Name('Guido van Rossum'))
751 self.assertIs(Name.BDFL, getattr(Name, 'BDFL'))
Ethan Furman2ddb39a2014-02-06 17:28:50 -0800752 test_pickle_dump_load(self.assertIs, Name.BDFL)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700753
754 def test_extending(self):
755 class Color(Enum):
756 red = 1
757 green = 2
758 blue = 3
759 with self.assertRaises(TypeError):
760 class MoreColor(Color):
761 cyan = 4
762 magenta = 5
763 yellow = 6
764
765 def test_exclude_methods(self):
766 class whatever(Enum):
767 this = 'that'
768 these = 'those'
769 def really(self):
770 return 'no, not %s' % self.value
771 self.assertIsNot(type(whatever.really), whatever)
772 self.assertEqual(whatever.this.really(), 'no, not that')
773
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700774 def test_wrong_inheritance_order(self):
775 with self.assertRaises(TypeError):
776 class Wrong(Enum, str):
777 NotHere = 'error before this point'
778
779 def test_intenum_transitivity(self):
780 class number(IntEnum):
781 one = 1
782 two = 2
783 three = 3
784 class numero(IntEnum):
785 uno = 1
786 dos = 2
787 tres = 3
788 self.assertEqual(number.one, numero.uno)
789 self.assertEqual(number.two, numero.dos)
790 self.assertEqual(number.three, numero.tres)
791
792 def test_wrong_enum_in_call(self):
793 class Monochrome(Enum):
794 black = 0
795 white = 1
796 class Gender(Enum):
797 male = 0
798 female = 1
799 self.assertRaises(ValueError, Monochrome, Gender.male)
800
801 def test_wrong_enum_in_mixed_call(self):
802 class Monochrome(IntEnum):
803 black = 0
804 white = 1
805 class Gender(Enum):
806 male = 0
807 female = 1
808 self.assertRaises(ValueError, Monochrome, Gender.male)
809
810 def test_mixed_enum_in_call_1(self):
811 class Monochrome(IntEnum):
812 black = 0
813 white = 1
814 class Gender(IntEnum):
815 male = 0
816 female = 1
817 self.assertIs(Monochrome(Gender.female), Monochrome.white)
818
819 def test_mixed_enum_in_call_2(self):
820 class Monochrome(Enum):
821 black = 0
822 white = 1
823 class Gender(IntEnum):
824 male = 0
825 female = 1
826 self.assertIs(Monochrome(Gender.male), Monochrome.black)
827
828 def test_flufl_enum(self):
829 class Fluflnum(Enum):
830 def __int__(self):
831 return int(self.value)
832 class MailManOptions(Fluflnum):
833 option1 = 1
834 option2 = 2
835 option3 = 3
836 self.assertEqual(int(MailManOptions.option1), 1)
837
Ethan Furman5e5a8232013-08-04 08:42:23 -0700838 def test_introspection(self):
839 class Number(IntEnum):
840 one = 100
841 two = 200
842 self.assertIs(Number.one._member_type_, int)
843 self.assertIs(Number._member_type_, int)
844 class String(str, Enum):
845 yarn = 'soft'
846 rope = 'rough'
847 wire = 'hard'
848 self.assertIs(String.yarn._member_type_, str)
849 self.assertIs(String._member_type_, str)
850 class Plain(Enum):
851 vanilla = 'white'
852 one = 1
853 self.assertIs(Plain.vanilla._member_type_, object)
854 self.assertIs(Plain._member_type_, object)
855
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700856 def test_no_such_enum_member(self):
857 class Color(Enum):
858 red = 1
859 green = 2
860 blue = 3
861 with self.assertRaises(ValueError):
862 Color(4)
863 with self.assertRaises(KeyError):
864 Color['chartreuse']
865
866 def test_new_repr(self):
867 class Color(Enum):
868 red = 1
869 green = 2
870 blue = 3
871 def __repr__(self):
872 return "don't you just love shades of %s?" % self.name
873 self.assertEqual(
874 repr(Color.blue),
875 "don't you just love shades of blue?",
876 )
877
878 def test_inherited_repr(self):
879 class MyEnum(Enum):
880 def __repr__(self):
881 return "My name is %s." % self.name
882 class MyIntEnum(int, MyEnum):
883 this = 1
884 that = 2
885 theother = 3
886 self.assertEqual(repr(MyIntEnum.that), "My name is that.")
887
888 def test_multiple_mixin_mro(self):
889 class auto_enum(type(Enum)):
890 def __new__(metacls, cls, bases, classdict):
891 temp = type(classdict)()
892 names = set(classdict._member_names)
893 i = 0
894 for k in classdict._member_names:
895 v = classdict[k]
896 if v is Ellipsis:
897 v = i
898 else:
899 i = v
900 i += 1
901 temp[k] = v
902 for k, v in classdict.items():
903 if k not in names:
904 temp[k] = v
905 return super(auto_enum, metacls).__new__(
906 metacls, cls, bases, temp)
907
908 class AutoNumberedEnum(Enum, metaclass=auto_enum):
909 pass
910
911 class AutoIntEnum(IntEnum, metaclass=auto_enum):
912 pass
913
914 class TestAutoNumber(AutoNumberedEnum):
915 a = ...
916 b = 3
917 c = ...
918
919 class TestAutoInt(AutoIntEnum):
920 a = ...
921 b = 3
922 c = ...
923
924 def test_subclasses_with_getnewargs(self):
925 class NamedInt(int):
Ethan Furman2ddb39a2014-02-06 17:28:50 -0800926 __qualname__ = 'NamedInt' # needed for pickle protocol 4
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700927 def __new__(cls, *args):
928 _args = args
929 name, *args = args
930 if len(args) == 0:
931 raise TypeError("name and value must be specified")
932 self = int.__new__(cls, *args)
933 self._intname = name
934 self._args = _args
935 return self
936 def __getnewargs__(self):
937 return self._args
938 @property
939 def __name__(self):
940 return self._intname
941 def __repr__(self):
942 # repr() is updated to include the name and type info
943 return "{}({!r}, {})".format(type(self).__name__,
944 self.__name__,
945 int.__repr__(self))
946 def __str__(self):
947 # str() is unchanged, even if it relies on the repr() fallback
948 base = int
949 base_str = base.__str__
950 if base_str.__objclass__ is object:
951 return base.__repr__(self)
952 return base_str(self)
953 # for simplicity, we only define one operator that
954 # propagates expressions
955 def __add__(self, other):
956 temp = int(self) + int( other)
957 if isinstance(self, NamedInt) and isinstance(other, NamedInt):
958 return NamedInt(
959 '({0} + {1})'.format(self.__name__, other.__name__),
960 temp )
961 else:
962 return temp
963
964 class NEI(NamedInt, Enum):
Ethan Furman2ddb39a2014-02-06 17:28:50 -0800965 __qualname__ = 'NEI' # needed for pickle protocol 4
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700966 x = ('the-x', 1)
967 y = ('the-y', 2)
968
Ethan Furman2aa27322013-07-19 19:35:56 -0700969
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700970 self.assertIs(NEI.__new__, Enum.__new__)
971 self.assertEqual(repr(NEI.x + NEI.y), "NamedInt('(the-x + the-y)', 3)")
972 globals()['NamedInt'] = NamedInt
973 globals()['NEI'] = NEI
974 NI5 = NamedInt('test', 5)
975 self.assertEqual(NI5, 5)
Ethan Furman2ddb39a2014-02-06 17:28:50 -0800976 test_pickle_dump_load(self.assertEqual, NI5, 5)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700977 self.assertEqual(NEI.y.value, 2)
Ethan Furman2ddb39a2014-02-06 17:28:50 -0800978 test_pickle_dump_load(self.assertIs, NEI.y)
Ethan Furmandc870522014-02-18 12:37:12 -0800979 test_pickle_dump_load(self.assertIs, NEI)
Ethan Furman6b3d64a2013-06-14 16:55:46 -0700980
Ethan Furmanca1b7942014-02-08 11:36:27 -0800981 def test_subclasses_with_getnewargs_ex(self):
982 class NamedInt(int):
983 __qualname__ = 'NamedInt' # needed for pickle protocol 4
984 def __new__(cls, *args):
985 _args = args
986 name, *args = args
987 if len(args) == 0:
988 raise TypeError("name and value must be specified")
989 self = int.__new__(cls, *args)
990 self._intname = name
991 self._args = _args
992 return self
993 def __getnewargs_ex__(self):
994 return self._args, {}
995 @property
996 def __name__(self):
997 return self._intname
998 def __repr__(self):
999 # repr() is updated to include the name and type info
1000 return "{}({!r}, {})".format(type(self).__name__,
1001 self.__name__,
1002 int.__repr__(self))
1003 def __str__(self):
1004 # str() is unchanged, even if it relies on the repr() fallback
1005 base = int
1006 base_str = base.__str__
1007 if base_str.__objclass__ is object:
1008 return base.__repr__(self)
1009 return base_str(self)
1010 # for simplicity, we only define one operator that
1011 # propagates expressions
1012 def __add__(self, other):
1013 temp = int(self) + int( other)
1014 if isinstance(self, NamedInt) and isinstance(other, NamedInt):
1015 return NamedInt(
1016 '({0} + {1})'.format(self.__name__, other.__name__),
1017 temp )
1018 else:
1019 return temp
1020
1021 class NEI(NamedInt, Enum):
1022 __qualname__ = 'NEI' # needed for pickle protocol 4
1023 x = ('the-x', 1)
1024 y = ('the-y', 2)
1025
1026
1027 self.assertIs(NEI.__new__, Enum.__new__)
1028 self.assertEqual(repr(NEI.x + NEI.y), "NamedInt('(the-x + the-y)', 3)")
1029 globals()['NamedInt'] = NamedInt
1030 globals()['NEI'] = NEI
1031 NI5 = NamedInt('test', 5)
1032 self.assertEqual(NI5, 5)
1033 test_pickle_dump_load(self.assertEqual, NI5, 5, protocol=(4, 4))
1034 self.assertEqual(NEI.y.value, 2)
1035 test_pickle_dump_load(self.assertIs, NEI.y, protocol=(4, 4))
Ethan Furmandc870522014-02-18 12:37:12 -08001036 test_pickle_dump_load(self.assertIs, NEI)
Ethan Furmanca1b7942014-02-08 11:36:27 -08001037
1038 def test_subclasses_with_reduce(self):
1039 class NamedInt(int):
1040 __qualname__ = 'NamedInt' # needed for pickle protocol 4
1041 def __new__(cls, *args):
1042 _args = args
1043 name, *args = args
1044 if len(args) == 0:
1045 raise TypeError("name and value must be specified")
1046 self = int.__new__(cls, *args)
1047 self._intname = name
1048 self._args = _args
1049 return self
1050 def __reduce__(self):
1051 return self.__class__, self._args
1052 @property
1053 def __name__(self):
1054 return self._intname
1055 def __repr__(self):
1056 # repr() is updated to include the name and type info
1057 return "{}({!r}, {})".format(type(self).__name__,
1058 self.__name__,
1059 int.__repr__(self))
1060 def __str__(self):
1061 # str() is unchanged, even if it relies on the repr() fallback
1062 base = int
1063 base_str = base.__str__
1064 if base_str.__objclass__ is object:
1065 return base.__repr__(self)
1066 return base_str(self)
1067 # for simplicity, we only define one operator that
1068 # propagates expressions
1069 def __add__(self, other):
1070 temp = int(self) + int( other)
1071 if isinstance(self, NamedInt) and isinstance(other, NamedInt):
1072 return NamedInt(
1073 '({0} + {1})'.format(self.__name__, other.__name__),
1074 temp )
1075 else:
1076 return temp
1077
1078 class NEI(NamedInt, Enum):
1079 __qualname__ = 'NEI' # needed for pickle protocol 4
1080 x = ('the-x', 1)
1081 y = ('the-y', 2)
1082
1083
1084 self.assertIs(NEI.__new__, Enum.__new__)
1085 self.assertEqual(repr(NEI.x + NEI.y), "NamedInt('(the-x + the-y)', 3)")
1086 globals()['NamedInt'] = NamedInt
1087 globals()['NEI'] = NEI
1088 NI5 = NamedInt('test', 5)
1089 self.assertEqual(NI5, 5)
1090 test_pickle_dump_load(self.assertEqual, NI5, 5)
1091 self.assertEqual(NEI.y.value, 2)
1092 test_pickle_dump_load(self.assertIs, NEI.y)
Ethan Furmandc870522014-02-18 12:37:12 -08001093 test_pickle_dump_load(self.assertIs, NEI)
Ethan Furmanca1b7942014-02-08 11:36:27 -08001094
1095 def test_subclasses_with_reduce_ex(self):
1096 class NamedInt(int):
1097 __qualname__ = 'NamedInt' # needed for pickle protocol 4
1098 def __new__(cls, *args):
1099 _args = args
1100 name, *args = args
1101 if len(args) == 0:
1102 raise TypeError("name and value must be specified")
1103 self = int.__new__(cls, *args)
1104 self._intname = name
1105 self._args = _args
1106 return self
1107 def __reduce_ex__(self, proto):
1108 return self.__class__, self._args
1109 @property
1110 def __name__(self):
1111 return self._intname
1112 def __repr__(self):
1113 # repr() is updated to include the name and type info
1114 return "{}({!r}, {})".format(type(self).__name__,
1115 self.__name__,
1116 int.__repr__(self))
1117 def __str__(self):
1118 # str() is unchanged, even if it relies on the repr() fallback
1119 base = int
1120 base_str = base.__str__
1121 if base_str.__objclass__ is object:
1122 return base.__repr__(self)
1123 return base_str(self)
1124 # for simplicity, we only define one operator that
1125 # propagates expressions
1126 def __add__(self, other):
1127 temp = int(self) + int( other)
1128 if isinstance(self, NamedInt) and isinstance(other, NamedInt):
1129 return NamedInt(
1130 '({0} + {1})'.format(self.__name__, other.__name__),
1131 temp )
1132 else:
1133 return temp
1134
1135 class NEI(NamedInt, Enum):
1136 __qualname__ = 'NEI' # needed for pickle protocol 4
1137 x = ('the-x', 1)
1138 y = ('the-y', 2)
1139
1140
1141 self.assertIs(NEI.__new__, Enum.__new__)
1142 self.assertEqual(repr(NEI.x + NEI.y), "NamedInt('(the-x + the-y)', 3)")
1143 globals()['NamedInt'] = NamedInt
1144 globals()['NEI'] = NEI
1145 NI5 = NamedInt('test', 5)
1146 self.assertEqual(NI5, 5)
1147 test_pickle_dump_load(self.assertEqual, NI5, 5)
1148 self.assertEqual(NEI.y.value, 2)
1149 test_pickle_dump_load(self.assertIs, NEI.y)
Ethan Furmandc870522014-02-18 12:37:12 -08001150 test_pickle_dump_load(self.assertIs, NEI)
Ethan Furmanca1b7942014-02-08 11:36:27 -08001151
Ethan Furmandc870522014-02-18 12:37:12 -08001152 def test_subclasses_without_direct_pickle_support(self):
Ethan Furman6b3d64a2013-06-14 16:55:46 -07001153 class NamedInt(int):
Ethan Furman2ddb39a2014-02-06 17:28:50 -08001154 __qualname__ = 'NamedInt'
Ethan Furman6b3d64a2013-06-14 16:55:46 -07001155 def __new__(cls, *args):
1156 _args = args
1157 name, *args = args
1158 if len(args) == 0:
1159 raise TypeError("name and value must be specified")
1160 self = int.__new__(cls, *args)
1161 self._intname = name
1162 self._args = _args
1163 return self
1164 @property
1165 def __name__(self):
1166 return self._intname
1167 def __repr__(self):
1168 # repr() is updated to include the name and type info
1169 return "{}({!r}, {})".format(type(self).__name__,
1170 self.__name__,
1171 int.__repr__(self))
1172 def __str__(self):
1173 # str() is unchanged, even if it relies on the repr() fallback
1174 base = int
1175 base_str = base.__str__
1176 if base_str.__objclass__ is object:
1177 return base.__repr__(self)
1178 return base_str(self)
1179 # for simplicity, we only define one operator that
1180 # propagates expressions
1181 def __add__(self, other):
1182 temp = int(self) + int( other)
1183 if isinstance(self, NamedInt) and isinstance(other, NamedInt):
1184 return NamedInt(
1185 '({0} + {1})'.format(self.__name__, other.__name__),
1186 temp )
1187 else:
1188 return temp
1189
1190 class NEI(NamedInt, Enum):
Ethan Furman2ddb39a2014-02-06 17:28:50 -08001191 __qualname__ = 'NEI'
Ethan Furman6b3d64a2013-06-14 16:55:46 -07001192 x = ('the-x', 1)
1193 y = ('the-y', 2)
1194
1195 self.assertIs(NEI.__new__, Enum.__new__)
1196 self.assertEqual(repr(NEI.x + NEI.y), "NamedInt('(the-x + the-y)', 3)")
1197 globals()['NamedInt'] = NamedInt
1198 globals()['NEI'] = NEI
1199 NI5 = NamedInt('test', 5)
1200 self.assertEqual(NI5, 5)
1201 self.assertEqual(NEI.y.value, 2)
Ethan Furman2ddb39a2014-02-06 17:28:50 -08001202 test_pickle_exception(self.assertRaises, TypeError, NEI.x)
1203 test_pickle_exception(self.assertRaises, PicklingError, NEI)
Ethan Furman6b3d64a2013-06-14 16:55:46 -07001204
Ethan Furmandc870522014-02-18 12:37:12 -08001205 def test_subclasses_without_direct_pickle_support_using_name(self):
1206 class NamedInt(int):
1207 __qualname__ = 'NamedInt'
1208 def __new__(cls, *args):
1209 _args = args
1210 name, *args = args
1211 if len(args) == 0:
1212 raise TypeError("name and value must be specified")
1213 self = int.__new__(cls, *args)
1214 self._intname = name
1215 self._args = _args
1216 return self
1217 @property
1218 def __name__(self):
1219 return self._intname
1220 def __repr__(self):
1221 # repr() is updated to include the name and type info
1222 return "{}({!r}, {})".format(type(self).__name__,
1223 self.__name__,
1224 int.__repr__(self))
1225 def __str__(self):
1226 # str() is unchanged, even if it relies on the repr() fallback
1227 base = int
1228 base_str = base.__str__
1229 if base_str.__objclass__ is object:
1230 return base.__repr__(self)
1231 return base_str(self)
1232 # for simplicity, we only define one operator that
1233 # propagates expressions
1234 def __add__(self, other):
1235 temp = int(self) + int( other)
1236 if isinstance(self, NamedInt) and isinstance(other, NamedInt):
1237 return NamedInt(
1238 '({0} + {1})'.format(self.__name__, other.__name__),
1239 temp )
1240 else:
1241 return temp
1242
1243 class NEI(NamedInt, Enum):
1244 __qualname__ = 'NEI'
1245 x = ('the-x', 1)
1246 y = ('the-y', 2)
1247 def __reduce_ex__(self, proto):
1248 return getattr, (self.__class__, self._name_)
1249
1250 self.assertIs(NEI.__new__, Enum.__new__)
1251 self.assertEqual(repr(NEI.x + NEI.y), "NamedInt('(the-x + the-y)', 3)")
1252 globals()['NamedInt'] = NamedInt
1253 globals()['NEI'] = NEI
1254 NI5 = NamedInt('test', 5)
1255 self.assertEqual(NI5, 5)
1256 self.assertEqual(NEI.y.value, 2)
1257 test_pickle_dump_load(self.assertIs, NEI.y)
1258 test_pickle_dump_load(self.assertIs, NEI)
1259
Ethan Furman6b3d64a2013-06-14 16:55:46 -07001260 def test_tuple_subclass(self):
1261 class SomeTuple(tuple, Enum):
Ethan Furman2ddb39a2014-02-06 17:28:50 -08001262 __qualname__ = 'SomeTuple' # needed for pickle protocol 4
Ethan Furman6b3d64a2013-06-14 16:55:46 -07001263 first = (1, 'for the money')
1264 second = (2, 'for the show')
1265 third = (3, 'for the music')
1266 self.assertIs(type(SomeTuple.first), SomeTuple)
1267 self.assertIsInstance(SomeTuple.second, tuple)
1268 self.assertEqual(SomeTuple.third, (3, 'for the music'))
1269 globals()['SomeTuple'] = SomeTuple
Ethan Furman2ddb39a2014-02-06 17:28:50 -08001270 test_pickle_dump_load(self.assertIs, SomeTuple.first)
Ethan Furman6b3d64a2013-06-14 16:55:46 -07001271
1272 def test_duplicate_values_give_unique_enum_items(self):
1273 class AutoNumber(Enum):
1274 first = ()
1275 second = ()
1276 third = ()
1277 def __new__(cls):
1278 value = len(cls.__members__) + 1
1279 obj = object.__new__(cls)
Ethan Furman520ad572013-07-19 19:47:21 -07001280 obj._value_ = value
Ethan Furman6b3d64a2013-06-14 16:55:46 -07001281 return obj
1282 def __int__(self):
Ethan Furman520ad572013-07-19 19:47:21 -07001283 return int(self._value_)
Ethan Furman6b3d64a2013-06-14 16:55:46 -07001284 self.assertEqual(
1285 list(AutoNumber),
1286 [AutoNumber.first, AutoNumber.second, AutoNumber.third],
1287 )
1288 self.assertEqual(int(AutoNumber.second), 2)
Ethan Furman2aa27322013-07-19 19:35:56 -07001289 self.assertEqual(AutoNumber.third.value, 3)
Ethan Furman6b3d64a2013-06-14 16:55:46 -07001290 self.assertIs(AutoNumber(1), AutoNumber.first)
1291
1292 def test_inherited_new_from_enhanced_enum(self):
1293 class AutoNumber(Enum):
1294 def __new__(cls):
1295 value = len(cls.__members__) + 1
1296 obj = object.__new__(cls)
Ethan Furman520ad572013-07-19 19:47:21 -07001297 obj._value_ = value
Ethan Furman6b3d64a2013-06-14 16:55:46 -07001298 return obj
1299 def __int__(self):
Ethan Furman520ad572013-07-19 19:47:21 -07001300 return int(self._value_)
Ethan Furman6b3d64a2013-06-14 16:55:46 -07001301 class Color(AutoNumber):
1302 red = ()
1303 green = ()
1304 blue = ()
1305 self.assertEqual(list(Color), [Color.red, Color.green, Color.blue])
1306 self.assertEqual(list(map(int, Color)), [1, 2, 3])
1307
1308 def test_inherited_new_from_mixed_enum(self):
1309 class AutoNumber(IntEnum):
1310 def __new__(cls):
1311 value = len(cls.__members__) + 1
1312 obj = int.__new__(cls, value)
Ethan Furman520ad572013-07-19 19:47:21 -07001313 obj._value_ = value
Ethan Furman6b3d64a2013-06-14 16:55:46 -07001314 return obj
1315 class Color(AutoNumber):
1316 red = ()
1317 green = ()
1318 blue = ()
1319 self.assertEqual(list(Color), [Color.red, Color.green, Color.blue])
1320 self.assertEqual(list(map(int, Color)), [1, 2, 3])
1321
Ethan Furmanbe3c2fe2013-11-13 14:25:45 -08001322 def test_equality(self):
1323 class AlwaysEqual:
1324 def __eq__(self, other):
1325 return True
1326 class OrdinaryEnum(Enum):
1327 a = 1
1328 self.assertEqual(AlwaysEqual(), OrdinaryEnum.a)
1329 self.assertEqual(OrdinaryEnum.a, AlwaysEqual())
1330
Ethan Furman6b3d64a2013-06-14 16:55:46 -07001331 def test_ordered_mixin(self):
1332 class OrderedEnum(Enum):
1333 def __ge__(self, other):
1334 if self.__class__ is other.__class__:
Ethan Furman520ad572013-07-19 19:47:21 -07001335 return self._value_ >= other._value_
Ethan Furman6b3d64a2013-06-14 16:55:46 -07001336 return NotImplemented
1337 def __gt__(self, other):
1338 if self.__class__ is other.__class__:
Ethan Furman520ad572013-07-19 19:47:21 -07001339 return self._value_ > other._value_
Ethan Furman6b3d64a2013-06-14 16:55:46 -07001340 return NotImplemented
1341 def __le__(self, other):
1342 if self.__class__ is other.__class__:
Ethan Furman520ad572013-07-19 19:47:21 -07001343 return self._value_ <= other._value_
Ethan Furman6b3d64a2013-06-14 16:55:46 -07001344 return NotImplemented
1345 def __lt__(self, other):
1346 if self.__class__ is other.__class__:
Ethan Furman520ad572013-07-19 19:47:21 -07001347 return self._value_ < other._value_
Ethan Furman6b3d64a2013-06-14 16:55:46 -07001348 return NotImplemented
1349 class Grade(OrderedEnum):
1350 A = 5
1351 B = 4
1352 C = 3
1353 D = 2
1354 F = 1
1355 self.assertGreater(Grade.A, Grade.B)
1356 self.assertLessEqual(Grade.F, Grade.C)
1357 self.assertLess(Grade.D, Grade.A)
1358 self.assertGreaterEqual(Grade.B, Grade.B)
Ethan Furmanbe3c2fe2013-11-13 14:25:45 -08001359 self.assertEqual(Grade.B, Grade.B)
1360 self.assertNotEqual(Grade.C, Grade.D)
Ethan Furman520ad572013-07-19 19:47:21 -07001361
Ethan Furman6b3d64a2013-06-14 16:55:46 -07001362 def test_extending2(self):
1363 class Shade(Enum):
1364 def shade(self):
1365 print(self.name)
1366 class Color(Shade):
1367 red = 1
1368 green = 2
1369 blue = 3
1370 with self.assertRaises(TypeError):
1371 class MoreColor(Color):
1372 cyan = 4
1373 magenta = 5
1374 yellow = 6
1375
1376 def test_extending3(self):
1377 class Shade(Enum):
1378 def shade(self):
1379 return self.name
1380 class Color(Shade):
1381 def hex(self):
1382 return '%s hexlified!' % self.value
1383 class MoreColor(Color):
1384 cyan = 4
1385 magenta = 5
1386 yellow = 6
1387 self.assertEqual(MoreColor.magenta.hex(), '5 hexlified!')
1388
1389
1390 def test_no_duplicates(self):
1391 class UniqueEnum(Enum):
1392 def __init__(self, *args):
1393 cls = self.__class__
1394 if any(self.value == e.value for e in cls):
1395 a = self.name
1396 e = cls(self.value).name
1397 raise ValueError(
1398 "aliases not allowed in UniqueEnum: %r --> %r"
1399 % (a, e)
1400 )
1401 class Color(UniqueEnum):
1402 red = 1
1403 green = 2
1404 blue = 3
1405 with self.assertRaises(ValueError):
1406 class Color(UniqueEnum):
1407 red = 1
1408 green = 2
1409 blue = 3
1410 grene = 2
1411
1412 def test_init(self):
1413 class Planet(Enum):
1414 MERCURY = (3.303e+23, 2.4397e6)
1415 VENUS = (4.869e+24, 6.0518e6)
1416 EARTH = (5.976e+24, 6.37814e6)
1417 MARS = (6.421e+23, 3.3972e6)
1418 JUPITER = (1.9e+27, 7.1492e7)
1419 SATURN = (5.688e+26, 6.0268e7)
1420 URANUS = (8.686e+25, 2.5559e7)
1421 NEPTUNE = (1.024e+26, 2.4746e7)
1422 def __init__(self, mass, radius):
1423 self.mass = mass # in kilograms
1424 self.radius = radius # in meters
1425 @property
1426 def surface_gravity(self):
1427 # universal gravitational constant (m3 kg-1 s-2)
1428 G = 6.67300E-11
1429 return G * self.mass / (self.radius * self.radius)
1430 self.assertEqual(round(Planet.EARTH.surface_gravity, 2), 9.80)
1431 self.assertEqual(Planet.EARTH.value, (5.976e+24, 6.37814e6))
1432
Ethan Furman2aa27322013-07-19 19:35:56 -07001433 def test_nonhash_value(self):
1434 class AutoNumberInAList(Enum):
1435 def __new__(cls):
1436 value = [len(cls.__members__) + 1]
1437 obj = object.__new__(cls)
Ethan Furman520ad572013-07-19 19:47:21 -07001438 obj._value_ = value
Ethan Furman2aa27322013-07-19 19:35:56 -07001439 return obj
1440 class ColorInAList(AutoNumberInAList):
1441 red = ()
1442 green = ()
1443 blue = ()
1444 self.assertEqual(list(ColorInAList), [ColorInAList.red, ColorInAList.green, ColorInAList.blue])
Ethan Furman1a162882013-10-16 19:09:31 -07001445 for enum, value in zip(ColorInAList, range(3)):
1446 value += 1
1447 self.assertEqual(enum.value, [value])
1448 self.assertIs(ColorInAList([value]), enum)
Ethan Furman2aa27322013-07-19 19:35:56 -07001449
Ethan Furmanb41803e2013-07-25 13:50:45 -07001450 def test_conflicting_types_resolved_in_new(self):
1451 class LabelledIntEnum(int, Enum):
1452 def __new__(cls, *args):
1453 value, label = args
1454 obj = int.__new__(cls, value)
1455 obj.label = label
1456 obj._value_ = value
1457 return obj
1458
1459 class LabelledList(LabelledIntEnum):
1460 unprocessed = (1, "Unprocessed")
1461 payment_complete = (2, "Payment Complete")
1462
1463 self.assertEqual(list(LabelledList), [LabelledList.unprocessed, LabelledList.payment_complete])
1464 self.assertEqual(LabelledList.unprocessed, 1)
1465 self.assertEqual(LabelledList(1), LabelledList.unprocessed)
Ethan Furman2aa27322013-07-19 19:35:56 -07001466
Ethan Furman6b3d64a2013-06-14 16:55:46 -07001467
Ethan Furmanf24bb352013-07-18 17:05:39 -07001468class TestUnique(unittest.TestCase):
1469
1470 def test_unique_clean(self):
1471 @unique
1472 class Clean(Enum):
1473 one = 1
1474 two = 'dos'
1475 tres = 4.0
1476 @unique
1477 class Cleaner(IntEnum):
1478 single = 1
1479 double = 2
1480 triple = 3
1481
1482 def test_unique_dirty(self):
1483 with self.assertRaisesRegex(ValueError, 'tres.*one'):
1484 @unique
1485 class Dirty(Enum):
1486 one = 1
1487 two = 'dos'
1488 tres = 1
1489 with self.assertRaisesRegex(
1490 ValueError,
1491 'double.*single.*turkey.*triple',
1492 ):
1493 @unique
1494 class Dirtier(IntEnum):
1495 single = 1
1496 double = 1
1497 triple = 3
1498 turkey = 3
1499
1500
Ethan Furman5875d742013-10-21 20:45:55 -07001501expected_help_output = """
1502Help on class Color in module %s:
1503
1504class Color(enum.Enum)
1505 | Method resolution order:
1506 | Color
1507 | enum.Enum
1508 | builtins.object
1509 |\x20\x20
1510 | Data and other attributes defined here:
1511 |\x20\x20
1512 | blue = <Color.blue: 3>
1513 |\x20\x20
1514 | green = <Color.green: 2>
1515 |\x20\x20
1516 | red = <Color.red: 1>
1517 |\x20\x20
1518 | ----------------------------------------------------------------------
1519 | Data descriptors inherited from enum.Enum:
1520 |\x20\x20
1521 | name
1522 | The name of the Enum member.
1523 |\x20\x20
1524 | value
1525 | The value of the Enum member.
1526 |\x20\x20
1527 | ----------------------------------------------------------------------
1528 | Data descriptors inherited from enum.EnumMeta:
1529 |\x20\x20
1530 | __members__
1531 | Returns a mapping of member name->value.
1532 |\x20\x20\x20\x20\x20\x20
1533 | This mapping lists all enum members, including aliases. Note that this
1534 | is a read-only view of the internal mapping.
1535""".strip()
1536
1537class TestStdLib(unittest.TestCase):
1538
1539 class Color(Enum):
1540 red = 1
1541 green = 2
1542 blue = 3
1543
1544 def test_pydoc(self):
1545 # indirectly test __objclass__
1546 expected_text = expected_help_output % __name__
1547 output = StringIO()
1548 helper = pydoc.Helper(output=output)
1549 helper(self.Color)
1550 result = output.getvalue().strip()
Victor Stinner4b0432d2014-06-16 22:48:43 +02001551 self.assertEqual(result, expected_text)
Ethan Furman5875d742013-10-21 20:45:55 -07001552
1553 def test_inspect_getmembers(self):
1554 values = dict((
1555 ('__class__', EnumMeta),
1556 ('__doc__', None),
1557 ('__members__', self.Color.__members__),
1558 ('__module__', __name__),
1559 ('blue', self.Color.blue),
1560 ('green', self.Color.green),
1561 ('name', Enum.__dict__['name']),
1562 ('red', self.Color.red),
1563 ('value', Enum.__dict__['value']),
1564 ))
1565 result = dict(inspect.getmembers(self.Color))
1566 self.assertEqual(values.keys(), result.keys())
1567 failed = False
1568 for k in values.keys():
1569 if result[k] != values[k]:
1570 print()
1571 print('\n%s\n key: %s\n result: %s\nexpected: %s\n%s\n' %
1572 ('=' * 75, k, result[k], values[k], '=' * 75), sep='')
1573 failed = True
1574 if failed:
1575 self.fail("result does not equal expected, see print above")
1576
1577 def test_inspect_classify_class_attrs(self):
1578 # indirectly test __objclass__
1579 from inspect import Attribute
1580 values = [
1581 Attribute(name='__class__', kind='data',
1582 defining_class=object, object=EnumMeta),
1583 Attribute(name='__doc__', kind='data',
1584 defining_class=self.Color, object=None),
1585 Attribute(name='__members__', kind='property',
1586 defining_class=EnumMeta, object=EnumMeta.__members__),
1587 Attribute(name='__module__', kind='data',
1588 defining_class=self.Color, object=__name__),
1589 Attribute(name='blue', kind='data',
1590 defining_class=self.Color, object=self.Color.blue),
1591 Attribute(name='green', kind='data',
1592 defining_class=self.Color, object=self.Color.green),
1593 Attribute(name='red', kind='data',
1594 defining_class=self.Color, object=self.Color.red),
1595 Attribute(name='name', kind='data',
1596 defining_class=Enum, object=Enum.__dict__['name']),
1597 Attribute(name='value', kind='data',
1598 defining_class=Enum, object=Enum.__dict__['value']),
1599 ]
1600 values.sort(key=lambda item: item.name)
1601 result = list(inspect.classify_class_attrs(self.Color))
1602 result.sort(key=lambda item: item.name)
1603 failed = False
1604 for v, r in zip(values, result):
1605 if r != v:
1606 print('\n%s\n%s\n%s\n%s\n' % ('=' * 75, r, v, '=' * 75), sep='')
1607 failed = True
1608 if failed:
1609 self.fail("result does not equal expected, see print above")
1610
Ethan Furman6b3d64a2013-06-14 16:55:46 -07001611if __name__ == '__main__':
1612 unittest.main()