Как подключить C библиотеку к питону, заюзать NumPy и не выстрелить в ногу
Максим Кольцов
Работа с Python C API
- SWIG
- SIP (PyQt)
- shiboken2 (PySide2)
- Boost::Python
- pybind11
Проблемы
- Сборка
- Преобразования типов
- Исключения
- GC
- GIL
- Вызов виртуальных методов
- Наследование в Python
Как компилировать:
CMake
set(CMAKE_CXX_STANDARD 11)
find_package(PythonInterp 3.4 REQUIRED)
find_package(PythonLibs 3.4 REQUIRED)
find_package(SIP REQUIRED)
include(SIPMacros)
set(SIP_CONCAT_PARTS 4)
set(SIP_EXTRA_OPTIONS -e -o -y module.pyi)
set(SIP_SOURCES src/module.sip ...)
set(SIP_EXTRA_FILES_DEPEND ${SIP_SOURCES})
generate_sip_python_module_code(
_module
src/module.sip
py_cpp_files)
build_sip_python_module(
_module
src/module.sip
"${py_cpp_files}")
А если NumPy?
list(GET py_cpp_files 0 PART0)
set_source_files_properties(${PART0}
PROPERTIES COMPILE_DEFINITIONS SIP_IS_PART0)
%UnitPostIncludeCode
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
#define PY_ARRAY_UNIQUE_SYMBOL module_ARRAY_API
#ifndef SIP_IS_PART0
#define NO_IMPORT_ARRAY
#endif
#include <numpy/arrayobject.h>
%End
%InitialisationCode
import_array();
%End
GC в Python C API
Py_INCREF / Py_DECREF
C++:
void init(int count, int lengths[], int* docs[]); void doSomething();
Python:
init(docs: List[List[int]]) doSomething()
Как передать int** и не потерять память?
Py_ssize_t documentCount = PyList_Size(docs);
std::unique_ptr<int[]> lengths { new int[documentCount] };
std::unique_ptr<int*[]> docPtrs { new int*[documentCount] };
PyObject *docsList = PyList_New(documentCount);
for (Py_ssize_t i = 0; i < documentCount; i++) {
PyObject *item = PyList_GetItem(docs, i);
PyObject *document = PyArray_FROMANY(
item,
NPY_UINT32,
1, 1,
NPY_ARRAY_C_CONTIGUOUS
);
if (document == nullptr) {
sipIsErr = 1;
break;
}
lengths[i] = PyArray_SHAPE((PyArrayObject*) document)[0];
docPtrs[i] = (int*) PyArray_DATA((PyArrayObject*) document);
PyList_SetItem(docsList, i, document);
}
sipKeepReference(sipSelf, (int) time(nullptr), docsList);
GIL!
Py_BEGIN_ALLOW_THREADS
sipRes = sipCpp->init(
documentCount,
lengths.get(), docPtrs.get()
);
Py_END_ALLOW_THREADS
PyObject* wrapMatrix(
double *matrix,
int d1, int d2,
PyObject *sipPySelf)
{
npy_intp dims[] = { d1, d2 };
return PyArray_SimpleNewFromData(2, dims, NPY_DOUBLE, matrix);
}
И обратно
Дополнительная безопасность
- Valgrind
- Address Sanitizer
- Thread Sanitizer
-
Собрать свой питон:
-
CFLAGS='-g -ggdb -fsanitize=address' ./configure --with-pydebug --without-pymalloc
-
-
Инжектить libasan через LD_PRELOAD
Как подключить C библиотеку к питону, заюзать NumPy и не выстрелить в ногу"
By Maxim Koltsov
Как подключить C библиотеку к питону, заюзать NumPy и не выстрелить в ногу"
- 1,629