【问题标题】:Python C API: PyDateTime_FromTimestamp causes segmentation faultPython C API:PyDateTime_FromTimestamp 导致分段错误
【发布时间】:2019-04-29 12:56:37
【问题描述】:

我跟着this answer调用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 = %08x\n", (unsigned int)(long long)timeTuple);
    printf("PyTuple_Check(timeTuple) = %d\n", PyTuple_Check(timeTuple));
    dateTime = PyDateTime_FromTimestamp(timeTuple);
    printf("ready to return\n");
    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 语句。

【问题讨论】:

  • 请添加ldd cpp.so 命令输出,并在您的Python 脚本中添加import sys; print(sys.version),然后粘贴其输出。

标签: python c++ python-c-api python-datetime


【解决方案1】:

根据[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 Objects强调是我的)

在使用任何这些函数之前,头文件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:\Work\Dev\StackOverflow\q055903897]> sopr.bat
*** Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ***

[prompt]> "c:\Install\x86\Microsoft\Visual Studio Community\2015\vc\vcvarsall.bat" x64

[prompt]> dir /b
code.py
cpp.c

[prompt]> cl /nologo /DDLL /MD /I"c:\Install\x64\Python\Python\03.06.08\include" cpp.c  /link /NOLOGO /DLL /LIBPATH:"c:\Install\x64\Python\Python\03.06.08\libs" /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:\Work\Dev\VEnvs\py_064_03.06.08_test0\Scripts\python.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.

【讨论】:

  • @cosmozhang:这回答了你的问题吗?
  • 感谢@CristiFati。这解决了我的问题。没有仔细阅读文档是我的错。
  • 那么请将答案标记为您问题的解决方案,以便其他人也可以看到。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-12-25
相关资源
最近更新 更多