Python `3.11` frame structure and various changes

Yes, the f_state field has been removed.
For 3.11, you can use the owner field defined in cpython/pycore_frame.h at main · python/cpython · GitHub, it won’t change before 3.12.

We should have a better API for 3.12

1 Like

Hey there again,

I was working on the support of 3.11 for the Blackfire profiler and faced an issue that I had previously mentioned above:

If the function is built-in(C function) we read it from frame->f_valuestack and if the function is a normal Python function we use: arg_name = PyTuple_GetItem(frame->f_code->co_varnames, int_arg_id-1);

I use f_valuestack to retrieve some arguments from the C functions(PyCFunction) inside the profiler. This field seems to be removed in 3.11 without a corresponding API.

So, the question for me is: Is there any other way to read PyCFunction call stack in the runtime? Again: this is used this for retrieving arguments of built-in functions before:

arg_id = 0 // positional index for the argument
arg_val = frame->f_valuestack[arg_id];

I have a pending PR to add PyFrame_GetVar(), would it fit your usecase? Can you call functions?gh-91248: Add PyFrame_GetVar() function by vstinner · Pull Request #95712 · python/cpython · GitHub

IIUC, this API will not solve the problem I described above, but it solves another one. Getting variables from frame->locals was always a hassle. I was using the following code to get the locals and then using another one to get the variables. And cell variables were always a problem, too.

static PyObject *_get_locals(PyFrameObject *fobj) {
#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 10
    return PyEval_GetLocals();
#else
    PyObject *locals;
    PyFrame_FastToLocals(fobj);
    locals = fobj->f_locals;
    PyFrame_LocalsToFast(fobj, 0);
    return locals;
#endif
}

Now coming to the original problem: the problem for me is I need to read locals of a C function.

Example: if user calls time.sleep(1.0), I need to get 1.0. We have a special configuration file where we set: fn_name: "time.sleep", arg_id: 0. From this, I was getting the C function’s argument by reading from frame->f_valuestack. Now, I am not sure how to accomplish this with PyFrame_GetVar?