Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Make distutils look for numpy header files in the correct place

In my installation, numpy's arrayobject.h is located at …/site-packages/numpy/core/include/numpy/arrayobject.h. I wrote a trivial Cython script that uses numpy:

cimport numpy as np  def say_hello_to(name):     print("Hello %s!" % name) 

I also have the following distutils setup.py (copied from the Cython user guide):

from distutils.core import setup from distutils.extension import Extension from Cython.Distutils import build_ext  ext_modules = [Extension("hello", ["hello.pyx"])]  setup(   name = 'Hello world app',   cmdclass = {'build_ext': build_ext},   ext_modules = ext_modules ) 

When I try to build with python setup.py build_ext --inplace, Cython tries to do the following:

gcc -fno-strict-aliasing -Wno-long-double -no-cpp-precomp -mno-fused-madd \ -fno-common -dynamic -DNDEBUG -g -Os -Wall -Wstrict-prototypes -DMACOSX \ -I/usr/include/ffi -DENABLE_DTRACE -arch i386 -arch ppc -pipe \ -I/System/Library/Frameworks/Python.framework/Versions/2.5/include/python2.5 \ -c hello.c -o build/temp.macosx-10.5-i386-2.5/hello.o 

Predictably, this fails to find arrayobject.h. How can I make distutils use the correct location of numpy include files (without making the user define $CFLAGS)?

like image 947
Vebjorn Ljosa Avatar asked Mar 04 '10 14:03

Vebjorn Ljosa


2 Answers

Use numpy.get_include():

from distutils.core import setup from distutils.extension import Extension from Cython.Distutils import build_ext import numpy as np                           # <---- New line  ext_modules = [Extension("hello", ["hello.pyx"],                                   include_dirs=[get_numpy_include()])]   # <---- New argument  setup(   name = 'Hello world app',   cmdclass = {'build_ext': build_ext},          ext_modules = ext_modules ) 
like image 82
Vebjorn Ljosa Avatar answered Sep 20 '22 12:09

Vebjorn Ljosa


The answer given by @vebjorn-ljosa is correct, but it causes problems when used in conjunction with install_requires=['numpy']. In this situation, your setup.py needs to import numpy, which will cause an error if you try to pip install your project without running pip install numpy first.

If your project depends on numpy, and you want numpy to be installed automatically as a dependency, you need to set include_dirs only when your extensions are actually being built. You can do this by subclassing build_ext:

from distutils.core import setup from distutils.extension import Extension from Cython.Distutils import build_ext  class CustomBuildExtCommand(build_ext):     """build_ext command for use when numpy headers are needed."""     def run(self):          # Import numpy here, only when headers are needed         import numpy          # Add numpy headers to include_dirs         self.include_dirs.append(numpy.get_include())          # Call original build_ext command         build_ext.run(self)  ext_modules = [Extension("hello", ["hello.pyx"])]  setup(   name = 'Hello world app',   cmdclass = {'build_ext': CustomBuildExtCommand},   install_requires=['numpy'],   ext_modules = ext_modules ) 

And you can use a similar trick to add cython as an automatically installed dependency:

from distutils.core import setup from distutils.extension import Extension  try:     from Cython.setuptools import build_ext except:     # If we couldn't import Cython, use the normal setuptools     # and look for a pre-compiled .c file instead of a .pyx file     from setuptools.command.build_ext import build_ext     ext_modules = [Extension("hello", ["hello.c"])] else:     # If we successfully imported Cython, look for a .pyx file     ext_modules = [Extension("hello", ["hello.pyx"])]  class CustomBuildExtCommand(build_ext):     """build_ext command for use when numpy headers are needed."""     def run(self):          # Import numpy here, only when headers are needed         import numpy          # Add numpy headers to include_dirs         self.include_dirs.append(numpy.get_include())          # Call original build_ext command         build_ext.run(self)  setup(   name = 'Hello world app',   cmdclass = {'build_ext': CustomBuildExtCommand},   install_requires=['cython', 'numpy'],   ext_modules = ext_modules ) 

Note: these approaches only work with pip install .. They won't work for python setup.py install or python setup.py develop as in these commands cause dependencies to be installed after your project, rather than before.

like image 32
R_Beagrie Avatar answered Sep 17 '22 12:09

R_Beagrie