- Extending the CPython interpreter with fast compiled modules,
- Interfacing Python code with external C/C++ libraries.

- For changing code from having dynamic Python semantics into having static-and-fast (but less generic) C semantics.
- Directly manipulating C data types defined in external libraries.

- A
`.pyx`file is compiled by Cython to a`.c`or`.cpp`file. - The
`.c`or`.cpp`file is compild by a C compiler (such as GCC) to a`.so`file.

First, create a file

def sum_cython(long n): cdef long i, s = 0 for i in range(n): s += i return s

Since we're using Sage, you can do

bash$ sage -cython sum.pyx bash$ ls sum.c sum.pyxNotice the new file

bash$ sage -sh bash$ gcc -I$SAGE_ROOT/local/include/python2.6 -bundle -undefined dynamic_lookup sum.c -o sum.soOn Linux, do:

bash$ sage -sh bash$ gcc -I$SAGE_ROOT/local/include/python2.6 -shared -fPIC sum.c -o sum.so

You must run Sage from the same directory that contains the file

bash$ sage ---------------------------------------------------------------------- | Sage Version 4.6, Release Date: 2010-10-30 | | Type notebook() for the GUI, and license() for information. | ---------------------------------------------------------------------- sage: import sum sage: sum.sum_cython(101) 5050 sage: timeit('sum.sum_cython(101)') 625 loops, best of 3: 627 ns per loop sage: timeit('sum.sum_cython(101)', number=10^6) # better quality timing 1000000 loops, best of 3: 539 ns per loopFinally, take a look at the (more than 1000 line) autogenerated C file

bash$ wc -l sum.c 1178 sum.c bash$ less sum.cNotice code like this, which illustrates that Cython generates code that supports

#if PY_MAJOR_VERSION < 3 #define __Pyx_BUILTIN_MODULE_NAME "__builtin__" #else #define __Pyx_BUILTIN_MODULE_NAME "builtins" #endif #if PY_MAJOR_VERSION >= 3 #define Py_TPFLAGS_CHECKTYPES 0 #define Py_TPFLAGS_HAVE_INDEX 0 #endifThe official Python docs say: "If you are writing a new extension module, you might consider Cython. It translates a Python-like language to C. The extension modules it creates are compatible with Python 3.x and 2.x."

If you scroll down further you'll get past the boilerplate and see the actual code:

... /* "/Users/wstein/edu/2010-2011/581d/notes/2010-11-08/sum.pyx":2 * def sum_cython(long n): * cdef long i, s = 0 # <<<<<<<<<<<<<< * for i in range(n): * s += i */ __pyx_v_s = 0; /* "/Users/wstein/edu/2010-2011/581d/notes/2010-11-08/sum.pyx":3 * def sum_cython(long n): * cdef long i, s = 0 * for i in range(n): # <<<<<<<<<<<<<< * s += i * return s */ __pyx_t_1 = __pyx_v_n; for (__pyx_t_2 = 0; __pyx_t_2 < __pyx_t_1; __pyx_t_2+=1) { __pyx_v_i = __pyx_t_2; /* "/Users/wstein/edu/2010-2011/581d/notes/2010-11-08/sum.pyx":4 * cdef long i, s = 0 * for i in range(n): * s += i # <<<<<<<<<<<<<< * return s */ __pyx_v_s += __pyx_v_i; } ...

There is a big comment that shows the original Cython code with context and a little ` <<<<<<<` pointing at the current line (these comment blocks with context were I think the first thing I personally added to Pyrex... before, it just gave that first line with the .pyx filename and line number, but nothing else). Below that big comment, there is the actual C code that Cython generates. For example, the Cython code ` s += i` is turned into the C code `__pyx_v_s += __pyx_v_i;`.

First, create a file `sum2.c` as follows:

#include <Python.h> static PyObject * sum2_sum_c(PyObject *self, PyObject *n_arg) { long i, s=0, n = PyInt_AsLong(n_arg); for (i=0; i<n; i++) { s += i; } PyObject* t = PyInt_FromLong(s); return t; } static PyMethodDef Sum2Methods[] = { {"sum_c", sum2_sum_c, METH_O, "Sum the numbers up to n."}, {NULL, NULL, 0, NULL} /* Sentinel */ }; PyMODINIT_FUNC initsum2(void) { PyObject *m; m = Py_InitModule("sum2", Sum2Methods); }

Now compile and run it as before:

bash$ sage -sh bash$ gcc -I$SAGE_ROOT/local/include/python2.6 -bundle -undefined dynamic_lookup sum2.c -o sum2.so bash$ sage ... sage: import sum2 sage: sum2.sum_c(101) 5050 sage: import sum sage: sum.sum_cython(101) 5050 sage: timeit('sum.sum_cython(1000000r)') 125 loops, best of 3: 2.54 ms per loop sage: timeit('sum2.sum_c(1000000r)') 125 loops, best of 3: 2.03 ms per loopNote that this is a little faster than the corresponding Cython code. This is because the Cython code is more careful, checking various error conditions, etc.

Note that the C code is 5 times as long as the Cython code.

Let's create a new setuptools project that includes the sum and sum2 extensions that we defined above. First, create the following file and call it `setup.py`. This should be in the same directory as sum.c and sum2.c.

from setuptools import setup, Extension ext_modules = [ Extension("sum", ["sum.c"]), Extension("sum2", ["sum2.c"]) ] setup( name = 'sum', version = '0.1', ext_modules = ext_modules)Then type

bash$ rm *.so # make sure something happens bash$ sage setup.py develop ... bash$ ls *.so sum.so sum2.so

Notice that running

setup.py developresulted in Python generating the right gcc commmand lines for your platform. You don't have to do anything differently on Linux, OS X, etc.

If you change `sum2.c`, and want to rebuild it, just type `sage setup.py develop` again to rebuild `sum2.so` If you change `sum.pyx`, you have to manually run Cython:

sage -cython sum.pyxthen again do

for i in range(n):to

for i in range(1,n+1):then rebuild:

bash$ sage -cython sum.pyx ... bash$ sage setup.py develop ... bash$ sage ... sage: import sum sage: sum.sum_cython(100) 5050

There are ways to make setup.py automatically notice when sum.pyx changes, and run Cython. A nice implementation of this will be in the next Cython release. See the setup.py and build_system.py files of Purple sage for an example of how to write a little build system write now (before the new version of Cython).

sage: load sum.pyx Compiling sum.pyx... sage: sum_cython(100) 5050Behind the scenes, Sage created a setup.py file, ran Cython, made a new module, compiled it, and imported everything it defines into the global namespace. If you look in the spyx subdirectory of the directory listed below,

sage: SAGE_TMP '/Users/wstein/.sage//temp/deep.local/14837/'You can also do

sage: attach sum.pyxThen every time sum.pyx changes, Sage will notice this and reload it. This can be useful for development of small chunks of Cython code.

You can also use the Sage notebook, and put `%cython` as the first line of a notebook cell. The rest of the cell will be compiled exactly as if it were written to a `.pyx` file and loaded as above. In fact, that is almost exactly what happens behind the scenes.

- How to create extension classes using Cython.
- How to call external C/C++ library code.