Skip to content

Pre-commit fails to run linters on scripts using PEP 723 because the dependencies are missing, this wrapper handles that.

License

Notifications You must be signed in to change notification settings

bitflight-devops/pre-commit-pep723-linter-wrapper

Repository files navigation

pep723-loader

A CLI wrapper that auto-installs PEP 723 inline script dependencies before executing linters and other tools.

The Problem

Python scripts with PEP 723 inline metadata declare their dependencies directly in the file:

# /// script
# requires-python = ">=3.11"
# dependencies = [
#   "requests>=2.31.0",
#   "rich>=13.0.0",
# ]
# ///

import requests
from rich import print
# ...

But when you run type checkers or linters on these scripts, they fail because the dependencies aren't installed:

$ mypy script.py
script.py:8: error: Cannot find implementation or library stub for module named "requests"
script.py:9: error: Cannot find implementation or library stub for module named "rich"

The Solution

pep723-loader wraps your linter and automatically installs the inline dependencies first:

$ pep723-loader mypy script.py
# Dependencies installed, then mypy runs successfully

Installation

pip install pep723-loader

Or with uv:

uv tool install pep723-loader

Usage

Command Line

# Wrap any linter or tool
pep723-loader mypy script.py
pep723-loader basedpyright script.py
pep723-loader ruff check script.py
pep723-loader bandit script.py

# Pass any arguments through to the wrapped command
pep723-loader mypy --strict --warn-unreachable script.py

# Works with directories too
pep723-loader mypy scripts/

Pre-commit Integration

There are two ways to use pep723-loader with pre-commit:

Simple: If tools are already installed

First, add the tools to your project's dev dependencies:

uv add --dev pep723-loader mypy

Then configure the hook:

- repo: local
  hooks:
    - id: mypy
      name: mypy
      entry: pep723-loader mypy
      language: system
      types: [python]
      pass_filenames: true

Recommended: Self-contained with uv

This approach keeps tools out of your project's dependencies - uv provides them on-demand:

- repo: local
  hooks:
    - id: mypy
      name: mypy
      entry: uv run -q --no-sync --with pep723-loader --with mypy pep723-loader mypy
      language: system
      types: [python]
      pass_filenames: true

    - id: basedpyright
      name: basedpyright
      entry: uv run -q --no-sync --with pep723-loader --with basedpyright pep723-loader basedpyright
      language: system
      types: [python]
      pass_filenames: true

What do these flags do?

Flag Purpose
-q Quiet mode - suppresses uv's output so only linter output is shown
--no-sync Skip syncing the project's dependencies - faster, we only need the tools
--with <pkg> Temporarily add a package for this invocation

The --with packages are ephemeral - they're not installed into your project's virtualenv. However, they're cached by uv, so after the first run there's no download overhead.

How It Works

  1. Scans Python files passed as arguments for PEP 723 # /// script metadata blocks
  2. Extracts dependencies using uv export --script
  3. Installs dependencies via uv pip install
  4. Executes the wrapped command with all original arguments
  5. Propagates the wrapped command's exit code

Requirements

  • Python 3.10+
  • uv (used for dependency extraction and installation)

Related Projects

  • pep723-uv-interpreter - VS Code extension that automatically sets the Python interpreter for PEP 723 scripts, enabling auto-completion and intellisense in your editor.

These tools are complementary: pep723-uv-interpreter solves the IDE experience, while pep723-loader solves CI/pre-commit linting.

License

Apache License 2.0

About

Pre-commit fails to run linters on scripts using PEP 723 because the dependencies are missing, this wrapper handles that.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages