From 9b858f5d20fce9560640f429c701641f078c9118 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Mon, 10 Jan 2022 12:05:53 +0300 Subject: [PATCH 1/2] bpo-46301: remove refleak from `Enum` tests --- Lib/test/test_enum.py | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py index 7e919fb9b42639..f0a8ce961fc14e 100644 --- a/Lib/test/test_enum.py +++ b/Lib/test/test_enum.py @@ -4449,6 +4449,12 @@ def test__all__(self): COMPLEX_A = 2j COMPLEX_B = 3j +class _ModuleWrapper: + """We use this class as a namespace for swapping modules.""" + + def __init__(self, module): + self.__dict__.update(module.__dict__) + class TestIntEnumConvert(unittest.TestCase): def setUp(self): # Reset the module-level test variables to their original integer @@ -4487,11 +4493,17 @@ def test_convert(self): [], msg='Names other than CONVERT_TEST_* found.') def test_convert_uncomparable(self): - uncomp = enum.Enum._convert_( - 'Uncomparable', - MODULE, - filter=lambda x: x.startswith('UNCOMPARABLE_'), - ) + # We swap a module to some other object with `__dict__` + # because otherwise refleak is created. + # `_convert_` uses a module side effect that does this. See 30472 + with support.swap_item( + sys.modules, MODULE, _ModuleWrapper(sys.modules[MODULE]), + ): + uncomp = enum.Enum._convert_( + 'Uncomparable', + MODULE, + filter=lambda x: x.startswith('UNCOMPARABLE_'), + ) # Should be ordered by `name` only: self.assertEqual( @@ -4500,11 +4512,14 @@ def test_convert_uncomparable(self): ) def test_convert_complex(self): - uncomp = enum.Enum._convert_( - 'Uncomparable', - MODULE, - filter=lambda x: x.startswith('COMPLEX_'), - ) + with support.swap_item( + sys.modules, MODULE, _ModuleWrapper(sys.modules[MODULE]), + ): + uncomp = enum.Enum._convert_( + 'Uncomparable', + MODULE, + filter=lambda x: x.startswith('COMPLEX_'), + ) # Should be ordered by `name` only: self.assertEqual( From 74f89406f632d46c0950255395481b357dbf90dd Mon Sep 17 00:00:00 2001 From: sobolevn Date: Mon, 10 Jan 2022 20:59:32 +0300 Subject: [PATCH 2/2] Fixes formatting --- Lib/test/test_enum.py | 87 +++++++++++++++++++++---------------------- 1 file changed, 43 insertions(+), 44 deletions(-) diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py index f0a8ce961fc14e..dfa81a52a93a4d 100644 --- a/Lib/test/test_enum.py +++ b/Lib/test/test_enum.py @@ -4456,29 +4456,27 @@ def __init__(self, module): self.__dict__.update(module.__dict__) class TestIntEnumConvert(unittest.TestCase): - def setUp(self): - # Reset the module-level test variables to their original integer - # values, otherwise the already created enum values get converted - # instead. - for suffix in ['A', 'B', 'C', 'D', 'E', 'F']: - globals()[f'CONVERT_TEST_NAME_{suffix}'] = 5 - globals()[f'CONVERT_STRING_TEST_NAME_{suffix}'] = 5 - def test_convert_value_lookup_priority(self): - test_type = enum.IntEnum._convert_( - 'UnittestConvert', - MODULE, - filter=lambda x: x.startswith('CONVERT_TEST_')) + with support.swap_item( + sys.modules, MODULE, _ModuleWrapper(sys.modules[MODULE]), + ): + test_type = enum.IntEnum._convert_( + 'UnittestConvert', + MODULE, + filter=lambda x: x.startswith('CONVERT_TEST_')) # We don't want the reverse lookup value to vary when there are # multiple possible names for a given value. It should always # report the first lexigraphical name in that case. self.assertEqual(test_type(5).name, 'CONVERT_TEST_NAME_A') def test_convert(self): - test_type = enum.IntEnum._convert_( - 'UnittestConvert', - MODULE, - filter=lambda x: x.startswith('CONVERT_TEST_')) + with support.swap_item( + sys.modules, MODULE, _ModuleWrapper(sys.modules[MODULE]), + ): + test_type = enum.IntEnum._convert_( + 'UnittestConvert', + MODULE, + filter=lambda x: x.startswith('CONVERT_TEST_')) # Ensure that test_type has all of the desired names and values. self.assertEqual(test_type.CONVERT_TEST_NAME_F, test_type.CONVERT_TEST_NAME_A) @@ -4497,13 +4495,12 @@ def test_convert_uncomparable(self): # because otherwise refleak is created. # `_convert_` uses a module side effect that does this. See 30472 with support.swap_item( - sys.modules, MODULE, _ModuleWrapper(sys.modules[MODULE]), - ): + sys.modules, MODULE, _ModuleWrapper(sys.modules[MODULE]), + ): uncomp = enum.Enum._convert_( - 'Uncomparable', - MODULE, - filter=lambda x: x.startswith('UNCOMPARABLE_'), - ) + 'Uncomparable', + MODULE, + filter=lambda x: x.startswith('UNCOMPARABLE_')) # Should be ordered by `name` only: self.assertEqual( @@ -4513,13 +4510,12 @@ def test_convert_uncomparable(self): def test_convert_complex(self): with support.swap_item( - sys.modules, MODULE, _ModuleWrapper(sys.modules[MODULE]), - ): + sys.modules, MODULE, _ModuleWrapper(sys.modules[MODULE]), + ): uncomp = enum.Enum._convert_( 'Uncomparable', MODULE, - filter=lambda x: x.startswith('COMPLEX_'), - ) + filter=lambda x: x.startswith('COMPLEX_')) # Should be ordered by `name` only: self.assertEqual( @@ -4546,10 +4542,13 @@ def test_convert_raise(self): filter=lambda x: x.startswith('CONVERT_TEST_')) def test_convert_repr_and_str(self): - test_type = enum.IntEnum._convert_( - 'UnittestConvert', - MODULE, - filter=lambda x: x.startswith('CONVERT_STRING_TEST_')) + with support.swap_item( + sys.modules, MODULE, _ModuleWrapper(sys.modules[MODULE]), + ): + test_type = enum.IntEnum._convert_( + 'UnittestConvert', + MODULE, + filter=lambda x: x.startswith('CONVERT_STRING_TEST_')) 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(format(test_type.CONVERT_STRING_TEST_NAME_A), '5') @@ -4559,17 +4558,14 @@ def test_convert_repr_and_str(self): CONVERT_STR_TEST_1 = 'hello' class TestStrEnumConvert(unittest.TestCase): - def setUp(self): - global CONVERT_STR_TEST_1 - global CONVERT_STR_TEST_2 - CONVERT_STR_TEST_2 = 'goodbye' - CONVERT_STR_TEST_1 = 'hello' - def test_convert(self): - test_type = enum.StrEnum._convert_( - 'UnittestConvert', - MODULE, - filter=lambda x: x.startswith('CONVERT_STR_')) + with support.swap_item( + sys.modules, MODULE, _ModuleWrapper(sys.modules[MODULE]), + ): + test_type = enum.StrEnum._convert_( + 'UnittestConvert', + MODULE, + filter=lambda x: x.startswith('CONVERT_STR_')) # 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_2, 'goodbye') @@ -4580,10 +4576,13 @@ def test_convert(self): [], msg='Names other than CONVERT_STR_* found.') def test_convert_repr_and_str(self): - test_type = enum.StrEnum._convert_( - 'UnittestConvert', - MODULE, - filter=lambda x: x.startswith('CONVERT_STR_')) + with support.swap_item( + sys.modules, MODULE, _ModuleWrapper(sys.modules[MODULE]), + ): + test_type = enum.StrEnum._convert_( + 'UnittestConvert', + MODULE, + filter=lambda x: x.startswith('CONVERT_STR_')) 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(format(test_type.CONVERT_STR_TEST_1), 'hello')