Pre-PEP: Requires-Implementation - declaring Python implementation compatibility in Core Metadata

Hi all,

I’ve been working on CPython tooling and Python documentation translation and want to raise a gap I keep running into. I’d like to know whether the packaging community sees it the same way, and whether Core Metadata is the right place to address it.

The problem

Python packaging has standardised mechanisms for all of the following:

  • Implementation identity at runtime - sys.implementation.name (PEP 421)

  • Implementation-compatible wheel selection - implementation tags in wheel filenames (PEP 425)

  • Implementation-specific dependencies implementation_name environment markers (PEP 508)

  • Python version constraints Requires-Python in Core Metadata

What’s missing is a distribution-level Core Metadata field declaring that a package only supports certain Python implementations.

Why existing mechanisms don’t cover it

Wheel tags handle this at the artifact level pip rejects an incompatible wheel based on its filename. But an sdist has no implementation tag. When an installer falls back to an sdist, there is no standard normative metadata signal that the package is incompatible with the running implementation. The user gets a build failure or runtime error instead of a clear rejection.

Environment markers (implementation_name == "cpython" in Requires-Dist) describe when a dependency should be installed. They don’t describe whether the package itself is compatible with an implementation. These are different semantic layers.

Trove classifiers (Programming Language :: Python :: Implementation :: PyPy) are descriptive. They communicate claims but are not a normative constraint that an installer uses to reject a candidate.

Supported-Platform has existed since Core Metadata 1.1 but its semantics have never been specified. Its documented scope is OS and CPU for binary distributions - a different domain. I don’t think repurposing it is a good fit.

Proposed direction

A new optional Core Metadata field, provisionally Requires-Implementation, modelled on Requires-Python:

[project]
requires-python = ">=3.12"
requires-implementation = ["cpython", "pypy"]

Requires-Implementation: cpython
Requires-Implementation: pypy

Values correspond to sys.implementation.name (PEP 421). The set is open — no central registry needed. A missing field means no restriction; existing packages are unaffected.

Concrete examples

  1. A package with CPython-internal assumptions ships only an sdist. A GraalPy user gets a confusing failure today. Requires-Implementation: cpython lets the installer reject the candidate before attempting a build.

  2. Some packages fail at the build step itself on incompatible implementations, before any wheel is produced. The incompatibility is known at the project level but there’s nowhere standard to declare it.

  3. Tools comparing behaviour across implementations need machine-readable declared compatibility, not just descriptive classifiers.

Recent precedent

PEP 794 (accepted September 2025, PEP 794 – Import Name Metadata | peps.python.org ) added Import-Name and Import-Namespace to Core Metadata so project-level information can be served by an index without inspecting individual artifacts explicitly because inferring from wheel contents doesn’t work for sdists. That’s structurally close to what I’m proposing. The Simple API’s data-core-metadata means no new transport mechanism is needed.

Questions for the community

I have a direction, not a finished design. I’d specifically like feedback on:

  1. Semantic layer Is Core Metadata the right place, or should this live somewhere else?

  2. Value representation, Simple list of strings, or a specifier syntax? Should version-qualified constraints be in scope now or deferred?

  3. Installer behaviour, MUST-reject or SHOULD-warn? Specified in Core Metadata or in installer behaviour specs?

  4. Wheel interaction, what happens if a package declares Requires-Implementation: cpython but publishes a py3-none-any wheel?

  5. Scope, should this initially be runtime-only, leaving build-environment constraints to a separate proposal?

  6. Supported-Platform — Leave it alone, or use this as an opportunity to finally clarify or deprecate it?

What I’m not proposing

  • PyPI rejecting uploads based on this field

  • A closed registry of implementation names

  • Any change to wheel compatibility tags

  • Build-environment constraints

There was a short discussion in January 2024 ( Python implementation in metadata ) about implementation metadata, which concluded that classifiers are the current answer. That thread was about forcing a particular implementation in a dev environment. This is different: I’m asking whether a package can declare its own runtime compatibility as a normative constraint for installer candidate selection, especially for sdists.

Feedback very welcome, especially from pip, uv, PyPI/Warehouse, and PyPA maintainers. If the gap is real and this direction makes sense, I’ll write a formal PEP draft.

If we put aside the case of building a sdist that is known to fail on the target interpreter, are there any other use cases for this proposal? I can’t think of any, to be honest.

And as far as sdists are concerned, this sounds like something that would be better handled using PEP 725, in particular the external.build-requires and/or external.host-requires fields in pyproject.toml, combined with a DepURL which can identify the Python implementation available in the build/host environment.

At a minimum, I suggest that you take your use case(s) to the PEP 725 discussion thread (which admittedly has been idle for a while), and explore there whether they can be handled with that proposal. If not, you can come back to this proposal with a much better argument for why you need specific metadata just to cover the target implementation.

1 Like

I went through PEP 725, and I agree that the sdist build failure case fits better there.

The main use case I’m seeing now is runtime compatibility. For example, RestrictedPython is CPython-only, but it is pure Python and currently publishes a py3-none-any wheel. So it can build fine but still not work on PyPy.

PEP 725 currently doesn’t define Python implementation as a virtual/runtime dependency, and build-requires / host-requires are build-time and not part of Core Metadata.

The main use case I’m seeing now is runtime compatibility. For example, RestrictedPython is CPython-only, but it is pure Python and currently publishes a py3-none-any wheel. So it can build fine but still not work on PyPy.

PEP 725 currently doesn’t define Python implementation as a virtual/runtime dependency, and build-requires / host-requires are build-time and not part of Core Metadata.

But PEP 425 does, and your original post even pointed this out and said
that you only care about:

But an sdist has no implementation tag. When an installer falls back to an sdist, there is no standard normative metadata signal that the package is incompatible with the running implementation.
Why do you now say that you actually care about wheels, and cite the
example of a project that is not utilizing implementation-compatible
wheel selection? Report a bug to RestrictedPython for distributing
incorrectly-built wheels. :slight_smile:

… and a bug to the packaging project, since packaging.tags is also
broken – per packaging.tags does not support `*-none-any` wheels for non`-py*` interpreter tags · Issue #311 · pypa/packaging · GitHub it does not
provide “cp3-none-any”, or indeed any implementation-specific tag other
than pp3 which was added after the fact as a special case (odd).

1 Like

The wheel part is fair, PEP 425 already handles implementation-specific wheel tags, so my RestrictedPython example was not the best one for that.

What I’m still trying to understand is the sdist case. PEP 425 is for built distributions, while an sdist has no implementation tag. There was also the No-SDist-Build discussion around using metadata to avoid trying to build an sdist in the first place.

So if a release is CPython-only, is there currently a standard way for an installer to know that from the sdist metadata before starting the build? If there is, I’d like to understand how it fits here.

Currently, no I don’t think there is. But as I said, I think it’s something that should be part of the PEP 725 discussions, and is only worth raising as its own individual proposal if the conclusion is that PEP 725 considers it out of scope (I feel like I’d be disappointed if that happened, though).

1 Like

@BHUVANSH855 do you actually have a real-world need for this? You say “a gap that I keep running into”, but there is nothing concrete you mention and you can’t really fix much here beyond getting a better error message. What specifically do you keep running into?

I’ll also note that this is your second proposal in a week for new Core Metadata (a security field being the other one, see here). It all feels a little premature (and AI-driven, as pointed out in the other thread).


It seems to me that this is a lot like upper bounds on python-requires, except the need being less clear. The recommended approach today for that one is: raise a clear error at the top of your build config file, so the user is clear on why the build fails - and not just “PyPy is not supported” but why that’s the case, or link to a tracking issue with discussion if you have one.

An installer can’t do much better: if a package is in a dependency tree, it is needed, so an error message it is. An installer will give an error message a bit earlier, but it’ll be more generic.


I have to say I don’t really understand the link to PEP 725. If the problem would have been “the package does build and work under an alternative interpreter, but a build dependency is missing” then yes sure (concrete examples would help). But that wasn’t the question here - the case provided is “package is CPython-only”.

Which I don’t even think is a well-defined situation. Being compatible with CPython is a key goal of alternative interpreters like PyPy. They succeed in many cases, and when they don’t, it’s often a bug or missing feature on their side that’s fixable. If package metadata would prevent them from even trying to install, it just makes their jobs harder.

Concrete example here: there’s some effort ongoing to make PyPy understand abi3 wheels. Say that works, that’d be a nice achievement. Packages that declare “doesn’t support PyPy” would then be unhelpfully out of date.

8 Likes

It’s entirely possible I was mistaken - to an extent my main purpose in linking this to PEP 725 was to ensure that the issue was brought to the attention of people who would have an informed opinion. The first part of your post has handled that just fine.

My thinking was that if a project only builds on CPython, doesn’t that mean that it has (in a sense) an external build dependency on CPython (rather than, say GraalPy)? You’re right that if it builds for any Python interpreter, but is only supported on a particular one, that’s something that should be handled by the build script and/or wheel tags. Or just by documentation (and the package failing at runtime with a reasonable error message).

As you say, the big issue is that there aren’t any clear use cases provided, so we’re all guessing to an extent.

Yes, but I see value in knowing beforehand whether there are known issues that would make it (temporarily?) impossible to work. That is a common situation for alternative implementations: they’d like to support a given extension, but due to priorities, bandwidth, size of refactoring, etc. cannot for many years.

But version Y of PyPy will never retroactively support a given extension, so maybe the way to avoid staleness would be to add versions to the classifier? E.g. “doesn’t support PyPy<7.20.x”?

I think the better objective is to list what is known to be supported, and that should usually be evergreen. IMHO the positive assertion helps when picking the extension stack to build your product from, just like “pure Python” or “cffi” used to help deciding whether a given project could be done in PyPy[1]. Also gives a strong signal on implementation diversity on the Python ecosystem.

If a project only builds and/or works on GraalPy, not trying to build it (or things that depend on it) on CPython could be a decision made even before downloading the sdist if we had this metadata in place, right?


I do have one personal use case that’s niche enough to not matter. I fuzz interpreters from different Python implementations and extensions built for these interpreters. Programmatically figuring out whether a given extension supports a given implementation beats attempting to build to find out. I could then fuzz extension X on CPython, PyPy and RustPython without trial-and-error to assess support[2].

I also have a project that downloads thousands of extensions, builds them and runs their test suites under different implementations (currently focused on FT support, but PyPy is a future target). The system figures out whether a given extension is supported, but having that information encoded in metadata would help.

But as I said, the N of people with this need is 1 or very near to it :slight_smile:. But If N is (slightly) >1, @BHUVANSH855 is very likely to be the other person who would be helped by this for that end, as he develops software that analyzes CPython and PyPy differences in extension/package code.

Full disclosure: I work with Bhuvansh on some projects where this would be useful, though I didn’t know about this idea until reading this thread.


  1. And once upon a time there were lists of PyPy-compatible extensions to guide these decisions ↩︎

  2. Although, being realistic, the trial-and-error time is dwarfed by the fuzzing time ↩︎

2 Likes

Hardly anybody sets the implementation classifiers, so why would any package authors set a new tag that intentionally makes their project break in environments where they aren’t testing?

There may be some mistaken line of thinking here that “allowing certain metadata” and “all eligible packages provide said metadata” are the same thing.


These sorts of “Pre-PEP” threads seem increasingly common. I don’t think any harm is meant, but there is a dramatic difference between my reaction seeing “Pre-PEP” from a well-established community member and “Pre-PEP” coming from someone new.

Not only do I ask that newer contributors not do this, but I’d also like more experienced folks to reconsider use of that label for a thread. Net-net, I think that “tag” is harmful to us having useful conversations.

I don’t know what the OP’s actual need is because it’s buried in layers. I want to read what the problem is, not peel an onion for my word salad.

3 Likes

Hand-wavy, yes - but not in a way that’s healthy to express in metadata, and I think that confuses a few possible root causes. The main two cases:

  1. It only builds on CPython because of bugs in other interpreters (e.g., its Python.h support, like PyPy’s cpyext layer).
  2. It only builds on CPython because it digs into CPython internals

(1) is a case of filing a bug with PyPy/GraalPy. (2) is a case of “please don’t do that, you’re on your own here”. We do not provide metadata for explicitly unsupported usage like (2).

The first part doesn’t help, it goes out of date as soon as PyPy does a new release. The second part: agreed, and classifiers can already do this.

Aside from the “N near 1 people need this”: what @sirosen said is correct, the metadata won’t be filled in by many package authors. If it’s a significant enough performance concern for your use case to actually matter, I suggest caching the result of failed builds and reusing that information before attempting to install the same version of that package in a next run of your tool. That will be more reliable, and you can do it today rather than only after a multi-year rollout trajectory (which is what we’d be looking at for new Core Metadata).

3 Likes