Skip to main content

pymaxlines

CI License: MIT

A pre-commit hook that fails when a Python file or function has too many lines of code.

Requires Python 3.12+.

Why

A file that runs into the thousands of lines is hard to navigate, test, and review, yet few Python linters enforce a limit. Ruff has no max-lines rule and does not plan to add one. McCabe complexity catches convoluted control flow but ignores sheer size — a 600-line function with simple branches passes just fine.

The problem compounds with large language model (LLM) coding agents. Long files exhaust the context window and push agents toward destructive rewrites — splitting a file on a token boundary instead of a logical one. Enforcing a line budget per file and per function keeps the codebase in a shape that both humans and agents can work with.

pymaxlines counts only code lines, the way oxlint's max-lines rule does with skipBlankLines and skipComments, so docstrings, comments, and blank lines stay free. It applies one limit per file and one per function, with separate thresholds for test files.

Quick example

A failing run prints one line per offender and exits 1:

$ pymaxlines src/app/service.py
src/app/service.py: 412 code lines (max 400)
src/app/service.py:88: function 'handle_request' has 73 code lines (max 60)

Installation

Add the hook to .pre-commit-config.yaml and run pre-commit install or prek install:

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/jeffzi/pymaxlines
    rev: main # pin to a release tag once one is published
    hooks:
      - id: check-max-lines

To run it outside pre-commit:

uvx pymaxlines file1.py file2.py

Usage

The hook runs on .py and .pyi files. A file is a test file when it sits under a tests/ directory or is named test_*.py or *_test.py. Everything else is a source file. The flag table below uses these terms in its "Applies to" column.

Defaults, all overridable with flags:

Flag Applies to Default Meaning
--max-lines source 400 code lines per file
--max-lines-test test 800 code lines per file
--max-lines-per-function source 60 code lines per function, 0 disables
--max-lines-per-function-test test 0 code lines per function, 0 disables
--skip-blank-lines all True exclude blank lines from counts
--skip-comments all True exclude comment-only lines
--skip-docstrings all True exclude standalone docstrings

Pass flags through the hook's args:

- id: check-max-lines
  args: [--max-lines, "300", --max-lines-per-function, "40"]

Each --skip-* flag has a --no-skip-* counterpart. To count comment-only and blank lines:

- id: check-max-lines
  args: [--no-skip-comments, --no-skip-blank-lines]

What counts as a code line

By default (all --skip-* flags on):

  • Blank lines, comment-only lines, and standalone docstrings (module, class, or function) are free.
  • Whitespace-only lines inside a multi-line string are free.
  • Every other line counts once, including non-blank lines inside multi-line strings.
  • A function's count includes its def line and every code line up to the end of its body, nested functions included.
  • Decorator lines count toward the file total but not toward the decorated function's count.

Turning a --skip-* flag off (e.g. --no-skip-blank-lines) makes that category count toward both file and function totals.

pymaxlines reports unreadable files (missing, undecodable, or syntax errors) and fails the run.

Contributing

Run task --list for the development workflow. task check runs every hook; task test:matrix runs the suite on each supported Python version.

License

MIT. See LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pymaxlines-0.3.0.tar.gz (7.8 kB view details)

Uploaded Source

Built Distribution

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

pymaxlines-0.3.0-py3-none-any.whl (7.7 kB view details)

Uploaded Python 3

File details

Details for the file pymaxlines-0.3.0.tar.gz.

File metadata

  • Download URL: pymaxlines-0.3.0.tar.gz
  • Upload date:
  • Size: 7.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for pymaxlines-0.3.0.tar.gz
Algorithm Hash digest
SHA256 5c6c5940285d02547dc45085e0774e68f31b713584c2b000e7fe7fd81c14da1b
MD5 0ae1252742b9e286507ce24cf32d1fc8
BLAKE2b-256 1aedf590a161e50d0d9aba0d3e86cea25413f02b8a032fb7f8f78efa2050e51a

See more details on using hashes here.

Provenance

The following attestation bundles were made for pymaxlines-0.3.0.tar.gz:

Publisher: publish.yml on jeffzi/pymaxlines

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pymaxlines-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: pymaxlines-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 7.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for pymaxlines-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 e21e7f87b2652ba0af872d726558b6219bc6f23eca06aa429be3296a41659e6f
MD5 aa93c3c509be9971e1e68fdfd4477e25
BLAKE2b-256 59f956922936d65a344a0cd3e26156fe16f78f2f22d343708faa06689def4b9b

See more details on using hashes here.

Provenance

The following attestation bundles were made for pymaxlines-0.3.0-py3-none-any.whl:

Publisher: publish.yml on jeffzi/pymaxlines

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.7.0

2 files

0.6.0

2 files

0.5.0

2 files

0.4.0

2 files

This release

0.3.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page