]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
GH-92239: Make sure that PEP 523 is supported, even when specializing first. (GH...
authorMark Shannon <mark@hotpy.org>
Wed, 4 May 2022 15:31:21 +0000 (09:31 -0600)
committerGitHub <noreply@github.com>
Wed, 4 May 2022 15:31:21 +0000 (09:31 -0600)
Lib/test/test_capi.py
Misc/NEWS.d/next/Core and Builtins/2022-05-03-14-55-40.gh-issue-92245.G17-5i.rst [new file with mode: 0644]
Modules/_testinternalcapi.c
Python/ceval.c
Python/specialize.c

index 1aed9b71c51aa3d884b56743b391cde0d97ae7de..ab4caefd35fbf637579cecc9e104835ba1d98cb3 100644 (file)
@@ -1142,5 +1142,33 @@ class Test_FrameAPI(unittest.TestCase):
         self.assertIs(gen, _testcapi.frame_getgenerator(frame))
 
 
+SUFFICIENT_TO_DEOPT_AND_SPECIALIZE = 100
+
+class Test_Pep523API(unittest.TestCase):
+
+    def do_test(self, func):
+        calls = []
+        start = SUFFICIENT_TO_DEOPT_AND_SPECIALIZE
+        count = start + SUFFICIENT_TO_DEOPT_AND_SPECIALIZE
+        for i in range(count):
+            if i == start:
+                _testinternalcapi.set_eval_frame_record(calls)
+            func()
+        _testinternalcapi.set_eval_frame_default()
+        self.assertEqual(len(calls), SUFFICIENT_TO_DEOPT_AND_SPECIALIZE)
+        for name in calls:
+            self.assertEqual(name, func.__name__)
+
+    def test_pep523_with_specialization_simple(self):
+        def func1():
+            pass
+        self.do_test(func1)
+
+    def test_pep523_with_specialization_with_default(self):
+        def func2(x=None):
+            pass
+        self.do_test(func2)
+
+
 if __name__ == "__main__":
     unittest.main()
diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-05-03-14-55-40.gh-issue-92245.G17-5i.rst b/Misc/NEWS.d/next/Core and Builtins/2022-05-03-14-55-40.gh-issue-92245.G17-5i.rst
new file mode 100644 (file)
index 0000000..7b1c5f5
--- /dev/null
@@ -0,0 +1,2 @@
+Make sure that PEP 523 is respected in all cases. In 3.11a7, specialization
+may have prevented Python-to-Python calls respecting PEP 523.
index 5d5b3e6b2fd62603115cfe8de89420e0a6c97b25..914b20b36f146b10ddc8e5250e6940fd95376b80 100644 (file)
@@ -15,6 +15,7 @@
 #include "pycore_atomic_funcs.h" // _Py_atomic_int_get()
 #include "pycore_bitutils.h"     // _Py_bswap32()
 #include "pycore_fileutils.h"    // _Py_normpath
+#include "pycore_frame.h"        // _PyInterpreterFrame
 #include "pycore_gc.h"           // PyGC_Head
 #include "pycore_hashtable.h"    // _Py_hashtable_new()
 #include "pycore_initconfig.h"   // _Py_GetConfigsAsDict()
@@ -22,7 +23,7 @@
 #include "pycore_interp.h"       // _PyInterpreterState_GetConfigCopy()
 #include "pycore_pyerrors.h"     // _Py_UTF8_Edit_Cost()
 #include "pycore_pystate.h"      // _PyThreadState_GET()
-#include "osdefs.h"               // MAXPATHLEN
+#include "osdefs.h"              // MAXPATHLEN
 
 
 static PyObject *
@@ -491,6 +492,38 @@ decode_locale_ex(PyObject *self, PyObject *args)
     return res;
 }
 
+static PyObject *record_list = NULL;
+
+static PyObject *
+set_eval_frame_default(PyObject *self, PyObject *Py_UNUSED(args))
+{
+    _PyInterpreterState_SetEvalFrameFunc(PyInterpreterState_Get(), _PyEval_EvalFrameDefault);
+    Py_CLEAR(record_list);
+    Py_RETURN_NONE;
+}
+
+static PyObject *
+record_eval(PyThreadState *tstate, struct _PyInterpreterFrame *f, int exc)
+{
+    PyList_Append(record_list, f->f_func->func_name);
+    return _PyEval_EvalFrameDefault(tstate, f, exc);
+}
+
+
+static PyObject *
+set_eval_frame_record(PyObject *self, PyObject *list)
+{
+    if (!PyList_Check(list)) {
+        PyErr_SetString(PyExc_TypeError, "argument must be a list");
+        return NULL;
+    }
+    Py_CLEAR(record_list);
+    Py_INCREF(list);
+    record_list = list;
+    _PyInterpreterState_SetEvalFrameFunc(PyInterpreterState_Get(), record_eval);
+    Py_RETURN_NONE;
+}
+
 
 static PyMethodDef TestMethods[] = {
     {"get_configs", get_configs, METH_NOARGS},
@@ -508,6 +541,8 @@ static PyMethodDef TestMethods[] = {
     {"get_getpath_codeobject", get_getpath_codeobject, METH_NOARGS, NULL},
     {"EncodeLocaleEx", encode_locale_ex, METH_VARARGS},
     {"DecodeLocaleEx", decode_locale_ex, METH_VARARGS},
+    {"set_eval_frame_default", set_eval_frame_default, METH_NOARGS, NULL},
+    {"set_eval_frame_record", set_eval_frame_record, METH_O, NULL},
     {NULL, NULL} /* sentinel */
 };
 
index 6f46c7f043d774005dad8c5226074b680e860049..b2735a1b2d3959c187c03abb0e1aac6041592ccf 100644 (file)
@@ -4890,6 +4890,7 @@ handle_eval_breaker:
 
         TARGET(CALL_PY_EXACT_ARGS) {
             assert(call_shape.kwnames == NULL);
+            DEOPT_IF(tstate->interp->eval_frame, CALL);
             _PyCallCache *cache = (_PyCallCache *)next_instr;
             int is_meth = is_method(stack_pointer, oparg);
             int argcount = oparg + is_meth;
@@ -4923,6 +4924,7 @@ handle_eval_breaker:
 
         TARGET(CALL_PY_WITH_DEFAULTS) {
             assert(call_shape.kwnames == NULL);
+            DEOPT_IF(tstate->interp->eval_frame, CALL);
             _PyCallCache *cache = (_PyCallCache *)next_instr;
             int is_meth = is_method(stack_pointer, oparg);
             int argcount = oparg + is_meth;
index 12871ceaf876c01b7c1191700e8f2c30e604ca3f..fa42993606fd639c99064cd9bf3328e86b555b8c 100644 (file)
@@ -440,6 +440,7 @@ initial_counter_value(void) {
 #define SPEC_FAIL_CALL_METHOD_WRAPPER 26
 #define SPEC_FAIL_CALL_OPERATOR_WRAPPER 27
 #define SPEC_FAIL_CALL_PYFUNCTION 28
+#define SPEC_FAIL_CALL_PEP_523 29
 
 /* COMPARE_OP */
 #define SPEC_FAIL_COMPARE_OP_DIFFERENT_TYPES 12
@@ -1471,6 +1472,11 @@ specialize_py_call(PyFunctionObject *func, _Py_CODEUNIT *instr, int nargs,
     assert(_Py_OPCODE(*instr) == CALL_ADAPTIVE);
     PyCodeObject *code = (PyCodeObject *)func->func_code;
     int kind = function_kind(code);
+    /* Don't specialize if PEP 523 is active */
+    if (_PyInterpreterState_GET()->eval_frame) {
+        SPECIALIZATION_FAIL(CALL, SPEC_FAIL_CALL_PEP_523);
+        return -1;
+    }
     if (kwnames) {
         SPECIALIZATION_FAIL(CALL, SPEC_FAIL_CALL_KWNAMES);
         return -1;