diff options
-rw-r--r-- | Lib/test/support/__init__.py | 55 | ||||
-rw-r--r-- | Lib/test/test_embed.py | 57 | ||||
-rw-r--r-- | Lib/test/test_types.py | 65 | ||||
-rw-r--r-- | Programs/_testembed.c | 14 |
4 files changed, 143 insertions, 48 deletions
diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 37e3305..f4dce79 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -2608,6 +2608,61 @@ def copy_python_src_ignore(path, names): return ignored +def iter_builtin_types(): + for obj in __builtins__.values(): + if not isinstance(obj, type): + continue + cls = obj + if cls.__module__ != 'builtins': + continue + yield cls + + +def iter_slot_wrappers(cls): + assert cls.__module__ == 'builtins', cls + + def is_slot_wrapper(name, value): + if not isinstance(value, types.WrapperDescriptorType): + assert not repr(value).startswith('<slot wrapper '), (cls, name, value) + return False + assert repr(value).startswith('<slot wrapper '), (cls, name, value) + assert callable(value), (cls, name, value) + assert name.startswith('__') and name.endswith('__'), (cls, name, value) + return True + + ns = vars(cls) + unused = set(ns) + for name in dir(cls): + if name in ns: + unused.remove(name) + + try: + value = getattr(cls, name) + except AttributeError: + # It's as though it weren't in __dir__. + assert name in ('__annotate__', '__annotations__', '__abstractmethods__'), (cls, name) + if name in ns and is_slot_wrapper(name, ns[name]): + unused.add(name) + continue + + if not name.startswith('__') or not name.endswith('__'): + assert not is_slot_wrapper(name, value), (cls, name, value) + if not is_slot_wrapper(name, value): + if name in ns: + assert not is_slot_wrapper(name, ns[name]), (cls, name, value, ns[name]) + else: + if name in ns: + assert ns[name] is value, (cls, name, value, ns[name]) + yield name, True + else: + yield name, False + + for name in unused: + value = ns[name] + if is_slot_wrapper(cls, name, value): + yield name, True + + def force_not_colorized(func): """Force the terminal not to be colorized.""" @functools.wraps(func) diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py index fb7995e..9602f1a 100644 --- a/Lib/test/test_embed.py +++ b/Lib/test/test_embed.py @@ -416,30 +416,49 @@ class EmbeddingTests(EmbeddingTestsMixin, unittest.TestCase): out, err = self.run_embedded_interpreter("test_repeated_init_exec", code) self.assertEqual(out, '20000101\n' * INIT_LOOPS) + @unittest.skip('inheritance across re-init is currently broken; see gh-117482') 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) + script = textwrap.dedent(""" + import test.support + + results = {} + def add(cls, slot, own): + value = getattr(cls, slot) + try: + subresults = results[cls.__name__] + except KeyError: + subresults = results[cls.__name__] = {} + subresults[slot] = [repr(value), own] + + for cls in test.support.iter_builtin_types(): + for slot, own in test.support.iter_slot_wrappers(cls): + add(cls, slot, own) + """) + + ns = {} + exec(script, ns, ns) + all_expected = ns['results'] + del ns + + script += textwrap.dedent(""" + import json + import sys + text = json.dumps(results) + print(text, file=sys.stderr) + """) + out, err = self.run_embedded_interpreter( + "test_repeated_init_exec", script, 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) + for i, text in enumerate(results, start=1): + result = json.loads(text) + for classname, expected in all_expected.items(): + with self.subTest(loop=i, cls=classname): + slots = result.pop(classname) + self.assertEqual(slots, expected) + self.assertEqual(result, {}) self.assertEqual(out, '') diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py index a84f43b..bcdd6d2 100644 --- a/Lib/test/test_types.py +++ b/Lib/test/test_types.py @@ -2,6 +2,7 @@ from test.support import ( run_with_locale, is_apple_mobile, cpython_only, no_rerun, + iter_builtin_types, iter_slot_wrappers, MISSING_C_DOCSTRINGS, ) import collections.abc @@ -32,26 +33,6 @@ 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): @@ -2371,6 +2352,36 @@ class FunctionTests(unittest.TestCase): class SubinterpreterTests(unittest.TestCase): + NUMERIC_METHODS = { + '__abs__', + '__add__', + '__bool__', + '__divmod__', + '__float__', + '__floordiv__', + '__index__', + '__int__', + '__lshift__', + '__mod__', + '__mul__', + '__neg__', + '__pos__', + '__pow__', + '__radd__', + '__rdivmod__', + '__rfloordiv__', + '__rlshift__', + '__rmod__', + '__rmul__', + '__rpow__', + '__rrshift__', + '__rshift__', + '__rsub__', + '__rtruediv__', + '__sub__', + '__truediv__', + } + @classmethod def setUpClass(cls): global interpreters @@ -2382,14 +2393,16 @@ class SubinterpreterTests(unittest.TestCase): @cpython_only @no_rerun('channels (and queues) might have a refleak; see gh-122199') - def test_slot_wrappers(self): + def test_static_types_inherited_slots(self): rch, sch = interpreters.channels.create() slots = [] script = '' for cls in iter_builtin_types(): - for slot in iter_own_slot_wrappers(cls): - slots.append((cls, slot)) + for slot, own in iter_slot_wrappers(cls): + if cls is bool and slot in self.NUMERIC_METHODS: + continue + slots.append((cls, slot, own)) script += textwrap.dedent(f""" text = repr({cls.__name__}.{slot}) sch.send_nowait(({cls.__name__!r}, {slot!r}, text)) @@ -2397,9 +2410,9 @@ class SubinterpreterTests(unittest.TestCase): exec(script) all_expected = [] - for cls, slot in slots: + for cls, slot, _ in slots: result = rch.recv() - assert result == (cls.__name__, slot, result[2]), (cls, slot, result) + assert result == (cls.__name__, slot, result[-1]), (cls, slot, result) all_expected.append(result) interp = interpreters.create() @@ -2407,7 +2420,7 @@ class SubinterpreterTests(unittest.TestCase): interp.prepare_main(sch=sch) interp.exec(script) - for i, _ in enumerate(slots): + for i, (cls, slot, _) in enumerate(slots): with self.subTest(cls=cls, slot=slot): expected = all_expected[i] result = rch.recv() diff --git a/Programs/_testembed.c b/Programs/_testembed.c index d149b6a..2c726c9 100644 --- a/Programs/_testembed.c +++ b/Programs/_testembed.c @@ -170,15 +170,23 @@ PyInit_embedded_ext(void) static int test_repeated_init_exec(void) { if (main_argc < 3) { - fprintf(stderr, "usage: %s test_repeated_init_exec CODE\n", PROGRAM); + fprintf(stderr, + "usage: %s test_repeated_init_exec CODE ...\n", PROGRAM); exit(1); } const char *code = main_argv[2]; + int loops = main_argc > 3 + ? main_argc - 2 + : INIT_LOOPS; - for (int i=1; i <= INIT_LOOPS; i++) { - fprintf(stderr, "--- Loop #%d ---\n", i); + for (int i=0; i < loops; i++) { + fprintf(stderr, "--- Loop #%d ---\n", i+1); fflush(stderr); + if (main_argc > 3) { + code = main_argv[i+2]; + } + _testembed_Py_InitializeFromConfig(); int err = PyRun_SimpleString(code); Py_Finalize(); |