Astropy supports using C extensions for wrapping C libraries and Cython for speeding up computationally-intensive calculations. Both Cython and C extension building can be customized using the get_extensions function of the setup_package.py file. If defined, this function must return a list of distutils.core.Extension objects. The creation process is left to the subpackage designer, and can be customized however is relevant for the extensions in the subpackage.
While C extensions must always be defined through the get_extensions mechanism, Cython files (ending in .pyx) are automatically located and loaded in separate extensions if they are not in get_extensions. For Cython extensions located in this way, headers for numpy C functions are included in the build, but no other external headers are included. .pyx files present in the extensions returned by get_extensions are not included in the list of extensions automatically generated extensions. Note that this allows disabling a Cython file by providing an extension that includes the Cython file, but giving it the special name ‘cython_skip’. Any extension with this package name will not be built by setup.py.
Note
If an Extension object is provided for Cython source files using the get_extensions mechanism, it is very important that the .pyx files be given as the source, rather than the .c files generated by Cython.
If your C extension needs to be linked from other third-party C code, you probably want to install its header files along side the Python module.
Create an include directory inside of your package for all of the header files.
Use the get_package_data hook in setup_package.py to install those header files. For example, the astropy.wcs package has this:
def get_package_data(): return {'astropy.wcs': ['include/*.h']}
In rare cases, some packages may need to be imported at build time. Unfortunately, anything that requires a C or Cython extension or processing through 2to3 will fail to import until the build phase has completed. In those cases, the _ASTROPY_SETUP_ variable can be used to determine if the package is being imported as part of the build and choose to not import problematic modules. _ASTROPY_SETUP_ is inserted into the builtins, and is True when inside of astropy’s setup.py script, and False otherwise.
For example, suppose there is a subpackage foo that needs to import a module called version.py at build time in order to set some version information, and also has a C extension, process, that will not be available in the source tree. In this case, astropy/foo/__init__.py would probably want to check the value of _ASTROPY_SETUP_ before importing the C extension:
try:
from . import process
except ImportError:
if not _ASTROPY_SETUP_:
raise
from . import version