Skip to content

Reject empty/invalid Tag components consistently - #1413

Open
Pitchfork-and-Torch wants to merge 3 commits into
pypa:mainfrom
Pitchfork-and-Torch:cook/tag-reject-empty-and-invalid-components
Open

Pitchfork-and-Torch wants to merge 3 commits into
pypa:mainfrom
Pitchfork-and-Torch:cook/tag-reject-empty-and-invalid-components

Conversation

@Pitchfork-and-Torch

@Pitchfork-and-Torch Pitchfork-and-Torch commented Sep 18, 2026 •

Copy link
Copy Markdown

Summary

parse_tag() already raises InvalidTag for empty components, but Tag() accepted them (Tag('', 'none', 'any') -> -none-any). Separately, only the interpreter was checked with str.isidentifier(), so tags like py3-none-any, and py3-none-any parsed successfully with junk in the platform field.

  • Tag.__init__ rejects empty components
  • Interpreter stays on str.isidentifier()
  • ABI allows ASCII letters, digits, underscore, and + (8 standard base64 characters may include +). / stays rejected
  • Platform allows ASCII letters, digits, and underscore

Test plan

  • tests/test_tags.py and tests/test_utils.py (327 tests) pass

Tag() accepted empty interpreter/abi/platform while parse_tag rejected
them, and parse_tag allowed trailing junk like `py3-none-any,` because
only the interpreter was checked with str.isidentifier(). Validate all
three components in both paths.

@sylvesterkaczmarek sylvesterkaczmarek left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

str.isidentifier() is Python-identifier grammar, not compatibility-tag grammar. In particular it rejects a digit-leading ABI token, even though the compatibility-tag spec allows implementation-defined ABI hashes whose base64 text can start with a digit. Could this validate the actual tag grammar instead of identifier syntax?

@Wkhanex Wkhanex left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I checked the other validation paths: empty components in direct Tag construction and punctuation in each parsed component raise InvalidTag, while compressed py2.py3-none-any still expands to the expected product. This doesn't resolve the digit-leading ABI grammar concern above.

Checked 8bb6234d3eb7 with standalone Python 3.12.14 checks (7 passing assertions). I haven't run the full upstream suite.

str.isidentifier() rejects ABI hashes that the compatibility-tag spec
allows to start with a digit. Match ABI and platform with
[0-9A-Za-z_]+. Interpreter validation stays isidentifier().
@Pitchfork-and-Torch

Copy link
Copy Markdown
Author

Digit-leading ABI is a real spec case. The compatibility tag spec says an unstable ABI may be the first 6 bytes of a hash, written as 8 base64 characters, so the component may start with a digit. str.isidentifier() rejects that.

ABI and platform now match [0-9A-Za-z_]+. Interpreter stays on isidentifier(), the existing 26.3 check. py3-0abcDEF1-any parses. The junk cases in this PR still fail: trailing comma, trailing space, and +.

Standard base64 also includes + and /. Those stay rejected. - is the field separator, and / is not a wheel filename character. Say if ABI should allow them.

tests/test_tags.py covers the digit-leading case (test_digit_leading_abi_parses). The tag test file passed locally, 245 tests.

@sylvesterkaczmarek

Copy link
Copy Markdown

Thanks for addressing the digit-leading ABI case. I verified py3-0abcDEF1-any now parses and reran the full repository tests locally on Python 3.12.14: 62,441 passed, 1 skipped (glibc-only), 427 deselected. One remaining spec-compatibility point before approval: the platform compatibility tag specification explicitly permits an unstable ABI hash encoded as 8 standard Base64 characters, which can include +. For example, 0abc+EF1 is valid eight-character Base64, but the new [0-9A-Za-z_]+ ABI regex rejects py3-0abc+EF1-any; parse_wheel_filename also rejects that filename. The wheel format disallows - in tag components, but does not prohibit +. I agree / cannot occur in a wheel filename. Could you allow + in ABI (not platform), cover the case with a positive regression, and replace the py3-none+-any invalid fixture with unambiguously invalid punctuation? The PR description also still says the new ABI/platform checks use isidentifier(), although the code now uses a regex. References: https://packaging.python.org/en/latest/specifications/platform-compatibility-tags/#abi-tag and https://packaging.python.org/en/latest/specifications/binary-distribution-format/#escaping-and-unicode .

Unstable ABI hashes are 8 standard base64 characters, so + is legal in ABI.
Platform tags stay letters, digits, and underscore. Slash stays rejected.
@Pitchfork-and-Torch

Copy link
Copy Markdown
Author
  • is allowed in ABI only. The ABI tag may be 8 standard base64 characters, and the wheel filename rules forbid - in a component but do not forbid +. / stays rejected because it is not a wheel filename character. Platform stays letters, digits, and underscore.

py3-0abc+EF1-any parses. parse_wheel_filename("foo-1.0-py3-0abc+EF1-any.whl") accepts that filename. The old py3-none+-any fixture is gone, because none+ is a legal ABI once + is allowed. The negative ABI case is now py3-none/-any, and py3-none-any+ still fails as a platform.

The PR description no longer says the ABI and platform checks use isidentifier(). Head is a60e074.

@sylvesterkaczmarek sylvesterkaczmarek left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Rechecked head a60e074. The ABI validator now permits digit-leading values and the '+' character used by standard Base64 ABI hashes, while keeping '+' out of platform tags and rejecting slash, whitespace and punctuation that cannot appear in filename tag components. The wheel filename parser accepts the positive '+' regression, and the invalid-fixture updates match the new grammar. This resolves the compatibility concerns I raised.

Validation on Python 3.12.14/macOS arm64: 327 focused tags/utils tests passed; direct positive/negative tag and wheel-filename cases passed; full repository suite passed (62,446 passed, 1 skipped for glibc-only, 427 deselected). No remaining blocker from my review.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants