Created
August 3, 2012 13:41
-
-
Save dfm/3247796 to your computer and use it in GitHub Desktop.
How to wrap C code in Python
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <Python.h> | |
#include <numpy/arrayobject.h> | |
#include "chi2.h" | |
/* Docstrings */ | |
static char module_docstring[] = | |
"This module provides an interface for calculating chi-squared using C."; | |
static char chi2_docstring[] = | |
"Calculate the chi-squared of some data given a model."; | |
/* Available functions */ | |
static PyObject *chi2_chi2(PyObject *self, PyObject *args); | |
/* Module specification */ | |
static PyMethodDef module_methods[] = { | |
{"chi2", chi2_chi2, METH_VARARGS, chi2_docstring}, | |
{NULL, NULL, 0, NULL} | |
}; | |
/* Initialize the module */ | |
PyMODINIT_FUNC init_chi2(void) | |
{ | |
PyObject *m = Py_InitModule3("_chi2", module_methods, module_docstring); | |
if (m == NULL) | |
return; | |
/* Load `numpy` functionality. */ | |
import_array(); | |
} | |
static PyObject *chi2_chi2(PyObject *self, PyObject *args) | |
{ | |
double m, b; | |
PyObject *x_obj, *y_obj, *yerr_obj; | |
/* Parse the input tuple */ | |
if (!PyArg_ParseTuple(args, "ddOOO", &m, &b, &x_obj, &y_obj, | |
&yerr_obj)) | |
return NULL; | |
/* Interpret the input objects as numpy arrays. */ | |
PyObject *x_array = PyArray_FROM_OTF(x_obj, NPY_DOUBLE, NPY_IN_ARRAY); | |
PyObject *y_array = PyArray_FROM_OTF(y_obj, NPY_DOUBLE, NPY_IN_ARRAY); | |
PyObject *yerr_array = PyArray_FROM_OTF(yerr_obj, NPY_DOUBLE, | |
NPY_IN_ARRAY); | |
/* If that didn't work, throw an exception. */ | |
if (x_array == NULL || y_array == NULL || yerr_array == NULL) { | |
Py_XDECREF(x_array); | |
Py_XDECREF(y_array); | |
Py_XDECREF(yerr_array); | |
return NULL; | |
} | |
/* How many data points are there? */ | |
int N = (int)PyArray_DIM(x_array, 0); | |
/* Get pointers to the data as C-types. */ | |
double *x = (double*)PyArray_DATA(x_array); | |
double *y = (double*)PyArray_DATA(y_array); | |
double *yerr = (double*)PyArray_DATA(yerr_array); | |
/* Call the external C function to compute the chi-squared. */ | |
double value = chi2(m, b, x, y, yerr, N); | |
/* Clean up. */ | |
Py_DECREF(x_array); | |
Py_DECREF(y_array); | |
Py_DECREF(yerr_array); | |
if (value < 0.0) { | |
PyErr_SetString(PyExc_RuntimeError, | |
"Chi-squared returned an impossible value."); | |
return NULL; | |
} | |
/* Build the output tuple */ | |
PyObject *ret = Py_BuildValue("d", value); | |
return ret; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "chi2.h" | |
double chi2(double m, double b, double *x, double *y, double *yerr, int N) { | |
int n; | |
double result = 0.0, diff; | |
for (n = 0; n < N; n++) { | |
diff = (y[n] - (m * x[n] + b)) / yerr[n]; | |
result += diff * diff; | |
} | |
return result; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
double chi2(double m, double b, double *x, double *y, double *yerr, int N); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from distutils.core import setup, Extension | |
import numpy.distutils.misc_util | |
c_ext = Extension("_chi2", ["_chi2.c", "chi2.c"]) | |
setup( | |
ext_modules=[c_ext], | |
include_dirs=numpy.distutils.misc_util.get_numpy_include_dirs(), | |
) |
Nice code however what if my module does not need fucntions but need to do something like this where it calls classes from a backend
module.
from importlib.machinery import FileFinder
from ._pycx_backend import *
import sys
extension_importers = [JsonImporter(), PycxImporter()]
hook_list = []
for importer in extension_importers:
hook_list.append((importer.find_loader, importer.extensions))
sys.path_hooks.insert(0, FileFinder.path_hook(*hook_list))
sys.path_importer_cache.clear()
Basically I want to write this in using C Code similar to how you wrote this without using all too annoying Cython because it is entirely unreadable.
Can't get it work on Python3.6. Importing in REPL tells me that Py_InitModule3
Edit: Confirmed its initialization function outdated. Here is the updated gist from StackOverflow: https://gist.github.com/GreenJoey/b08528d6abe62da70f28f73c39c0efd0
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
How about a ctypes version: https://gist.github.com/3313315 ...