mirror of https://github.com/python/cpython
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 <lukasz@langa.pl> Co-authored-by: Ethan Furman <ethan@stoneleaf.us>
This commit is contained in:
parent
a3c11cebf1
commit
24da544014
19
Lib/enum.py
19
Lib/enum.py
|
@ -1390,17 +1390,28 @@ def _power_of_two(value):
|
||||||
return value == 2 ** _high_bit(value)
|
return value == 2 ** _high_bit(value)
|
||||||
|
|
||||||
def global_enum_repr(self):
|
def global_enum_repr(self):
|
||||||
return '%s.%s' % (self.__class__.__module__, self._name_)
|
"""
|
||||||
|
use module.enum_name instead of class.enum_name
|
||||||
|
|
||||||
|
the module is the last module in case of a multi-module name
|
||||||
|
"""
|
||||||
|
module = self.__class__.__module__.split('.')[-1]
|
||||||
|
return '%s.%s' % (module, self._name_)
|
||||||
|
|
||||||
def global_flag_repr(self):
|
def global_flag_repr(self):
|
||||||
module = self.__class__.__module__
|
"""
|
||||||
|
use module.flag_name instead of class.flag_name
|
||||||
|
|
||||||
|
the module is the last module in case of a multi-module name
|
||||||
|
"""
|
||||||
|
module = self.__class__.__module__.split('.')[-1]
|
||||||
cls_name = self.__class__.__name__
|
cls_name = self.__class__.__name__
|
||||||
if self._name_ is None:
|
if self._name_ is None:
|
||||||
return "%x" % (module, cls_name, self._value_)
|
return "%s.%s(0x%x)" % (module, cls_name, self._value_)
|
||||||
if _is_single_bit(self):
|
if _is_single_bit(self):
|
||||||
return '%s.%s' % (module, self._name_)
|
return '%s.%s' % (module, self._name_)
|
||||||
if self._boundary_ is not FlagBoundary.KEEP:
|
if self._boundary_ is not FlagBoundary.KEEP:
|
||||||
return module + module.join(self.name.split('|'))
|
return '|'.join(['%s.%s' % (module, name) for name in self.name.split('|')])
|
||||||
else:
|
else:
|
||||||
name = []
|
name = []
|
||||||
for n in self._name_.split('|'):
|
for n in self._name_.split('|'):
|
||||||
|
|
|
@ -28,6 +28,9 @@ def load_tests(loader, tests, ignore):
|
||||||
))
|
))
|
||||||
return tests
|
return tests
|
||||||
|
|
||||||
|
MODULE = ('test.test_enum', '__main__')[__name__=='__main__']
|
||||||
|
SHORT_MODULE = MODULE.split('.')[-1]
|
||||||
|
|
||||||
# for pickle tests
|
# for pickle tests
|
||||||
try:
|
try:
|
||||||
class Stooges(Enum):
|
class Stooges(Enum):
|
||||||
|
@ -143,6 +146,23 @@ class classproperty:
|
||||||
def __get__(self, instance, ownerclass):
|
def __get__(self, instance, ownerclass):
|
||||||
return self.fget(ownerclass)
|
return self.fget(ownerclass)
|
||||||
|
|
||||||
|
# for global repr tests
|
||||||
|
|
||||||
|
@enum.global_enum
|
||||||
|
class HeadlightsK(IntFlag, boundary=enum.KEEP):
|
||||||
|
OFF_K = 0
|
||||||
|
LOW_BEAM_K = auto()
|
||||||
|
HIGH_BEAM_K = auto()
|
||||||
|
FOG_K = auto()
|
||||||
|
|
||||||
|
|
||||||
|
@enum.global_enum
|
||||||
|
class HeadlightsC(IntFlag, boundary=enum.CONFORM):
|
||||||
|
OFF_C = 0
|
||||||
|
LOW_BEAM_C = auto()
|
||||||
|
HIGH_BEAM_C = auto()
|
||||||
|
FOG_C = auto()
|
||||||
|
|
||||||
|
|
||||||
# tests
|
# tests
|
||||||
|
|
||||||
|
@ -3224,6 +3244,34 @@ class TestIntFlag(unittest.TestCase):
|
||||||
self.assertEqual(repr(~(Open.WO | Open.CE)), 'Open.RW')
|
self.assertEqual(repr(~(Open.WO | Open.CE)), 'Open.RW')
|
||||||
self.assertEqual(repr(Open(~4)), '-5')
|
self.assertEqual(repr(Open(~4)), '-5')
|
||||||
|
|
||||||
|
def test_global_repr_keep(self):
|
||||||
|
self.assertEqual(
|
||||||
|
repr(HeadlightsK(0)),
|
||||||
|
'%s.OFF_K' % SHORT_MODULE,
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
repr(HeadlightsK(2**0 + 2**2 + 2**3)),
|
||||||
|
'%(m)s.LOW_BEAM_K|%(m)s.FOG_K|0x8' % {'m': SHORT_MODULE},
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
repr(HeadlightsK(2**3)),
|
||||||
|
'%(m)s.HeadlightsK(0x8)' % {'m': SHORT_MODULE},
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_global_repr_conform1(self):
|
||||||
|
self.assertEqual(
|
||||||
|
repr(HeadlightsC(0)),
|
||||||
|
'%s.OFF_C' % SHORT_MODULE,
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
repr(HeadlightsC(2**0 + 2**2 + 2**3)),
|
||||||
|
'%(m)s.LOW_BEAM_C|%(m)s.FOG_C' % {'m': SHORT_MODULE},
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
repr(HeadlightsC(2**3)),
|
||||||
|
'%(m)s.OFF_C' % {'m': SHORT_MODULE},
|
||||||
|
)
|
||||||
|
|
||||||
def test_format(self):
|
def test_format(self):
|
||||||
Perm = self.Perm
|
Perm = self.Perm
|
||||||
self.assertEqual(format(Perm.R, ''), '4')
|
self.assertEqual(format(Perm.R, ''), '4')
|
||||||
|
@ -4085,7 +4133,7 @@ class TestIntEnumConvert(unittest.TestCase):
|
||||||
def test_convert_value_lookup_priority(self):
|
def test_convert_value_lookup_priority(self):
|
||||||
test_type = enum.IntEnum._convert_(
|
test_type = enum.IntEnum._convert_(
|
||||||
'UnittestConvert',
|
'UnittestConvert',
|
||||||
('test.test_enum', '__main__')[__name__=='__main__'],
|
MODULE,
|
||||||
filter=lambda x: x.startswith('CONVERT_TEST_'))
|
filter=lambda x: x.startswith('CONVERT_TEST_'))
|
||||||
# We don't want the reverse lookup value to vary when there are
|
# We don't want the reverse lookup value to vary when there are
|
||||||
# multiple possible names for a given value. It should always
|
# multiple possible names for a given value. It should always
|
||||||
|
@ -4095,7 +4143,7 @@ class TestIntEnumConvert(unittest.TestCase):
|
||||||
def test_convert(self):
|
def test_convert(self):
|
||||||
test_type = enum.IntEnum._convert_(
|
test_type = enum.IntEnum._convert_(
|
||||||
'UnittestConvert',
|
'UnittestConvert',
|
||||||
('test.test_enum', '__main__')[__name__=='__main__'],
|
MODULE,
|
||||||
filter=lambda x: x.startswith('CONVERT_TEST_'))
|
filter=lambda x: x.startswith('CONVERT_TEST_'))
|
||||||
# Ensure that test_type has all of the desired names and values.
|
# Ensure that test_type has all of the desired names and values.
|
||||||
self.assertEqual(test_type.CONVERT_TEST_NAME_F,
|
self.assertEqual(test_type.CONVERT_TEST_NAME_F,
|
||||||
|
@ -4115,7 +4163,7 @@ class TestIntEnumConvert(unittest.TestCase):
|
||||||
with self.assertWarns(DeprecationWarning):
|
with self.assertWarns(DeprecationWarning):
|
||||||
enum.IntEnum._convert(
|
enum.IntEnum._convert(
|
||||||
'UnittestConvert',
|
'UnittestConvert',
|
||||||
('test.test_enum', '__main__')[__name__=='__main__'],
|
MODULE,
|
||||||
filter=lambda x: x.startswith('CONVERT_TEST_'))
|
filter=lambda x: x.startswith('CONVERT_TEST_'))
|
||||||
|
|
||||||
@unittest.skipUnless(python_version >= (3, 9),
|
@unittest.skipUnless(python_version >= (3, 9),
|
||||||
|
@ -4124,16 +4172,15 @@ class TestIntEnumConvert(unittest.TestCase):
|
||||||
with self.assertRaises(AttributeError):
|
with self.assertRaises(AttributeError):
|
||||||
enum.IntEnum._convert(
|
enum.IntEnum._convert(
|
||||||
'UnittestConvert',
|
'UnittestConvert',
|
||||||
('test.test_enum', '__main__')[__name__=='__main__'],
|
MODULE,
|
||||||
filter=lambda x: x.startswith('CONVERT_TEST_'))
|
filter=lambda x: x.startswith('CONVERT_TEST_'))
|
||||||
|
|
||||||
def test_convert_repr_and_str(self):
|
def test_convert_repr_and_str(self):
|
||||||
module = ('test.test_enum', '__main__')[__name__=='__main__']
|
|
||||||
test_type = enum.IntEnum._convert_(
|
test_type = enum.IntEnum._convert_(
|
||||||
'UnittestConvert',
|
'UnittestConvert',
|
||||||
module,
|
MODULE,
|
||||||
filter=lambda x: x.startswith('CONVERT_STRING_TEST_'))
|
filter=lambda x: x.startswith('CONVERT_STRING_TEST_'))
|
||||||
self.assertEqual(repr(test_type.CONVERT_STRING_TEST_NAME_A), '%s.CONVERT_STRING_TEST_NAME_A' % module)
|
self.assertEqual(repr(test_type.CONVERT_STRING_TEST_NAME_A), '%s.CONVERT_STRING_TEST_NAME_A' % SHORT_MODULE)
|
||||||
self.assertEqual(str(test_type.CONVERT_STRING_TEST_NAME_A), 'CONVERT_STRING_TEST_NAME_A')
|
self.assertEqual(str(test_type.CONVERT_STRING_TEST_NAME_A), 'CONVERT_STRING_TEST_NAME_A')
|
||||||
self.assertEqual(format(test_type.CONVERT_STRING_TEST_NAME_A), '5')
|
self.assertEqual(format(test_type.CONVERT_STRING_TEST_NAME_A), '5')
|
||||||
|
|
||||||
|
@ -4151,7 +4198,7 @@ class TestStrEnumConvert(unittest.TestCase):
|
||||||
def test_convert(self):
|
def test_convert(self):
|
||||||
test_type = enum.StrEnum._convert_(
|
test_type = enum.StrEnum._convert_(
|
||||||
'UnittestConvert',
|
'UnittestConvert',
|
||||||
('test.test_enum', '__main__')[__name__=='__main__'],
|
MODULE,
|
||||||
filter=lambda x: x.startswith('CONVERT_STR_'))
|
filter=lambda x: x.startswith('CONVERT_STR_'))
|
||||||
# Ensure that test_type has all of the desired names and values.
|
# Ensure that test_type has all of the desired names and values.
|
||||||
self.assertEqual(test_type.CONVERT_STR_TEST_1, 'hello')
|
self.assertEqual(test_type.CONVERT_STR_TEST_1, 'hello')
|
||||||
|
@ -4162,12 +4209,11 @@ class TestStrEnumConvert(unittest.TestCase):
|
||||||
[], msg='Names other than CONVERT_STR_* found.')
|
[], msg='Names other than CONVERT_STR_* found.')
|
||||||
|
|
||||||
def test_convert_repr_and_str(self):
|
def test_convert_repr_and_str(self):
|
||||||
module = ('test.test_enum', '__main__')[__name__=='__main__']
|
|
||||||
test_type = enum.StrEnum._convert_(
|
test_type = enum.StrEnum._convert_(
|
||||||
'UnittestConvert',
|
'UnittestConvert',
|
||||||
module,
|
MODULE,
|
||||||
filter=lambda x: x.startswith('CONVERT_STR_'))
|
filter=lambda x: x.startswith('CONVERT_STR_'))
|
||||||
self.assertEqual(repr(test_type.CONVERT_STR_TEST_1), '%s.CONVERT_STR_TEST_1' % module)
|
self.assertEqual(repr(test_type.CONVERT_STR_TEST_1), '%s.CONVERT_STR_TEST_1' % SHORT_MODULE)
|
||||||
self.assertEqual(str(test_type.CONVERT_STR_TEST_2), 'goodbye')
|
self.assertEqual(str(test_type.CONVERT_STR_TEST_2), 'goodbye')
|
||||||
self.assertEqual(format(test_type.CONVERT_STR_TEST_1), 'hello')
|
self.assertEqual(format(test_type.CONVERT_STR_TEST_1), 'hello')
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Fix some edge cases of ``enum.Flag`` string representation in the REPL.
|
||||||
|
Patch by Pablo Galindo.
|
Loading…
Reference in New Issue