Skip to content

Commit f8a2fab

Browse files
authored
pythonGH-92239: Make sure that PEP 523 is supported, even when specializing first. (pythonGH-92245)
1 parent 9d20e1a commit f8a2fab

File tree

5 files changed

+74
-1
lines changed

5 files changed

+74
-1
lines changed

Lib/test/test_capi.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1142,5 +1142,33 @@ def test_frame_get_generator(self):
11421142
self.assertIs(gen, _testcapi.frame_getgenerator(frame))
11431143

11441144

1145+
SUFFICIENT_TO_DEOPT_AND_SPECIALIZE = 100
1146+
1147+
class Test_Pep523API(unittest.TestCase):
1148+
1149+
def do_test(self, func):
1150+
calls = []
1151+
start = SUFFICIENT_TO_DEOPT_AND_SPECIALIZE
1152+
count = start + SUFFICIENT_TO_DEOPT_AND_SPECIALIZE
1153+
for i in range(count):
1154+
if i == start:
1155+
_testinternalcapi.set_eval_frame_record(calls)
1156+
func()
1157+
_testinternalcapi.set_eval_frame_default()
1158+
self.assertEqual(len(calls), SUFFICIENT_TO_DEOPT_AND_SPECIALIZE)
1159+
for name in calls:
1160+
self.assertEqual(name, func.__name__)
1161+
1162+
def test_pep523_with_specialization_simple(self):
1163+
def func1():
1164+
pass
1165+
self.do_test(func1)
1166+
1167+
def test_pep523_with_specialization_with_default(self):
1168+
def func2(x=None):
1169+
pass
1170+
self.do_test(func2)
1171+
1172+
11451173
if __name__ == "__main__":
11461174
unittest.main()
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Make sure that PEP 523 is respected in all cases. In 3.11a7, specialization
2+
may have prevented Python-to-Python calls respecting PEP 523.

Modules/_testinternalcapi.c

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,15 @@
1515
#include "pycore_atomic_funcs.h" // _Py_atomic_int_get()
1616
#include "pycore_bitutils.h" // _Py_bswap32()
1717
#include "pycore_fileutils.h" // _Py_normpath
18+
#include "pycore_frame.h" // _PyInterpreterFrame
1819
#include "pycore_gc.h" // PyGC_Head
1920
#include "pycore_hashtable.h" // _Py_hashtable_new()
2021
#include "pycore_initconfig.h" // _Py_GetConfigsAsDict()
2122
#include "pycore_pathconfig.h" // _PyPathConfig_ClearGlobal()
2223
#include "pycore_interp.h" // _PyInterpreterState_GetConfigCopy()
2324
#include "pycore_pyerrors.h" // _Py_UTF8_Edit_Cost()
2425
#include "pycore_pystate.h" // _PyThreadState_GET()
25-
#include "osdefs.h" // MAXPATHLEN
26+
#include "osdefs.h" // MAXPATHLEN
2627

2728

2829
static PyObject *
@@ -491,6 +492,38 @@ decode_locale_ex(PyObject *self, PyObject *args)
491492
return res;
492493
}
493494

495+
static PyObject *record_list = NULL;
496+
497+
static PyObject *
498+
set_eval_frame_default(PyObject *self, PyObject *Py_UNUSED(args))
499+
{
500+
_PyInterpreterState_SetEvalFrameFunc(PyInterpreterState_Get(), _PyEval_EvalFrameDefault);
501+
Py_CLEAR(record_list);
502+
Py_RETURN_NONE;
503+
}
504+
505+
static PyObject *
506+
record_eval(PyThreadState *tstate, struct _PyInterpreterFrame *f, int exc)
507+
{
508+
PyList_Append(record_list, f->f_func->func_name);
509+
return _PyEval_EvalFrameDefault(tstate, f, exc);
510+
}
511+
512+
513+
static PyObject *
514+
set_eval_frame_record(PyObject *self, PyObject *list)
515+
{
516+
if (!PyList_Check(list)) {
517+
PyErr_SetString(PyExc_TypeError, "argument must be a list");
518+
return NULL;
519+
}
520+
Py_CLEAR(record_list);
521+
Py_INCREF(list);
522+
record_list = list;
523+
_PyInterpreterState_SetEvalFrameFunc(PyInterpreterState_Get(), record_eval);
524+
Py_RETURN_NONE;
525+
}
526+
494527

495528
static PyMethodDef TestMethods[] = {
496529
{"get_configs", get_configs, METH_NOARGS},
@@ -508,6 +541,8 @@ static PyMethodDef TestMethods[] = {
508541
{"get_getpath_codeobject", get_getpath_codeobject, METH_NOARGS, NULL},
509542
{"EncodeLocaleEx", encode_locale_ex, METH_VARARGS},
510543
{"DecodeLocaleEx", decode_locale_ex, METH_VARARGS},
544+
{"set_eval_frame_default", set_eval_frame_default, METH_NOARGS, NULL},
545+
{"set_eval_frame_record", set_eval_frame_record, METH_O, NULL},
511546
{NULL, NULL} /* sentinel */
512547
};
513548

Python/ceval.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4890,6 +4890,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
48904890

48914891
TARGET(CALL_PY_EXACT_ARGS) {
48924892
assert(call_shape.kwnames == NULL);
4893+
DEOPT_IF(tstate->interp->eval_frame, CALL);
48934894
_PyCallCache *cache = (_PyCallCache *)next_instr;
48944895
int is_meth = is_method(stack_pointer, oparg);
48954896
int argcount = oparg + is_meth;
@@ -4923,6 +4924,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
49234924

49244925
TARGET(CALL_PY_WITH_DEFAULTS) {
49254926
assert(call_shape.kwnames == NULL);
4927+
DEOPT_IF(tstate->interp->eval_frame, CALL);
49264928
_PyCallCache *cache = (_PyCallCache *)next_instr;
49274929
int is_meth = is_method(stack_pointer, oparg);
49284930
int argcount = oparg + is_meth;

Python/specialize.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,7 @@ initial_counter_value(void) {
440440
#define SPEC_FAIL_CALL_METHOD_WRAPPER 26
441441
#define SPEC_FAIL_CALL_OPERATOR_WRAPPER 27
442442
#define SPEC_FAIL_CALL_PYFUNCTION 28
443+
#define SPEC_FAIL_CALL_PEP_523 29
443444

444445
/* COMPARE_OP */
445446
#define SPEC_FAIL_COMPARE_OP_DIFFERENT_TYPES 12
@@ -1471,6 +1472,11 @@ specialize_py_call(PyFunctionObject *func, _Py_CODEUNIT *instr, int nargs,
14711472
assert(_Py_OPCODE(*instr) == CALL_ADAPTIVE);
14721473
PyCodeObject *code = (PyCodeObject *)func->func_code;
14731474
int kind = function_kind(code);
1475+
/* Don't specialize if PEP 523 is active */
1476+
if (_PyInterpreterState_GET()->eval_frame) {
1477+
SPECIALIZATION_FAIL(CALL, SPEC_FAIL_CALL_PEP_523);
1478+
return -1;
1479+
}
14741480
if (kwnames) {
14751481
SPECIALIZATION_FAIL(CALL, SPEC_FAIL_CALL_KWNAMES);
14761482
return -1;

0 commit comments

Comments
 (0)