pythonsetuptoolspython-packagingpython-wheelpep517

How to force a platform wheel using build and pyproject.toml?


I am trying to force a Python3 non-universal wheel I'm building to be a platform wheel, despite not having any native build steps that happen during the distribution-packaging process.

The wheel will include an OS-specific shared library, but that library is built and copied into my package directory by a larger build system that my package knows nothing about. By the time my Python3 package is ready to be built into a wheel, my build system has already built the native shared library and copied it into the package directory.

This SO post details a solution that works for the now-deprecated setup.py approach, but I'm unsure how to accomplish the same result using the new and now-standard build / pyproject.toml system:

mypackage/
  mypackage.py  # Uses platform.system and importlib to load the local OS-specific library
  pyproject.toml
  mysharedlib.so  # Or .dylib on macOS, or .dll on Windows

Based on the host OS performing the build, I would like the resulting wheel to be manylinux, macos, or windows.

I build with python3 -m build --wheel, and that always emits mypackage-0.1-py3-none-any.whl.

What do I have to change to force the build to emit a platform wheel?


Solution

  • Update 2 Sept 2023: -C=--build-option=--plat {your-platform-tag} no longer works, so I added my preferred replacement to the end of the list.

    ==========

    OK, after some research and reading of code, I can present a bit of information and a few solutions that might meet other people's needs, summarized here:

    Firstly, pyproject.toml is not mutually exclusive from setup.py. setuptools will complain about deprecation if you create a distribution package via python3 setup.py ... and no pyproject.toml file is present.

    However, setup.py is still around and available, but it's a mistake to duplicate project configuration values (name, version, etc). So, put as much as your package will allow inside your pyproject.toml file, and use setup.py for things like overriding the Distribution class, or overriding the bdist_wheel module, etc.

    As far as creating platform wheels, there are a few approaches that work, with pros and cons:

    1. Override the bdist_wheel command class in setup.py as described here and set self.root_is_pure to False in the finalize_options override. This forces the python tag (e.g. cp39) to be set, along with the platform tag.
    2. Override the Distribution class in setup.py as described here and override has_ext_modules() to simply return True. This also forces the python and platform tags to be set.
    3. Add an unused minimal extension module to your packaging definition, as described here and here. This lengthens the build process and adds a useless "dummy" shared library to be dragged along wherever your wheel goes.
    4. This solution appears to not work anymore! Add the argument -C=--build-option=--plat {your-platform-tag} to the build invocation (for my case that's python -m build -w -n, for example). This leaves the Python tag untouched but you have to supply your own tag; there's no way to say "use whatever the native platform is". You can discover the exact platform tag with the command wheel.bdist_wheel.get_platform(pathlib.Path('.')) after importing the pathlib and wheel.bdist_wheel packages, but that can be cumbersome because wheel isn't a standard library package.
    5. Simply rename your wheel from mypkg-py3-none-any.whl to mypkg-py3-none-macosx_13_0_x86_64.whl- it appears that the platform tag is only encoded into the filename, and not any of the package metadata that's generated during the distribution-package process.
    6. Use the wheel package utility to update the tags, to turn the pure wheel into a platform wheel. python -m wheel tags --platform-tag macosx_13_0_x86_64 mypkg-py3-none-any.whl will emit a new platform wheel with the tags you want.

    In the end I chose the final options because it required the least amount of work- no setup.py files need to be introduced solely to accomplish this, and the build logs make it clear that a platform wheel (not a pure wheel) is being created.