How to make a pure python wheel for 2.7, >=3.6

The preppy package runs all its tests in Python 2.7.x Python >= 3.6.

It is a single pure python file.

As a result of some fixes needed for 3.14.0ax I need to make a new wheel.

However, I see that universal=1 is being deprecated and will vanish in future.

How do I indicate that my wheel is not version sensitive?

The only things pip checks before installing a wheel, are the tags in the wheel’s file name. So all that needs to be done is changing the file name of the wheel to indicate support for Python 2, that older versions of pip and the latest ones alike will recognise, e.g. preppy-0.0.0-py2.py3-none-any.whl.

I made a hatchling build hook to do this automatically (thanks Ofek!). It’s a bit of a hack. And if you’re going to use a hack anyway, you could just manually rename the wheel file to the above format. Otherwise, read on.

It might be necessary to change to a src/project_name project format, not a single file repo format. But otherwise this is a stripped down version of how I built Python 2 compatible wheels, using hatchling :

pyproject.toml

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"


[project]
name = "Preppy"
version = "0.0.0"
authors = [ {name = 'Robin Becker', email = 'robin@example.com'},]
description = "Preppy is a Python package."
classifiers = [
    "Programming Language :: Python",
]

[project.urls]   
"Homepage" = "https://www.example.com/your_home_page"

[tool.hatch.metadata]
allow-direct-references=true


[tool.hatch.build.hooks.custom]
path = "hatch_build.py"

hatch_build.py


import sysconfig

from hatchling.builders.hooks.plugin.interface import BuildHookInterface


class CustomHook(BuildHookInterface):
    def initialize(self, version, build_data):
        if self.target_name not in ('wheel', 'bdist'):
            return

        # https://peps.python.org/pep-0425/#platform-tag
        platform_tag = sysconfig.get_platform().replace('-','_').replace('.','_')

        build_data['tag'] = f'py2.py3-none-{platform_tag}'

I can’t remember what I needed allow-direct-references for - that might have been for something else unrelated.

Thanks, if it’s just the name that makes the difference I can just rename. I just hate all the churn around packaging.

FWIW here is what I had to do to get the renaming done with old style setup.py using pypa build

	if 'bdist_wheel' in sys.argv:
		bfn = f'preppy-{version}-py3-none-any.whl'
		renamed = False
		if '--dist-dir' in sys.argv:
			distDir = sys.argv[sys.argv.index('--dist-dir')+1]
			fn = glob.glob(os.path.join(distDir,bfn))
			if len(fn)==1:
				fn = fn[0]
				nfn = fn.replace('-py3-','-py2.py3-')
				if os.path.isfile(nfn): os.remove(nfn)
				os.rename(fn, nfn)
				print(f'##### {bfn} was renamed to {os.path.basename(nfn)} #####')
				renamed = True
		if not renamed:
			print(f'!!!!! {bfn} not renamed !!!!!')
1 Like