Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to force a python wheel to be platform specific when building it?

I am working on a python2 package in which the setup.py contains some custom install commands. These commands actually build some Rust code and output some .dylib files that are moved into the python package.

An important point is that the Rust code is outside the python package.

setuptools is supposed to detect automatically if the python package is pure python or platform specific (if it contains some C extensions for instance). In my case, when I run python setup.py bdist_wheel, the generated wheel is tagged as a pure python wheel: <package_name>-<version>-py2-none-any.whl. This is problematic because I need to run this code on different platforms, and thus I need to generated one wheel per platform.

Is there a way, when building a wheel, to force the build to be platform specific ?

like image 823
Adrien Ball Avatar asked Jul 17 '17 17:07

Adrien Ball


People also ask

What is setup () in python?

The setup script is the centre of all activity in building, distributing, and installing modules using the Distutils. The main purpose of the setup script is to describe your module distribution to the Distutils, so that the various commands that operate on your modules do the right thing.

Do python wheels contain source code?

Most wheels are pure-Python, which means that they only contain Python source code.

What is a universal wheel python?

A universal wheel is a wheel for a pure-Python project that supports both Python 2 and 3. There are multiple ways to tell setuptools and distutils that a wheel should be universal. Option 1 is to specify the option in your project's setup.cfg file: [bdist_wheel] universal = 1.


2 Answers

Here's the code that I usually look at from uwsgi

The basic approach is:

setup.py

# ...

try:
    from wheel.bdist_wheel import bdist_wheel as _bdist_wheel
    class bdist_wheel(_bdist_wheel):
        def finalize_options(self):
            _bdist_wheel.finalize_options(self)
            self.root_is_pure = False
except ImportError:
    bdist_wheel = None

setup(
    # ...
    cmdclass={'bdist_wheel': bdist_wheel},
)

The root_is_pure bit tells the wheel machinery to build a non-purelib (pyX-none-any) wheel. You can also get fancier by saying there are binary platform-specific components but no cpython abi specific components.

like image 127
Anthony Sottile Avatar answered Sep 30 '22 09:09

Anthony Sottile


The modules setuptools, distutils and wheel decide whether a python distribution is pure by checking if it has ext_modules.

If you build an external module on your own, you can still list it in ext_modules so that the building tools know it exists. The trick is to provide an empty list of sources so that setuptools and distutils will not try to build it. For example,

setup(
    ...,
    ext_modules=[
        setuptools.Extension(
            name='your.external.module',
            sources=[]
        )
    ]
)

This solution worked better for me than patching the bdist_wheel command. The reason is that bdist_wheel calls the install command internally and that command checks again for the existence of ext_modules to decide between purelib or platlib install. If you don't list the external module, you end up with the lib installed in a purelib subfolder inside the wheel. That causes problems when using auditwheel repair, which complains about the extensions being installed in a purelib folder.

like image 35
paulinus Avatar answered Sep 30 '22 08:09

paulinus