Upcoming changes in the pypa/wheel project

I’ve done a fairly big refactoring of wheel in order to move it towards being the command line tool I want it to become. The PR can be seen here. The main reason I’m writing about it here because I’m permanently removing the last remaining setuptools link which was the wheel.bdist_wheel module and the corresponding entry point. Setuptools has not needed wheel to build wheels for almost a year now, so I figured the time is ripe to throw out the obsolete code.

I’ve made most of the internal modules private, save for wheel.wheelfile whose fate is still unclear. I also made the wheel.metadata import emit a warning, as abruptly removing it caused an issue before.

Let me know if you have objections or concerns about the modifications. If I hear nothing, I’ll just go ahead and merge the PR.

3 Likes

My only concern is that there are still some packages out there that import bdist_wheel from wheel to create their custom bdist_wheel command. But I do realize that deprecation warnings don’t really work, so feel free to disregard this.

The deprecation warning has been there for almost a year, so they could not really complain about such a change being abrupt :slight_smile:
I could send a few PRs their way I guess, to make sure they have a clear migration path.

1 Like

In general I would consider “almost a year” to be abrupt in terms of deprecation timelines. I don’t know what the impact or benefits of this change would be though.

From the code search above I see this:

I don’t know whether that code would be broken by this change but the comment suggests that some people find changes in the wheel package’s API troublesome.

3 Likes

The problem is that wheel doesn’t have a public API, as the README has stated for 7 years already. But people still use it. As for piwheels, they have an open PR that fixes the issue.

2 Likes

I think in light of how other changes to the core packaging tools have been received (there’s a huge thread about build backends breaking), you’ll have a better time by keeping as much compatibility shim as you can to avoid old code from breaking.

We keep seeing that the ecosystem of people who use these tools moves very slowly, and more and more Python users are slow to move. It’s harder to pin to particular package versions than overall Python version, and so you’re more likely to get complaints about breaking changes.

If you want to be passive aggressive about it towards people who have ignored the docs (your call, but I wouldn’t oppose it :wink: ), require an obnoxious environment variable be set to use the shims - ENABLE_OBSOLETE_INSECURE_AND_UNSUPPORTED_FEATURES=1 - the kind of thing that any security team is going to ask questions about. (Or if you’re really mean, put PASSWORD in the variable name.) Print the variable and a link to a doc page (or this discussion) when they try and use it without the variable set.

It directly makes users aware that they’re doing something wrong, but also gives them a way to immediately re-run with their own workaround, which is going to reduce the amount of issues opened by at least 90%. Your sanity will thank you.

10 Likes

I’m not sure I’m following. Do you mean that without this workaround, the imports would stop working? If so, why not just point out the real solution (from setuptools.command.bdist_wheel import bdist_wheel) if we’re going to break things for the offending projects anyway?

What is the scenario for this breaking sdist builds? That packages have pinned to an old version of setuptools in their build requirements but left wheel unpinned?

I don’t see how this would happen, as wheel was never involved in any way in sdist builds done by setuptools. And if they were importing wheel itself in their setup.py, well…we discussed that scenario already.

2 Likes

That’s because no one could find alternatives. Even trivial customisations like platform specific files have been (as far as I’m aware) impossible without monkeypatching distribution.package_data in a bdist_wheel subclass. No one goes digging through these internal APIs for fun.

Is switching to from setuptools.command.bdist_wheel import bdist_wheel a valid replacement or is that just someone else’s private API?

2 Likes

That’s because no one could find alternatives. Even trivial customisations like platform specific files have been (as far as I’m aware) impossible without monkeypatching distribution.package_data in a bdist_wheel subclass. No one goes digging through these internal APIs for fun.

Perhaps so at least in some cases, but they should expect to get burned if they don’t vendor code from private APIs. Note that I’m preserving wheel.wheelfile as-is without deprecation warnings until there is a viable alternative.

Is switching to from setuptools.command.bdist_wheel import bdist_wheel a valid replacement or is that just someone else’s private API?

It’s as valid as it has been for as long as I can remember. Certainly more so now that the command canonically lives in setuptools, and people have subclassed setuptools commands for ages.

4 Likes

Yes, but it’s only a viable option for the author of the package, not the person who’s suddenly failing to install it. And while everyone waits for the author to make a new release (which is hopefully compatible enough with the release users were using to do the upgrade…), they’ll come to your issue tracker to complain about it.

There are a few tricks you can use to have a wheel.bdist_wheel that just imports setuptools to get the implementation. Using any of them is a small price to pay for not upsetting users, in the overall scheme of things.

1 Like

As happened with setuptools this week: [BUG] Version 78.0.1 breaks install of ansible-vault package · Issue #4910 · pypa/setuptools · GitHub

One option is to try it, but be ready to revert and postpone if need be.

1 Like

That’s exactly what I had in place until now, and it’s been emitting deprecation warnings since August 2024. I don’t want to remain stuck with compatibility shims forever, so I figured that if someone hasn’t seen them yet, they’re never likely to. That said, I could just replace it with a simpler shim that raises an ImportError if the setuptools.commands.bdist_wheel import fails, explaining why this happens (the user upgraded wheel but not setuptools for whatever reason). That ought to lessen the impact of these changes.

1 Like

Yikes - but I honestly don’t expect this to have as big an impact even if something goes wrong, given how wheel is no longer a wheel-building dependency of setuptools.

2 Likes

There’s a lot more than that if you search for the class import.

1 Like

I’ve softened the blow a bit by readding the bdist_wheel module as a shim for setuptools.command.bdist_wheel. The entry point and implementation are gone though, so if someone only upgrades wheel and not setuptools and tries to import wheel.bdist_wheel, they will get a human readable error prompting them to upgrade their setuptools.

11 Likes

Looks like these changes were released half an hour ago. SymPy pyodide CI job is immediately broken with:

packages/auditwheel_emscripten/__init__.py", line 1, in <module>
    from .exports import get_exports
  File "/opt/hostedtoolcache/Python/3.12.9/x64/lib/python3.12/site-packages/auditwheel_emscripten/exports.py", line 7, in <module>
    from .wheel_utils import is_emscripten_wheel, unpack
  File "/opt/hostedtoolcache/Python/3.12.9/x64/lib/python3.12/site-packages/auditwheel_emscripten/wheel_utils.py", line 9, in <module>
    from wheel.cli.pack import pack as pack_wheel
ModuleNotFoundError: No module named 'wheel.cli'

I don’t consider that to be any major problem. The CI job is there to show whether things are working and right now it shows that something is not working. I assume it will get fixed somewhere fairly soon. I don’t think there is anything to be done about it on SymPy’s end except perhaps pin a version of something in CI. The fix would either have to be in wheel or in pyodide somewhere I guess.

This does not cause any great upset for me but as a heads up if I am noticing this within 30 minutes of the wheel release then I think you can expect that there will be others arriving with pitchforks. Hopefully it is not as unfriendly as the setuptools issue but I suggest being ready to revert some of the changes or add back compatibility shims or something.

2 Likes

Hmm, we’ve just got an extremely weird error on our CI:

+ python -m pip install --no-deps --no-build-isolation -vv .
(...)
  creating '/private/var/folders/r1/4882c7yd7wx9hcpc__xp04_m0000gn/T/pip-modern-metadata-dy4tzepf/pyarrow-20.0.0a0.dist-info'
  error: invalid command 'bdist_wheel'
  error: subprocess-exited-with-error

I don’t know where that bdist_wheel command comes from. We don’t invoke it directly ourselves.

Reading the github issue it looks like in some Python distributions on MacOS come with an old version of setuptools, and setuptools was importing wheel more recently than expected, check: v0.46.0 breaking builds on macos · Issue #659 · pypa/wheel · GitHub

I beleive the wheel release has been yanked: wheel · PyPI

1 Like