(Work in progress. Please suggest improvements.)
Sometimes you want to write code that uses Sage and make it available to the world but you don't want to write your library within Sage. This is a quick guide to making sure that your package compiles and runs properly.
This guide assumes that your project is in the usual Python package format.
myproject/
myproject/
__init__.py
(source files)
README.md
setup.py
Take a look at How To Package Your Python Code if you are new to writing Python packages.
If your package sources are written purely in Python, even if they use Sage
functionality via from sage.x import y
then you're already done! The only
difference is to call your setup.py
script with Sage:
$ sage setup.py install --user
This will install your package into Sage's standard location for third-party Python modules.
If your module sources are written in Cython but do not interface with Sage's
Cython modules then you are also already done! Just make sure your setup.py
script is written to compile your Cython extensions properly. See the Cython
Compilation reference
page for more details.
If on the other hand you have Cython modules in your package that depend on Sage
Cython modules then some extra work is needed. That is, if module1.pyx
contains,
# module1.pyx
from sage.x cimport y
For example, suppose your setup.py
script looked something like this,
# setup.py
import os
from distutils.core import setup
from distutils.extension import Extension
from Cython.Build import cythonize
# list mypackage's Cython extension modules. will not compile if they depend on
# Sage extension modules
ext_modules = [
Extension(
'mypackage.module1',
sources=[os.path.join('mypackage','module1.pyx')]
),
Extension(
'mypackage.module2',
sources=[
os.path.join('mypackage','module2.pyx'),
os.path.join('mypackage','utils.c')
],
),
]
setup(
name = 'mypackage',
packages = ['mypackage'],
ext_modules = cythonize(ext_modules),
)
In order for these extensions to properly link to Sage's Cython modules you will
need to include the appropriate Cython headers in the modules' include_dirs
lists,
# setup.py
import os
from distutils.core import setup
from distutils.extension import Extension
from Cython.Build import cythonize
#
# define ext_modules - the list of your project's Cython modules
#
# add the sage include directories to each extension module
from sage.env import sage_include_directories
for mod in ext_modules:
mod.include_dirs.extend(sage_include_directories())
#
# call distutils.core.setup()
#
Our setup script is complete. You can now install your external package into
Sage as you would a Cython package. The only difference is to call your
setup.py
script with Sage,
$ sage setup.py install --user
If your code uses Sage-style doctests,
$ sage setup.py install --user
$ sage -t --force-lib mypackage
If your code uses an alternate test suite then build your package in-place and run your test suite. For example, if you use Nose,
$ sage setup.py build_ext --inplace
$ nosetests
There are many design decisions to make when it comes to writing tests for Python software.
If you have any suggestions or improvements please leave a comment here.
I think there are many things wrong with the Cython part. If this guide really gets taken seriously, this should be addressed (maybe I could do that). In short:
build_ext
does not work, you must usecythonize
.sage_include_directories
fromsage.env
Extension
objects after creating them, you can give theinclude_dirs
to distutils.