Hi Gabriele!
> I hope you would indulge me in asking for some details about the new CFrame structure, even in the form of existing literature (e.g. PEP) where the idea behind it is explained.
There isn't too much documentation on this, unfortunately (since these are all very unstable, low-level interpreter details), but a good place to start would be
https://bugs.python.org/issue46090.
Based on my own understanding (from reading the source code):
- There are three relevant structures: CFrame, InterpreterFrame, and PyFrameObject.
- PyFrameObjects are just PyObject wrappers around an InterpreterFrame, where all of the *actual* frame state for the Python stack is maintained.
- They are created lazily (for example, when sys._getframe() is called).
- See
https://github.com/python/cpython/pull/27077.
- InterpreterFrames live in a "datastack" for fast allocation and deallocation.
- This "datastack" lives on the PyThreadState.
- Because of how it is designed, InterpreterFrames must be allocated/deallocated "in order".
- If an InterpreterFrame is cleared, but still has a live PyFrameObject that points to it, it will copy itself *into* the PyFrameObject first (to guarantee that the PyFrameObject keeps working).
- See
https://github.com/python/cpython/pull/26076.
- A single CFrame is statically allocated inside of each _PyEval_EvalFrameDefault call, so it corresponds to the C stack, not the Python stack.
- It links to a chain of one or more InterpreterFrames.
- Multiple InterpreterFrames can correspond to a single CFrame!
- This is a performance optimization in 3.11: rather than enter a new call to _PyEval_EvalFrameDefault, calls to pure-Python code just create a new InterpreterFrame, set it as the current one, and continue execution.
- You can see how many InterpreterFrames correspond to the current CFrame by reading the "depth" member of the current InterpreterFrame.
- A value of 0 indicates that this is the only InterpreterFrame for this CFrame.
- A value of 42 means that this optimization has been performed 42 times (and there are currently 43 InterpreterFrames executing in this CFrame).
> Also, I'd like to a quick question, if I may. There now appear to be two ways of unwinding the frame stack: either iterate over CFrame.previous, or the more traditional PyFrameObject.f_back. I suspect there are reasons why these are perhaps not actually equivalent, and indeed this is mainly what I'd like to read in the literature I've requested above.
The above outline probably makes the differences clear:
- PyFrameObject.f_back just gives you a dummy wrapper around the previous frame object.
- It's not really useful for unwinding anything.
- InterpreterFrame.previous gives you the previous interpreter frame (duh!).
- This is probably what you want.
- CFrame.previous gives you the previous call to _PyEval_EvalFrameDefault.
- It's not really useful for unwinding anything.
- This is only really useful to maintain the current tracing state when returning.
Hopefully this helps! Somebody (Pablo or Mark?) will probably jump in here if I got anything wrong.
Brandt
Message archived at
https://mail.python.org/archives/list/pytho...@python.org/message/GDCPNESE2BWJUNPYFANCZVZK4EZNTKAF/