-
Notifications
You must be signed in to change notification settings - Fork 61
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 structure PyFrameObject
as regular C-Python (See issue 270). Therefore the following documentation applies to Stackless Python up and including version 3.7 only.
One important difference is the C structure PyFrameObject
. 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 >= 3
static 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_FUNC
PyInit_framedemo(void)
#else
void
initframedemo(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, Extension
module1 = Extension('framedemo', sources = ['framedemomodule.c'])
setup (name = 'FrameDemo',
version = '0.0',
description = 'This demo shows how to access a PyFrameObject',
ext_modules = [module1])
and run python setup.py build_ext
.