Repository navigation
Fix silent PyPI hosted scan with no lockfile (#638) - #1885
Mikola Lysenko (mikolalysenko) wants to merge 4 commits into
Conversation
Assisted-by: Claude Code:claude-opus-5-5
A hosted scan of a Python project with no root requirements.txt, tool lock (uv.lock, poetry.lock, pdm.lock, Pipfile.lock), PEP 751 or PEP 723 lock, or Hatch configuration left its PyPI patches unpinned while `--json` showed an empty redirect.warnings list. Every other ecosystem reports this case (redirect_npm_no_lockfile and friends). The PyPI rewriter lane now adds one redirect_pypi_no_lockfile warning per run in that case. It names the files looked for, notes that other requirements files such as requirements-dev.txt are not read, and tells a pyproject.toml project to generate and commit its lock. Refs #638 Assisted-by: Claude Code:claude-opus-5-5
Adds a hosted scan test for each project layout in #638 that has no Python lock: only requirements-dev.txt, only requirements/base.txt, no requirements file, and a pyproject.toml without uv.lock. Each run must report the warning once, keep the redirect_unconfirmed row, and write nothing. CLI_CONTRACT.md now lists the new warning code. Refs #638 Assisted-by: Claude Code:claude-opus-5-5
Review follow-up for redirect_pypi_no_lockfile. The remedy now follows the project's tooling: a Pipfile is told to run `pipenv lock`, and a pyproject that names uv, Poetry or PDM (a [tool.*] table or its build backend) is told to run that tool's lock. Any other pyproject, such as a plain setuptools project, gets both a root requirements.txt pin and the lock commands. The detail also lists every input that would silence it (pylock.<name>.toml, PEP 723 script locks, Hatch) and says "readable", since an unreadable file on disk is treated as missing. The presence check now matches root keys only, like the rewriters it stands in for. Refs #638 Assisted-by: Claude Code:claude-opus-5-5
|
BugBot review Generated by Claude Code |
There was a problem hiding this comment.
✅ 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 d0dc53c. Configure here.
|
Ready for review at
Generated by Claude Code |
Final review brief (
|
LLM Description written by Claude Code:claude-opus-5-5
Fixes #638
Summary
A
scan --mode hosted --jsonover a pip project whose venv holds apatched package, but which has no root requirements.txt or Python lock,
exited 0 with an empty
warningslist. The only machine-readable signalwas the per-purl
redirect_unconfirmedrow (#1029). Every otherecosystem reports this case with a stable code
(
redirect_npm_no_lockfile,redirect_composer_no_lockfile,redirect_gem_no_gemfile, ...). PyPI now reportsredirect_pypi_no_lockfile, once per run.Layouts covered (the issue's matrix and its uv comment):
requirements-dev.txtrequirements/base.txtpyproject.tomlproject whoseuv.lockis not committed (thedetail tells the user to run
uv lock/poetry lock/pdm lock)Root cause
Each PyPI hosted rewriter (pdm, Pipenv, requirements, Hatch, uv/pylock,
Poetry) acts only on the file it owns and returns silently when that file
is absent. No PyPI-level check covered "none of them is present", unlike
the npm branch in
patch/redirect/mod.rsor composer'sformats/composer/hosted.rs.Fix
warn_no_pypi_wiringruns last in the PyPI rewriter group(
patch/redirect/mod.rs). When a pypi patch reaches the lane and theproject has no root
requirements.txt,uv.lock/poetry.lock/pdm.lock/Pipfile.lock, PEP 751 / PEP 723 lock, or Hatchconfiguration, it pushes one warning. The warning names the files looked
for and says that other requirements files (such as requirements-dev.txt)
are not read. When any of those files is present, it owns the diagnostics
(e.g.
redirect_requirements_entry_not_found) and no warning is added.Patches withheld earlier (platform wheels #701, pdm/Pipenv refusals) don't
trigger it. Unreadable or undecodable candidate files already refuse the
project before the rewriters run, so they can't produce a misleading
"no lockfile".
CLI_CONTRACT.md lists the new code as an additive v5.0
redirect_*warning. Wrappers (
npm/,pypi/,gem/) don't name warning codes,so they need no change.
Out of scope, as the issue says: the hosted exit status for an
unpinned patch is still 0 (the open #704 decision).
Tests
patch::redirect::pypi_no_lockfile_tests::every_lockless_layout_warns_oncepypi_no_lockfile_tests::pyproject_without_a_lock_names_the_lock_steppypi_no_lockfile_tests::a_python_lock_or_root_requirements_suppresses_the_warningpypi_no_lockfile_tests::no_warning_without_a_pypi_patch_in_the_lane--jsonenvelope carries the code once, keeps theredirect_unconfirmedrow, writes nothing, exit 0mode_migration_pypi::hosted_scan_without_a_python_lock_warns_no_lockfileRed → green:
cargo test -p socket-patch-core --lib pypi_no_lockfilegave 2 failed (
every_lockless_layout_warns_once: "requirements-dev.txtonly: []",
pyproject_without_a_lock_names_the_lock_step), 2 passed(the negative cases).
Local runs:
cargo fmt --all -- --check: okcargo clippy --workspace --all-features -- -D warnings: okcargo test -p socket-patch-cli --all-features --test mode_migration_pypi:75 passed (the new test re-run on d0dc53c: passed)
pypi_no_lockfile_testson d0dc53c: 6 passedcargo test -p socket-patch-core --all-features --lib: 6998 passed and4 failed. All 4 failures depend on chmod making a directory unwritable
(
copy_tree::relax_loop_must_not_traverse_symlinked_root,vlt_heal::an_unremovable_hidden_lock_keeps_every_store_entry,pypi_poetry::wire_write_failure_maps_error_and_leaves_lock_untouched,pypi_requirements::wire_failure_rolls_back_already_written_files), sothey can't pass in this container, which runs as root (uid 0). None of
them is touched by this diff.
cargo test --workspace --all-featuresran out of disk in thissession's container (about 20G of build cache). CI runs the whole
workspace.
/code-review high
Fixed in d0dc53c:
pipenv lockfor aPipfile, and
uv lock/poetry lock/pdm lockwhen pyprojectnames that tool (a
[tool.*]table or its build backend). A plainpyproject gets both a root requirements.txt pin and the lock commands.
Tested by
the_remedy_names_the_projects_own_lock_step.script lock, Hatch) and says "readable", because on disk an unreadable
file is read as absent (
the_detail_names_every_input_looked_for).stands in for.
Not changed:
the shared
governing_locks/python_lock/hatchpredicates therewriters themselves use. Having each rewriter report "saw an input"
would touch six rewriters for a single warning. The suppression test
covers every input, so a new input that isn't added here fails there
only if a test for it is added; flagged here for review.
is_hatchTOML parse. It runs only on the lockless path,which is once per run and has nothing else to do, so it isn't worth
threading state between rewriters.
Generated by Claude Code