Cython – C-Extensions for Python


  • William Stein (2006-01-18): initial version
  • William Stein (2007-07-28): update from sagex to cython
  • Martin Albrecht & William Stein (2011-08): cfile & cargs

Returns the name of the ATLAS library to use. On Darwin or Cygwin, this is 'blas', and otherwise it is 'atlas'.


sage: sage.misc.cython.atlas() # random -- depends on OS

Return the name of the cblas library on this system. If the environment variable $SAGE_CBLAS is set, just return its value. If not, return 'cblas' if /usr/lib/ or /usr/lib/libcblas.dylib exists, return 'blas' if /usr/lib/libblas.dll.a exists, and return 'gslcblas' otherwise.


sage: sage.misc.cython.cblas() # random -- depends on OS, etc.


  • code – string containing code that could be in a .pyx file that is attached or put in a %cython block in the notebook.

OUTPUT: a module, which results from compiling the given code and importing it


sage: module = sage.misc.cython.compile_and_load("def f(int n):\n    return n*n")
sage: module.f(10)
sage.misc.cython.cython(filename, verbose=False, compile_message=False, use_cache=False, create_local_c_file=False, annotate=True, sage_namespace=True, create_local_so_file=False)

Compile a Cython file. This converts a Cython file to a C (or C++ file), and then compiles that. The .c file and the .so file are created in a temporary directory.


  • filename - the name of the file to be compiled. Should end with ‘pyx’.
  • verbose (bool, default False) - if True, print debugging information.
  • compile_message (bool, default False) - if True, print 'Compiling <filename>...' to the standard error.
  • use_cache (bool, default False) - if True, check the temporary build directory to see if there is already a corresponding .so file. If so, and if the .so file is newer than the Cython file, don’t recompile, just reuse the .so file.
  • create_local_c_file (bool, default False) - if True, save a copy of the .c file in the current directory.
  • annotate (bool, default True) - if True, create an html file which annotates the conversion from .pyx to .c. By default this is only created in the temporary directory, but if create_local_c_file is also True, then save a copy of the .html file in the current directory.
  • sage_namespace (bool, default True) - if True, import sage.all.
  • create_local_so_file (bool, default False) - if True, save a copy of the compiled .so file in the current directory.


Before trac ticket #12975, it would have beeen needed to write #clang c++, but upper case C++ has resulted in an error:

sage: code = [
... "#clang C++",
... "#cinclude %s/include/singular %s/include/factory"%(SAGE_LOCAL, SAGE_LOCAL),
... "#clib m readline singular givaro ntl gmpxx gmp",
... "from sage.rings.polynomial.multi_polynomial_libsingular cimport MPolynomial_libsingular",
... "from sage.libs.singular.polynomial cimport singular_polynomial_pow",
... "def test(MPolynomial_libsingular p):",
... "    singular_polynomial_pow(&p._poly, p._poly, 2, p._parent_ring)"]
sage: cython(os.linesep.join(code))

The function test now manipulates internal C data of polynomials, squaring them:

sage: P.<x,y>=QQ[]
sage: test(x)
sage: x

Compile filename and make it available as a loadable shared object file.


  • filename - string: a Cython (.spyx) file


EFFECT: A compiled, python “importable” loadable shared object file is created.


Shared object files are not reloadable. The intent is for imports in other scripts. A possible development cycle might go thus:

  • Attach a .spyx file
  • Interactively test and edit it to your satisfaction
  • Use cython_create_local_so to create the shared object file
  • Import the .so file in other scripts


sage: curdir = os.path.abspath(os.curdir)
sage: dir = tmp_dir(); os.chdir(dir)
sage: f = file('hello.spyx', 'w')
sage: s = "def hello():\n  print 'hello'\n"
sage: f.write(s)
sage: f.close()
sage: cython_create_local_so('hello.spyx')
Compiling hello.spyx...
sage: sys.path.append('.')
sage: import hello
sage: hello.hello()
sage: os.chdir(curdir)


  • David Fu (2008-04-09): initial version
sage.misc.cython.cython_lambda(vars, expr, verbose=False, compile_message=False, use_cache=False)

Create a compiled function which evaluates expr assuming machine values for vars.


  • vars - list of pairs (variable name, c-data type), where the variable names and data types are strings, OR a string such as 'double x, int y, int z'
  • expr - an expression involving the vars and constants; you can access objects defined in the current module scope globals() using sage.object_name.


Accessing globals() doesn’t actually work, see trac ticket #12446.


We create a Lambda function in pure Python (using the r to make sure the 3.2 is viewed as a Python float):

sage: f = lambda x,y: x*x + y*y + x + y + 17r*x + 3.2r

We make the same Lambda function, but in a compiled form.

sage: g = cython_lambda('double x, double y', 'x*x + y*y + x + y + 17*x + 3.2')
sage: g(2,3)
sage: g(0,0)

The following should work but doesn’t, see trac ticket #12446:

sage: a = 25
sage: f = cython_lambda('double x', 'sage.math.sin(x) + sage.a')
sage: f(10)  # known bug
sage: a = 50
sage: f(10)  # known bug

Given a string s, find each substring of the form '$ABC'. If the environment variable $ABC is set, replace '$ABC' with its value and move on to the next such substring. If it is not set, stop parsing there.


sage: from sage.misc.cython import environ_parse
sage: environ_parse('$SAGE_LOCAL') == SAGE_LOCAL
sage: environ_parse('$THIS_IS_NOT_DEFINED_ANYWHERE')
sage: os.environ['DEFINE_THIS'] = 'hello'

This is used by the testing infrastructure to test building Cython programs.


  • name – string; name of a key to the TESTS dictionary above

OUTPUT: a module, which results from compiling the given code and importing it


sage: module = sage.misc.cython.import_test("trac11680b")
sage: module.f(2,3,4)
sage.misc.cython.parse_keywords(kwd, s)

Given a keyword kwd and a string s, return a list of all arguments on the same line as that keyword in s, as well as a new copy of s in which each occurrence of kwd is in a comment. If a comment already occurs on the line containing kwd, no words after the # are added to the list.


sage: sage.misc.cython.parse_keywords('clib', " clib foo bar baz\n #cinclude bar\n")
(['foo', 'bar', 'baz'], ' #clib foo bar baz\n #cinclude bar\n')

sage: sage.misc.cython.parse_keywords('clib', "# qux clib foo bar baz\n #cinclude bar\n")
(['foo', 'bar', 'baz'], '# qux clib foo bar baz\n #cinclude bar\n')
sage: sage.misc.cython.parse_keywords('clib', "# clib foo bar # baz\n #cinclude bar\n")
(['foo', 'bar'], '# clib foo bar # baz\n #cinclude bar\n')

Preparse a pyx file:

  • include cdefs.pxi, interrupt.pxi, stdsage.pxi
  • parse clang pragma (c or c++)
  • parse clib pragma (additional libraries to link in)
  • parse cinclude (additional include directories)
  • parse cfile (additional files to be included)
  • parse cargs (additional parameters passed to the compiler)

The pragmas:

  • clang - may be either 'c' or 'c++' indicating whether a C or C++ compiler should be used
  • clib - additional libraries to be linked in, the space separated list is split and passed to distutils.
  • cinclude - additional directories to search for header files. The space separated list is split and passed to distutils.
  • cfile - additional C or C++ files to be compiled. Also, $SAGE_SRC and $SAGE_LOCAL are expanded, but other environment variables are not.
  • cargs - additional parameters passed to the compiler

OUTPUT: preamble, libs, includes, language, files, args


sage: from sage.misc.cython import pyx_preparse
sage: pyx_preparse("")
('\ninclude "interrupt.pxi"  # ctrl-c interrupt block support\ninclude "stdsage.pxi"  # ctrl-c interrupt block support\n\ninclude "cdefs.pxi"\n',
[], ['-w', '-O2'])
sage: s, libs, inc, lang, f, args = pyx_preparse("# clang c++\n #clib foo\n # cinclude bar\n")
sage: lang

sage: libs
['foo', 'mpfr',
'gmp', 'gmpxx',
'gsl', '...blas', ...,
sage: libs[1:] == sage.misc.cython.standard_libs

sage: inc

sage: s, libs, inc, lang, f, args = pyx_preparse("# cargs -O3 -ggdb\n")
sage: args
['-w', '-O2', '-O3', '-ggdb']


sage: module = sage.misc.cython.import_test("trac11680")  # long time (7s on sage.math, 2012)
sage: R.<x> = QQ[]
sage: module.evaluate_at_power_of_gen(x^3 + x - 7, 5)  # long time
x^15 + x^5 - 7

Given a filename f, replace it by a filename that is a valid Python module name.

This means that the characters are all alphanumeric or _‘s and doesn’t begin with a numeral.


sage: from sage.misc.cython import sanitize
sage: sanitize('abc')
sage: sanitize('abc/def')
sage: sanitize('123/def-hij/')
sage.misc.cython.subtract_from_line_numbers(s, n)

Given a string s and an integer n, for any line of s which has the form 'text:NUM:text' subtract n from NUM and return 'text:(NUM-n):text'. Return other lines of s without change.


sage: from sage.misc.cython import subtract_from_line_numbers
sage: subtract_from_line_numbers('hello:1234:hello', 3)
sage: subtract_from_line_numbers('text:123\nhello:1234:', 3)

Previous topic

Random Number States

Next topic

Message delivery.

This Page