From 34cb17d682e602576516f4fe9f70f0fee379c11a Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 23 Jul 2024 13:57:26 -0600 Subject: [PATCH 1/2] gh-117482: Expand Tests for Slot Wrappers of Inherited Slots of Static Builtin Types (gh-122192) --- Lib/test/test_embed.py | 28 +++++++++++++++++++++ Lib/test/test_types.py | 55 +++++++++++++++++++++++++++++++++--------- 2 files changed, 71 insertions(+), 12 deletions(-) diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py index 24617ab24c6958..de705abbc5cdd9 100644 --- a/Lib/test/test_embed.py +++ b/Lib/test/test_embed.py @@ -5,6 +5,7 @@ from collections import namedtuple import contextlib +import io import json import os import os.path @@ -389,6 +390,33 @@ def test_ucnhash_capi_reset(self): out, err = self.run_embedded_interpreter("test_repeated_init_exec", code) self.assertEqual(out, '9\n' * INIT_LOOPS) + def test_static_types_inherited_slots(self): + slots = [] + script = ['import sys'] + from test.test_types import iter_builtin_types, iter_own_slot_wrappers + for cls in iter_builtin_types(): + for slot in iter_own_slot_wrappers(cls): + slots.append((cls, slot)) + attr = f'{cls.__name__}.{slot}' + script.append(f'print("{attr}:", {attr}, file=sys.stderr)') + script.append('') + script = os.linesep.join(script) + + with contextlib.redirect_stderr(io.StringIO()) as stderr: + exec(script) + expected = stderr.getvalue().splitlines() + + out, err = self.run_embedded_interpreter("test_repeated_init_exec", script) + results = err.split('--- Loop #')[1:] + results = [res.rpartition(' ---\n')[-1] for res in results] + + self.maxDiff = None + for i, result in enumerate(results, start=1): + with self.subTest(loop=i): + self.assertEqual(result.splitlines(), expected) + self.assertEqual(out, '') + + class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase): maxDiff = 4096 UTF8_MODE_ERRORS = ('surrogatepass' if MS_WINDOWS else 'surrogateescape') diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py index ca41c76649ca99..e25d22a0bb910e 100644 --- a/Lib/test/test_types.py +++ b/Lib/test/test_types.py @@ -28,6 +28,26 @@ def clear_typing_caches(): f() +def iter_builtin_types(): + for obj in __builtins__.values(): + if not isinstance(obj, type): + continue + cls = obj + if cls.__module__ != 'builtins': + continue + yield cls + + +@cpython_only +def iter_own_slot_wrappers(cls): + for name, value in vars(cls).items(): + if not name.startswith('__') or not name.endswith('__'): + continue + if 'slot wrapper' not in str(value): + continue + yield name + + class TypesTests(unittest.TestCase): def test_truth_values(self): @@ -2267,24 +2287,35 @@ def setUpClass(cls): def test_slot_wrappers(self): rch, sch = interpreters.create_channel() - # For now it's sufficient to check int.__str__. - # See https://github.com/python/cpython/issues/117482 - # and https://github.com/python/cpython/pull/117660. - script = textwrap.dedent(f''' - text = repr(int.__str__) - sch = interpreters.SendChannel({sch.id}) - sch.send_nowait(text) - ''') + slots = [] + script = '' + for cls in iter_builtin_types(): + for slot in iter_own_slot_wrappers(cls): + slots.append((cls, slot)) + attr = f'{cls.__name__}.{slot}' + script += textwrap.dedent(f""" + sch.send_nowait('{attr}: ' + repr({attr})) + """) exec(script) - expected = rch.recv() + all_expected = [] + for cls, slot in slots: + result = rch.recv() + assert result.startswith(f'{cls.__name__}.{slot}: '), (cls, slot, result) + all_expected.append(result) interp = interpreters.create() - interp.run('from test.support import interpreters') + interp.run(textwrap.dedent(f""" + from test.support import interpreters + sch = interpreters.SendChannel({sch.id}) + """)) interp.run(script) - results = rch.recv() - self.assertEqual(results, expected) + for i, _ in enumerate(slots): + with self.subTest(cls=cls, slot=slot): + expected = all_expected[i] + result = rch.recv() + self.assertEqual(result, expected) if __name__ == '__main__': From 371265ada32dcfa392828e91eab397e470f504cc Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 23 Jul 2024 14:59:08 -0600 Subject: [PATCH 2/2] Skip test_slot_wrappers when checking for refleaks. --- Lib/test/test_types.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py index e25d22a0bb910e..c3d08e5b13682e 100644 --- a/Lib/test/test_types.py +++ b/Lib/test/test_types.py @@ -1,6 +1,7 @@ # Python test set -- part 6, built-in types from test.support import run_with_locale, cpython_only, MISSING_C_DOCSTRINGS +from test.test_import import no_rerun import collections.abc from collections import namedtuple import copy @@ -2284,6 +2285,7 @@ def setUpClass(cls): raise unittest.SkipTest('subinterpreters required') @cpython_only + @no_rerun('channels (and queues) might have a refleak; see gh-122199') def test_slot_wrappers(self): rch, sch = interpreters.create_channel()