(Python3) How handle C/C++ dependencies with Pip/Pypi?

Viewed 364

I have C/C++ dependencies (TA-Lib) for the Python package that I developed. Not being user-friendly, I don't want the user to install or compile C to use my package. Simply, I just want the user to be able to install my package with pip install <package>.

So, I installed my C dependency in the setup.py before the setup() function by substituing the setup's commands (install, develop, build, etc.).

Here is the code:

import platform
from setuptools import setup
from setuptools.command.install import install
from setuptools.command.develop import develop
from distutils.command.build import build
from distutils.command.build_ext import build_ext
from distutils.command.sdist import sdist
from wheel.bdist_wheel import bdist_wheel
import os

WINDOW_OS = "Windows"
LINUX_OS = "Linux"
CURRENT_OS = platform.system()


def talib_install():
    dir = "build_helper"
    if CURRENT_OS == LINUX_OS:
        os.system(f"cd {dir} && sh talib-install.sh")
    elif CURRENT_OS == WINDOW_OS:
        os.system(f"pip install {dir} TA_Lib-0.4.24-cp39-cp39-win_amd64.whl")
    else:
        raise Exception("Unknown OS")


class TalibBuild(build):
    def get_sub_commands(self):
        # Force "build_ext" invocation.
        commands = build.get_sub_commands(self)
        for c in commands:
            if c == 'build_ext':
                return commands
        return ['build_ext'] + commands


class TalibBuildExt(build_ext):
    def run(self):
        talib_install()
        build_ext.run(self)


class TalibSdit(sdist):
    def run(self) -> None:
        talib_install()
        return super().run()


class TalibDevelop(develop):
    def run(self) -> None:
        talib_install()
        return super().run()

    def finalize_options(self) -> None:
        return super().finalize_options()


class TalibInstall(install):
    def run(self):
        talib_install()
        install.run(self)

    def finalize_options(self) -> None:
        return super().finalize_options()


class TalibBdistWheel(bdist_wheel):
    def run(self):
        talib_install()
        # Run wheel build command
        super().run()

    def finalize_options(self):
        super().finalize_options()


cmdclass = {
    "install": TalibInstall,
    "develop": TalibDevelop,
    "sdist": TalibSdit,
    "bdist_wheel": TalibBdistWheel,
    "build": TalibBuild,
    "build_ext": TalibBuildExt
}

install_requirements = [
    "setuptools==59.5.0",
    "tensorboard",
    "torch-tb-profiler",
    "geneticalgorithm2"
]

talib_install()
setup(
    data_files=["build_helper/talib-install.sh"],
    install_requires=install_requirements,
    cmdclass=cmdclass
)

In local, the following commands are working well: pip install -e . and pip setup.py install.

The problem occurs when I upload my package on Pypi and try installing it on Colab using pip install mypackage. I have the feeling that the installation does not pass through the C code installation step. (I make sure that I have talib-install.sh script in the data_files.)

Moreover, in my release I have two files: wheel archive and source archive. When I execute pip install <link_to_the_source_achive> on Colab, the C code installation works. But pip install <link_to_the_wheel> doesn't work.

I thought that, by default, pip uses the wheel archive to install. I deleted it so that not having a choice, pip chooses the source. But it doesn't work either...

What is the difference between pip install <package> and pip install <link_to_source>? How pip is working? Any thoughts about how I can resolve my issue?

(I am working on Linux but I want it to work on Windows and MacOs too.)

Thank you very much.

0 Answers
Related