Static linter that checks a requirements file is fully version-pinned and hash-pinned, for CI and pre-commit.
Project description
pinlint
A static linter that checks a requirements file is fully version-pinned and hash-pinned. Built for CI and pre-commit, so unpinned or unhashed dependencies fail review before anything is installed.
Install
pip install pinlint
30-second example
pinlint requirements.txt
requirements.txt:3: unpinned: requests is not pinned to an exact version (use ==)
requirements.txt:7: missing-hash: flask has no --hash
2 issue(s) found
Exit code is 1 when there are findings, 0 when the file is clean, so it drops straight into CI or a pre-commit hook.
As a library:
from pinlint import lint_file
findings = lint_file(
"requirements.txt", require_hashes=True, allow_unpinned=False, follow_includes=True
)
for f in findings:
print(f.file, f.line, f.code, f.message)
Why this exists
Reproducible, tamper-evident installs need every requirement pinned to an exact version
and carrying a hash. The existing tools each do something adjacent: pip-compile --generate-hashes generates such a file, pip install --require-hashes enforces hashes
at install time, and requirements-txt-fixer tidies formatting. None of them is a fast,
static check you can run in review to assert that an arbitrary requirements file is fully
pinned and hashed. pinlint is that check.
Comparison
| pinlint | pip-compile | pip --require-hashes | requirements-txt-fixer | |
|---|---|---|---|---|
| Static check, no install | yes | n/a | no (install time) | yes |
| Flags unpinned versions | yes | generates | at install | no |
| Flags missing hashes | yes | generates | at install | no |
| CI / pre-commit gate | yes | partial | no | yes (formatting only) |
Checks
unpinned: the requirement is not pinned with==or===to an exact version.missing-hash: the requirement has no--hash(unless--no-hashes).unpinnable: an editable, URL, or VCS install that cannot be version-pinned.parse-error: the line could not be parsed as a requirement.
It understands comments, blank lines, backslash line continuations, --hash options,
environment markers and extras, and -r and -c includes (followed with cycle
protection). The only dependency is packaging, the canonical PEP 508 parser.
Options
--allow-unpinneddo not require exact version pins.--no-hashesdo not require--hashentries.--no-followdo not follow-rand-cincludes.--allow PACKAGEignore findings for a package name (repeatable).--format text|jsonchoose the output format;jsonsuits CI and editors.
Pre-commit
pinlint ships a hook, so you can add it to .pre-commit-config.yaml:
repos:
- repo: https://github.com/amaar-mc/pinlint
rev: v0.2.0
hooks:
- id: pinlint
The hook runs on files matching requirements.*\.txt.
Testing
pip install -e ".[dev]"
pytest
Tests use golden requirements files for each rule, including includes, cycles, line continuations, and the CLI exit codes.
Contributing
Issues and pull requests are welcome. See CONTRIBUTING.md.
License
MIT. See LICENSE.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pinlint-0.2.0.tar.gz.
File metadata
- Download URL: pinlint-0.2.0.tar.gz
- Upload date:
- Size: 842.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
69a778792638fdd62d211f266e52b5536d7c5f37ad7ca75c8cfdf0b9db112a0d
|
|
| MD5 |
a7a5e3efe827a0e1f33465a062a9dce7
|
|
| BLAKE2b-256 |
b019d7c707038c37f861f82bc1cd5b9d3fdad8ad3694a332c4ebe07175ad5312
|
File details
Details for the file pinlint-0.2.0-py3-none-any.whl.
File metadata
- Download URL: pinlint-0.2.0-py3-none-any.whl
- Upload date:
- Size: 8.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2adede7265632c93327c23c3244fe24a6d62c43ee136bbd685616f3a5e9c1df1
|
|
| MD5 |
a740d89e1cb4bf3a044d5975751e987a
|
|
| BLAKE2b-256 |
d45e82aafda073be1d91634fe16d509597b4771a8f49e3af6f3e1f2ccf1d84f2
|