I am currently developing a python package that uses cython
and numpy
and I want the package to be installable using the pip install
command from a clean python installation. All dependencies should be installed automatically. I am using setuptools
with the following setup.py
:
import setuptools
my_c_lib_ext = setuptools.Extension(
name="my_c_lib",
sources=["my_c_lib/some_file.pyx"]
)
setuptools.setup(
name="my_lib",
version="0.0.1",
author="Me",
author_email="me@myself.com",
description="Some python library",
packages=["my_lib"],
ext_modules=[my_c_lib_ext],
setup_requires=["cython >= 0.29"],
install_requires=["numpy >= 1.15"],
classifiers=[
"Programming Language :: Python :: 3",
"Operating System :: OS Independent"
]
)
This has worked great so far. The pip install
command downloads cython
for the build and is able to build my package and install it together with numpy
.
Now I want to improve the performance of my cython
code, which leads to some changes in my setup.py
. I need to add include_dirs=[numpy.get_include()]
to either the call of setuptools.Extension(...)
or setuptools.setup(...)
which means that I also need to import numpy
. (See http://docs.cython.org/en/latest/src/tutorial/numpy.html and Make distutils look for numpy header files in the correct place for rationals.)
This is bad. Now the user cannot call pip install
from a clean environment, because import numpy
will fail. The user needs to pip install numpy
before installing my library. Even if I move "numpy >= 1.15"
from install_requires
to setup_requires
the installation fails, because the import numpy
is evaluated earlier.
Is there a way to evaluate the include_dirs
at a later point of the installation, for example, after the dependencies from setup_requires
or install_requires
have been resolved? I really like to have all dependencies resolved automatically and I dont want the user to type multiple pip install
commands.
The following snippet works, but it is not officially supported because it uses an undocumented (and private) method:
class NumpyExtension(setuptools.Extension):
# setuptools calls this function after installing dependencies
def _convert_pyx_sources_to_lang(self):
import numpy
self.include_dirs.append(numpy.get_include())
super()._convert_pyx_sources_to_lang()
my_c_lib_ext = NumpyExtension(
name="my_c_lib",
sources=["my_c_lib/some_file.pyx"]
)
The article How to Bootstrap numpy installation in setup.py proposes using a cmdclass
with custom build_ext
class. Unfortunately, this breaks the build of the cython
extension because cython
also customizes build_ext
.