Skip to content

Reject invalid classifiers, warn on license classifiers#18419

Merged
woodruffw merged 3 commits intomainfrom
ww/warn-license-classifier
Mar 13, 2026
Merged

Reject invalid classifiers, warn on license classifiers#18419
woodruffw merged 3 commits intomainfrom
ww/warn-license-classifier

Conversation

@woodruffw
Copy link
Copy Markdown
Member

@woodruffw woodruffw commented Mar 12, 2026

Summary

This makes two changes to our handling of trove classifiers in uv's build backend:

  1. We now reject malformed trove classifiers. I've done this by adding a Classifier newtype that parses and rejects anything that looks wrong (there's no formal grammar for classifiers, so this is a pretty primitive check, but it should be enough to preempt most common user errors). We don't use an allowlist at the moment, although in principle we could do that as well/instead.
  2. We now emit a user warning on any license classifiers, since these have been deprecated since PEP 639 was accepted. I've added this within PyProjectToml::license_metadata since in the future we'll also want to produce a hard error when license classifiers are present and new-style structured license metadata is also present. That change will require a breaking release however.

Closes #16354.

Test Plan

I've added some new unit tests for this, plus an integration test for the user warning.

Signed-off-by: William Woodruff <william@astral.sh>
@woodruffw woodruffw requested a review from konstin March 12, 2026 14:50
@woodruffw woodruffw self-assigned this Mar 12, 2026
Comment on lines +920 to +924
// A classifier has two or more non-empty parts separated by ` :: `.
let mut parts = s.split(" :: ");
let valid = parts.next().is_some_and(|p| !p.is_empty())
&& parts.next().is_some_and(|p| !p.is_empty())
&& parts.all(|p| !p.is_empty());
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flagging: I wrote it this way because I'm allergic to intermediate allocations, and the most "obvious" form would involve one (via Vec<&str>). Happy to change though if this seems like a silly micro-optimization 🙂

Signed-off-by: William Woodruff <william@astral.sh>
@woodruffw woodruffw requested a review from zsol March 12, 2026 15:03
Signed-off-by: William Woodruff <william@astral.sh>
@konstin
Copy link
Copy Markdown
Member

konstin commented Mar 13, 2026

There's some risk we break uv_build for existing packages, is there a way to check whether the classifiers in the wild (or their subset for uv_build projects) are valid? The risk is that this transitively affects packages that depend on the source dist of the package with the invalid classifier.

Code looks good otherwise.

@woodruffw
Copy link
Copy Markdown
Member Author

is there a way to check whether the classifiers in the wild (or their subset for uv_build projects) are valid?

I could probably do a batch scan of PyPI, although I think PyPI has also validated classifiers on upload for a very long time so I wouldn't expect anything in the last 7+ years to have an invalid classifier in its metadata.

Still, I agree it's a risk. Do you think it makes sense to start with a warning here instead and then ratchet down in the next breaking release?

@konstin
Copy link
Copy Markdown
Member

konstin commented Mar 13, 2026

I could probably do a batch scan of PyPI, although I think PyPI has also validated classifiers on upload for a very long time so I wouldn't expect anything in the last 7+ years to have an invalid classifier in its metadata.

That's perfect then!

@woodruffw woodruffw merged commit e979ed7 into main Mar 13, 2026
54 checks passed
@woodruffw woodruffw deleted the ww/warn-license-classifier branch March 13, 2026 13:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[uv_build] Emit warning when both license (PEP639) and legacy license classifiers are present in pyproject.toml

2 participants