Cppy (Python C++ Extension Headers)
Cppy is a small C++ header library designed to simplify the creation of Python extension modules. Its core feature is a `PyObject` smart pointer, which automates Python's reference counting mechanism and provides convenient methods for common object operations. The current version is 1.3.1. As a C++ header library, its release cadence is typically driven by CPython API changes or new convenience features rather than a fixed schedule.
Warnings
- gotcha Cppy is a C++ header library, not a Python module for direct runtime import. Its primary use is during the compilation of Python C++ extension modules. The Python 'cppy' package provides build-time integration components like `CppyBuildExt` for `setup.py`.
- breaking Cppy requires C++11 or a later standard for compilation. Older C++ compilers or build configurations not explicitly setting C++11 (or newer) will fail to compile extensions using Cppy.
- gotcha Properly managing reference counts in Python C extensions is a common source of bugs. Cppy's `cppy::ptr` smart pointer is designed to alleviate this by automatically handling `Py_INCREF` and `Py_DECREF`.
- gotcha On Windows, when compiling, you might encounter issues related to FH4 Exception Handling, potentially requiring `VCRUNTIME140_1.dll`. This can be disabled if not needed.
- gotcha When using `setuptools` with a PEP 517 compatible build system (i.e., `pyproject.toml`), `cppy` must be listed as a build-time requirement to be available during the `setup.py` execution.
Install
-
pip install cppy -
pip install git+https://github.com/nucleic/cppy
Imports
- cppy/cppy.h
#include <cppy/cppy.h>
- CppyBuildExt
from cppy import CppyBuildExt
Quickstart
import os
from setuptools import setup, Extension
from setuptools.command.build_ext import build_ext
# A minimal C++ source file that includes cppy
# In a real project, this would be in a separate .cpp file
cpp_source = '''
#include <Python.h>
#include <cppy/cppy.h>
static PyObject* greet_method(PyObject* self, PyObject* args) {
const char* name;
if (!PyArg_ParseTuple(args, "s", &name)) {
return NULL;
}
std::string greeting = "Hello, " + std::string(name) + "!";
cppy::ptr result_ptr(PyUnicode_FromString(greeting.c_str()));
return result_ptr.release(); // release ownership to Python
}
static PyMethodDef methods[] = {
{"greet", greet_method, METH_VARARGS, "Greet a person."},
{NULL, NULL, 0, NULL}
};
static struct PyModuleDef mymodule = {
PyModuleDef_HEAD_INIT,
"_my_extension", // Name of the module
NULL, // Module documentation
-1, // Size of per-interpreter state of the module
methods
};
PyMODINIT_FUNC PyInit__my_extension(void) {
return PyModule_Create(&mymodule);
}
'''
# Write the C++ code to a temporary file for demonstration
with open('my_extension_module.cpp', 'w') as f:
f.write(cpp_source)
class CustomBuildExt(build_ext):
def build_extension(self, ext):
# Example of setting C++ standard if not using CppyBuildExt directly
# For cppy, C++11 is typically required.
if self.compiler.compiler_type == 'msvc':
ext.extra_compile_args = ['/std:c++11']
else:
ext.extra_compile_args = ['-std=c++11']
super().build_extension(ext)
setup(
name='my-cpp-extension',
version='0.1.0',
description='A simple C++ extension using cppy',
ext_modules=[
Extension(
'_my_extension',
sources=['my_extension_module.cpp'],
include_dirs=[os.path.join(os.environ.get('VIRTUAL_ENV', '/usr/local'), 'include')], # Adjust if cppy headers not found
language='c++'
)
],
# For real projects, you might use CppyBuildExt directly:
# cmdclass={'build_ext': CppyBuildExt},
# Or ensure cppy is in build-system.requires in pyproject.toml
# and then 'from cppy import CppyBuildExt' in setup.py
cmdclass={'build_ext': CustomBuildExt},
setup_requires=['cppy'], # Ensure cppy is available during setup
install_requires=[]
)
# To demonstrate usage after hypothetical build and installation:
# import _my_extension
# print(_my_extension.greet("World"))