Skip to content

Commit 24da544

Browse files
pablogsalambvethanfurman
authored
bpo-44929: [Enum] Fix global repr (GH-27789)
* Fix typo in __repr__ code * Add more tests for global int flag reprs * use last module if multi-module string - when an enum's `__module__` contains several module names, only use the last one Co-authored-by: Łukasz Langa <[email protected]> Co-authored-by: Ethan Furman <[email protected]>
1 parent a3c11ce commit 24da544

File tree

3 files changed

+74
-15
lines changed

3 files changed

+74
-15
lines changed

Lib/enum.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1390,17 +1390,28 @@ def _power_of_two(value):
13901390
return value == 2 ** _high_bit(value)
13911391

13921392
def global_enum_repr(self):
1393-
return '%s.%s' % (self.__class__.__module__, self._name_)
1393+
"""
1394+
use module.enum_name instead of class.enum_name
1395+
1396+
the module is the last module in case of a multi-module name
1397+
"""
1398+
module = self.__class__.__module__.split('.')[-1]
1399+
return '%s.%s' % (module, self._name_)
13941400

13951401
def global_flag_repr(self):
1396-
module = self.__class__.__module__
1402+
"""
1403+
use module.flag_name instead of class.flag_name
1404+
1405+
the module is the last module in case of a multi-module name
1406+
"""
1407+
module = self.__class__.__module__.split('.')[-1]
13971408
cls_name = self.__class__.__name__
13981409
if self._name_ is None:
1399-
return "%x" % (module, cls_name, self._value_)
1410+
return "%s.%s(0x%x)" % (module, cls_name, self._value_)
14001411
if _is_single_bit(self):
14011412
return '%s.%s' % (module, self._name_)
14021413
if self._boundary_ is not FlagBoundary.KEEP:
1403-
return module + module.join(self.name.split('|'))
1414+
return '|'.join(['%s.%s' % (module, name) for name in self.name.split('|')])
14041415
else:
14051416
name = []
14061417
for n in self._name_.split('|'):

Lib/test/test_enum.py

Lines changed: 57 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ def load_tests(loader, tests, ignore):
2828
))
2929
return tests
3030

31+
MODULE = ('test.test_enum', '__main__')[__name__=='__main__']
32+
SHORT_MODULE = MODULE.split('.')[-1]
33+
3134
# for pickle tests
3235
try:
3336
class Stooges(Enum):
@@ -143,6 +146,23 @@ def __init__(self, fget=None, fset=None, fdel=None, doc=None):
143146
def __get__(self, instance, ownerclass):
144147
return self.fget(ownerclass)
145148

149+
# for global repr tests
150+
151+
@enum.global_enum
152+
class HeadlightsK(IntFlag, boundary=enum.KEEP):
153+
OFF_K = 0
154+
LOW_BEAM_K = auto()
155+
HIGH_BEAM_K = auto()
156+
FOG_K = auto()
157+
158+
159+
@enum.global_enum
160+
class HeadlightsC(IntFlag, boundary=enum.CONFORM):
161+
OFF_C = 0
162+
LOW_BEAM_C = auto()
163+
HIGH_BEAM_C = auto()
164+
FOG_C = auto()
165+
146166

147167
# tests
148168

@@ -3224,6 +3244,34 @@ def test_repr(self):
32243244
self.assertEqual(repr(~(Open.WO | Open.CE)), 'Open.RW')
32253245
self.assertEqual(repr(Open(~4)), '-5')
32263246

3247+
def test_global_repr_keep(self):
3248+
self.assertEqual(
3249+
repr(HeadlightsK(0)),
3250+
'%s.OFF_K' % SHORT_MODULE,
3251+
)
3252+
self.assertEqual(
3253+
repr(HeadlightsK(2**0 + 2**2 + 2**3)),
3254+
'%(m)s.LOW_BEAM_K|%(m)s.FOG_K|0x8' % {'m': SHORT_MODULE},
3255+
)
3256+
self.assertEqual(
3257+
repr(HeadlightsK(2**3)),
3258+
'%(m)s.HeadlightsK(0x8)' % {'m': SHORT_MODULE},
3259+
)
3260+
3261+
def test_global_repr_conform1(self):
3262+
self.assertEqual(
3263+
repr(HeadlightsC(0)),
3264+
'%s.OFF_C' % SHORT_MODULE,
3265+
)
3266+
self.assertEqual(
3267+
repr(HeadlightsC(2**0 + 2**2 + 2**3)),
3268+
'%(m)s.LOW_BEAM_C|%(m)s.FOG_C' % {'m': SHORT_MODULE},
3269+
)
3270+
self.assertEqual(
3271+
repr(HeadlightsC(2**3)),
3272+
'%(m)s.OFF_C' % {'m': SHORT_MODULE},
3273+
)
3274+
32273275
def test_format(self):
32283276
Perm = self.Perm
32293277
self.assertEqual(format(Perm.R, ''), '4')
@@ -4085,7 +4133,7 @@ def setUp(self):
40854133
def test_convert_value_lookup_priority(self):
40864134
test_type = enum.IntEnum._convert_(
40874135
'UnittestConvert',
4088-
('test.test_enum', '__main__')[__name__=='__main__'],
4136+
MODULE,
40894137
filter=lambda x: x.startswith('CONVERT_TEST_'))
40904138
# We don't want the reverse lookup value to vary when there are
40914139
# multiple possible names for a given value. It should always
@@ -4095,7 +4143,7 @@ def test_convert_value_lookup_priority(self):
40954143
def test_convert(self):
40964144
test_type = enum.IntEnum._convert_(
40974145
'UnittestConvert',
4098-
('test.test_enum', '__main__')[__name__=='__main__'],
4146+
MODULE,
40994147
filter=lambda x: x.startswith('CONVERT_TEST_'))
41004148
# Ensure that test_type has all of the desired names and values.
41014149
self.assertEqual(test_type.CONVERT_TEST_NAME_F,
@@ -4115,7 +4163,7 @@ def test_convert_warn(self):
41154163
with self.assertWarns(DeprecationWarning):
41164164
enum.IntEnum._convert(
41174165
'UnittestConvert',
4118-
('test.test_enum', '__main__')[__name__=='__main__'],
4166+
MODULE,
41194167
filter=lambda x: x.startswith('CONVERT_TEST_'))
41204168

41214169
@unittest.skipUnless(python_version >= (3, 9),
@@ -4124,16 +4172,15 @@ def test_convert_raise(self):
41244172
with self.assertRaises(AttributeError):
41254173
enum.IntEnum._convert(
41264174
'UnittestConvert',
4127-
('test.test_enum', '__main__')[__name__=='__main__'],
4175+
MODULE,
41284176
filter=lambda x: x.startswith('CONVERT_TEST_'))
41294177

41304178
def test_convert_repr_and_str(self):
4131-
module = ('test.test_enum', '__main__')[__name__=='__main__']
41324179
test_type = enum.IntEnum._convert_(
41334180
'UnittestConvert',
4134-
module,
4181+
MODULE,
41354182
filter=lambda x: x.startswith('CONVERT_STRING_TEST_'))
4136-
self.assertEqual(repr(test_type.CONVERT_STRING_TEST_NAME_A), '%s.CONVERT_STRING_TEST_NAME_A' % module)
4183+
self.assertEqual(repr(test_type.CONVERT_STRING_TEST_NAME_A), '%s.CONVERT_STRING_TEST_NAME_A' % SHORT_MODULE)
41374184
self.assertEqual(str(test_type.CONVERT_STRING_TEST_NAME_A), 'CONVERT_STRING_TEST_NAME_A')
41384185
self.assertEqual(format(test_type.CONVERT_STRING_TEST_NAME_A), '5')
41394186

@@ -4151,7 +4198,7 @@ def setUp(self):
41514198
def test_convert(self):
41524199
test_type = enum.StrEnum._convert_(
41534200
'UnittestConvert',
4154-
('test.test_enum', '__main__')[__name__=='__main__'],
4201+
MODULE,
41554202
filter=lambda x: x.startswith('CONVERT_STR_'))
41564203
# Ensure that test_type has all of the desired names and values.
41574204
self.assertEqual(test_type.CONVERT_STR_TEST_1, 'hello')
@@ -4162,12 +4209,11 @@ def test_convert(self):
41624209
[], msg='Names other than CONVERT_STR_* found.')
41634210

41644211
def test_convert_repr_and_str(self):
4165-
module = ('test.test_enum', '__main__')[__name__=='__main__']
41664212
test_type = enum.StrEnum._convert_(
41674213
'UnittestConvert',
4168-
module,
4214+
MODULE,
41694215
filter=lambda x: x.startswith('CONVERT_STR_'))
4170-
self.assertEqual(repr(test_type.CONVERT_STR_TEST_1), '%s.CONVERT_STR_TEST_1' % module)
4216+
self.assertEqual(repr(test_type.CONVERT_STR_TEST_1), '%s.CONVERT_STR_TEST_1' % SHORT_MODULE)
41714217
self.assertEqual(str(test_type.CONVERT_STR_TEST_2), 'goodbye')
41724218
self.assertEqual(format(test_type.CONVERT_STR_TEST_1), 'hello')
41734219

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix some edge cases of ``enum.Flag`` string representation in the REPL.
2+
Patch by Pablo Galindo.

0 commit comments

Comments
 (0)