PEP 694 - PyPI upload API 2.0 (Round 4)

Following up here—I originally posted these on the staged-releases thread ( Pre-Pep: Staged Releases separated from PEP-694 - #60 by IIITManjeet ), but they’re about the PEP text itself, so this seems like the better place.

I’ve been building a client against the current draft (a prototype in a fork of uv, prompted by astral-sh/uv#20789: Staged uploads in uv · Issue #20789 · astral-sh/uv · GitHub ) and ran into a few places where I wasn’t sure how to interpret the spec. I thought I’d flag them in case any are easy to clarify before implementations start diverging. I don’t think Round 4 covers these.

1. blake2b doesn’t pin a digest length

The spec says any hashlib algorithm may be used as a key, with at least one from hashlib.algorithms_guaranteed, and the example shows:

"hashes": {
  "sha256": "...",
  "blake2b": "..."
}

But hashlib.blake2b defaults to a 512-bit digest, while PyPI’s legacy upload API has always used Blake2b-256 (blake2_256_digest). Since the server has to choose a digest length to compute and compare, the key name alone isn’t enough—a client following the hashlib default and a server carrying over legacy behaviour would both believe they’re conformant and still disagree.

A possible fix would be to pin the digest length for variable-output algorithms, or name the key blake2b_256.

2. The mechanism object is snake_case while the rest of the protocol is kebab-case

Everything else is api-version, expires-at, session-token, file-upload-session, but the mechanism object uses file_url and attestations_url. This feels like something that’s easy to normalize now, but harder once implementations exist.

3. The error example uses details; RFC 9457 specifies detail

Since the PEP mandates RFC 9457 problem details, a client using an off-the-shelf RFC 9457 parser would silently miss that field.

4. 409 Location header

Minor: the 409 on a duplicate name/version MUST carry a Location header, but the text doesn’t say whether it should be absolute. Resolving it relative to the endpoint handles both cases; explicitly stating it would remove the ambiguity.

Happy to be corrected on any of these—they’re from reading and implementing rather than testing against a real server, since there isn’t one yet.