├── README.md ├── ex1 ├── Makefile ├── demomodule.c ├── dev.py └── setup.py ├── ex2-gsl ├── Makefile ├── demomodule.c ├── dev.py └── setup.py ├── ex3-algorithm ├── Makefile ├── algorithm.c ├── algorithm.h ├── demomodule.c ├── dev.py └── setup.py ├── ex4-numpy ├── Makefile ├── algorithm.c ├── algorithm.h ├── demomodule.c ├── dev.py └── setup.py └── ex5-c-api ├── Makefile ├── README.md ├── algorithm.c ├── algorithm.h ├── consumermodule.c ├── demomodule.c ├── demomodule.h ├── dev.py └── setup.py /README.md: -------------------------------------------------------------------------------- 1 | # A demo of the Python C API. 2 | -------------------------------------------------------------------------------- /ex1/Makefile: -------------------------------------------------------------------------------- 1 | demo.cpython-35m-x86_64-linux-gnu.so: demomodule.c 2 | python3 setup.py build_ext -i 3 | 4 | 5 | clean: 6 | rm -fR build *.so 7 | -------------------------------------------------------------------------------- /ex1/demomodule.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | static PyObject * 4 | demo_add(PyObject *self, PyObject *args) 5 | { 6 | PyObject *ret = NULL; 7 | double x, y, result; 8 | 9 | if (!PyArg_ParseTuple(args, "dd", &x, &y)) { 10 | goto out; 11 | } 12 | result = x + y; 13 | ret = Py_BuildValue("d", result); 14 | out: 15 | return ret; 16 | } 17 | 18 | static PyMethodDef demo_methods[] = { 19 | {"add", (PyCFunction) demo_add, METH_VARARGS, 20 | "Print a lovely skit to standard output."}, 21 | {NULL, NULL, 0, NULL} /* sentinel */ 22 | }; 23 | 24 | static struct PyModuleDef demomodule = { 25 | PyModuleDef_HEAD_INIT, 26 | "demo", 27 | NULL, 28 | -1, 29 | demo_methods 30 | }; 31 | 32 | PyMODINIT_FUNC 33 | PyInit_demo(void) 34 | { 35 | return PyModule_Create(&demomodule); 36 | } 37 | -------------------------------------------------------------------------------- /ex1/dev.py: -------------------------------------------------------------------------------- 1 | """ 2 | Example code for demostrating the demo Python C module. 3 | """ 4 | 5 | import demo 6 | 7 | print("Hello from demo") 8 | print("add: ", demo.add(3, 4)) 9 | 10 | print(demo.add(23, "234")) 11 | -------------------------------------------------------------------------------- /ex1/setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, Extension 2 | 3 | demo_module = Extension( 4 | 'demo', 5 | sources=["demomodule.c"] 6 | ) 7 | 8 | setup( 9 | name="demo", 10 | description="Demo C module", 11 | ext_modules=[demo_module]) 12 | 13 | -------------------------------------------------------------------------------- /ex2-gsl/Makefile: -------------------------------------------------------------------------------- 1 | demo.cpython-35m-x86_64-linux-gnu.so: demomodule.c 2 | python3 setup.py build_ext -i 3 | 4 | 5 | clean: 6 | rm -fR build *.so 7 | -------------------------------------------------------------------------------- /ex2-gsl/demomodule.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /* Wrapper for GSL function gsl_sf_hydrogenicR, 5 | * https://www.gnu.org/software/gsl/doc/html/specfunc.html#normalized-hydrogenic-bound-states 6 | */ 7 | static PyObject * 8 | demo_hydrogenicR(PyObject *self, PyObject *args) 9 | { 10 | PyObject *ret = NULL; 11 | double n, l, Z, r, result; 12 | 13 | if (!PyArg_ParseTuple(args, "dddd", &n, &l, &Z, &r)) { 14 | goto out; 15 | } 16 | if (n <= l) { 17 | PyErr_SetString(PyExc_ValueError, "n must be >= l"); 18 | goto out; 19 | } 20 | /* Check bounds on other input parameters */ 21 | 22 | result = gsl_sf_hydrogenicR(n, l, Z, r); 23 | ret = Py_BuildValue("d", result); 24 | out: 25 | return ret; 26 | } 27 | 28 | static PyMethodDef demo_methods[] = { 29 | {"hydrogenicR", (PyCFunction) demo_hydrogenicR, METH_VARARGS, 30 | "Returns the result of gsl_sf_hydrogenicR"}, 31 | {NULL, NULL, 0, NULL} /* sentinel */ 32 | }; 33 | 34 | static struct PyModuleDef demomodule = { 35 | PyModuleDef_HEAD_INIT, 36 | "demo", 37 | NULL, 38 | -1, 39 | demo_methods 40 | }; 41 | 42 | PyMODINIT_FUNC 43 | PyInit_demo(void) 44 | { 45 | return PyModule_Create(&demomodule); 46 | } 47 | -------------------------------------------------------------------------------- /ex2-gsl/dev.py: -------------------------------------------------------------------------------- 1 | """ 2 | Example code for demostrating the demo Python C module. 3 | """ 4 | 5 | import demo 6 | 7 | print("Hello from demo") 8 | print("hydrogenicR: ", demo.hydrogenicR(10, 4, 1, 2)) 9 | print("hydrogenicR: ", demo.hydrogenicR(-5, 4, 1, 2)) 10 | 11 | -------------------------------------------------------------------------------- /ex2-gsl/setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, Extension 2 | 3 | demo_module = Extension( 4 | 'demo', 5 | sources=["demomodule.c"], 6 | libraries=["m", "gsl", "gslcblas"], 7 | ) 8 | 9 | setup( 10 | name="demo", 11 | description="Demo C module", 12 | ext_modules=[demo_module]) 13 | 14 | -------------------------------------------------------------------------------- /ex3-algorithm/Makefile: -------------------------------------------------------------------------------- 1 | demo.cpython-35m-x86_64-linux-gnu.so: demomodule.c 2 | python3 setup.py build_ext -i 3 | 4 | clean: 5 | rm -fR build *.so 6 | -------------------------------------------------------------------------------- /ex3-algorithm/algorithm.c: -------------------------------------------------------------------------------- 1 | 2 | int 3 | fancy_algorithm(double x, double y, double *result) 4 | { 5 | int ret = 0; 6 | 7 | /* Some arbitrary checks on the input values */ 8 | if (x < 0) { 9 | ret = -1; /* We would really have symbolic constants here */ 10 | goto out; 11 | } 12 | /* Input checks are ok, run our 'fancy' algorithm. */ 13 | 14 | *result = x * y; 15 | out: 16 | return ret; 17 | } 18 | -------------------------------------------------------------------------------- /ex3-algorithm/algorithm.h: -------------------------------------------------------------------------------- 1 | 2 | int fancy_algorithm(double x, double y, double *result); 3 | -------------------------------------------------------------------------------- /ex3-algorithm/demomodule.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "algorithm.h" 3 | 4 | static PyObject * 5 | demo_fancy_algorithm(PyObject *self, PyObject *args) 6 | { 7 | PyObject *ret = NULL; 8 | double x, y, result; 9 | int err; 10 | 11 | if (!PyArg_ParseTuple(args, "dd", &x, &y)) { 12 | goto out; 13 | } 14 | err = fancy_algorithm(x, y, &result); 15 | if (err != 0) { 16 | PyErr_Format(PyExc_ValueError, "Error %d occured in fancy_algorithm", err); 17 | goto out; 18 | } 19 | ret = Py_BuildValue("d", result); 20 | out: 21 | return ret; 22 | } 23 | 24 | static PyMethodDef demo_methods[] = { 25 | {"fancy_algorithm", (PyCFunction) demo_fancy_algorithm, METH_VARARGS, 26 | "Runs an algorithm defined in a local C file."}, 27 | {NULL, NULL, 0, NULL} /* sentinel */ 28 | }; 29 | 30 | static struct PyModuleDef demomodule = { 31 | PyModuleDef_HEAD_INIT, 32 | "demo", 33 | NULL, 34 | -1, 35 | demo_methods 36 | }; 37 | 38 | PyMODINIT_FUNC 39 | PyInit_demo(void) 40 | { 41 | return PyModule_Create(&demomodule); 42 | } 43 | -------------------------------------------------------------------------------- /ex3-algorithm/dev.py: -------------------------------------------------------------------------------- 1 | """ 2 | Example code for demostrating the demo Python C module. 3 | """ 4 | 5 | import demo 6 | 7 | print("Hello from demo") 8 | print("fancy_algorithm: ", demo.fancy_algorithm(3.6, 4.9)) 9 | 10 | demo.fancy_algorithm(-1, 10) 11 | -------------------------------------------------------------------------------- /ex3-algorithm/setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, Extension 2 | 3 | demo_module = Extension( 4 | 'demo', 5 | sources=["demomodule.c", "algorithm.c"] 6 | ) 7 | 8 | setup( 9 | name="demo", 10 | description="Demo C module", 11 | ext_modules=[demo_module]) 12 | 13 | -------------------------------------------------------------------------------- /ex4-numpy/Makefile: -------------------------------------------------------------------------------- 1 | demo.cpython-35m-x86_64-linux-gnu.so: demomodule.c 2 | python3 setup.py build_ext -i 3 | 4 | clean: 5 | rm -fR build *.so 6 | -------------------------------------------------------------------------------- /ex4-numpy/algorithm.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | int 5 | fancy_algorithm(double *x, double *y, int n, double *result) 6 | { 7 | int ret = 0; 8 | 9 | if (x == NULL || y == NULL || result == NULL) { 10 | ret = -2; 11 | goto out; 12 | } 13 | 14 | /* Do a simple componentwise multiply. This could be anything though */ 15 | for (int j = 0; j < (int) n; j++) { 16 | result[j] = x[j] * y[j]; 17 | } 18 | out: 19 | return ret; 20 | } 21 | -------------------------------------------------------------------------------- /ex4-numpy/algorithm.h: -------------------------------------------------------------------------------- 1 | 2 | int fancy_algorithm(double *x, double *y, int n, double *result); 3 | -------------------------------------------------------------------------------- /ex4-numpy/demomodule.c: -------------------------------------------------------------------------------- 1 | 2 | #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION 3 | #include 4 | #include 5 | #include 6 | 7 | #include "algorithm.h" 8 | 9 | static PyObject * 10 | demo_fancy_algorithm(PyObject *self, PyObject *args) 11 | { 12 | int err; 13 | PyObject *ret = NULL; 14 | PyObject *x = NULL; 15 | PyObject *y = NULL; 16 | PyArrayObject *x_array = NULL; 17 | PyArrayObject *y_array = NULL; 18 | PyArrayObject *result_array = NULL; 19 | npy_intp *shape_x, *shape_y; 20 | 21 | if (!PyArg_ParseTuple(args, "OO", &x, &y)) { 22 | goto out; 23 | } 24 | x_array = (PyArrayObject *) PyArray_FROM_OTF(x, NPY_FLOAT64, NPY_ARRAY_IN_ARRAY); 25 | if (x_array == NULL) { 26 | goto out; 27 | } 28 | if (PyArray_NDIM(x_array) != 1) { 29 | PyErr_SetString(PyExc_ValueError, "Dim x != 1"); 30 | goto out; 31 | } 32 | y_array = (PyArrayObject *) PyArray_FROM_OTF(y, NPY_FLOAT64, NPY_ARRAY_IN_ARRAY); 33 | if (y_array == NULL) { 34 | goto out; 35 | } 36 | if (PyArray_NDIM(y_array) != 1) { 37 | PyErr_SetString(PyExc_ValueError, "Dim y != 1"); 38 | goto out; 39 | } 40 | shape_x = PyArray_DIMS(x_array); 41 | shape_y = PyArray_DIMS(y_array); 42 | if (shape_x[0] != shape_y[0]) { 43 | PyErr_SetString(PyExc_ValueError, "x and y not same shape"); 44 | goto out; 45 | } 46 | /* Allocate the result array */ 47 | result_array = (PyArrayObject *) PyArray_SimpleNew(1, shape_x, NPY_FLOAT64); 48 | if (result_array == NULL) { 49 | goto out; 50 | } 51 | /* Now we can run the real calculation using pointers to the memory that 52 | * numpy has allocated */ 53 | 54 | err = fancy_algorithm( 55 | (double *) PyArray_DATA(x_array), 56 | (double *) PyArray_DATA(y_array), 57 | (int) shape_x[0], 58 | (double *) PyArray_DATA(result_array)); 59 | if (err != 0) { 60 | PyErr_Format(PyExc_ValueError, "Error %d occured in fancy_algorithm", err); 61 | goto out; 62 | } 63 | ret = (PyObject *) result_array; 64 | result_array = NULL; 65 | out: 66 | Py_XDECREF(x_array); 67 | Py_XDECREF(y_array); 68 | Py_XDECREF(result_array); 69 | return ret; 70 | } 71 | 72 | static PyMethodDef demo_methods[] = { 73 | {"fancy_algorithm", (PyCFunction) demo_fancy_algorithm, METH_VARARGS, 74 | "Runs an algorithm defined in a local C file."}, 75 | {NULL, NULL, 0, NULL} /* sentinel */ 76 | }; 77 | 78 | static struct PyModuleDef demomodule = { 79 | PyModuleDef_HEAD_INIT, 80 | "demo", 81 | NULL, 82 | -1, 83 | demo_methods 84 | }; 85 | 86 | PyMODINIT_FUNC 87 | PyInit_demo(void) 88 | { 89 | PyObject *mod = NULL; 90 | import_array(); 91 | mod = PyModule_Create(&demomodule); 92 | return mod; 93 | } 94 | 95 | -------------------------------------------------------------------------------- /ex4-numpy/dev.py: -------------------------------------------------------------------------------- 1 | """ 2 | Example code for demostrating the demo Python C module. 3 | """ 4 | 5 | import numpy as np 6 | import demo 7 | 8 | print("Hello from demo") 9 | 10 | n = 100000 11 | 12 | # for _ in range(10000): 13 | # x = np.random.random(n) 14 | # y = np.random.random(n) 15 | # z = demo.fancy_algorithm(x, y) 16 | # assert np.all(z == (x * y)) 17 | 18 | for _ in range(10000): 19 | x = np.random.random(n) 20 | y = np.random.random(n - 1) 21 | try: 22 | z = demo.fancy_algorithm(x, y) 23 | except ValueError: 24 | pass 25 | else: 26 | assert False 27 | 28 | -------------------------------------------------------------------------------- /ex4-numpy/setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, Extension 2 | 3 | import numpy as np 4 | 5 | demo_module = Extension( 6 | 'demo', 7 | sources=["demomodule.c", "algorithm.c"], 8 | include_dirs=[np.get_include()], 9 | ) 10 | 11 | setup( 12 | name="demo", 13 | description="Demo C module", 14 | ext_modules=[demo_module]) 15 | 16 | -------------------------------------------------------------------------------- /ex5-c-api/Makefile: -------------------------------------------------------------------------------- 1 | all: consumer.cpython-35m-x86_64-linux-gnu.so demo.cpython-35m-x86_64-linux-gnu.so 2 | 3 | consumer.cpython-35m-x86_64-linux-gnu.so: consumermodule.c 4 | python3 setup.py build_ext -i 5 | 6 | demo.cpython-35m-x86_64-linux-gnu.so: demomodule.c 7 | python3 setup.py build_ext -i 8 | 9 | clean: 10 | rm -fR build *.so 11 | -------------------------------------------------------------------------------- /ex5-c-api/README.md: -------------------------------------------------------------------------------- 1 | This is a simple demonstration of the Python mechanism for exporting a 2 | C API from an extension module (like numpy). 3 | 4 | See [here]( 5 | https://docs.python.org/3.5/extending/extending.html#providing-a-c-api-for-an-extension-module) 6 | for details. 7 | 8 | Here we have two modules, demo and consumer. The demo module compiles 9 | some C code for fancy_algorithm, and it exports this using the Capsule 10 | API. The consumer module then loads the demo module dynamically and 11 | dereferences a pointer to the function fancy_algorithm when it calls 12 | it (via a neat preprocessor trick). 13 | 14 | What this example doesn't show is how to package the header files that are 15 | needed to make this work. This must be done using setuptools, etc. 16 | 17 | **WARNING** There is lots of error checking left out here, so it is *not* a good example to 18 | base real code on. I put this together to try and figure out how this worked in as simple 19 | a way as possible; it might help other people trying to figure it out too. 20 | -------------------------------------------------------------------------------- /ex5-c-api/algorithm.c: -------------------------------------------------------------------------------- 1 | 2 | int 3 | fancy_algorithm(double x, double y, double *result) 4 | { 5 | int ret = 0; 6 | 7 | /* Some arbitrary checks on the input values */ 8 | if (x < 0) { 9 | ret = -1; /* We would really have symbolic constants here */ 10 | goto out; 11 | } 12 | /* Input checks are ok, run our 'fancy' algorithm. */ 13 | 14 | *result = x * y; 15 | out: 16 | return ret; 17 | } 18 | -------------------------------------------------------------------------------- /ex5-c-api/algorithm.h: -------------------------------------------------------------------------------- 1 | 2 | int fancy_algorithm(double x, double y, double *result); 3 | -------------------------------------------------------------------------------- /ex5-c-api/consumermodule.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "demomodule.h" 3 | 4 | static PyObject * 5 | consumer_fancy_algorithm(PyObject *self, PyObject *args) 6 | { 7 | PyObject *ret = NULL; 8 | double x, y, result; 9 | int err; 10 | 11 | if (!PyArg_ParseTuple(args, "dd", &x, &y)) { 12 | goto out; 13 | } 14 | printf("consumer running fancy_algorithm\n"); 15 | err = fancy_algorithm(x, y, &result); 16 | if (err != 0) { 17 | PyErr_Format(PyExc_ValueError, "Error %d occured in fancy_algorithm", err); 18 | goto out; 19 | } 20 | ret = Py_BuildValue("d", result); 21 | out: 22 | return ret; 23 | } 24 | 25 | static PyMethodDef consumer_methods[] = { 26 | {"fancy_algorithm", (PyCFunction) consumer_fancy_algorithm, METH_VARARGS, 27 | "Runs an algorithm defined in another Python module.."}, 28 | {NULL, NULL, 0, NULL} /* sentinel */ 29 | }; 30 | 31 | static struct PyModuleDef consumermodule = { 32 | PyModuleDef_HEAD_INIT, 33 | "consumer", 34 | NULL, 35 | -1, 36 | consumer_methods 37 | }; 38 | 39 | PyMODINIT_FUNC 40 | PyInit_consumer(void) 41 | { 42 | PyObject *m; 43 | 44 | m = PyModule_Create(&consumermodule); 45 | if (m == NULL) { 46 | goto out; 47 | } 48 | if (import_demo() < 0) { 49 | return NULL; 50 | } 51 | out: 52 | return m; 53 | } 54 | -------------------------------------------------------------------------------- /ex5-c-api/demomodule.c: -------------------------------------------------------------------------------- 1 | #include 2 | #define DEMO_MODULE 3 | #include "demomodule.h" 4 | 5 | static PyObject * 6 | demo_fancy_algorithm(PyObject *self, PyObject *args) 7 | { 8 | PyObject *ret = NULL; 9 | double x, y, result; 10 | int err; 11 | 12 | if (!PyArg_ParseTuple(args, "dd", &x, &y)) { 13 | goto out; 14 | } 15 | printf("demo running fancy_algorithm\n"); 16 | err = fancy_algorithm(x, y, &result); 17 | if (err != 0) { 18 | PyErr_Format(PyExc_ValueError, "Error %d occured in fancy_algorithm", err); 19 | goto out; 20 | } 21 | ret = Py_BuildValue("d", result); 22 | out: 23 | return ret; 24 | } 25 | 26 | static PyMethodDef demo_methods[] = { 27 | {"fancy_algorithm", (PyCFunction) demo_fancy_algorithm, METH_VARARGS, 28 | "Runs an algorithm defined in a local C file."}, 29 | {NULL, NULL, 0, NULL} /* sentinel */ 30 | }; 31 | 32 | static struct PyModuleDef demomodule = { 33 | PyModuleDef_HEAD_INIT, 34 | "demo", 35 | NULL, 36 | -1, 37 | demo_methods 38 | }; 39 | 40 | PyMODINIT_FUNC 41 | PyInit_demo(void) 42 | { 43 | PyObject *m; 44 | static void *demo_API[1]; 45 | PyObject *c_api_object; 46 | 47 | m = PyModule_Create(&demomodule); 48 | if (m == NULL) { 49 | goto out; 50 | } 51 | 52 | /* Initialize the C API pointer array */ 53 | demo_API[0] = (void *) fancy_algorithm; 54 | 55 | /* Create a Capsule containing the API pointer array's address */ 56 | c_api_object = PyCapsule_New((void *)demo_API, "demo._C_API", NULL); 57 | 58 | if (c_api_object != NULL) { 59 | PyModule_AddObject(m, "_C_API", c_api_object); 60 | } 61 | out: 62 | return m; 63 | } 64 | -------------------------------------------------------------------------------- /ex5-c-api/demomodule.h: -------------------------------------------------------------------------------- 1 | #ifndef Py_DEMOMODULE_H 2 | #define Py_DEMOMODULE_H 3 | #ifdef __cplusplus 4 | extern "C" { 5 | #endif 6 | 7 | #ifdef DEMO_MODULE 8 | 9 | #include "algorithm.h" 10 | 11 | #else 12 | 13 | static void **demo_API; 14 | 15 | /* int fancy_algorithm(double x, double y, double *result); */ 16 | #define fancy_algorithm\ 17 | (*(int (*)(double x, double y, double *result)) demo_API[0]) 18 | 19 | 20 | /* Return -1 on error, 0 on success. 21 | * PyCapsule_Import will set an exception if there's an error. 22 | */ 23 | static int 24 | import_demo(void) 25 | { 26 | demo_API = (void **)PyCapsule_Import("demo._C_API", 0); 27 | return (demo_API != NULL) ? 0 : -1; 28 | } 29 | 30 | #endif 31 | 32 | #ifdef __cplusplus 33 | } 34 | #endif 35 | #endif 36 | -------------------------------------------------------------------------------- /ex5-c-api/dev.py: -------------------------------------------------------------------------------- 1 | """ 2 | Example code for demostrating the demo Python C module. 3 | """ 4 | 5 | # import demo 6 | import consumer 7 | 8 | # print(demo.fancy_algorithm(1, 2)) 9 | print(consumer.fancy_algorithm(1, 2)) 10 | 11 | # print("Hello from demo") 12 | # print("fancy_algorithm: ", demo.fancy_algorithm(3.6, 4.9)) 13 | # print("fancy_algorithm: ", consumer.fancy_algorithm(3.6, 4.9)) 14 | 15 | -------------------------------------------------------------------------------- /ex5-c-api/setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, Extension 2 | 3 | demo_module = Extension( 4 | 'demo', 5 | sources=["demomodule.c", "algorithm.c"] 6 | ) 7 | 8 | consumer_module = Extension( 9 | 'consumer', 10 | sources=["consumermodule.c"] 11 | ) 12 | 13 | setup( 14 | name="demo", 15 | description="Demo C module", 16 | ext_modules=[demo_module, consumer_module]) 17 | 18 | --------------------------------------------------------------------------------