web-dev-qa-db-fra.com

Faites en sorte que distutils recherche les fichiers d’en-tête numpy au bon endroit

Dans mon installation, arrayobject.h de numpy se trouve à …/site-packages/numpy/core/include/numpy/arrayobject.h. J'ai écrit un script Cython trivial qui utilise numpy:

cimport numpy as np

def say_hello_to(name):
    print("Hello %s!" % name)

J'ai également le code suivant setup.py (copié à partir du guide d'utilisation de Cython ):

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
)

Lorsque j'essaie de construire avec python setup.py build_ext --inplace, Cython essaie de faire ce qui suit:

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

De manière prévisible, cela ne parvient pas à trouver arrayobject.h. Comment faire en sorte que distutils utilise l'emplacement correct des fichiers numpy include (sans que l'utilisateur définisse $ CFLAGS)?

41
Vebjorn Ljosa

Utilisez 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"])]

setup(
  name = 'Hello world app',
  cmdclass = {'build_ext': build_ext},
  include_dirs = [np.get_include()],         # <---- New line
  ext_modules = ext_modules
)
62
Vebjorn Ljosa

La réponse donnée par @ vebjorn-ljosa est correcte, mais elle pose problème lorsqu'elle est utilisée avec install_requires=['numpy']. Dans ce cas, votre fichier setup.py doit importer numpy, ce qui provoquera une erreur si vous essayez de pip install votre projet sans exécuter pip install numpy auparavant.

Si votre projet dépend de numpy et que vous souhaitez installer numpy automatiquement en tant que dépendance, vous devez définir include_dirs uniquement lorsque vos extensions sont en cours de construction. Vous pouvez le faire en sous-classant 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
)

Et vous pouvez utiliser une astuce similaire pour ajouter cython en tant que dépendance automatiquement installée:

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
)

Remarque: ces approches ne fonctionnent qu'avec pip install .. Ils ne fonctionneront pas pour python setup.py install ou python setup.py develop car ces commandes entraînent l'installation de dépendances après votre projet plutôt qu'avant.

21
R_Beagrie

Pour ceux qui n'utilisent pas Cython, une légère modification de la solution de R_Beagrie sans cette dépendance consiste à importer simplement build_ext à partir de distutils.command.build_ext au lieu de Cython.

from distutils.core import setup
from distutils.extension import Extension
from distutils.command.build_ext 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.c"])]

setup(
  name = 'Hello world app',
  cmdclass = {'build_ext': CustomBuildExtCommand},
  install_requires=['numpy'],
  ext_modules = ext_modules
)
0
tgbrooks