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
_______________________________________________
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-le...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at 
https://mail.python.org/archives/list/python-dev@python.org/message/GDCPNESE2BWJUNPYFANCZVZK4EZNTKAF/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to