Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[C API] Add public getter functions for the internal PyFrameObject structure #84601

Closed
vstinner opened this issue Apr 28, 2020 · 34 comments
Closed
Labels
3.11 only security fixes topic-C-API

Comments

@vstinner
Copy link
Member

BPO 40421
Nosy @scoder, @vstinner, @nedbat, @encukou, @markshannon, @miss-islington, @brandtbucher
PRs
  • bpo-40421: Add pyframe.h header file #19755
  • bpo-40421: Add Include/cpython/code.h header file #19756
  • bpo-40421: Add PyFrame_GetCode() function #19757
  • bpo-40421: Add PyFrame_GetLastInstr() function #19764
  • bpo-40421: Add PyFrame_GetBack() function #19765
  • Add symbols of the stable ABI to python3dll.c #23598
  • [3.9] bpo-42415: Add symbols of the stable ABI to python3dll.c (GH-23598) #23801
  • bpo-40421: Fix PyFrame_GetCode() documentation #31535
  • bpo-40421: What's New in Python 3.11: PyFrameObject.f_lasti #31536
  • [3.9] bpo-40421: Fix PyFrame_GetCode() documentation (GH-31535) #31537
  • [3.10] bpo-40421: Fix PyFrame_GetCode() documentation (GH-31535) #31538
  • bpo-40421: Add missing getters for frame object attributes to C-API. #32114
  • bpo-40421: test_capi uses assertEqual(), not assertEquals() #32361
  • bpo-40421: Add new PyFrame_GetLasti C-API function #32413
  • bpo-40421: Cleanup PyFrame C API #32417
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields:

    assignee = None
    closed_at = <Date 2022-04-06.13:18:28.654>
    created_at = <Date 2020-04-28.13:17:50.323>
    labels = ['expert-C-API', '3.11']
    title = '[C API] Add public getter functions for the internal PyFrameObject structure'
    updated_at = <Date 2022-04-08.14:45:29.774>
    user = 'https://github.com/vstinner'

    bugs.python.org fields:

    activity = <Date 2022-04-08.14:45:29.774>
    actor = 'vstinner'
    assignee = 'none'
    closed = True
    closed_date = <Date 2022-04-06.13:18:28.654>
    closer = 'vstinner'
    components = ['C API']
    creation = <Date 2020-04-28.13:17:50.323>
    creator = 'vstinner'
    dependencies = []
    files = []
    hgrepos = []
    issue_num = 40421
    keywords = ['patch']
    message_count = 34.0
    messages = ['367527', '367532', '367535', '367545', '367550', '367604', '368342', '368350', '368424', '383167', '383209', '403814', '403815', '406144', '408953', '413719', '413720', '413797', '413835', '413842', '413843', '413844', '413845', '413848', '413849', '413850', '414314', '414344', '415777', '415791', '416447', '416866', '416867', '416870']
    nosy_count = 7.0
    nosy_names = ['scoder', 'vstinner', 'nedbat', 'petr.viktorin', 'Mark.Shannon', 'miss-islington', 'brandtbucher']
    pr_nums = ['19755', '19756', '19757', '19764', '19765', '23598', '23801', '31535', '31536', '31537', '31538', '32114', '32361', '32413', '32417']
    priority = 'normal'
    resolution = 'fixed'
    stage = 'resolved'
    status = 'closed'
    superseder = None
    type = None
    url = 'https://bugs.python.org/issue40421'
    versions = ['Python 3.11']

    @vstinner
    Copy link
    Member Author

    Similarly to bpo-39573 (make PyObject opaque) and bpo-39947 (make PyThreadState opaque), I propose to add getter functions to access PyFrameObject members without exposing the PyFrameObject structure in the C API.

    The first step is to identify common usage of the PyFrameObject structure inside CPython code base to add getter functions, and maybe a few setter functions as well.

    frameobject.h is not part of Python.h, but it's part of the public C API. The long term plan is to move PyFrameObject structure to the internal C API to hide implementation details from the public C API.

    @vstinner vstinner added 3.9 only security fixes topic-C-API labels Apr 28, 2020
    @vstinner
    Copy link
    Member Author

    New changeset 7c59d7c by Victor Stinner in branch 'master':
    bpo-40421: Add pyframe.h header file (GH-19755)
    7c59d7c

    @vstinner
    Copy link
    Member Author

    New changeset b8f704d by Victor Stinner in branch 'master':
    bpo-40421: Add Include/cpython/code.h header file (GH-19756)
    b8f704d

    @vstinner
    Copy link
    Member Author

    New changeset a42ca74 by Victor Stinner in branch 'master':
    bpo-40421: Add PyFrame_GetCode() function (GH-19757)
    a42ca74

    @vstinner
    Copy link
    Member Author

    I looked how Cython uses PyFrameObject:

    • read f_lasti
    • read/write f_back
    • write f_lineno
    • read f_localsplus
    • read/write f_trace

    Details:

    • Cython/Debugger/libpython.py: code using the Python API of gdb to read PyFrameObject.f_lasti. It it used to compute the line number of a frame. The python_step() function puts a watch point on "f->f_lasti".
    • Cython/Utility/Coroutine.c: set PyFrameObject.f_back using "f->f_back = tstate->frame;" and "Py_CLEAR(f->f_back);".
    • Cython/Utility/ModuleSetupCode.c, __Pyx_PyFrame_SetLineNumber(): set PyFrameObject.f_lineno member. The limited C API flavor of this function does nothing, since this member cannot be set in the limited C API.
    • Cython/Utility/ObjectHandling.c, __Pyx_PyFrame_GetLocalsplus(): complex implementation to access PyFrameObject.f_localsplus:
      // Initialised by module init code.
      static size_t __pyx_pyframe_localsplus_offset = 0;
    
      #include "frameobject.h"
      // This is the long runtime version of
      //     #define __Pyx_PyFrame_GetLocalsplus(frame)  ((frame)->f_localsplus)
      // offsetof(PyFrameObject, f_localsplus) differs between regular C-Python and Stackless Python.
      // Therefore the offset is computed at run time from PyFrame_type.tp_basicsize. That is feasible,
      // because f_localsplus is the last field of PyFrameObject (checked by Py_BUILD_ASSERT_EXPR below).
      #define __Pxy_PyFrame_Initialize_Offsets()  \
        ((void)__Pyx_BUILD_ASSERT_EXPR(sizeof(PyFrameObject) == offsetof(PyFrameObject, f_localsplus) + Py_MEMBER_SIZE(PyFrameObject, f_localsplus)), \
         (void)(__pyx_pyframe_localsplus_offset = ((size_t)PyFrame_Type.tp_basicsize) - Py_MEMBER_SIZE(PyFrameObject, f_localsplus)))
      #define __Pyx_PyFrame_GetLocalsplus(frame)  \
        (assert(__pyx_pyframe_localsplus_offset), (PyObject **)(((char *)(frame)) + __pyx_pyframe_localsplus_offset))
    • Cython/Utility/Profile.c, __Pyx_TraceLine(): read PyFrameObject.f_trace.

    • Cython/Utility/Profile.c, __Pyx_TraceSetupAndCall(): set PyFrameObject.f_trace.

    @vstinner
    Copy link
    Member Author

    New changeset 7036477 by Victor Stinner in branch 'master':
    bpo-40421: Add PyFrame_GetBack() function (GH-19765)
    7036477

    @markshannon
    Copy link
    Member

    "maybe a few setter functions as well"
    Please don't add any setter functions, that would a major change to the VM and would need a PEP.

    Also, could you remove PyFrame_GetLastInstr(PyFrameObject *frame)?
    The only purpose of f_lasti is to get the line number and that can be done directly via PyFrame_GetLineNumber(PyFrameObject *frame)

    Perhaps Stefan can tell us why Cython needs to access the internals of the frame object.

    @vstinner
    Copy link
    Member Author

    vstinner commented May 7, 2020

    Please don't add any setter functions, that would a major change to the VM and would need a PEP.

    There is no such "major change". PyFrameObject structure was fully exposed in the public C API since the earliest Python version.

    I don't see how adding setter is a major change, since it's already possible to directly modify *any* field of PyFrameObject without any kind of protection.

    My plan is to have two milestones:

    A) Make the structure opaque, so it's not longer possible to directly access it.

    B) Deprecate or remove a few getter or setter functions, or move them to the internal C API.

    I don't think that moving directly to step (B) is a realistic migration plan. So far, nobody analyzed all C extensions on PyPI to see how PyFrameObject is accessed.

    I prefer to move slowly, step by step.

    Breaking C extensions which currently *modify* directly PyFrameObject on purpose doesn't seem like a reasonable option to me.

    --

    I'm trying to distinguish functions which should be "safe"/"portable" from the ones which may be too "CPython specific":

    Note: Compared to accessing directly PyFrameObject.f_code, PyFrame_GetCode() also avoids the issue of borrowed references since it returns a strong reference.

    PyFrame_GetBack() looks specific to the current implementation of CPython. Another implementation might decide to not chain frames. Or maybe don't provide an easy way to traverse the chain of frames. Or at least, it might be a different API than PyFrame_GetBack().

    --

    Also, could you remove PyFrame_GetLastInstr(PyFrameObject *frame)?

    I didn't add it :-) It's the pending PR 19764.

    I didn't merge it since it's unclear to me if it's a good idea to directly expose it or not. Cython uses it, but Cython also abuses CPython internals in general, usually for best performances :-)

    *Maybe* one compromise would be to add a private _PyFrame_GetLastInstr() to ease migration to step (A) (replace direct access to the structure with function calls).

    @scoder
    Copy link
    Contributor

    scoder commented May 8, 2020

    Adding to the list above:

    "f_lasti" is used in "libpython.py", which is an almost exact copy of the one in CPython's "Tools/gdb/" for debugging Python code in gdb. If that implementation changes, we can probably adapt, especially since it uses runtime generated code. I don't find a C function for this necessary at this point, given that CPython will also need to access its own internals here in some way.

    "f_localsplus" is used in the FASTCALL implementation and seems specific to Py3.6/7(?) which lacks the vectorcall protocol. So I guess it won't impact 3.8+ anymore. (The reason why its access is so complex is that StacklessPython has a slightly different frame struct, but we need to be binary(!) compatible with both.)

    "f_back" is used in the coroutine implementation for correct exception traceback building, and the usage is pretty much copied from CPython. I doubt that there is much use for setting it outside of "code that tries to reimplement CPython behaviour", but Cython has to do exactly that here.

    "f_lineno" is also used for traceback generation, because Cython creates frames when an exception occurs and needs to tell it what the current code line is. It's only used on newly created frames, although I can't guarantee that that will never change. Being able to read and set it seems reasonable.

    "f_trace" is obviously used for tracing/profiling, and there isn't currently a good C-API for that, besides talking to frame struct fields (which is quite efficient when it comes to performance).

    @vstinner
    Copy link
    Member Author

    New changeset fcc6935 by Victor Stinner in branch 'master':
    Add symbols of the stable ABI to python3dll.c (GH-23598)
    fcc6935

    @vstinner
    Copy link
    Member Author

    New changeset 1662868 by Victor Stinner in branch '3.9':
    Add symbols of the stable ABI to python3dll.c (GH-23598) (GH-23801)
    1662868

    @vstinner
    Copy link
    Member Author

    The coverage project has a ctrace C extension which access PyFrameObject.f_lasti which is gone in Python 3.11. It uses MyFrame_lasti() helper to handle Python 3.10 lasti change:
    ---

    // The f_lasti field changed meaning in 3.10.0a7. It had been bytes, but
    // now is instructions, so we need to adjust it to use it as a byte index.
    #if PY_VERSION_HEX >= 0x030A00A7
    #define MyFrame_lasti(f)    (f->f_lasti * 2)
    #else
    #define MyFrame_lasti(f)    f->f_lasti
    #endif // 3.10.0a7

    f_lasti is used for two things in coverage/ctracer/tracer.c:

    (1) get the last opcode:
    ----

                /* Need to distinguish between RETURN_VALUE and YIELD_VALUE. Read
                 * the current bytecode to see what it is.  In unusual circumstances
                 * (Cython code), co_code can be the empty string, so range-check
                 * f_lasti before reading the byte.
                 */
                int bytecode = RETURN_VALUE;
                PyObject * pCode = MyFrame_GetCode(frame)->co_code;
                int lasti = MyFrame_lasti(frame);
    
                if (lasti < PyBytes_GET_SIZE(pCode)) {
                    bytecode = PyBytes_AS_STRING(pCode)[lasti];
                }
                if (bytecode != YIELD_VALUE) {
                    int first = MyFrame_GetCode(frame)->co_firstlineno;
                    if (CTracer_record_pair(self, self->pcur_entry->last_line, -first) < 0) {
                        goto error;
                    }
                }

    (2) get the line number, with a special case for generator which is not started yet (lasti < 0)
    ---

        /* A call event is really a "start frame" event, and can happen for
         * re-entering a generator also.  f_lasti is -1 for a true call, and a
         * real byte offset for a generator re-entry.
         */
        if (frame->f_lasti < 0) {
            self->pcur_entry->last_line = -MyFrame_GetCode(frame)->co_firstlineno;
        }
        else {
            self->pcur_entry->last_line = PyFrame_GetLineNumber(frame);
        }

    Since Python 3.10.0a3, PyFrame_GetLineNumber() handles the case of negative f_lasti, thanks to the commit 877df85 related to the PEP-626 implementation:
    ---

    int
    PyCode_Addr2Line(PyCodeObject *co, int addrq)
    {
        if (addrq < 0) {
            return co->co_firstlineno;
        }
        ...
    }

    => coverage would need an abstraction to get the last opcode: use case (1).

    I recall that an old version of asyncio also had to get latest opcode, in pure Python, to workaround the CPython bpo-21209 bug:

    +# Check for CPython issue bpo-21209
    +def has_yield_from_bug():
    + class MyGen:
    + def __init__(self):
    + self.send_args = None
    + def __iter__(self):
    + return self
    + def __next__(self):
    + return 42
    + def send(self, *what):
    + self.send_args = what
    + return None
    + def yield_from_gen(gen):
    + yield from gen
    + value = (1, 2, 3)
    + gen = MyGen()
    + coro = yield_from_gen(gen)
    + next(coro)
    + coro.send(value)
    + return gen.send_args != (value,)
    +_YIELD_FROM_BUG = has_yield_from_bug()
    +del has_yield_from_bug

    (...)

    + if _YIELD_FROM_BUG:
    + # For for CPython issue bpo-21209: using "yield from" and a custom
    + # generator, generator.send(tuple) unpacks the tuple instead of passing
    + # the tuple unchanged. Check if the caller is a generator using "yield
    + # from" to decide if the parameter should be unpacked or not.
    + def send(self, *value):
    + frame = sys._getframe()
    + caller = frame.f_back
    + assert caller.f_lasti >= 0
    + if caller.f_code.co_code[caller.f_lasti] != _YIELD_FROM:
    + value = value[0]
    + return self.gen.send(value)
    + else:
    + def send(self, value):
    + return self.gen.send(value)

    Hopefully, this code could be be removed from asyncio, since the bug was fixed (and asyncio is now only maintained in the Python stdlib, it's not longer a third party module).

    @vstinner
    Copy link
    Member Author

    See also bpo-45247: [C API] Add explicit support for Cython to the C API.

    @nedbat
    Copy link
    Member

    nedbat commented Nov 11, 2021

    I went ahead and changed the coverage.py code to this:

    #if PY_VERSION_HEX >= 0x030B00A0
    // 3.11 moved f_lasti into an internal structure. This is totally the wrong way
    // to make this work, but it's all I've got until https://bugs.python.org/issue40421
    // is resolved.
    #include <internal/pycore_frame.h>
    #define MyFrame_lasti(f)    ((f)->f_frame->f_lasti * 2)
    #elif PY_VERSION_HEX >= 0x030A00A7
    // The f_lasti field changed meaning in 3.10.0a7. It had been bytes, but
    // now is instructions, so we need to adjust it to use it as a byte index.
    #define MyFrame_lasti(f)    ((f)->f_lasti * 2)
    #else
    #define MyFrame_lasti(f)    ((f)->f_lasti)
    #endif

    If we had an API that let me distinguish between call and resume and between return and yield, I could get rid of this.

    @encukou
    Copy link
    Member

    encukou commented Dec 20, 2021

    The docs for PyFrame_GetCode say it's returning an "int".
    https://docs.python.org/3/c-api/reflection.html#c.PyFrame_GetCode
    Same for PyFrame_GetBack. Are there more?

    "Get the frame code." is not very clear. Could this link to https://docs.python.org/3/c-api/code.html ?

    @vstinner
    Copy link
    Member Author

    The gevent project is not compatible with Python 3.11: it gets and sets directly PyFrameObject.f_code member which has been removed in Python 3.11 (moved to PyFrameObject.f_frame.f_code internal C API).

    gevent issue: gevent/gevent#1867

    @vstinner
    Copy link
    Member Author

    The f_code member has been removed by in bpo-44032 by the commit b11a951.

    The f_frame member has been added in bpo-44590 by the commit ae0a2b7.

    See also bpo-46355 [C API] Document PyFrameObject and PyThreadState changes and explain how to port code to Python 3.11.

    @vstinner
    Copy link
    Member Author

    I created bpo-46836: "[C API] Move PyFrameObject to the internal C API".

    @vstinner
    Copy link
    Member Author

    The docs for PyFrame_GetCode say it's returning an "int".

    Oh. I missed your comment. I created #75716 to fix the return type in the documentation.

    @vstinner
    Copy link
    Member Author

    Ned Batchelder:

    I went ahead and changed the coverage.py code to this: (...)

    I proposed a coverage PR using PyObject_GetAttrString(frame, "f_lasti") which should works on all Python versions:
    nedbat/coveragepy#1331

    @vstinner
    Copy link
    Member Author

    Mark Shannon:

    The only purpose of f_lasti is to get the line number and that can be done directly via PyFrame_GetLineNumber(PyFrameObject *frame)

    I found the uwsgi project on PyPI which uses f_lasti with PyCode_Addr2Line(). I wrote #75717 to suggest using PyFrame_GetLineNumber() in What's New in Python 3.11.

    @vstinner
    Copy link
    Member Author

    I found the uwsgi project on PyPI which uses f_lasti with PyCode_Addr2Line().

    Oh, plugins/python/profiler.c uses that to define PyFrame_GetLineNumber() on Python older than 2.7, Python 3.0 and Python 3.1. In 2022, it's no longer relevant.

    But well, there might be other code in the wild using PyCode_Addr2Line() with f_lasti, so IMO it's worth it to document the suggestion ;-)

    @vstinner
    Copy link
    Member Author

    New changeset 78859e5 by Victor Stinner in branch 'main':
    bpo-40421: Fix PyFrame_GetCode() documentation (GH-31535)
    78859e5

    @miss-islington
    Copy link
    Contributor

    New changeset b0de629 by Miss Islington (bot) in branch '3.10':
    bpo-40421: Fix PyFrame_GetCode() documentation (GH-31535)
    b0de629

    @miss-islington
    Copy link
    Contributor

    New changeset 763e23e by Miss Islington (bot) in branch '3.9':
    bpo-40421: Fix PyFrame_GetCode() documentation (GH-31535)
    763e23e

    @vstinner
    Copy link
    Member Author

    New changeset 8a716bc by Victor Stinner in branch 'main':
    bpo-40421: What's New in Python 3.11: PyFrameObject.f_lasti (GH-31536)
    8a716bc

    @vstinner
    Copy link
    Member Author

    vstinner commented Mar 1, 2022

    I searched for "\<_f" regex in Cython (0.29.x) branch. I found the following code getting or setting PyFrameObject fields.

    == Set f_back ==

    __Pyx_Coroutine_SendEx() at Utility/Coroutine.c:721:

    f->f_back = PyThreadState_GetFrame(tstate);
    

    __Pyx_Coroutine_ResetFrameBackpointer() at Utility/Coroutine.c:775:

        Py_CLEAR(f->f_back);

    == Set f_lineno ==

    __Pyx_PyFrame_SetLineNumber() at Utility/ModuleSetupCode.c:543:

        #define __Pyx_PyFrame_SetLineNumber(frame, lineno)  (frame)->f_lineno = (lineno)

    __Pyx_PyFrame_SetLineNumber() is called by 3 functions:

    • __Pyx_AddTraceback()
    • __Pyx_call_line_trace_func()
    • __Pyx_TraceSetupAndCall()

    __Pyx_AddTraceback() pseudo-code:

    static void __Pyx_AddTraceback(const char *funcname, int c_line,
                                   int py_line, const char *filename)
    {
        py_frame = PyFrame_New(..., py_code, ...);
        __Pyx_PyFrame_SetLineNumber(py_frame, py_line);
        ...
    }

    == f_localsplus ==

    If the CYTHON_FAST_PYCALL macro is defined, sizeof(PyFrameObject) is used to get the f_localsplus member.

    __Pyx_PyFrame_GetLocalsplus() at Utility/ObjectHandling.c:1996:
    ---

    // Initialised by module init code.
    static size_t __pyx_pyframe_localsplus_offset = 0;
    
    #include "frameobject.h"
    
    #define __Pxy_PyFrame_Initialize_Offsets()  \
      ((void)__Pyx_BUILD_ASSERT_EXPR(sizeof(PyFrameObject) == offsetof(PyFrameObject, f_localsplus) + Py_MEMBER_SIZE(PyFrameObject, f_localsplus)), \
       (void)(__pyx_pyframe_localsplus_offset = ((size_t)PyFrame_Type.tp_basicsize) - Py_MEMBER_SIZE(PyFrameObject, f_localsplus)))
    #define __Pyx_PyFrame_GetLocalsplus(frame)  \
      (assert(__pyx_pyframe_localsplus_offset), (PyObject **)(((char *)(frame)) + __pyx_pyframe_localsplus_offset))

    == Get f_trace ==

    __Pyx_TraceLine() at Utility/Profile.c:238:

    if (__Pyx_IsTracing(tstate, 0, 0) && tstate->c_tracefunc && $frame_cname->f_trace)
    

    == Set f_frame ==

    __Pyx_TraceSetupAndCall() at Utility/Profile.c:299:

    (*frame)->f_tstate = tstate;
    

    @markshannon
    Copy link
    Member

    I've outlined the requirements for a frame stack API at faster-cpython/ideas#309.

    The problem with adding an API for PyFrameObject (beyond simple getters) is that it makes assumptions about the frame stack that aren't valid.

    A stack of frames is not just a linked list of frames. It never was, and it certainly isn't now.

    @vstinner
    Copy link
    Member Author

    I created bpo-47092: [C API] Add PyFrame_GetVar(frame, name) function.

    @vstinner
    Copy link
    Member Author

    Issue title: "[C API] Add getter functions for PyFrameObject and maybe move PyFrameObject to the internal C API"

    bpo-46836 moved PyFrameObject to the internal C API. I update the issue title.

    @vstinner vstinner added 3.11 only security fixes and removed 3.9 only security fixes labels Mar 22, 2022
    @vstinner vstinner changed the title [C API] Add getter functions for PyFrameObject and maybe move PyFrameObject to the internal C API [C API] Add public getter functions for the internal PyFrameObject structure Mar 22, 2022
    @vstinner vstinner added 3.11 only security fixes and removed 3.9 only security fixes labels Mar 22, 2022
    @vstinner vstinner changed the title [C API] Add getter functions for PyFrameObject and maybe move PyFrameObject to the internal C API [C API] Add public getter functions for the internal PyFrameObject structure Mar 22, 2022
    @markshannon
    Copy link
    Member

    New changeset 74b95d8 by Mark Shannon in branch 'main':
    bpo-40421: Add missing getters for frame object attributes to C-API. (GH-32114)
    74b95d8

    @vstinner
    Copy link
    Member Author

    vstinner commented Apr 6, 2022

    New changeset 14a9b48 by Victor Stinner in branch 'main':
    bpo-40421: test_capi uses assertEqual(), not assertEquals() (GH-32361)
    14a9b48

    @vstinner
    Copy link
    Member Author

    vstinner commented Apr 6, 2022

    IMO the initial goal is now reached. I close the issue. Thanks to everyone who helped implementing these changes!

    The PyFrameObject structure is now opaque in Python 3.11. New getter functions of the Python 3.11 C API:

    • PyFrame_GetBuiltins()
    • PyFrame_GetGenerator()
    • PyFrame_GetGlobals()
    • PyFrame_GetLocals()

    Finally, the PyFrameObject structure now has its own page in the C API documentation:
    https://docs.python.org/dev/c-api/frame.html

    As explained in previous comments, the work is not done: Cython, greenlet, gevent and coverage still need more getter and/or setter functions. Adding more functions is being discussed in this external issue:
    faster-cpython/ideas#309

    I propose to open new specific issues to add new functions. For example, open an issue to add a getter for the f_lasti member.

    @vstinner
    Copy link
    Member Author

    vstinner commented Apr 6, 2022

    The PyFrameObject structure was made opaque by bpo-46836.

    @ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Labels
    3.11 only security fixes topic-C-API
    Projects
    None yet
    Development

    No branches or pull requests

    6 participants