Python C API:PyDateTime_FromTimestamp会导致分段错误

Python C API: PyDateTime_FromTimestamp causes segmentation fault

本文关键字:分段 错误 FromTimestamp API PyDateTime Python      更新时间:2023-10-16

我按照这个答案调用PyDateTime_FromTimestamp在C++中创建一个datetime对象。但是当PyDateTime_FromTimestamp被召唤时,我得到了一个Segmentation fault

这是我C++代码:

#include <python3.6/Python.h>
#include <stdio.h>
#include <python3.6/datetime.h>
#include <sys/time.h>
static PyObject *iGetDateTime_PyFn(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args)) {
static double doubleValue = 1314761451;
PyObject *floatObj = NULL;
PyObject *timeTuple = NULL;
PyObject *dateTime = NULL;
floatObj = PyFloat_FromDouble(doubleValue);
timeTuple = Py_BuildValue("(O)", floatObj);
printf("timeTuple = %08xn", (unsigned int)(long long)timeTuple);
printf("PyTuple_Check(timeTuple) = %dn", PyTuple_Check(timeTuple));
dateTime = PyDateTime_FromTimestamp(timeTuple);
printf("ready to returnn");
return dateTime;
}
static PyMethodDef all_methods[] = {
{ "get_datetime", iGetDateTime_PyFn, METH_VARARGS, NULL },
{ NULL, NULL, 0, NULL }
};
static struct PyModuleDef main_module = {
PyModuleDef_HEAD_INIT,
"cpp",
NULL,
-1,
all_methods
};
PyMODINIT_FUNC PyInit_cpp(void) {
return PyModule_Create(&main_module);
}

我用这个命令编译:

g++ --shared -fPIC -o cpp.so t1.cpp

我的g++版本是7.3.0.

在python中,我执行:

import cpp
print(cpp.get_datetime())

我打印了以下内容:

timeTuple = a7934358
PyTuple_Check(timeTuple) = 1
Segmentation fault (core dumped)

如我们所见,timeTuple已成功构建,并被检查为tuple。但是我们不能得到return句子。

根据 [GitHub]: python/cpython - (3.6) cpython/Include/datetime.h (${PYTHON_SRC_DIR}/Include/datetime.h):

  1. PyDateTime_FromTimestamp是一个预处理器

    #define PyDateTime_FromTimestamp(args) 
    PyDateTimeAPI->DateTime_FromTimestamp( 
    (PyObject*) (PyDateTimeAPI->DateTimeType), args, NULL)
    
  2. PyDateTimeAPI初始化为NULL(在文件的前面)

    static PyDateTime_CAPI *PyDateTimeAPI = NULL;
    

在调用宏时导致段错误(访问冲突)。

该修复需要通过PyDateTime_IMPORT宏初始化PyDateTimeAPI

#define PyDateTime_IMPORT 
PyDateTimeAPI = (PyDateTime_CAPI *)PyCapsule_Import(PyDateTime_CAPSULE_NAME, 0)

最初,我在浏览代码时发现了这一点(我在getDateTimePyFn函数中做到了这一点),然后我遇到了[Python 3.Docs]:DateTime对象(重点是我的)

在使用任何这些函数之前,头文件datetime.h必须包含在源代码中(请注意,Python.h不包含此功能),并且必须调用宏PyDateTime_IMPORT,通常作为模块初始化函数的一部分。

我修改了您的代码,我将在Win上举例说明(因为它对我来说更容易,并且行为是可重现的)。

CPP.C

#include <stdio.h>
#include <Python.h>
#include <datetime.h>
#define MOD_NAME "cpp"

static double doubleValue = 1314761451;
static PyObject *getDateTimePyFn(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args)) {
PyObject *floatObj = NULL,
*timeTuple = NULL,
*dateTime = NULL;
floatObj = PyFloat_FromDouble(doubleValue);
if (!floatObj)
{
return NULL;
}
timeTuple = Py_BuildValue("(O)", floatObj);
Py_XDECREF(floatObj);
if (!timeTuple)
{
return NULL;
}
dateTime = PyDateTime_FromTimestamp(timeTuple);
Py_XDECREF(timeTuple);
return dateTime;
}

static PyMethodDef all_methods[] = {
{ "get_datetime", getDateTimePyFn, METH_VARARGS, NULL },
{ NULL, NULL, 0, NULL }
};

static struct PyModuleDef main_module = {
PyModuleDef_HEAD_INIT,
MOD_NAME,
NULL,
-1,
all_methods
};

PyMODINIT_FUNC PyInit_cpp(void) {
PyDateTime_IMPORT;  // @TODO - cfati: !!! This initializes the struct containing the function pointer !!!
return PyModule_Create(&main_module);
}

code.py

#!/usr/bin/env python3
import sys
import cpp

def main():
print("cpp.get_datetime returned: {:}".format(cpp.get_datetime()))

if __name__ == "__main__":
print("Python {:s} on {:s}n".format(sys.version, sys.platform))
main()
print("Done.")

输出

[cfati@CFATI-5510-0:e:WorkDevStackOverflowq055903897]> sopr.bat
*** Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ***
[prompt]> "c:Installx86MicrosoftVisual Studio Community2015vcvcvarsall.bat" x64
[prompt]> dir /b
code.py
cpp.c
[prompt]> cl /nologo /DDLL /MD /I"c:Installx64PythonPython3.06.08include" cpp.c  /link /NOLOGO /DLL /LIBPATH:"c:Installx64PythonPython3.06.08libs" /OUT:cpp.pyd
cpp.c
Creating library cpp.lib and object cpp.exp
[prompt]> dir /b
code.py
cpp.c
cpp.exp
cpp.lib
cpp.obj
cpp.pyd
[prompt]> "e:WorkDevVEnvspy_064_03.06.08_test0Scriptspython.exe" code.py
Python 3.6.8 (tags/v3.6.8:3c6b436a57, Dec 24 2018, 00:16:47) [MSC v.1916 64 bit (AMD64)] on win32
cpp.get_datetime returned: 2011-08-31 06:30:51
Done.