- Notifications
You must be signed in to change notification settings - Fork61
Portable usage of PyFrameObject
Stackless Python is API and ABI compatible with corresponding versions of C-Python, but undocumented implementation details are not always compatible.
Starting with version 3.8 Stackless-Python uses the same C structurePyFrameObject
as regular C-Python (See issue270). Therefore the following documentation applies to Stackless Python up and including version 3.7 only.
One important difference is the C structurePyFrameObject
. In Stackless, it contains one additional field and the order of fields differs slightly. Some extension modules and Python compilers need to set up frames. Here is a demo how to access the fields of PyFrameObject in a portable way. It is a minimal, Python extension module.
#include <Python.h>#include <stdio.h>#include <stddef.h> #include <frameobject.h>/* back port from Python 3 */#ifndef Py_BUILD_ASSERT_EXPR/* Assert a build-time dependency, as an expression. Your compile will fail if the condition isn't true, or can't be evaluated by the compiler. This can be used in an expression: its value is 0. Example: #define foo_to_char(foo) \ ((char *)(foo) \ + Py_BUILD_ASSERT_EXPR(offsetof(struct foo, string) == 0)) Written by Rusty Russell, public domain, http://ccodearchive.net/ */#define Py_BUILD_ASSERT_EXPR(cond) \ (sizeof(char [1 - 2*!(cond)]) - 1)#endif#ifndef Py_MEMBER_SIZE/* Get the size of a structure member in bytes */#define Py_MEMBER_SIZE(type, member) sizeof(((type *)0)->member)#endif/* A definition from Stackless Python */#ifndef Py_TPFLAGS_HAVE_STACKLESS_CALL#define Py_TPFLAGS_HAVE_STACKLESS_CALL (1UL<<15)#endif/* A portable test, that does not initialize the Stackless subsystem. */#define IS_STACKLESS_PYTHON() PyType_HasFeature(&PyFunction_Type, Py_TPFLAGS_HAVE_STACKLESS_CALL)/* The offset of the PyFrameObject fields "f_code" and "f_localsplus" differs between * C-Python and Stackless Python. Therefore the offset has to be computed at run-time. */#define PYFRAME_LOCALSPLUS_OFFSET \ ((void)Py_BUILD_ASSERT_EXPR(sizeof(PyFrameObject) == offsetof(PyFrameObject, f_localsplus) + Py_MEMBER_SIZE(PyFrameObject, f_localsplus)), \ (PyFrame_Type.tp_basicsize - Py_MEMBER_SIZE(PyFrameObject, f_localsplus)))#define PYFRAME_CODE_OFFSET \ ((void)Py_BUILD_ASSERT_EXPR(sizeof(PyFrameObject) == offsetof(PyFrameObject, f_localsplus) + Py_MEMBER_SIZE(PyFrameObject, f_localsplus)), \ (IS_STACKLESS_PYTHON() ? (PyFrame_Type.tp_basicsize - Py_MEMBER_SIZE(PyFrameObject, f_localsplus) - Py_MEMBER_SIZE(PyFrameObject, f_code)) : \ (offsetof(PyFrameObject, f_back) + Py_MEMBER_SIZE(PyFrameObject, f_back))))/* Portable (between C-Python and Stackless) macros for PyFrameObject */#define PYFRAME_BACK(frame) ((frame)->f_back)#define PYFRAME_CODE(frame) (*((PyCodeObject **)(((char *)(frame)) + PYFRAME_CODE_OFFSET)))#define PYFRAME_BUILTINS(frame) ((frame)->f_builtins)#define PYFRAME_GLOBALS(frame) ((frame)->f_globals)#define PYFRAME_LOCALS(frame) ((frame)->f_locals)#define PYFRAME_VALUESTACK(frame) ((frame)->f_valuestack)#define PYFRAME_STACKTOP(frame) ((frame)->f_stacktop)#define PYFRAME_TRACE(frame) ((frame)->f_trace)#define PYFRAME_TRACE_LINES(frame) ((frame)->f_trace_opcodes)#define PYFRAME_TRACE_OPCODES(frame) ((frame)->f_trace_lines)#define PYFRAME_GEN(frame) ((frame)->f_gen)#define PYFRAME_EXC_TYPE(frame) ((frame)->f_exc_type)#define PYFRAME_EXC_VALUE(frame) ((frame)->f_exc_value)#define PYFRAME_EXC_TRACEBACK(frame) ((frame)->f_exc_traceback)#define PYFRAME_TSTATE(frame) ((frame)->f_tstate)#define PYFRAME_LASTI(frame) ((frame)->f_lasti)#define PYFRAME_LINENO(frame) ((frame)->f_lineno)#define PYFRAME_IBLOCK(frame) ((frame)->f_iblock)#define PYFRAME_EXECUTING(frame) ((frame)->f_executing)#define PYFRAME_BLOCKSTACK(frame) ((frame)->f_blockstack)#define PYFRAME_LOCALSPLUS(frame) ((PyObject **)(((char *)(frame)) + PYFRAME_LOCALSPLUS_OFFSET))#ifdef STACKLESS#define PYFRAME_EXECUTE(frame) ((frame)->f_execute)#endif/***************************************************** * Minimal extension module code below this line *****************************************************/#if PY_MAJOR_VERSION >= 3static struct PyModuleDef framedemomodule = { PyModuleDef_HEAD_INIT, "framedemo", /* name of module */ NULL, /* module documentation, may be NULL */ -1, /* size of per-interpreter state of the module, or -1 if the module keeps state in global variables. */};PyMODINIT_FUNCPyInit_framedemo(void)#elsevoidinitframedemo(void)#endif{ printf("offsetof(PyFrame_Object, f_localsplus) = %zu\n", PYFRAME_LOCALSPLUS_OFFSET); printf("offsetof(PyFrame_Object, f_code) = %zu\n", PYFRAME_CODE_OFFSET);#if PY_MAJOR_VERSION >= 3 return PyModule_Create(&framedemomodule);#else Py_InitModule("framedemo", NULL);#endif}
To build this module, copy the following lines to "setup.py"
from distutils.core import setup, Extensionmodule1 = Extension('framedemo', sources = ['framedemomodule.c'])setup (name = 'FrameDemo', version = '0.0', description = 'This demo shows how to access a PyFrameObject', ext_modules = [module1])
and runpython setup.py build_ext
.