The problem
A compiled or hash-pinned requirements.txt is skipped whole. src/update_time/updaters/update_requirements_txt.py logs Skipping <path>: compiled or hash-pinned requirements file and moves on, and the README explains why: bumping one pin without recompiling its transitive dependencies and hashes would corrupt the file.
Skipping the rewrite is right. Skipping the checks is a consequence nobody chose. Not one pin in such a file is checked for a yank, a vulnerability, or an archived project, and those files are where a project has done the most work to be reproducible. The setup that takes security most seriously gets the least reporting.
The skip is visible at INFO, so the user is told the file was skipped. They are not told what that costs them.
What the increment adds
Reading such a file for the warnings alone. Every exact pin it holds is checked against PyPI's yank metadata, against OSV's PyPI advisories, and against PyPI's project status, and each is reported as it would be from any other file. No line is rewritten, and no version is resolved.
A pin in a compiled file is an exact == version, which is what all three checks need, so the checks need no new source and no new matching rule.
The design constraint
Update-time reads a file in order to rewrite it. The updaters resolve a new version, write it, and report the warnings they gathered on the way. This increment separates the two for the first time: a file that is read, checked, and never written.
That separation is the whole risk of the work. It is worth naming before any code moves, because the wrong shape duplicates the checks in a second place, where they can drift from the ones the rewriting path runs.
Decisions to take
What a marker means here. A requirements.txt pin takes markers today, and an ignore[vulnerable] on a line in a compiled file has to keep silencing that warning. An ignore[update] on such a line asks for something the file never gets, so it may deserve the redundancy warning that other unreachable scopes get.
What the INFO line says. "Skipping" is no longer true once the file is checked. The message has to say that the file was checked but not rewritten, or the log contradicts the behaviour.
Whether a hash-pinned file differs from a compiled one. Both are skipped by the same rule today, recognised by an autogenerated header, by --hash= lines, or by a sibling .in file. Whether all three kinds should be checked, or only those whose pins are exact, is worth settling from real files rather than from the rule.
Out of scope
Regenerating the file. Running uv pip compile or pip-tools would update it properly, and it needs the source file, the tool, and a resolution of every transitive pin and hash. It is the natural sequel to this work, not part of it.
Auditing the transitive dependencies as dependencies in their own right. The pins in the file are checked; what they pull in is what uv audit and pip-audit are for.
The problem
A compiled or hash-pinned
requirements.txtis skipped whole.src/update_time/updaters/update_requirements_txt.pylogsSkipping <path>: compiled or hash-pinned requirements fileand moves on, and the README explains why: bumping one pin without recompiling its transitive dependencies and hashes would corrupt the file.Skipping the rewrite is right. Skipping the checks is a consequence nobody chose. Not one pin in such a file is checked for a yank, a vulnerability, or an archived project, and those files are where a project has done the most work to be reproducible. The setup that takes security most seriously gets the least reporting.
The skip is visible at
INFO, so the user is told the file was skipped. They are not told what that costs them.What the increment adds
Reading such a file for the warnings alone. Every exact pin it holds is checked against PyPI's yank metadata, against OSV's PyPI advisories, and against PyPI's project status, and each is reported as it would be from any other file. No line is rewritten, and no version is resolved.
A pin in a compiled file is an exact
==version, which is what all three checks need, so the checks need no new source and no new matching rule.The design constraint
Update-time reads a file in order to rewrite it. The updaters resolve a new version, write it, and report the warnings they gathered on the way. This increment separates the two for the first time: a file that is read, checked, and never written.
That separation is the whole risk of the work. It is worth naming before any code moves, because the wrong shape duplicates the checks in a second place, where they can drift from the ones the rewriting path runs.
Decisions to take
What a marker means here. A
requirements.txtpin takes markers today, and anignore[vulnerable]on a line in a compiled file has to keep silencing that warning. Anignore[update]on such a line asks for something the file never gets, so it may deserve the redundancy warning that other unreachable scopes get.What the
INFOline says. "Skipping" is no longer true once the file is checked. The message has to say that the file was checked but not rewritten, or the log contradicts the behaviour.Whether a hash-pinned file differs from a compiled one. Both are skipped by the same rule today, recognised by an autogenerated header, by
--hash=lines, or by a sibling.infile. Whether all three kinds should be checked, or only those whose pins are exact, is worth settling from real files rather than from the rule.Out of scope
Regenerating the file. Running
uv pip compileor pip-tools would update it properly, and it needs the source file, the tool, and a resolution of every transitive pin and hash. It is the natural sequel to this work, not part of it.Auditing the transitive dependencies as dependencies in their own right. The pins in the file are checked; what they pull in is what
uv auditandpip-auditare for.