summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/test/support/__init__.py55
-rw-r--r--Lib/test/test_embed.py57
-rw-r--r--Lib/test/test_types.py65
-rw-r--r--Programs/_testembed.c14
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();