How to declare a python package as incompatible with a specific platform or architecture?

A recurring problem with packaging metadata is that it is still not possible to officially declare a specific platform or architecture as not supported.

Not mentioning it means that people will be unlikely to spot it while looking at pypi and they might even succeed installing the package and endup raising false bugs. This translated to wasted time not only for users but also for maintainers.

If we could declare specific combinations as not supported those users might not endup installing in unsupported environments.

I am not sure if we could use trove to declare this (just raised Add support for negative classifier matches (incompatibility) · Issue #196 · pypa/trove-classifiers · GitHub) or another mechanism would be needed.

In the past I used a dirty trick to prevent people from installing, by adding a conditional dependency that does not exists. Still this trick breaks poetry users for all platforms as poetry tries to build a lock file even for dependencies that it cannot find, so the trick from ansible-lint/.config/requirements.in at main · ansible/ansible-lint · GitHub is still causing occasional false bug reports.

will-not-work-on-windows-try-from-wsl-instead; platform_system=='Windows'
3 Likes

Where this has come up in other topics recently, it’s been pointed
out that some projects with this particular need work around the
problem by only publishing platform-specific wheels to PyPI (no
sdists, nor pure-Python wheels). This prevents installation on
unsupported platforms, but doesn’t necessarily provide the user with
a clear explanation. It also, for actual pure-Python projects, means
publishing a bunch of redundant wheels that have the same contents
with different filenames.

In the case of a pure-Python application like ansible-lint which may
be at least nominally runnable, another popular solution is to
detect the platform at runtime and report a user-friendly error
message (of course it’s not so friendly that the user has gotten all
the way through installing to running it before discovering they
didn’t pay attention to any big bold warnings in the project’s
documentation).

This lacking feature is also yet another example of how Python
packages are not really well adapted for application distribution,
but rather more focused on solving library dependency needs with the
unspoken assumption that installing an application is going to
happen some other way (e.g. from a curated distribution like an app
store, or perhaps a container image, where coupling to the platform
is more clearly indicated).

5 Likes

Very much this. Unfortunately, reality hasn’t gone the way we’d planned, so people do expect to distribute applications via the (library-focused) packaging toolset.

Ideally, we’d be working on looking at how we adapt the existing standards for the application use case, but there seems to be a lack of people interested in working on that. So we end up with a bunch of people with library packaging expertise trying to field questions from people with application deployment use cases.

2 Likes

Would a dumb runtime check/error at the top of an __init__.py not be easier? At least then you’ve got space to put a decent explanation in the error message in. Pip would likely have to use a catch-all boilerplate platform not supported message.

The feature request is not necessarily for application packaging though. There are platform specific packages, in particular those binding platform specific native libraries.

Some examples: PyObjC only works on macOS, bindings for io_uring only work on linux, pywin32 likely only works on Windows (with some wiggle room for Wine).

I currently try to give an explicit error in my setup.py file for PyObjC when detecting a wheel build on a non-supported platform.

1 Like

When I needed to do this I simply didn’t upload an sdist (just wheels for the supported platforms).

In the long discussion on flagging platform incompatibility with metadata (if it really is too hard to get pip to provide a useful error message), some Pythonistas justifiably pointed out sdists protect against rare events when authors yank repos, and against relying on 3rd party code hosting services (especially Github).

This concern is valid but there plenty of other good options available to mitigate this in my opinion, that don’t cause half the problems sdists and pip do.

Good point. There are no standards (yet) for declaring platform or other compatibility with sdists, so installers have to assume that a sdist might work on every platform. We could create such a standard, although it would have the problem that installers could end up backtracking to an older version to find a “compatible” sdist. So it’s not a trivial thing to add.

Build backends could add tool specific features to make the build fail on unsupported platforms (a check in setup.py is an example of this) but build errors aren’t always reported very clearly to the user. Fixing this would require an update to PEP 517 to allow backends to return structured information to the frontend, rather than just raw stdout. But it’s still the best solution we have right now.

1 Like

Having declared platform support in the form of markers would allow tools writing universal resolutions (poetry/pdm/uv) to catch these: When a user tries to install a package that depends on ansible-lint on windows, we can tell them something like “You depend on foo and foo depends on ansible-lint, which only supports platform_system != 'windows'”. From a universal resolution, we could also collect the platform support markers and say something like “Your project will install at most on platform_system != 'windows' and (platform_machine = 'x86_64' or platform_machine = 'aarch64' or platform_machine = 'arm64')”.

Dependency specifiers can already contain platform information:

https://packaging.python.org/en/latest/specifications/dependency-specifiers/#environment-markers

It feels hackish to upload a canary package whose only job is to break the installation. And this doesn’t convey useful information to the user. If nothing else, it’s prior art to use for markers.

Markers would work, yes. We could add a new metadata item which contains a marker, and that says that the package only works when that marker is satisfied. We would also need a key in pyproject.toml to declare it.

To be usable in this situation, the new metadata version would need to be supported by build backends and indexes, and indexes would need to start publishing metadata files for sdists, not just for wheels[1]. There would still be the problem of how to prevent backtracking to older sdists that don’t declare what platforms they support[2].

Basically, someone needs to write a PEP.


  1. this latter point is not essential, but it would speed up the resolution process significantly ↩︎

  2. because for backward compatibility those must be assumed to support all platforms ↩︎

3 Likes

We could add a new metadata item which contains a marker, and that says that the package only works when that marker is satisfied.

Metadata 1.1 added Supported-Platform, though it seems to be purely informational in nature. If anything, it would be confusing to have both so probably the new thing would also deprecate further use of that.