Skip to content

WIP bpo-44800: Rename _PyInterpreterFrame to _Py_framedata #27525

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

Closed
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
a133e0d
bpo-44800: Clearly distinguish execution & introspection frames
ncoghlan Aug 1, 2021
d8858aa
'frame' and 'frame data' replaces introspection and execution frames
ncoghlan Aug 14, 2021
cd340b0
Merge remote-tracking branch 'origin/main' into bpo-44800-rename-inte…
ncoghlan Aug 14, 2021
ccf953b
Tweak some comments
ncoghlan Aug 14, 2021
4a097bd
Another comment fix
ncoghlan Aug 14, 2021
bd00490
Fix LLTRACE macro compile error
ncoghlan Aug 14, 2021
04aa7e8
Revert unintended function name changes
ncoghlan Aug 14, 2021
e9018e7
Fix comment alignment
ncoghlan Aug 14, 2021
0ce41c8
Follow proposed new naming conventions in gdb hooks
ncoghlan Aug 14, 2021
c269e1f
Merge remote-tracking branch 'origin/main' into bpo-44800-rename-inte…
ncoghlan Aug 21, 2021
4eeff9a
Reduce conflicts for main branch merge
ncoghlan Mar 6, 2022
6fa0f53
Fix bad search & replace
ncoghlan Mar 6, 2022
776ca80
main branch has no underscore
ncoghlan Mar 6, 2022
682af23
Reduce function header conflicts
ncoghlan Mar 6, 2022
c76e63b
Yet more merge conflict reduction
ncoghlan Mar 6, 2022
b1d1438
Merged and compiles, naming is inconsistent
ncoghlan Mar 6, 2022
cae935d
Reinstate _Py_framedata struct rename
ncoghlan Mar 6, 2022
2866bfa
Fix type declaration for gen/coro frame data
ncoghlan Mar 6, 2022
239a62f
Document frame related naming conventions
ncoghlan Mar 6, 2022
2680f35
Migrate gen/coro iframe field to fdata naming convention
ncoghlan Mar 6, 2022
ebda1d3
Use fdata for frame data locals and parameters
ncoghlan Mar 12, 2022
269a4a0
frame -> fdata in ceval.c & allow compilation
ncoghlan Mar 12, 2022
34cf023
Disambiguate f_fdata and f_frame_data
ncoghlan Mar 12, 2022
55d9276
Merge remote-tracking branch 'origin/main' into bpo-44800-rename-inte…
ncoghlan Mar 12, 2022
3eba918
Document the currently implemented conventions
ncoghlan Mar 12, 2022
e8a4adf
Note the 'current_frame' exception
ncoghlan Mar 12, 2022
3d654a0
Fix test_gdb
ncoghlan Mar 12, 2022
b09b114
Fix header file include guard var
ncoghlan Mar 12, 2022
9b51976
Distinguish frame state error messages
ncoghlan Mar 12, 2022
0a3611c
super() does not access C frame structs
ncoghlan Mar 12, 2022
08410cc
new_frame -> new_fdata in frame push
ncoghlan Mar 12, 2022
c694768
Add missing error check in PyImport_Import
ncoghlan Mar 12, 2022
ba87ef3
No Python frame seems legit for PyImport_Import()
ncoghlan Mar 12, 2022
7168f7d
Get test_gdb passing locally
ncoghlan Mar 12, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Include/cpython/ceval.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ PyAPI_FUNC(PyObject *) _PyEval_GetBuiltinId(_Py_Identifier *);
flag was set, else return 0. */
PyAPI_FUNC(int) PyEval_MergeCompilerFlags(PyCompilerFlags *cf);

PyAPI_FUNC(PyObject *) _PyEval_EvalFrameDefault(PyThreadState *tstate, struct _interpreter_frame *f, int exc);
PyAPI_FUNC(PyObject *) _PyEval_EvalFrameDefault(PyThreadState *tstate, _Py_framedata *fdata, int exc);

PyAPI_FUNC(void) _PyEval_SetSwitchInterval(unsigned long microseconds);
PyAPI_FUNC(unsigned long) _PyEval_GetSwitchInterval(void);
Expand Down
23 changes: 17 additions & 6 deletions Include/cpython/frameobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,26 @@
# error "this header file must not be included directly"
#endif

/* Starting in CPython 3.11, CPython separates the frame state between the
* full frame objects exposed by the Python and C runtime state introspection
* APIs, and internal lighter weight frame data structs, which are simple C
* structures owned by either the interpreter eval loop (while executing
* ordinary functions), by a generator or coroutine object (for frames that
* are able to be suspended), or by their corresponding full frame object (if
* a state instrospection API has been invoked and the full frame object has
* taken responsibility for the lifecycle of the frame data storage).
*
* This split storage eliminates a lot of allocation and deallocation of full
* Python objects during code execution, providing a significant speed gain
* over the previous approach of using full Python objects for both
* introspection and code execution.
*/
// Declaration of _Py_framedata is in cpython/pystate.h for use in PyThreadState

struct _frame {
PyObject_HEAD
struct _frame *f_back; /* previous frame, or NULL */
struct _interpreter_frame *f_frame; /* points to the frame data */
_Py_framedata *f_fdata; /* points to the frame runtime data */
PyObject *f_trace; /* Trace function */
int f_lineno; /* Current line number. Only valid if non-zero */
char f_trace_lines; /* Emit per-line trace events? */
Expand All @@ -24,11 +40,6 @@ PyAPI_DATA(PyTypeObject) PyFrame_Type;
PyAPI_FUNC(PyFrameObject *) PyFrame_New(PyThreadState *, PyCodeObject *,
PyObject *, PyObject *);

/* only internal use */
PyFrameObject*
_PyFrame_New_NoTrack(struct _interpreter_frame *, int);


/* The rest of the interface is specific for frame objects */

/* Conversions between "fast locals" and locals in dictionary */
Expand Down
12 changes: 9 additions & 3 deletions Include/cpython/pystate.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ typedef struct _stack_chunk {
PyObject * data[1]; /* Variable sized */
} _PyStackChunk;


// Declared here so the thread state can use it without creating an include loop
// See cpython/frameobject.h for an explanation of the type
// See internal/pycore_framedata.h for the struct definition
typedef struct _Py_execution_frame _Py_framedata;

// The PyThreadState typedef is in Include/pystate.h.
struct _ts {
/* See Python/ceval.c for comments explaining most fields */
Expand All @@ -77,8 +83,8 @@ struct _ts {
struct _ts *next;
PyInterpreterState *interp;

/* Borrowed reference to the current frame (it can be NULL) */
struct _interpreter_frame *frame;
/* Borrowed reference to the current execution frame (it can be NULL) */
_Py_framedata *fdata;
int recursion_depth;
int recursion_headroom; /* Allow 50 more calls to handle any errors. */
int stackcheck_counter;
Expand Down Expand Up @@ -223,7 +229,7 @@ PyAPI_FUNC(void) PyThreadState_DeleteCurrent(void);

/* Frame evaluation API */

typedef PyObject* (*_PyFrameEvalFunction)(PyThreadState *tstate, struct _interpreter_frame *, int);
typedef PyObject* (*_PyFrameEvalFunction)(PyThreadState *tstate, _Py_framedata *, int);

PyAPI_FUNC(_PyFrameEvalFunction) _PyInterpreterState_GetEvalFrameFunc(
PyInterpreterState *interp);
Expand Down
4 changes: 2 additions & 2 deletions Include/genobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ extern "C" {
and coroutine objects. */
#define _PyGenObject_HEAD(prefix) \
PyObject_HEAD \
/* Note: gi_frame can be NULL if the generator is "finished" */ \
struct _interpreter_frame *prefix##_xframe; \
/* Note: gi_fdata can be NULL if the generator is "finished" */ \
_Py_framedata *prefix##_fdata; \
/* The code object backing the generator */ \
PyCodeObject *prefix##_code; \
/* List of weak reference. */ \
Expand Down
8 changes: 4 additions & 4 deletions Include/internal/pycore_ceval.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ extern PyObject *_PyEval_BuiltinsFromGlobals(


static inline PyObject*
_PyEval_EvalFrame(PyThreadState *tstate, struct _interpreter_frame *frame, int throwflag)
_PyEval_EvalFrame(PyThreadState *tstate, _Py_framedata *fdata, int throwflag)
{
return tstate->interp->eval_frame(tstate, frame, throwflag);
return tstate->interp->eval_frame(tstate, fdata, throwflag);
}

extern PyObject *
Expand Down Expand Up @@ -107,9 +107,9 @@ static inline void _Py_LeaveRecursiveCall_inline(void) {

#define Py_LeaveRecursiveCall() _Py_LeaveRecursiveCall_inline()

struct _interpreter_frame *_PyEval_GetFrame(void);
_Py_framedata *_PyEval_GetFrameData(void);

PyObject *_Py_MakeCoro(PyFrameConstructor *, struct _interpreter_frame *);
PyObject *_Py_MakeCoro(PyFrameConstructor *, _Py_framedata *);

#ifdef __cplusplus
}
Expand Down
128 changes: 0 additions & 128 deletions Include/internal/pycore_frame.h

This file was deleted.

133 changes: 133 additions & 0 deletions Include/internal/pycore_framedata.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
#ifndef Py_INTERNAL_FRAMEDATA_H
#define Py_INTERNAL_FRAMEDATA_H
#ifdef __cplusplus
extern "C" {
#endif

/* Internal-use-only frame object constructor */
PyFrameObject*
_PyFrame_New_NoTrack(_Py_framedata *, int);

/* These values are chosen so that the inline functions below all
* compare f_state to zero.
*/
enum _framestate {
FRAME_CREATED = -2,
FRAME_SUSPENDED = -1,
FRAME_EXECUTING = 0,
FRAME_RETURNED = 1,
FRAME_UNWINDING = 2,
FRAME_RAISED = 3,
FRAME_CLEARED = 4
};

typedef signed char PyFrameState;

// The _Py_framedata typedef is in Include/pyframeobject.h
struct _Py_execution_frame {
PyObject *globals;
PyObject *builtins;
PyObject *locals;
PyCodeObject *code;
PyFrameObject *frame_obj; // Full frame object (if created)
/* Borrowed reference to a generator, or NULL */
PyObject *generator;
_Py_framedata *previous;
int lasti; /* Last instruction if called */
int stackdepth; /* Depth of value stack */
int nlocalsplus;
PyFrameState state; /* What state the frame is in */
PyObject *stack[1];
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The file name change obscures the changes to the field names here, where the following previously had the f_ prefix:

  • globals
  • builtins
  • locals
  • code
  • lasti
  • state

To reduce the diff size, while still ensuring that the field prefix was consistently different between PyFrameObject and _Py_framedata, I went with the option of standardising the data struct on "no field prefix" (it was previously split 50/50 between that and the f_ prefix), rather than introducing a new prefix the way I did in the original PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reverted this file rename from the PR - the internal header now remains pycore_frame.h, with both the full frame object and frame data struct field definitions in it.

};

static inline int _Py_framedata_IsRunnable(_Py_framedata *fdata) {
return fdata->state < FRAME_EXECUTING;
}

static inline int _Py_framedata_IsExecuting(_Py_framedata *fdata) {
return fdata->state == FRAME_EXECUTING;
}

static inline int _Py_framedata_HasCompleted(_Py_framedata *fdata) {
return fdata->state > FRAME_EXECUTING;
}

#define FRAME_SPECIALS_SIZE ((sizeof(_Py_framedata)-1)/sizeof(PyObject *))

_Py_framedata *
_Py_framedata_HeapAlloc(PyFrameConstructor *con, PyObject *locals);

static inline void
_Py_framedata_InitializeSpecials(
_Py_framedata *fdata, PyFrameConstructor *con,
PyObject *locals, int nlocalsplus)
{
fdata->code = (PyCodeObject *)Py_NewRef(con->fc_code);
fdata->builtins = Py_NewRef(con->fc_builtins);
fdata->globals = Py_NewRef(con->fc_globals);
fdata->locals = Py_XNewRef(locals);
fdata->nlocalsplus = nlocalsplus;
fdata->stackdepth = 0;
fdata->frame_obj = NULL;
fdata->generator = NULL;
fdata->lasti = -1;
fdata->state = FRAME_CREATED;
}

/* Gets the pointer to the locals array
* that precedes this frame.
*/
static inline PyObject**
_Py_framedata_GetLocalsArray(_Py_framedata *fdata)
{
return ((PyObject **)fdata) - fdata->nlocalsplus;
}

/* For use by _Py_framedata_GetFrameObject
Do not call directly. */
PyFrameObject *
_Py_framedata_MakeAndSetFrameObject(_Py_framedata *fdata);

/* Gets the PyFrameObject for this frame, lazily
* creating it if necessary.
* Returns a borrowed referennce */
static inline PyFrameObject *
_Py_framedata_GetFrameObject(_Py_framedata *fdata)
{
PyFrameObject *res = fdata->frame_obj;
if (res != NULL) {
return res;
}
return _Py_framedata_MakeAndSetFrameObject(fdata);
}

/* Clears all references in the frame.
* If take is non-zero, then the execution frame
* may be transfered to the frame object it references
* instead of being cleared. Either way
* the caller no longer owns the references
* in the frame.
* take should be set to 1 for heap allocated
* frames like the ones in generators and coroutines.
*/
int
_Py_framedata_Clear(_Py_framedata *fdata, int take);

int
_Py_framedata_Traverse(_Py_framedata *fdata, visitproc visit, void *arg);

int
_Py_framedata_FastToLocalsWithError(_Py_framedata *frame);

void
_Py_framedata_LocalsToFast(_Py_framedata *frame, int clear);

_Py_framedata *_PyThreadState_Push_framedata(
PyThreadState *tstate, PyFrameConstructor *con, PyObject *locals);

void _PyThreadState_Pop_framedata(PyThreadState *tstate, _Py_framedata *frame);

#ifdef __cplusplus
}
#endif
#endif /* !Py_INTERNAL_FRAMEDATA_H */
4 changes: 2 additions & 2 deletions Lib/test/test_socket.py
Original file line number Diff line number Diff line change
Expand Up @@ -2090,11 +2090,11 @@ def _testSendFrame(self):
self.cf = self.build_can_frame(0x00, b'\x01\x02\x03\x04\x05')
self.cli.send(self.cf)

def testSendMaxFrame(self):
def testSendMafdata(self):
cf, addr = self.s.recvfrom(self.bufsize)
self.assertEqual(self.cf, cf)

def _testSendMaxFrame(self):
def _testSendMafdata(self):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are false positives

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reverted this unintended change.

self.cf = self.build_can_frame(0x00, b'\x07' * 8)
self.cli.send(self.cf)

Expand Down
Loading