mirror of
https://github.com/python/cpython.git
synced 2025-09-27 02:39:58 +00:00
[3.12] gh-117482: Fix the Slot Wrapper Inheritance Tests (gh-122250)
The tests were only checking cases where the slot wrapper was present in the initial case. They were missing when the slot wrapper was added in the additional initializations. This fixes that.
(cherry-picked from commit 490e0ad83a
, AKA gh-122248)
This commit is contained in:
parent
e29910447b
commit
5bd2ea2b94
5 changed files with 129 additions and 50 deletions
|
@ -2413,3 +2413,58 @@ def copy_python_src_ignore(path, names):
|
||||||
'build',
|
'build',
|
||||||
}
|
}
|
||||||
return ignored
|
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
|
||||||
|
|
|
@ -391,29 +391,47 @@ class EmbeddingTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
self.assertEqual(out, '9\n' * INIT_LOOPS)
|
self.assertEqual(out, '9\n' * INIT_LOOPS)
|
||||||
|
|
||||||
def test_static_types_inherited_slots(self):
|
def test_static_types_inherited_slots(self):
|
||||||
slots = []
|
script = textwrap.dedent("""
|
||||||
script = ['import sys']
|
import test.support
|
||||||
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:
|
results = {}
|
||||||
exec(script)
|
def add(cls, slot, own):
|
||||||
expected = stderr.getvalue().splitlines()
|
value = getattr(cls, slot)
|
||||||
|
try:
|
||||||
|
subresults = results[cls.__name__]
|
||||||
|
except KeyError:
|
||||||
|
subresults = results[cls.__name__] = {}
|
||||||
|
subresults[slot] = [repr(value), own]
|
||||||
|
|
||||||
out, err = self.run_embedded_interpreter("test_repeated_init_exec", script)
|
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 = err.split('--- Loop #')[1:]
|
||||||
results = [res.rpartition(' ---\n')[-1] for res in results]
|
results = [res.rpartition(' ---\n')[-1] for res in results]
|
||||||
|
|
||||||
self.maxDiff = None
|
self.maxDiff = None
|
||||||
for i, result in enumerate(results, start=1):
|
for i, text in enumerate(results, start=1):
|
||||||
with self.subTest(loop=i):
|
result = json.loads(text)
|
||||||
self.assertEqual(result.splitlines(), expected)
|
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, '')
|
self.assertEqual(out, '')
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
# Python test set -- part 6, built-in types
|
# Python test set -- part 6, built-in types
|
||||||
|
|
||||||
from test.support import run_with_locale, cpython_only, MISSING_C_DOCSTRINGS
|
from test.support import (
|
||||||
|
run_with_locale, cpython_only, iter_builtin_types, iter_slot_wrappers,
|
||||||
|
MISSING_C_DOCSTRINGS,
|
||||||
|
)
|
||||||
from test.test_import import no_rerun
|
from test.test_import import no_rerun
|
||||||
import collections.abc
|
import collections.abc
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
|
@ -29,26 +32,6 @@ def clear_typing_caches():
|
||||||
f()
|
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):
|
class TypesTests(unittest.TestCase):
|
||||||
|
|
||||||
def test_truth_values(self):
|
def test_truth_values(self):
|
||||||
|
@ -2286,14 +2269,14 @@ class SubinterpreterTests(unittest.TestCase):
|
||||||
|
|
||||||
@cpython_only
|
@cpython_only
|
||||||
@no_rerun('channels (and queues) might have a refleak; see gh-122199')
|
@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.create_channel()
|
rch, sch = interpreters.create_channel()
|
||||||
|
|
||||||
slots = []
|
slots = []
|
||||||
script = ''
|
script = ''
|
||||||
for cls in iter_builtin_types():
|
for cls in iter_builtin_types():
|
||||||
for slot in iter_own_slot_wrappers(cls):
|
for slot, own in iter_slot_wrappers(cls):
|
||||||
slots.append((cls, slot))
|
slots.append((cls, slot, own))
|
||||||
attr = f'{cls.__name__}.{slot}'
|
attr = f'{cls.__name__}.{slot}'
|
||||||
script += textwrap.dedent(f"""
|
script += textwrap.dedent(f"""
|
||||||
sch.send_nowait('{attr}: ' + repr({attr}))
|
sch.send_nowait('{attr}: ' + repr({attr}))
|
||||||
|
@ -2301,7 +2284,7 @@ class SubinterpreterTests(unittest.TestCase):
|
||||||
|
|
||||||
exec(script)
|
exec(script)
|
||||||
all_expected = []
|
all_expected = []
|
||||||
for cls, slot in slots:
|
for cls, slot, _ in slots:
|
||||||
result = rch.recv()
|
result = rch.recv()
|
||||||
assert result.startswith(f'{cls.__name__}.{slot}: '), (cls, slot, result)
|
assert result.startswith(f'{cls.__name__}.{slot}: '), (cls, slot, result)
|
||||||
all_expected.append(result)
|
all_expected.append(result)
|
||||||
|
@ -2313,7 +2296,7 @@ class SubinterpreterTests(unittest.TestCase):
|
||||||
"""))
|
"""))
|
||||||
interp.run(script)
|
interp.run(script)
|
||||||
|
|
||||||
for i, _ in enumerate(slots):
|
for i, (cls, slot, _) in enumerate(slots):
|
||||||
with self.subTest(cls=cls, slot=slot):
|
with self.subTest(cls=cls, slot=slot):
|
||||||
expected = all_expected[i]
|
expected = all_expected[i]
|
||||||
result = rch.recv()
|
result = rch.recv()
|
||||||
|
|
|
@ -10094,7 +10094,25 @@ expect_manually_inherited(PyTypeObject *type, void **slot)
|
||||||
&& typeobj != PyExc_StopIteration
|
&& typeobj != PyExc_StopIteration
|
||||||
&& typeobj != PyExc_SyntaxError
|
&& typeobj != PyExc_SyntaxError
|
||||||
&& typeobj != PyExc_UnicodeDecodeError
|
&& typeobj != PyExc_UnicodeDecodeError
|
||||||
&& typeobj != PyExc_UnicodeEncodeError)
|
&& typeobj != PyExc_UnicodeEncodeError
|
||||||
|
|
||||||
|
&& type != &PyBool_Type
|
||||||
|
&& type != &PyBytes_Type
|
||||||
|
&& type != &PyMemoryView_Type
|
||||||
|
&& type != &PyComplex_Type
|
||||||
|
&& type != &PyEnum_Type
|
||||||
|
&& type != &PyFilter_Type
|
||||||
|
&& type != &PyFloat_Type
|
||||||
|
&& type != &PyFrozenSet_Type
|
||||||
|
&& type != &PyLong_Type
|
||||||
|
&& type != &PyMap_Type
|
||||||
|
&& type != &PyRange_Type
|
||||||
|
&& type != &PyReversed_Type
|
||||||
|
&& type != &PySlice_Type
|
||||||
|
&& type != &PyTuple_Type
|
||||||
|
&& type != &PyUnicode_Type
|
||||||
|
&& type != &PyZip_Type)
|
||||||
|
|
||||||
{
|
{
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -10112,10 +10130,8 @@ expect_manually_inherited(PyTypeObject *type, void **slot)
|
||||||
/* This is a best-effort list of builtin types
|
/* This is a best-effort list of builtin types
|
||||||
that have their own tp_getattr function. */
|
that have their own tp_getattr function. */
|
||||||
if (typeobj == PyExc_BaseException
|
if (typeobj == PyExc_BaseException
|
||||||
|| type == &PyBool_Type
|
|
||||||
|| type == &PyByteArray_Type
|
|| type == &PyByteArray_Type
|
||||||
|| type == &PyBytes_Type
|
|| type == &PyBytes_Type
|
||||||
|| type == &PyClassMethod_Type
|
|
||||||
|| type == &PyComplex_Type
|
|| type == &PyComplex_Type
|
||||||
|| type == &PyDict_Type
|
|| type == &PyDict_Type
|
||||||
|| type == &PyEnum_Type
|
|| type == &PyEnum_Type
|
||||||
|
@ -10129,7 +10145,6 @@ expect_manually_inherited(PyTypeObject *type, void **slot)
|
||||||
|| type == &PyReversed_Type
|
|| type == &PyReversed_Type
|
||||||
|| type == &PySet_Type
|
|| type == &PySet_Type
|
||||||
|| type == &PySlice_Type
|
|| type == &PySlice_Type
|
||||||
|| type == &PyStaticMethod_Type
|
|
||||||
|| type == &PySuper_Type
|
|| type == &PySuper_Type
|
||||||
|| type == &PyTuple_Type
|
|| type == &PyTuple_Type
|
||||||
|| type == &PyZip_Type)
|
|| type == &PyZip_Type)
|
||||||
|
|
|
@ -163,15 +163,23 @@ PyInit_embedded_ext(void)
|
||||||
static int test_repeated_init_exec(void)
|
static int test_repeated_init_exec(void)
|
||||||
{
|
{
|
||||||
if (main_argc < 3) {
|
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);
|
exit(1);
|
||||||
}
|
}
|
||||||
const char *code = main_argv[2];
|
const char *code = main_argv[2];
|
||||||
|
int loops = main_argc > 3
|
||||||
|
? main_argc - 2
|
||||||
|
: INIT_LOOPS;
|
||||||
|
|
||||||
for (int i=1; i <= INIT_LOOPS; i++) {
|
for (int i=0; i < loops; i++) {
|
||||||
fprintf(stderr, "--- Loop #%d ---\n", i);
|
fprintf(stderr, "--- Loop #%d ---\n", i+1);
|
||||||
fflush(stderr);
|
fflush(stderr);
|
||||||
|
|
||||||
|
if (main_argc > 3) {
|
||||||
|
code = main_argv[i+2];
|
||||||
|
}
|
||||||
|
|
||||||
_testembed_Py_InitializeFromConfig();
|
_testembed_Py_InitializeFromConfig();
|
||||||
int err = PyRun_SimpleString(code);
|
int err = PyRun_SimpleString(code);
|
||||||
Py_Finalize();
|
Py_Finalize();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue