Skip to main content

Static linter that checks a requirements file is fully version-pinned and hash-pinned, for CI and pre-commit.

Project description

pinlint

pinlint logo

PyPI CI License: MIT

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-unpinned do not require exact version pins.
  • --no-hashes do not require --hash entries.
  • --no-follow do not follow -r and -c includes.
  • --allow PACKAGE ignore findings for a package name (repeatable).
  • --format text|json choose the output format; json suits 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

pinlint-0.2.0.tar.gz (842.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pinlint-0.2.0-py3-none-any.whl (8.5 kB view details)

Uploaded Python 3

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

Hashes for pinlint-0.2.0.tar.gz
Algorithm Hash digest
SHA256 69a778792638fdd62d211f266e52b5536d7c5f37ad7ca75c8cfdf0b9db112a0d
MD5 a7a5e3efe827a0e1f33465a062a9dce7
BLAKE2b-256 b019d7c707038c37f861f82bc1cd5b9d3fdad8ad3694a332c4ebe07175ad5312

See more details on using hashes here.

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

Hashes for pinlint-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 2adede7265632c93327c23c3244fe24a6d62c43ee136bbd685616f3a5e9c1df1
MD5 a740d89e1cb4bf3a044d5975751e987a
BLAKE2b-256 d45e82aafda073be1d91634fe16d509597b4771a8f49e3af6f3e1f2ccf1d84f2

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page