Add numpy.get_include() argument to setuptools without preinstalled numpy

后端 未结 3 1904
时光取名叫无心
时光取名叫无心 2020-11-30 10:03

I am currently developing a python package that uses cython and numpy and I want the package to be installable using the pip install c

3条回答
  •  臣服心动
    2020-11-30 10:52

    One (hacky) suggestion would be using the fact that extension.include_dirs is first requested in build_ext, which is called after the setup dependencies are downloaded.

    class MyExt(setuptools.Extension):
        def __init__(self, *args, **kwargs):
            self.__include_dirs = []
            super().__init__(*args, **kwargs)
    
        @property
        def include_dirs(self):
            import numpy
            return self.__include_dirs + [numpy.get_include()]
    
        @include_dirs.setter
        def include_dirs(self, dirs):
            self.__include_dirs = dirs
    
    
    my_c_lib_ext = MyExt(
        name="my_c_lib",
        sources=["my_c_lib/some_file.pyx"]
    )
    
    setup(
        ...,
        setup_requires=['cython', 'numpy'],
    )
    

    Update

    Another (less, but I guess still pretty hacky) solution would be overriding build instead of build_ext, since we know that build_ext is a subcommand of build and will always be invoked by build on installation. This way, we don't have to touch build_ext and leave it to Cython. This will also work when invoking build_ext directly (e.g., via python setup.py build_ext to rebuild the extensions inplace while developing) because build_ext ensures all options of build are initialized, and by coincidence, Command.set_undefined_options first ensures the command has finalized (I know, distutils is a mess).

    Of course, now we're misusing build - it runs code that belongs to build_ext finalization. However, I'd still probably go with this solution rather than with the first one, ensuring the relevant piece of code is properly documented.

    import setuptools
    from distutils.command.build import build as build_orig
    
    
    class build(build_orig):
    
        def finalize_options(self):
            super().finalize_options()
            # I stole this line from ead's answer:
            __builtins__.__NUMPY_SETUP__ = False
            import numpy
            # or just modify my_c_lib_ext directly here, ext_modules should contain a reference anyway
            extension = next(m for m in self.distribution.ext_modules if m == my_c_lib_ext)
            extension.include_dirs.append(numpy.get_include())
    
    
    my_c_lib_ext = setuptools.Extension(
        name="my_c_lib",
        sources=["my_c_lib/some_file.pyx"]
    )
    
    setuptools.setup(
        ...,
        ext_modules=[my_c_lib_ext],
        cmdclass={'build': build},
        ...
    )
    

提交回复
热议问题