https://github.com/protocolbuffers/protobuf/pull/10403 From da973aff2adab60a9e516d3202c111dbdde1a50f Mon Sep 17 00:00:00 2001 From: Alexander Shadchin Date: Sun, 14 Aug 2022 21:13:49 +0300 Subject: [PATCH] Fix build with Python 3.11 The PyFrameObject structure members have been removed from the public C API. --- python/google/protobuf/pyext/descriptor.cc | 75 ++++++++++++++++++---- 1 file changed, 62 insertions(+), 13 deletions(-) diff --git a/python/google/protobuf/pyext/descriptor.cc b/python/google/protobuf/pyext/descriptor.cc index fc83acf01a7..fc97b0fa6c1 100644 --- a/python/google/protobuf/pyext/descriptor.cc +++ b/python/google/protobuf/pyext/descriptor.cc @@ -58,6 +58,37 @@ : 0) \ : PyBytes_AsStringAndSize(ob, (charpp), (sizep))) +#if PY_VERSION_HEX < 0x030900B1 && !defined(PYPY_VERSION) +static PyCodeObject* PyFrame_GetCode(PyFrameObject *frame) +{ + Py_INCREF(frame->f_code); + return frame->f_code; +} + +static PyFrameObject* PyFrame_GetBack(PyFrameObject *frame) +{ + Py_XINCREF(frame->f_back); + return frame->f_back; +} +#endif + +#if PY_VERSION_HEX < 0x030B00A7 && !defined(PYPY_VERSION) +static PyObject* PyFrame_GetLocals(PyFrameObject *frame) +{ + if (PyFrame_FastToLocalsWithError(frame) < 0) { + return NULL; + } + Py_INCREF(frame->f_locals); + return frame->f_locals; +} + +static PyObject* PyFrame_GetGlobals(PyFrameObject *frame) +{ + Py_INCREF(frame->f_globals); + return frame->f_globals; +} +#endif + namespace google { namespace protobuf { namespace python { @@ -96,48 +127,66 @@ bool _CalledFromGeneratedFile(int stacklevel) { // This check is not critical and is somewhat difficult to implement correctly // in PyPy. PyFrameObject* frame = PyEval_GetFrame(); + PyCodeObject* frame_code = nullptr; + PyObject* frame_globals = nullptr; + PyObject* frame_locals = nullptr; + bool result = false; + if (frame == nullptr) { - return false; + goto exit; } + Py_INCREF(frame); while (stacklevel-- > 0) { - frame = frame->f_back; + PyFrameObject* next_frame = PyFrame_GetBack(frame); + Py_DECREF(frame); + frame = next_frame; if (frame == nullptr) { - return false; + goto exit; } } - if (frame->f_code->co_filename == nullptr) { - return false; + frame_code = PyFrame_GetCode(frame); + if (frame_code->co_filename == nullptr) { + goto exit; } char* filename; Py_ssize_t filename_size; - if (PyString_AsStringAndSize(frame->f_code->co_filename, + if (PyString_AsStringAndSize(frame_code->co_filename, &filename, &filename_size) < 0) { // filename is not a string. PyErr_Clear(); - return false; + goto exit; } if ((filename_size < 3) || (strcmp(&filename[filename_size - 3], ".py") != 0)) { // Cython's stack does not have .py file name and is not at global module // scope. - return true; + result = true; + goto exit; } if (filename_size < 7) { // filename is too short. - return false; + goto exit; } if (strcmp(&filename[filename_size - 7], "_pb2.py") != 0) { // Filename is not ending with _pb2. - return false; + goto exit; } - if (frame->f_globals != frame->f_locals) { + frame_globals = PyFrame_GetGlobals(frame); + frame_locals = PyFrame_GetLocals(frame); + if (frame_globals != frame_locals) { // Not at global module scope - return false; + goto exit; } #endif - return true; + result = true; +exit: + Py_XDECREF(frame_globals); + Py_XDECREF(frame_locals); + Py_XDECREF(frame_code); + Py_XDECREF(frame); + return result; } // If the calling code is not a _pb2.py file, raise AttributeError.