I am trying to create an sdist which I can upload to PyPI and which can then be pip installed successfully by users. The project in question is https://github.com/ratt-ru/CubiCal, and I already have an sdist uploaded to PyPI (https://pypi.org/project/cubical/). Documentation is here.
This project makes use of Cython. According to Cython's documentation, the correct way to distribute Cython code is to bundle the .c and .cpp files generated by cythonize, and I am currently doing so.
The problem is that both pip install cubical and pip install path/to/sdist install the package, but do not correctly build and link extensions. That is, no .so files are generated and consequently the installed package cannot import its Cython modules. I know that the setup.py script works when building from source (e.g. git cloning the repo, cythonizing the extensions, and running pip install -e CubiCal/).
In summary, how do I change my setup.py script to ensure that extensions are handled correctly when installing from an sdist?
Setup.py:
import os
import sys
import glob
import cubical
from setuptools import setup, find_packages
from setuptools.extension import Extension
from setuptools.command.build_ext import build_ext
from setuptools import Command
with open('README.md') as f:
long_description = f.read()
# Try get location of numpy headers. Compilation requires these headers.
try:
import numpy as np
except ImportError:
include_path = ''
else:
include_path = np.get_include()
# Use Cython if available.
try:
from Cython.Build import cythonize
import Cython.Compiler.Options as CCO
except ImportError:
cythonize = None
cmpl_args = ['-ffast-math',
'-O2',
'-march=native',
'-mtune=native',
'-ftree-vectorize' ]
cmpl_args_omp = cmpl_args + ['-fopenmp']
link_args = []
link_args_omp = link_args + ['-lgomp']
# which extensions need to compile through C++ rather than C
cpp_extensions = "cytf_plane", "cyf_slope", "cyt_slope", "rebinning"
class gocythonize(Command):
""" Cythonise CubiCal kernels. """
description = 'Cythonise CubiCal kernels.'
user_options = [('force', 'f', 'Force cythonisation.'),]
def initialize_options(self):
pass
def finalize_options(self):
self.force = self.force or 0
def run(self):
CCO.buffer_max_dims = 9
extensions = []
for source in glob.glob("cubical/kernels/*.pyx"):
name, ext = os.path.splitext(source)
omp = name.endswith("_omp")
# identify which kernels need to go via the C++ compiler
cpp = any([x in name for x in cpp_extensions])
extensions.append(
Extension(name.replace("/","."), [source],
include_dirs=[include_path],
extra_compile_args=cmpl_args_omp if omp else cmpl_args,
extra_link_args=link_args_omp if omp else link_args,
language="c++" if cpp else "c"))
cythonize(extensions, compiler_directives={'binding': True}, annotate=True, force=self.force)
extensions = []
for source in glob.glob("cubical/kernels/*.pyx"):
name, _ = os.path.splitext(source)
is_cpp = any([s in name for s in cpp_extensions])
is_omp = name.endswith("_omp")
extensions.append(
Extension(name.replace("/","."), [name + ".cpp" if is_cpp else name + ".c"],
include_dirs=[include_path],
extra_compile_args=cmpl_args_omp if is_omp else cmpl_args,
extra_link_args=link_args_omp if is_omp else link_args))
# Check for readthedocs environment variable.
on_rtd = os.environ.get('READTHEDOCS') == 'True'
if on_rtd:
requirements = ['numpy',
'cython',
'futures',
'matplotlib',
'scipy']
else:
requirements = ['numpy',
'futures',
'python-casacore>=2.1.2',
'sharedarray',
'matplotlib',
'cython',
'scipy',
'astro-tigger-lsm']
setup(name='cubical',
version=cubical.VERSION,
description='Fast calibration implementation exploiting complex optimisation.',
url='https://github.com/ratt-ru/CubiCal',
classifiers=[
"Development Status :: 3 - Alpha",
"Intended Audience :: Science/Research",
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
"Operating System :: POSIX :: Linux",
"Programming Language :: Python",
"Topic :: Scientific/Engineering :: Astronomy"],
author='Jonathan Kenyon',
author_email='jonosken@gmail.com',
license='GNU GPL v3',
long_description=long_description,
long_description_content_type='text/markdown',
cmdclass={'build_ext': build_ext,
'gocythonize': gocythonize},
packages=['cubical',
'cubical.data_handler',
'cubical.machines',
'cubical.tools',
'cubical.kernels',
'cubical.plots',
'cubical.database',
'cubical.madmax'],
install_requires=requirements,
include_package_data=True,
zip_safe=False,
ext_modules = extensions,
entry_points={'console_scripts': ['gocubical = cubical.main:main']},
)