8000 Merge branch 'main' into typed-clinic/converters · python/cpython@75cb81e · GitHub
[go: up one dir, main page]

Skip to content

Commit 75cb81e

Browse files
authored
Merge branch 'main' into typed-clinic/converters
2 parents bdc130d + a454a66 commit 75cb81e

File tree

19 files changed

+622
-293
lines changed

19 files changed

+622
-293
lines changed

.github/CODEOWNERS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ configure* @erlend-aasland @corona10
2222
**/*hamt* @1st1
2323
Objects/set* @rhettinger
2424
Objects/dict* @methane @markshannon
25+
Objects/typevarobject.c @JelleZijlstra
2526
Objects/type* @markshannon
2627
Objects/codeobject.c @markshannon
2728
Objects/frameobject.c @markshannon
@@ -33,6 +34,7 @@ Python/flowgraph.c @markshannon @iritkatriel
3334
Python/ast_opt.c @isidentical
3435
Lib/test/test_patma.py @brandtbucher
3536
Lib/test/test_peepholer.py @brandtbucher
37+
Lib/test/test_type_*.py @JelleZijlstra
3638

3739
# Exceptions
3840
Lib/traceback.py @iritkatriel

Include/internal/pycore_opcode.h

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/opcode.h

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/opcode.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,8 +233,9 @@ def pseudo_op(name, op, real_ops):
233233
hasfree.append(176)
234234

235235
# Instrumented instructions
236-
MIN_INSTRUMENTED_OPCODE = 238
236+
MIN_INSTRUMENTED_OPCODE = 237
237237

238+
def_op('INSTRUMENTED_LOAD_SUPER_ATTR', 237)
238239
def_op('INSTRUMENTED_POP_JUMP_IF_NONE', 238)
239240
def_op('INSTRUMENTED_POP_JUMP_IF_NOT_NONE', 239)
240241
def_op('INSTRUMENTED_RESUME', 240)

Lib/test/test_monitoring.py

Lines changed: 220 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
"""Test suite for the sys.monitoring."""
22

33
import collections
4+
import dis
45
import functools
56
import operator
67
import sys
8+
import textwrap
79
import types
810
import unittest
911

@@ -506,7 +508,7 @@ def test_lines_single(self):
506508
sys.monitoring.set_events(TEST_TOOL, 0)
507509
sys.monitoring.register_callback(TEST_TOOL, E.LINE, None)
508510
start = LineMonitoringTest.test_lines_single.__code__.co_firstlineno
509-
self.assertEqual(events, [start+7, 14, start+8])
511+
self.assertEqual(events, [start+7, 16, start+8])
510512
finally:
511513
sys.monitoring.set_events(TEST_TOOL, 0)
512514
sys.monitoring.register_callback(TEST_TOOL, E.LINE, None)
@@ -524,7 +526,7 @@ def test_lines_loop(self):
524526
sys.monitoring.set_events(TEST_TOOL, 0)
525527
sys.monitoring.register_callback(TEST_TOOL, E.LINE, None)
526528
start = LineMonitoringTest.test_lines_loop.__code__.co_firstlineno
527-
self.assertEqual(events, [start+7, 21, 22, 21, 22, 21, start+8])
529+
self.assertEqual(events, [start+7, 23, 24, 23, 24, 23, start+8])
528530
finally:
529531
sys.monitoring.set_events(TEST_TOOL, 0)
530532
sys.monitoring.register_callback(TEST_TOOL, E.LINE, None)
@@ -546,7 +548,7 @@ def test_lines_two(self):
546548
sys.monitoring.register_callback(TEST_TOOL, E.LINE, None)
547549
sys.monitoring.register_callback(TEST_TOOL2, E.LINE, None)
548550
start = LineMonitoringTest.test_lines_two.__code__.co_firstlineno
549-
expected = [start+10, 14, start+11]
551+
10000 expected = [start+10, 16, start+11]
550552
self.assertEqual(events, expected)
551553
self.assertEqual(events2, expected)
552554
finally:
@@ -1177,6 +1179,221 @@ def func():
11771179
('return', None),
11781180
('line', 'check_events', 11)])
11791181

1182+
class TestLoadSuperAttr(CheckEvents):
1183+
RECORDERS = CallRecorder, LineRecorder, CRaiseRecorder, CReturnRecorder
1184+
1185+
def _exec(self, co):
1186+
d = {}
1187+
exec(co, d, d)
1188+
return d
1189+
1190+
def _exec_super(self, codestr, optimized=False):
1191+
# The compiler checks for statically visible shadowing of the name
1192+
# `super`, and declines to emit `LOAD_SUPER_ATTR` if shadowing is found.
1193+
# So inserting `super = super` prevents the compiler from emitting
1194+
# `LOAD_SUPER_ATTR`, and allows us to test that monitoring events for
1195+
# `LOAD_SUPER_ATTR` are equivalent to those we'd get from the
1196+
# un-optimized `LOAD_GLOBAL super; CALL; LOAD_ATTR` form.
1197+
assignment = "x = 1" if optimized else "super = super"
1198+
codestr = f"{assignment}\n{textwrap.dedent(codestr)}"
1199+
co = compile(codestr, "<string>", "exec")
1200+
# validate that we really do have a LOAD_SUPER_ATTR, only when optimized
1201+
self.assertEqual(self._has_load_super_attr(co), optimized)
1202+
return self._exec(co)
1203+
1204+
def _has_load_super_attr(self, co):
1205+
has = any(instr.opname == "LOAD_SUPER_ATTR" for instr in dis.get_instructions(co))
1206+
if not has:
1207+
has = any(
1208+
isinstance(c, types.CodeType) and self._has_load_super_attr(c)
1209+
for c in co.co_consts
1210+
)
1211+
return has
1212+
1213+
def _super_method_call(self, optimized=False):
1214+
codestr = """
1215+
class A:
1216+
def method(self, x):
1217+
return x
1218+
1219+
class B(A):
1220+
def method(self, x):
1221+
return super(
1222+
).method(
1223+
x
1224+
)
1225+
1226+
b = B()
1227+
def f():
1228+
return b.method(1)
1229+
"""
1230+
d = self._exec_super(codestr, optimized)
1231+
expected = [
1232+
('line', 'check_events', 10),
1233+
('call', 'f', sys.monitoring.MISSING),
1234+
('line', 'f', 1),
1235+
('call', 'method', d["b"]),
1236+
('line', 'method', 1),
1237+
('call', 'super', sys.monitoring.MISSING),
1238+
('C return', 'super', sys.monitoring.MISSING),
1239+
('line', 'method', 2),
1240+
('line', 'method', 3),
1241+
('line', 'method', 2),
1242+
('call', 'method', 1),
1243+
('line', 'method', 1),
1244+
('line', 'method', 1),
1245+
('line', 'check_events', 11),
1246+
('call', 'set_events', 2),
1247+
]
1248+
return d["f"], expected
1249+
1250+
def test_method_call(self):
1251+
nonopt_func, nonopt_expected = self._super_method_call(optimized=False)
1252+
opt_func, opt_expected = self._super_method_call(optimized=True)
1253+
1254+
self.check_events(nonopt_func, recorders=self.RECORDERS, expected=nonopt_expected)
1255+
self.check_events(opt_func, recorders=self.RECORDERS, expected=opt_expected)
1256+
1257+
def _super_method_call_error(self, optimized=False):
1258+
codestr = """
1259+
class A:
1260+
def method(self, x):
1261+
return x
1262+
1263+
class B(A):
1264+
def method(self, x):
1265+
return super(
1266+
x,
1267+
self,
1268+
).method(
1269+
x
1270+
)
1271+
1272+
b = B()
1273+
def f():
1274+
try:
1275+
return b.method(1)
1276+
except TypeError:
1277+
pass
1278+
else:
1279+
assert False, "should have raised TypeError"
1280+
"""
1281+
d = self._exec_super(codestr, optimized)
1282+
expected = [
1283+
('line', 'check_events', 10),
1284+
('call', 'f', sys.monitoring.MISSING),
1285+
('line', 'f', 1),
1286+
('line', 'f', 2),
1287+
('call', 'method', d["b"]),
1288+
('line', 'method', 1),
1289+
('line', 'method', 2),
1290+
('line', 'method', 3),
1291+
('line', 'method', 1),
1292+
('call', 'super', 1),
1293+
('C raise', 'super', 1),
1294+
('line', 'f', 3),
1295+
('line', 'f', 4),
1296+
('line', 'check_events', 11),
1297+
('call', 'set_events', 2),
1298+
]
1299+
return d["f"], expected
1300+
1301+
def test_method_call_error(self):
1302+
nonopt_func, nonopt_expected = self._super_method_call_error(optimized=False)
1303+
opt_func, opt_expected = self._super_method_call_error(optimized=True)
1304+
1305+
self.check_events(nonopt_func, recorders=self.RECORDERS, expected=nonopt_expected)
1306+
self.check_events(opt_func, recorders=self.RECORDERS, expected=opt_expected)
1307+
1308+
def _super_attr(self, optimized=False):
1309+
codestr = """
1310+
class A:
1311+
x = 1
1312+
1313+
class B(A):
1314+
def method(self):
1315+
return super(
1316+
).x
1317+
1318+
b = B()
1319+
def f():
1320+
return b.method()
1321+
"""
1322+
d = self._exec_super(codestr, optimized)
1323+
expected = [
1324+
('line', 'check_events', 10),
1325+
('call', 'f', sys.monitoring.MISSING),
1326+
('line', 'f', 1),
1327+
('call', 'method', d["b"]),
1328+
('line', 'method', 1),
1329+
('call', 'super', sys.monitoring.MISSING),
1330+
('C return', 'super', sys.monitoring.MISSING),
1331+
('line', 'method', 2),
1332+
('line', 'method', 1),
1333+
('line', 'check_events', 11),
1334+
('call', 'set_events', 2)
1335+
]
1336+
return d["f"], expected
1337+
1338+
def test_attr(self):
1339+
nonopt_func, nonopt_expected = self._super_attr(optimized=False)
1340+
opt_func, opt_expected = self._super_attr(optimized=True)
1341+
1342+
self.check_events(nonopt_func, recorders=self.RECORDERS, expected=nonopt_expected)
1343+
self.check_events(opt_func, recorders=self.RECORDERS, expected=opt_expected)
1344+
1345+
def test_vs_other_type_call(self):
1346+
code_template = textwrap.dedent("""
1347+
class C:
1348+
def method(self):
1349+
return {cls}().__repr__{call}
1350+
c = C()
1351+
def f():
1352+
return c.method()
1353+
""")
1354+
1355+
def get_expected(name, call_method, ns):
1356+
repr_arg = 0 if name == "int" else sys.monitoring.MISSING
1357+
return [
1358+
('line', 'check_events', 10),
1359+
('call', 'f', sys.monitoring.MISSING),
1360+
('line', 'f', 1),
1361+
('call', 'method', ns["c"]),
1362+
('line', 'method', 1),
1363+
('call', name, sys.monitoring.MISSING),
1364+
('C return', name, sys.monitoring.MISSING),
1365+
*(
1366+
[
1367+
('call', '__repr__', repr_arg),
1368+
('C return', '__repr__', repr_arg),
1369+
] if call_method else []
1370+
),
1371+
('line', 'check_events', 11),
1372+
('call', 'set_events', 2),
1373+
]
1374+
1375+
for call_method in [True, False]:
1376+
with self.subTest(call_method=call_method):
1377+
call_str = "()" if call_method else ""
1378+
code_super = code_template.format(cls="super", call=call_str)
1379+
code_int = code_template.format(cls="int", call=call_str)
1380+
co_super = compile(code_super, '<string>', 'exec')
1381+
self.assertTrue(self._has_load_super_attr(co_super))
1382+
ns_super = self._exec(co_super)
1383+
ns_int = self._exec(code_int)
1384+
1385+
self.check_events(
1386+
ns_super["f"],
1387+
recorders=self.RECORDERS,
1388+
expected=get_expected("super", call_method, ns_super)
1389+
)
1390+
self.check_events(
1391+
ns_int["f"],
1392+
recorders=self.RECORDERS,
1393+
expected=get_expected("int", call_method, ns_int)
1394+
)
1395+
1396+
11801397
class TestSetGetEvents(MonitoringTestBase, unittest.TestCase):
11811398

11821399
def test_global(self):

Lib/test/test_typing.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3134,6 +3134,24 @@ def bar(self, x: str) -> str:
31343134

31353135
self.assertIsInstance(Test(), PSub)
31363136

3137+
def test_pep695_generic_protocol_callable_members(self):
3138+
@runtime_checkable
3139+
class Foo[T](Protocol):
3140+
def meth(self, x: T) -> None: ...
3141+
3142+
class Bar[T]:
3143+
def meth(self, x: T) -> None: ...
3144+
3145+
self.assertIsInstance(Bar(), Foo)
3146+
self.assertIsSubclass(Bar, Foo)
3147+
3148+
@runtime_checkable
3149+
class SupportsTrunc[T](Protocol):
3150+
def __trunc__(self) -> T: ...
3151+
3152+
self.assertIsInstance(0.0, SupportsTrunc)
3153+
self.assertIsSubclass(float, SupportsTrunc)
3154+
31373155
def test_init_called(self):
31383156
T = TypeVar('T')
31393157

Lib/typing.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1663,7 +1663,7 @@ class _TypingEllipsis:
16631663
_TYPING_INTERNALS = frozenset({
16641664
'__parameters__', '__orig_bases__', '__orig_class__',
16651665
'_is_protocol', '_is_runtime_protocol', '__protocol_attrs__',
1666-
'__callable_proto_members_only__',
1666+
'__callable_proto_members_only__', '__type_params__',
16671667
})
16681668

16691669
_SPECIAL_NAMES = frozenset({

Modules/_io/bufferedio.c

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2424,13 +2424,6 @@ _io_BufferedRandom___init___impl(buffered *self, PyObject *raw,
24242424
#include "clinic/bufferedio.c.h"
24252425
#undef clinic_state
24262426

2427-
static int
2428-
bufferediobase_traverse(PyObject *self, visitproc visit, void *arg)
2429-
{
2430-
Py_VISIT(Py_TYPE(self));
2431-
return 0;
2432-
}
2433-
24342427
static PyMethodDef bufferediobase_methods[] = {
24352428
_IO__BUFFEREDIOBASE_DETACH_METHODDEF
24362429
_IO__BUFFEREDIOBASE_READ_METHODDEF
@@ -2444,13 +2437,13 @@ static PyMethodDef bufferediobase_methods[] = {
24442437
static PyType_Slot bufferediobase_slots[] = {
24452438
{Py_tp_doc, (void *)bufferediobase_doc},
24462439
{Py_tp_methods, bufferediobase_methods},
2447-
{Py_tp_traverse, bufferediobase_traverse},
24482440
{0, NULL},
24492441
};
24502442

2443+
/* Do not set Py_TPFLAGS_HAVE_GC so that tp_traverse and tp_clear are inherited */
24512444
PyType_Spec bufferediobase_spec = {
24522445
.name = "_io._BufferedIOBase",
2453-
.flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC |
2446+
.flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
24542447
Py_TPFLAGS_IMMUTABLETYPE),
24552448
.slots = bufferediobase_slots,
24562449
};

0 commit comments

Comments
 (0)
0