GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Fix requirements.txt pins not matched under PEP 440 (#475) by mikolalysenko · Pull Request #478 · SocketDev/socket-patch

Conversation

@mikolalysenko

@mikolalysenko Mikola Lysenko (mikolalysenko) commented Oct 1, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Collaborator

LLM Description written by Claude Code:claude-opus-5-5

Fixes #475

Summary

Hand-written requirements.txt pins like six==1.16 (for an installed six 1.16.0) are now matched the way pip matches them, under PEP 440 equality. Before this change, scan --mode hosted skipped such a pin, reported "no requirements.txt entry", exited 0, and left the project installing the unpatched release. This is a regression from v4.0.0, introduced in #239.

Root cause

Every pin matcher compared the pinned version with the patch version as a raw string. pip compares them under PEP 440, which zero-pads release segments and ignores leading zeros, case and alternate pre/post/dev spellings. So ==1.16, ==1.16.0.0 and ==01.16.0 all select exactly 1.16.0. The same raw comparison was in three writers:

  • hosted patch/redirect/requirements.rs skipped the pin with redirect_requirements_entry_not_found and exited 0;
  • vendored vendor/pypi_requirements.rs refused with a false pypi_requirement_not_pinned;
  • Hatch utils/hatch.rs (used by hosted and vendored Hatch) refused hand-written pyproject.toml pins like urllib3==1.26.18.0 with "requires an exact ==1.26.18 declaration". Same cause at the same boundary, so it is fixed here too.

Fix

  • New utils/pep440.rs: a small parser for the packaging version grammar, plus PEP 440 equality (epoch, zero-trimmed release, normalized pre/post/dev, case-folded local segments). Digit runs are compared as leading-zero-trimmed strings, so long numbers can't overflow. Invalid versions are never equal to anything, so callers fail closed.
  • All three writers now use it for == pins. === (arbitrary equality) keeps plain string comparison, as PEP 440 defines it, and wildcards (==1.*) are still ranges.

Notes / follow-ups

  • Rollback spelling: hosted mode keeps no ledger in v5, so hosted rollback re-derives name==<patch version>. A redirected six==1.16 therefore rolls back to six==1.16.0, which installs the same release. This matches how rollback already normalizes six == 1.16.0 today. Vendored revert is ledger-based and stays byte-exact.
  • Lock-only discovery (no venv): utils/requirements.rs::exact_pin still carries the spelled version into the purl (pkg:pypi/[email protected]). Matching that to the 1.16.0 release needs PyPI's canonical release spelling, not just local normalization. The issue lists this as a "may" and its repro uses an installed venv, so it's left as a follow-up rather than guessed at here.

Test evidence

Regression tests, each shown failing on main and passing with the fix:

Issue variant Test main this PR
hosted ==X.Y, ==X.Y.Z.0, ==0X.Y.Z, spaced + marker patch::redirect::requirements::tests::pep440_equivalent_pins_are_rewritten ❌ redirect_requirements_entry_not_found ✅
hosted, end to end (get <uuid> --mode hosted over requests==2.31, ==2.31.0.0, Requests==02.31.0) in_process_get_hosted_ecosystems::pypi_requirements_hosted_rewrites_pep440_equivalent_pin ❌ file unchanged ✅
vendored false pypi_requirement_not_pinned vendor::pypi_requirements::tests::find_pin_classifies_every_shape (new PEP 440 cases; === / wildcard still Range) ❌ ✅
Hatch equivalent pins utils::hatch::tests::pep440_equivalent_pins_are_exact_declarations ❌ ✅
helper utils::pep440::tests::* (equal spellings, non-equal releases, invalid input, == vs ===/wildcards) new ✅

Local runs (Linux):

  • cargo clippy --workspace --all-features -- -D warnings: clean.
  • cargo test --workspace --all-features --no-fail-fast: everything passes except 12 permission-based write-failure tests, which fail because this sandbox runs as uid 0 (root ignores read-only bits). They fail identically on main, and all 12 pass when re-run as uid 65534.
  • cargo test -p socket-patch-cli --all-features --test e2e_vendor_pypi_build -- --ignored (real uv + pip + PyPI): 7/7 pass.
  • cargo fmt: the changed code is rustfmt-formatted. CI has no fmt step and main itself isn't fmt-clean (rustfmt 1.8 rewrites ~125 unrelated files), so unrelated reflows were left out.
  • No wrapper changes: npm/, pypi/ and gem/ only dispatch to the binary.

🤖 Generated with Claude Code


Note

Medium Risk
Changes pin-matching on the patch redirect path for PyPI; incorrect PEP 440 logic could mis-redirect or skip pins, but invalid versions fail closed and ===/wildcards are unchanged.

Overview
Fixes #475: PyPI pin matching now treats == the same way pip/uv/Hatch do under PEP 440, instead of comparing version strings literally.

Adds utils/pep440 with packaging-style parsing and equality (versions_equal, is_exact_pin_of). Hosted requirements.txt redirect, vendored pypi_requirements pin discovery, and Hatch pyproject.toml rewrites all use it for == pins. === stays plain string equality; wildcards and ranges are unchanged.

Pins like requests==2.31, ==2.31.0.0, or ==02.31.0 now redirect to the patched wheel when the grant is 2.31.0, instead of skipping with “no entry” / false “not pinned” and leaving the project unpatched.

Regression coverage: unit tests for the helper and each rewriter, plus an in-process get --mode hosted test over equivalent requirements.txt pins.

Reviewed by Cursor Bugbot for commit bcc5335. Configure here.


Generated by Claude Code

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@claude
Start fix for #475
9a79da3
Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko Mikola Lysenko (mikolalysenko) mentioned this pull request Oct 1, 2026
Hosted requirements.txt rewrite skips PEP 440-equivalent pins like six==1.16 for an installed 1.16.0, so scan exits 0 and pip installs the unpatched release (regression from v4.0.0) #475
Closed
Claude (claude) added 2 commits October 1, 2026 15:40
@claude
Match requirements pins like pip does (PEP 440)
eea2f62
A hand-written pin such as `six==1.16` installs six 1.16.0, but the
hosted requirements.txt rewrite compared versions as raw strings and
skipped it, so `scan` exited 0 and the project stayed unpatched. The
vendored requirements writer and the Hatch rewriter refused the same
pins as "not pinned".

Add a small PEP 440 equality helper (zero-padded release segments,
leading zeros, case and pre/post/dev spellings) and use it for `==`
pins in all three writers. `===` keeps plain string equality, as PEP
440 defines it.

Fixes #475

Assisted-by: Claude Code:claude-opus-5-5
@claude
Test hosted get over a PEP 440-equivalent pin
bcc5335
Mirrors the #475 repro end to end: `get <uuid> --mode hosted` over
`requests==2.31`, `==2.31.0.0` and `Requests==02.31.0` must redirect
the pin to the hosted wheel. Fails on main, passes with the fix.

Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko
Mikola Lysenko (mikolalysenko) marked this pull request as ready for review October 1, 2026 16:03
@mikolalysenko

Mikola Lysenko (mikolalysenko) commented Oct 1, 2026

Copy link
Copy Markdown
Collaborator Author

BugBot review


Generated by Claude Code

cursor[bot] reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

cursor[bot]
cursor Bot reviewed Oct 1, 2026
View reviewed changes

@cursor cursor Bot left a comment •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Stale Bugbot comment from a previous run.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@mikolalysenko Mikola Lysenko (mikolalysenko) added the Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review label Oct 1, 2026
@mikolalysenko

Mikola Lysenko (mikolalysenko) commented Oct 1, 2026

Copy link
Copy Markdown
Collaborator Author

Burn-down agent: ready for review at bcc5335 (bcc533518a74ca2234958cb56379a4333e97d6cb).

  • CI: 97/97 non-skipped checks green (3 skipped).
  • Bugbot: reviewed bcc5335, no findings; no unresolved review threads.
  • Mergeable with no conflicts (15 commits behind main, merges cleanly).
  • Reviewer focus: the new utils/pep440.rs equality rules, and the note that hosted rollback re-derives name==<patch version> (so six==1.16 rolls back as six==1.16.0).

Generated by Claude Code

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@mikolalysenko Mikola Lysenko (mikolalysenko) mentioned this pull request Oct 2, 2026
Lockfile-only requirements.txt discovery skips exact pins written with spaces (six == 1.15.0, six (==1.15.0)), so a fresh checkout reports "No patches available" and pip installs the unpatched release #523
Closed
@mikolalysenko

Mikola Lysenko (mikolalysenko) commented Oct 2, 2026

Copy link
Copy Markdown
Collaborator Author

Reviewed bcc533518a74ca2234958cb56379a4333e97d6cb. Recommendation: ready to merge from code review. No actionable correctness or security regressions found. Checked the parser against PyPA's version grammar, the normalization and invalid-input boundaries, == versus ===, and all three writer integrations.

Validation: 5 PEP 440-focused core tests, all 36 vendored-requirements unit tests, and the in-process hosted-get regression passed. Full workspace and real pip/uv matrices were not rerun. The separately documented lock-only discovery follow-up remains outside this change.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@Tanmay182003

Tanmay Singla (Tanmay182003) commented Oct 2, 2026

Copy link
Copy Markdown
  • A requirements.txt with no hashes used to get requests @ --hash=sha256:abc.
  • Since Fix requirements.txt writers ignoring pip hash mode (#376) #383 landed on main, it gets requests @ #sha256=abc. May need to rebase anf change line 311?
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

Claude (claude) added 2 commits October 2, 2026 14:54
@claude
Merge remote-tracking branch 'origin/main' into agent/fix-requirement…
9633810
…s-pep440-pin-match
@claude
Expect #sha256= pin in the PEP 440 hosted-get test
c331d18
Since #383, a hosted scan of a requirements.txt that is not in
hash-checking mode pins the patched wheel with the url's #sha256=
fragment rather than --hash, so the PEP 440 regression test now
expects that form, matching the existing hosted pypi test.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01CVbFzTeYSTvB5iKjY6FRg7
@mikolalysenko

Mikola Lysenko (mikolalysenko) commented Oct 2, 2026

Copy link
Copy Markdown
Collaborator Author

Tanmay Singla (@Tanmay182003) Good catch, thanks. Fixed in c331d18. I merged main into the branch (9633810, a merge commit rather than a rebase) and changed the expectation at in_process_get_hosted_ecosystems.rs:311 to requests @ {url}#sha256={SHA256}, the same form #383 uses in the existing hosted pypi test.

Checked locally on the merged tree:

  • in_process_get_hosted_ecosystems: 8/8 pass.
  • socket-patch-core lib: all pass except 4 permission-based tests that only fail because this sandbox runs as root. They pass when run unprivileged.
  • cargo clippy --workspace --all-features -- -D warnings is clean.

The existing --hash= assertions in redirect/requirements.rs are for inputs that are already in hash-checking mode, so #383 still writes --hash for those and they pass unchanged.


Generated by Claude Code

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@mikolalysenko

Mikola Lysenko (mikolalysenko) commented Oct 2, 2026

Copy link
Copy Markdown
Collaborator Author

BugBot review


Generated by Claude Code

cursor[bot] reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

cursor[bot]
cursor Bot reviewed Oct 2, 2026
View reviewed changes

@cursor cursor Bot left a comment

Copy link
Copy Markdown

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit c331d18. Configure here.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@mikolalysenko

Mikola Lysenko (mikolalysenko) commented Oct 2, 2026

Copy link
Copy Markdown
Collaborator Author

CI: native (macos-latest, 1.1.45) failed on c331d18. One case of the bun native backtest failed: space-unicode hosted FAIL ['refusalCodesExact']. The other 43 cases passed (job).

This doesn't look like it comes from this PR:

  • The PR only changes pypi matching (utils/pep440.rs, redirect/requirements.rs, vendor/pypi_requirements.rs, utils/hatch.rs), and the bun hosted path never calls any of it.
  • The same cell passed on main at d63ae5f, which is exactly what the branch merged. It also passed on the previous head, bcc5335.
  • On this commit, bun 1.1.45 on ubuntu passed, and every other macOS bun version that has finished passed.

I couldn't see which refusal code appeared, because the log only records the check name and the artifact isn't reachable from my sandbox. No fix for it exists yet. I'll re-run the failed job once the workflow finishes, since GitHub won't re-run it while the rest of the run is still going. If it fails again, I'll treat it as a real failure and dig in.


Generated by Claude Code

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@mikolalysenko

Mikola Lysenko (mikolalysenko) commented Oct 2, 2026

Copy link
Copy Markdown
Collaborator Author

[agent] All checks green on c331d18: 297 passed, 3 skipped. native (macos-latest, 1.1.45) passed when I re-ran it once, so the earlier space-unicode hosted refusal-code failure didn't repeat. It's the Bun backtest flake that #565 is fixing. Bugbot reviewed c331d18 and found nothing, and there are no unresolved threads. Locally, clippy is clean and the workspace tests pass, apart from 3 covgap_commands_vendor read-only-dir tests that only fail because my sandbox runs as root. That file isn't touched by this PR. Tanmay Singla (@Tanmay182003), could you re-check now that the #sha256= expectation is fixed?


Generated by Claude Code

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@mikolalysenko
Mikola Lysenko (mikolalysenko) requested a review from Tanmay Singla (Tanmay182003) October 2, 2026 16:01
Tanmay182003
Tanmay Singla (Tanmay182003) approved these changes Oct 2, 2026
View reviewed changes
@mikolalysenko
Mikola Lysenko (mikolalysenko) enabled auto-merge (squash) October 2, 2026 16:22
@mikolalysenko
Mikola Lysenko (mikolalysenko) merged commit bf0e0d1 into main Oct 2, 2026
538 of 539 checks passed

Uh oh!

There was an error while loading. Please reload this page.

@mikolalysenko
Mikola Lysenko (mikolalysenko) deleted the agent/fix-requirements-pep440-pin-match branch October 2, 2026 16:23
@mikolalysenko

Mikola Lysenko (mikolalysenko) commented Oct 2, 2026

Copy link
Copy Markdown
Collaborator Author

Second pass reviewed c331d185c09f449cd6238786fb2cef9b869802ad: ready from code review; no additional code change needed. I checked the clean main merge and the #sha256= expectation update against the hosted requirements writer. The PEP 440 matching paths remain consistent with the merged hash-pinning behavior.

Validation at this exact head: in_process_get_hosted_ecosystems::pypi_requirements_hosted_rewrites_pep440_equivalent_pin passed, including all equivalent-pin variants. The earlier 5 PEP 440 and 36 vendored-requirements tests were not repeated in this incremental pass. No new blocking finding.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@mikolalysenko Mikola Lysenko (mikolalysenko) mentioned this pull request Oct 2, 2026
Lock-only requirements.txt discovery sends PEP 440-equivalent pins verbatim (six==1.16 → pkg:pypi/[email protected]), so a fresh checkout reports "No patches available" while the same file with a venv is patched #604
Closed
Mikola Lysenko (mikolalysenko) added a commit that referenced this pull request Oct 9, 2026
@mikolalysenko @claude
Find lock-only patches for PEP 440 pin spellings
6843538
On a fresh checkout (no venv), requirements.txt discovery sent a pin
as written: `six==1.16` became `pkg:pypi/[email protected]`, `==1.16.0.0`
became `@1.16.0.0`, `==01.16.0` became `@01.16.0`. pip installs the
registry release 1.16.0 for all three, but the patch API keys it
`@1.16.0`, so the scan said "No patches available" and the unpatched
release was installed. The same file with a venv holding six was
patched.

Scan now also asks the API for the other PEP 440 spellings of a
lockfile-only PyPI pure-release pin (leading zeros dropped, release
padded or trimmed to at least three segments). A patch returned under
another spelling is counted as that lockfile-only package, so
`notInstalled` and the vendored baseline pre-check see it. The
rewriters already match `==` pins under PEP 440 (#478).

Fixes #604

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
@mikolalysenko Mikola Lysenko (mikolalysenko) mentioned this pull request Oct 9, 2026
Fix lock-only PEP 440 pin spellings missing patches (#604) #1334
Merged
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@cursor cursor[bot] cursor[bot] left review comments
@Tanmay182003 Tanmay Singla (Tanmay182003) Tanmay182003 approved these changes

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Hosted requirements.txt rewrite skips PEP 440-equivalent pins like six==1.16 for an installed 1.16.0, so scan exits 0 and pip installs the unpatched release (regression from v4.0.0)

Uh oh!

There was an error while loading. Please reload this page.

3 participants

@mikolalysenko @Tanmay182003 @claude
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.