Cython support functions

AUTHORS:

  • 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
sage.misc.cython.compile_and_load(code)

INPUT:

  • 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

EXAMPLES:

sage: module = sage.misc.cython.compile_and_load("def f(int n):\n    return n*n")
sage: module.f(10)
100
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.

INPUT:

  • 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.

TESTS:

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

sage: import sysconfig; code = [
....: "#clang C++",
....: "#cinclude /usr/include/singular /usr/include/singular/singular /usr/include/{0}/singular /usr/include/{0}/singular/singular".format(sysconfig.get_config_var('MULTIARCH')),
....: "#clib m readline singular-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
x^2

Check that compiling c++ code works:

sage: cython("#clang C++\n"+
....:        "from libcpp.vector cimport vector\n"
....:        "cdef vector[int] * v = new vector[int](4)\n")
sage.misc.cython.cython_create_local_so(filename)

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

INPUT:

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

OUTPUT: None

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

Note

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

EXAMPLES:

sage: curdir = os.path.abspath(os.curdir)
sage: dir = tmp_dir(); os.chdir(dir)
sage: f = open('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()
hello
sage: os.chdir(curdir)

AUTHORS:

  • David Fu (2008-04-09): initial version
sage.misc.cython.cython_import(filename, verbose=False, compile_message=False, use_cache=False, create_local_c_file=True, **kwds)

Compile a file containing Cython code, then import and return the module. Raises an ImportError if anything goes wrong.

INPUT:

  • filename - a string; name of a file that contains Cython code

See the function sage.misc.cython.cython() for documentation for the other inputs.

OUTPUT:

  • the module that contains the compiled Cython code.
sage.misc.cython.cython_import_all(filename, globals, verbose=False, compile_message=False, use_cache=False, create_local_c_file=True)

Imports all non-private (i.e., not beginning with an underscore) attributes of the specified Cython module into the given context. This is similar to:

from module import *

Raises an ImportError exception if anything goes wrong.

INPUT:

  • filename - a string; name of a file that contains Cython code
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.

INPUT:

  • 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.

Warning

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

EXAMPLES:

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)
55.2
sage: g(0,0)
3.2

In order to access Sage globals, prefix them with sage.:

sage: f = cython_lambda('double x', 'sage.sin(x) + sage.a')
sage: f(0)
Traceback (most recent call last):
...
NameError: global 'a' is not defined
sage: a = 25
sage: f(10)
24.45597888911063
sage: a = 50
sage: f(10)
49.45597888911063
sage.misc.cython.environ_parse(s)

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.

EXAMPLES:

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

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

INPUT:

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

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

EXAMPLES:

sage: module = sage.misc.cython.import_test("trac11680b")
sage: module.f(2,3,4)
9
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.

EXAMPLES:

sage: import sage.misc.cython
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')
sage.misc.cython.pyx_preparse(s)

Preparse a pyx file:

  • include cdefs.pxi, signals.pxi from cysignals, 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

EXAMPLES:

sage: from sage.misc.cython import pyx_preparse
sage: pyx_preparse("")
('\ninclude "cysignals/signals.pxi"  # ctrl-c interrupt block support\ninclude "stdsage.pxi"\n\ninclude "cdefs.pxi"\n',
['mpfr',
'gmp',
'gmpxx',
'stdc++',
'pari',
'm',
'ec',
'gsl',
...
'ntl'],
['.../include',
'.../include/python...',
'.../python.../numpy/core/include',
...
'.../sagemath/ext',
...
'.../cysignals'...],
'c',
[], ['-w', '-O2'],...)
sage: s, libs, inc, lang, f, args, libdirs = pyx_preparse("# clang c++\n #clib foo\n # cinclude bar\n")
sage: lang
'c++'

sage: libs
['foo', 'mpfr',
'gmp', 'gmpxx',
'stdc++',
'pari',
'm',
'ec',
'gsl',
...
'ntl']
sage: libs[1:] == sage.misc.cython.standard_libs
True

sage: inc
['bar',
'.../include',
'.../include/python...',
'.../python.../numpy/core/include',
...
'.../sagemath/ext',
...
'.../cysignals'...]

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

TESTS:

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
sage.misc.cython.sanitize(f)

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.

EXAMPLES:

sage: from sage.misc.cython import sanitize
sage: sanitize('abc')
'abc'
sage: sanitize('abc/def')
'abc_def'
sage: sanitize('123/def-hij/file.py')
'_123_def_hij_file_py'
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.

EXAMPLES:

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