Skip to content

Commit 30904e7

Browse files
Fix test_slot_wrappers and test_static_types_inherited_slots.
1 parent 716c677 commit 30904e7

File tree

4 files changed

+115
-32
lines changed

4 files changed

+115
-32
lines changed

Lib/test/test_embed.py

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -417,29 +417,53 @@ def test_datetime_reset_strptime(self):
417417
self.assertEqual(out, '20000101\n' * INIT_LOOPS)
418418

419419
def test_static_types_inherited_slots(self):
420-
slots = []
421-
script = ['import sys']
422-
from test.test_types import iter_builtin_types, iter_own_slot_wrappers
420+
script = textwrap.dedent("""
421+
import json
422+
import sys
423+
424+
results = {}
425+
def add(cls, slot, own):
426+
value = getattr(cls, slot)
427+
try:
428+
subresults = results[cls.__name__]
429+
except KeyError:
430+
subresults = results[cls.__name__] = {}
431+
subresults[slot] = repr(value)
432+
433+
{body}
434+
435+
text = json.dumps(results)
436+
print(text, file=sys.stderr)
437+
""")
438+
body = []
439+
from test.test_types import iter_builtin_types, iter_slot_wrappers
423440
for cls in iter_builtin_types():
424-
for slot in iter_own_slot_wrappers(cls):
425-
slots.append((cls, slot))
426-
attr = f'{cls.__name__}.{slot}'
427-
script.append(f'print("{attr}:", {attr}, file=sys.stderr)')
428-
script.append('')
429-
script = os.linesep.join(script)
441+
body.append('')
442+
body.append(f'cls = {cls.__name__}')
443+
for slot, own in iter_slot_wrappers(cls):
444+
body.append(f'add(cls, {slot!r}, {own})')
445+
body.pop(0)
446+
script = script.replace('{body}', os.linesep.join(body))
430447

431448
with contextlib.redirect_stderr(io.StringIO()) as stderr:
432-
exec(script)
433-
expected = stderr.getvalue().splitlines()
449+
ns = {}
450+
exec(script, ns, ns)
451+
expected = json.loads(stderr.getvalue())
434452

435-
out, err = self.run_embedded_interpreter("test_repeated_init_exec", script)
453+
out, err = self.run_embedded_interpreter(
454+
"test_repeated_init_exec", script, script)
436455
results = err.split('--- Loop #')[1:]
437456
results = [res.rpartition(' ---\n')[-1] for res in results]
438457

439458
self.maxDiff = None
440-
for i, result in enumerate(results, start=1):
459+
for i, text in enumerate(results, start=1):
460+
failed = True
441461
with self.subTest(loop=i):
442-
self.assertEqual(result.splitlines(), expected)
462+
result = json.loads(text)
463+
self.assertEqual(result, expected)
464+
failed = False
465+
if failed:
466+
break
443467
self.assertEqual(out, '')
444468

445469

Lib/test/test_types.py

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,49 @@ def iter_builtin_types():
4141

4242

4343
@cpython_only
44-
def iter_own_slot_wrappers(cls):
45-
for name, value in vars(cls).items():
46-
if not name.startswith('__') or not name.endswith('__'):
47-
continue
48-
if 'slot wrapper' not in str(value):
44+
def iter_slot_wrappers(cls):
45+
assert cls.__module__ == 'builtins', cls
46+
47+
def is_slot_wrapper(name, value):
48+
if not isinstance(value, types.WrapperDescriptorType):
49+
assert not repr(value).startswith('<slot wrapper '), (cls, name, value)
50+
return False
51+
assert repr(value).startswith('<slot wrapper '), (cls, name, value)
52+
assert callable(value), (cls, name, value)
53+
assert name.startswith('__') and name.endswith('__'), (cls, name, value)
54+
return True
55+
56+
ns = vars(cls)
57+
unused = set(ns)
58+
for name in dir(cls):
59+
if name in ns:
60+
unused.remove(name)
61+
62+
try:
63+
value = getattr(cls, name)
64+
except AttributeError:
65+
# It's as though it weren't in __dir__.
66+
assert name in ('__annotate__', '__annotations__', '__abstractmethods__'), (cls, name)
67+
if name in ns and is_slot_wrapper(name, ns[name]):
68+
unused.add(name)
4969
continue
50-
yield name
70+
71+
if not name.startswith('__') or not name.endswith('__'):
72+
assert not is_slot_wrapper(name, value), (cls, name, value)
73+
if not is_slot_wrapper(name, value):
74+
if name in ns:
75+
assert not is_slot_wrapper(name, ns[name]), (cls, name, value, ns[name])
76+
else:
77+
if name in ns:
78+
assert ns[name] is value, (cls, name, value, ns[name])
79+
yield name, True
80+
else:
81+
yield name, False
82+
83+
for name in unused:
84+
value = ns[name]
85+
if is_slot_wrapper(cls, name, value):
86+
yield name, True
5187

5288

5389
class TypesTests(unittest.TestCase):
@@ -2387,26 +2423,26 @@ def test_slot_wrappers(self):
23872423
slots = []
23882424
script = ''
23892425
for cls in iter_builtin_types():
2390-
for slot in iter_own_slot_wrappers(cls):
2391-
slots.append((cls, slot))
2426+
for slot, own in iter_slot_wrappers(cls):
2427+
slots.append((cls, slot, own))
23922428
script += textwrap.dedent(f"""
23932429
text = repr({cls.__name__}.{slot})
23942430
sch.send_nowait(({cls.__name__!r}, {slot!r}, text))
23952431
""")
23962432

23972433
exec(script)
23982434
all_expected = []
2399-
for cls, slot in slots:
2435+
for cls, slot, _ in slots:
24002436
result = rch.recv()
2401-
assert result == (cls.__name__, slot, result[2]), (cls, slot, result)
2437+
assert result == (cls.__name__, slot, result[-1]), (cls, slot, result)
24022438
all_expected.append(result)
24032439

24042440
interp = interpreters.create()
24052441
interp.exec('from test.support import interpreters')
24062442
interp.prepare_main(sch=sch)
24072443
interp.exec(script)
24082444

2409-
for i, _ in enumerate(slots):
2445+
for i, (cls, slot, _) in enumerate(slots):
24102446
with self.subTest(cls=cls, slot=slot):
24112447
expected = all_expected[i]
24122448
result = rch.recv()

Objects/typeobject.c

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10877,7 +10877,25 @@ expect_manually_inherited(PyTypeObject *type, void **slot)
1087710877
&& typeobj != PyExc_StopIteration
1087810878
&& typeobj != PyExc_SyntaxError
1087910879
&& typeobj != PyExc_UnicodeDecodeError
10880-
&& typeobj != PyExc_UnicodeEncodeError)
10880+
&& typeobj != PyExc_UnicodeEncodeError
10881+
10882+
&& type != &PyBool_Type
10883+
&& type != &PyBytes_Type
10884+
&& type != &PyMemoryView_Type
10885+
&& type != &PyComplex_Type
10886+
&& type != &PyEnum_Type
10887+
&& type != &PyFilter_Type
10888+
&& type != &PyFloat_Type
10889+
&& type != &PyFrozenSet_Type
10890+
&& type != &PyLong_Type
10891+
&& type != &PyMap_Type
10892+
&& type != &PyRange_Type
10893+
&& type != &PyReversed_Type
10894+
&& type != &PySlice_Type
10895+
&& type != &PyTuple_Type
10896+
&& type != &PyUnicode_Type
10897+
&& type != &PyZip_Type)
10898+
1088110899
{
1088210900
return 1;
1088310901
}
@@ -10895,10 +10913,8 @@ expect_manually_inherited(PyTypeObject *type, void **slot)
1089510913
/* This is a best-effort list of builtin types
1089610914
that have their own tp_getattr function. */
1089710915
if (typeobj == PyExc_BaseException
10898-
|| type == &PyBool_Type
1089910916
|| type == &PyByteArray_Type
1090010917
|| type == &PyBytes_Type
10901-
|| type == &PyClassMethod_Type
1090210918
|| type == &PyComplex_Type
1090310919
|| type == &PyDict_Type
1090410920
|| type == &PyEnum_Type
@@ -10912,7 +10928,6 @@ expect_manually_inherited(PyTypeObject *type, void **slot)
1091210928
|| type == &PyReversed_Type
1091310929
|| type == &PySet_Type
1091410930
|| type == &PySlice_Type
10915-
|| type == &PyStaticMethod_Type
1091610931
|| type == &PySuper_Type
1091710932
|| type == &PyTuple_Type
1091810933
|| type == &PyZip_Type)

Programs/_testembed.c

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -170,15 +170,23 @@ PyInit_embedded_ext(void)
170170
static int test_repeated_init_exec(void)
171171
{
172172
if (main_argc < 3) {
173-
fprintf(stderr, "usage: %s test_repeated_init_exec CODE\n", PROGRAM);
173+
fprintf(stderr,
174+
"usage: %s test_repeated_init_exec CODE ...\n", PROGRAM);
174175
exit(1);
175176
}
176177
const char *code = main_argv[2];
178+
int loops = main_argc > 3
179+
? main_argc - 2
180+
: INIT_LOOPS;
177181

178-
for (int i=1; i <= INIT_LOOPS; i++) {
179-
fprintf(stderr, "--- Loop #%d ---\n", i);
182+
for (int i=0; i < loops; i++) {
183+
fprintf(stderr, "--- Loop #%d ---\n", i+1);
180184
fflush(stderr);
181185

186+
if (main_argc > 3) {
187+
code = main_argv[i+2];
188+
}
189+
182190
_testembed_Py_InitializeFromConfig();
183191
int err = PyRun_SimpleString(code);
184192
Py_Finalize();

0 commit comments

Comments
 (0)