You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

拆分文件实现Python C API NumPy包装器时触发段错误求助

问题

我用Python C API编写NumPy数组包装器,单文件时代码正常、测试全过,但拆分到多文件后触发段错误,无法定位原因;另外,我想仅定义一次#define PY_ARRAY_UNIQUE_SYMBOL ...和#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION,但遇到重复定义报错,推测正确的定义方式会影响文件拆分结构。

示例说明

我开发的模块接收NumPy数组,将其乘以系数后写入另一个数组,文件结构如下:

dir/
├── module.c
├── module_example.py
├── module_examples.so
├── module_headers.h
└── module_implementation.c

测试代码(module_example.py)

#!/usr/bin/env python
import numpy as np
from module_examples import foo
import unittest


class TestNumpyFloatWrappers(unittest.TestCase):
    def test_numpy_wrapper(self):
        a = np.arange(10, dtype=float)
        b = np.arange(10, dtype=float)
        foo(input=a, output=b, factor=3.0)


if __name__ == "__main__":
    unittest.main()

头文件(module_headers.h)

#ifndef PYARV_MODULE_HEADERS_H
#define PYARV_MODULE_HEADERS_H

#define PY_SSIZE_T_CLEAN
#include <Python.h>
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
#include <numpy/arrayobject.h>

PyObject * foo(PyObject *Py_UNUSED(self), PyObject *args, PyObject *kwargs);

#endif//PYARV_MODULE_HEADERS_H

实现文件(module_implementation.c)

#include "module_headers.h"

PyObject *foo(PyObject *Py_UNUSED(self), PyObject *args, PyObject *kwargs)
{
    PyArrayObject *input_array;
    PyArrayObject *output_array;
    double factor;
#define N_ARRAYS 2
    PyArrayObject **arrays[N_ARRAYS] = {&input_array, &output_array};
    char *arg_names[] = {
            "input",
            "output",
            "factor",
            NULL};

    if (!PyArg_ParseTupleAndKeywords(args, kwargs,
                                     "$O!O!d:multiply",
                                     arg_names,
                                     &PyArray_Type,
                                     &input_array,
                                     &PyArray_Type,
                                     &output_array,
                                     &factor))
    {
        return NULL;
    }
    for (int i = 0; i < N_ARRAYS; i++)
    {
        PyObject *array = *arrays[i];
        if (PyArray_NDIM(array) != 1)
        {
            PyErr_SetString(PyExc_ValueError, "Array must be 1-dimensional");
            return NULL;
        }
        if (PyArray_TYPE(array) != NPY_DOUBLE)
        {
            PyErr_SetString(PyExc_ValueError, "Array must be of type double");
            return NULL;
        }

        if (!PyArray_IS_C_CONTIGUOUS(array))
        {
            PyErr_SetString(PyExc_ValueError, "Array must be C contiguous.");
            return NULL;
        }
    }

    npy_double *input_buffer = (npy_double *) PyArray_DATA(input_array);
    npy_double *output_buffer = (npy_double *) PyArray_DATA(output_array);
    size_t input_buffer_size = PyArray_SIZE(input_array);
    size_t output_buffer_size = PyArray_SIZE(output_array);

    if (input_buffer_size != output_buffer_size)
    {
        PyErr_SetString(PyExc_ValueError, "The input and output arrays are of differing lengths.");
        return NULL;
    }

    NPY_BEGIN_THREADS_DEF;
    NPY_BEGIN_THREADS; /* No longer need the Python GIL */

    for (size_t i = 0; i < input_buffer_size; i++)
    {
        output_buffer[i] = input_buffer[i] * factor;
    }

    NPY_END_THREADS; /* We return the Python GIL. */

    Py_RETURN_NONE;
}

模块入口文件(module.c)

/* // 运行测试会触发段错误
#define PY_ARRAY_UNIQUE_SYMBOL EXAMPLE_ARRAY_API
#include "module_headers.h"
*/

/* // 单文件时正常运行
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#define PY_ARRAY_UNIQUE_SYMBOL EXAMPLE_ARRAY_API
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
#include <numpy/arrayobject.h>

PyObject *foo(PyObject *Py_UNUSED(self), PyObject *args, PyObject *kwargs)
{
  //...
}
*/

static PyMethodDef example_methods[] = {
        {"foo", (PyCFunction) foo, METH_VARARGS | METH_KEYWORDS, NULL},
        {NULL},
};

static struct PyModuleDef example_module = {
        .m_base = PyModuleDef_HEAD_INIT,
        .m_doc = "Something is going wrong here.",
        .m_name = "examples",
        .m_size = -1,
        .m_methods = example_methods,
};

PyObject *
PyInit_module_examples(void)
{
    import_array();
    PyObject *module = PyModule_Create(&example_module);
    if (
            !module || PyModule_AddStringConstant(module, "__version__", Py_STRINGIFY(NPB_VERSION)))
    {
        Py_XDECREF(module);
        return NULL;
    }
    return module;
}

调试尝试

用Python调试器运行时提示:

..../python ..../module_example.py
process exited with status -1 (attach failed (Not allowed to attach to process.  Look in the console messages (Console.app), near the debugserver entries, when the attach failed.  The subsystem that denied the attach permission will likely have logged an informative message about why it was denied.))

Process finished with exit code 0

解决方案

核心问题分析

  1. 段错误原因:
    拆分文件后,module.c定义了PY_ARRAY_UNIQUE_SYMBOL再包含头文件,但module_implementation.c直接包含头文件未定义该符号,导致两个源文件使用了不同的NumPy API符号表。import_array()仅初始化了module.c中的符号,module_implementation.c里的NumPy函数指针未被正确初始化,调用时触发段错误。

  2. 宏重复定义问题:
    头文件中定义NPY_NO_DEPRECATED_API会导致所有包含该头文件的源文件重复定义该宏,引发编译错误。正确做法是在每个源文件包含头文件前统一定义这些宏,或用公共配置头文件管理。

修正步骤

1. 修改头文件(module_headers.h)

移除头文件中的NPY_NO_DEPRECATED_API定义,避免重复定义:

#ifndef PYARV_MODULE_HEADERS_H
#define PYARV_MODULE_HEADERS_H

#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <numpy/arrayobject.h>

PyObject * foo(PyObject *Py_UNUSED(self), PyObject *args, PyObject *kwargs);

#endif//PYARV_MODULE_HEADERS_H

2. 修改模块入口文件(module.c)

在包含头文件前定义PY_ARRAY_UNIQUE_SYMBOL和NPY_NO_DEPRECATED_API:

#define PY_ARRAY_UNIQUE_SYMBOL EXAMPLE_ARRAY_API
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
#include "module_headers.h"

static PyMethodDef example_methods[] = {
        {"foo", (PyCFunction) foo, METH_VARARGS | METH_KEYWORDS, NULL},
        {NULL},
};

static struct PyModuleDef example_module = {
        .m_base = PyModuleDef_HEAD_INIT,
        .m_doc = "Something is going wrong here.",
        .m_name = "examples",
        .m_size = -1,
        .m_methods = example_methods,
};

PyObject *
PyInit_module_examples(void)
{
    import_array();
    PyObject *module = PyModule_Create(&example_module);
    if (!module || PyModule_AddStringConstant(module, "__version__", "1.0")) {
        Py_XDECREF(module);
        return NULL;
    }
    return module;
}

3. 修改实现文件(module_implementation.c)

同样在包含头文件前定义相同的宏,确保符号表一致:

#define PY_ARRAY_UNIQUE_SYMBOL EXAMPLE_ARRAY_API
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
#include "module_headers.h"

// foo函数实现保持不变...

额外调试建议

调试时的attach失败是系统权限问题,与段错误无关。可以用gdb直接调试:

gdb python
(gdb) run module_example.py
(gdb) bt  # 查看段错误堆栈

内容的提问来源于stack exchange,提问作者oliversm

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 19:35:53