Skip to content

ryumasai/pydocfix

Repository files navigation

pydocfix

PyPI - Version PyPI - Python Version

A Python docstring linter that checks signature ↔ docstring consistency and auto-fixes violations.

Inspired by pydoclint, pydocfix goes further by automatically repairing the issues it finds.

Note

This project is in beta (v0.1.0b2). APIs and behavior may change before the stable release.

Why pydocfix?

pydoclint pioneered fast signature ↔ docstring consistency checking for Python. However, it can only report violations — all corrections must be done by hand.

pydocfix is built on pydocstring-rs, a CST (Concrete Syntax Tree) parser for docstrings written in Rust by the same author. CST preserves every token's byte offset, whitespace, and formatting, enabling:

  • Byte-level diagnostics — point to the exact token (parameter name, type annotation, section header), not just the line
  • Surgical auto-fix — edits replace precise byte ranges, so fixes never corrupt adjacent content
  • Iterative fix loop — apply non-overlapping fixes, re-parse, repeat until stable

Features

  • Auto-fix — Automatically repair docstring issues with safe/unsafe classification
  • Many rules across multiple categories (Summary, Parameters, Returns, Yields, Raises, Docstring)
  • Google & NumPy style support (powered by pydocstring-rs)
  • Signature ↔ docstring consistency — type mismatches, missing/extra parameters, ordering
  • Default value checking — detect missing optional / default annotations
  • Precise diagnostics — byte-level position information for every violation
  • Baseline — suppress existing violations so only new ones are reported
  • noqa — suppress specific violations inline or file-wide

Benchmark

pydocfix vs pydoclint

pydocfix performs linting and auto-fix generation in a single pass, yet is significantly faster than pydoclint (lint-only) thanks to parallel file processing and a Rust-based CST parser:

Project Files Lines pydocfix pydoclint Speedup
numpy 425 252K 0.74 sec 2.93 sec 4.0x
scikit-learn 635 372K 0.70 sec 4.41 sec 6.3x

Median of 5 runs (+ 1 warmup). pydocfix automatically parallelises across CPU cores (-j flag); pydoclint runs single-threaded.

Feature comparison

pydocfix pydoclint
Auto-fix (safe + unsafe)
Google style
NumPy style
Sphinx style
Parameter checking
Return type checking
Yield checking
Raises checking
Class docstring / __init__ rules -
Class attribute checking -
Default value checking (optional / default)
Byte-level diagnostics
Baseline suppression
Inline # noqa
flake8 plugin
pre-commit hook
Parallel execution

Installation

pip install pydocfix

Requires Python 3.11+.

Quick Start

# Check docstrings (report only)
pydocfix check src/

# Show diff of proposed fixes
pydocfix check src/ --diff

# Apply safe fixes
pydocfix check src/ --fix

# Apply safe + unsafe fixes
pydocfix check src/ --fix --unsafe-fixes

# Select / ignore specific rules or categories
pydocfix check src/ --select PRM --ignore RTN,YLD

# Parallel execution (auto-detected for ≥8 files; override with --jobs)
pydocfix check src/ --jobs 4

Configuration

Configure via pyproject.toml:

[tool.pydocfix]
# Rule selection (see "Rule selectors" for syntax details)
select = ["ALL"]
ignore = ["RIS"]
extend-safe-fixes = ["PRM"]
extend-unsafe-fixes = ["RTN", "YLD"]

# Type annotation style: "signature" | "docstring" | "both" | omitted (default)
#   omitted    — PRM103/RTN103/YLD103 and PRM104/RTN104/YLD104 are all disabled
#   signature  — redundant docstring types flagged (x104); missing signature annotations flagged (x105)
#   docstring  — missing docstring types flagged (x103); redundant signature annotations flagged (x106)
#   both       — missing docstring types flagged (x103); missing signature annotations flagged (x105)
type_annotation_style = "signature"

# Paths/patterns to exclude (in addition to built-in defaults)
exclude = ["tests/", "docs/"]

# Skip section-level rules (PRM001, RTN001, YLD001, RIS001) for one-line docstrings (default: true)
skip_short_docstrings = true

# Treat Optional[T], T | None, and Union[T, None] as equivalent to T
# when comparing types in PRM101/RTN101/YLD101 (default: false)
allow_optional_shorthand = false

# Path to the baseline file (relative to pyproject.toml)
baseline = ".pydocfix-baseline.json"

Rules

Each rule is classified as safe fix, unsafe fix, or report-only.

  • Safe fixes can be applied automatically with --fix (no risk of changing semantics)
  • Unsafe fixes require --fix --unsafe-fixes (may alter docstring meaning)

Summary (SUM)

Code Default Fix Description
SUM001 Missing summary line
SUM002 safe Summary doesn't end with period

Parameters (PRM)

Code Default Fix Description
PRM001 unsafe Missing Args/Parameters section
PRM002 safe Unnecessary Args/Parameters section
PRM003 safe self/cls documented in docstring
PRM004 unsafe Parameter in signature missing from docstring
PRM005 unsafe Parameter in docstring not in signature
PRM006 unsafe Parameter order mismatch
PRM007 unsafe Duplicate parameter name
PRM008 Parameter has no description
PRM009 safe Missing */** prefix on *args/**kwargs
PRM101 unsafe Docstring type doesn't match signature annotation
PRM102 unsafe No type in docstring or signature
PRM103 unsafe No type in docstring
PRM104 safe Redundant type in docstring (signature has annotation)
PRM105 No type annotation in signature (type_annotation_style = "signature" or "both")
PRM106 Redundant type annotation in signature (type_annotation_style = "docstring")
PRM201 unsafe Missing optional for parameter with default
PRM202 unsafe Missing default for parameter with default

Returns (RTN)

Code Default Fix Description
RTN001 unsafe Missing Returns section
RTN002 safe Unnecessary Returns section
RTN003 Returns entry has no description
RTN101 unsafe Return type mismatch
RTN102 unsafe No return type anywhere
RTN103 unsafe No return type in docstring
RTN104 safe Redundant return type in docstring
RTN105 No return type annotation in signature (type_annotation_style = "signature" or "both")
RTN106 Redundant return type annotation in signature (type_annotation_style = "docstring")

Yields (YLD)

Code Default Fix Description
YLD001 unsafe Missing Yields section
YLD002 safe Unnecessary Yields section
YLD003 Yields entry has no description
YLD101 unsafe Yield type mismatch
YLD102 unsafe No yield type anywhere
YLD103 unsafe No yield type in docstring
YLD104 safe Redundant yield type in docstring
YLD105 No yield type annotation in signature (type_annotation_style = "signature" or "both")
YLD106 Redundant yield type annotation in signature (type_annotation_style = "docstring")

Raises (RIS)

Code Default Fix Description
RIS001 unsafe Missing Raises section
RIS002 safe Unnecessary Raises section
RIS003 Raises entry has no description
RIS004 unsafe Raised exception not documented
RIS005 unsafe Documented exception not raised

Docstring (DOC)

Code Default Fix Description
DOC001 unsafe Section order doesn't match convention

Rule selectors

--select, --ignore, --extend-safe-fixes, --extend-unsafe-fixes (CLI) and their pyproject.toml equivalents all accept the same rule selector syntax:

Format Example Matches
Exact code PRM001 PRM001 only
Category prefix PRM All PRM rules
ALL ALL Every rule

Suppressing violations

Inline suppression (# noqa)

Add a # noqa comment on the closing """ line to suppress violations for that docstring.

def foo(x):
    """Short summary."""  # noqa                    # suppress all rules for this docstring

def bar(x):
    """Short summary."""  # noqa: PRM001            # suppress only PRM001

def baz(x):
    """Short summary."""  # noqa: PRM001, RTN001    # suppress multiple rules

# For multiline docstrings, put the comment on the closing """ line
def qux(x: int) -> int:
    """Short summary.

    Args:
        x: A value.
    """  # noqa: RTN001
    return x

Unused # noqa codes are reported as NOQ001 (and removed by --fix).

File-level suppression

Put a # pydocfix: noqa comment on its own line anywhere in the file to suppress violations for every docstring in the file.

# pydocfix: noqa            # suppress all rules in this file
# pydocfix: noqa: PRM001    # suppress only PRM001 in this file

Baseline

The baseline lets you record the current violation state of a project and suppress those existing violations on future runs — so only new violations are reported. This makes gradual adoption easier: fix violations at your own pace.

# Record all current violations as the baseline
pydocfix check src/ --baseline .pydocfix-baseline.json --generate-baseline

# Future runs only report violations not in the baseline
pydocfix check src/ --baseline .pydocfix-baseline.json

Or configure the baseline path in pyproject.toml so you don't need the flag every time:

[tool.pydocfix]
baseline = ".pydocfix-baseline.json"

Then generate and use it:

pydocfix check src/ --generate-baseline    # write baseline
pydocfix check src/                        # only new violations reported

The baseline file is a JSON file that records violations by symbol name (e.g. MyClass.my_method) rather than line number, so it stays stable when unrelated code is added or removed. Fixed violations are automatically removed from the baseline on the next run.

pre-commit

Add to .pre-commit-config.yaml:

repos:
  - repo: https://github.com/ryumasai/pydocfix
    rev: v0.1.0b2
    hooks:
      - id: pydocfix

To enable auto-fix:

      - id: pydocfix
        args: [--fix]

License

MIT

About

A Python docstring linter with auto-fix — checks signature ↔ docstring consistency using a CST-based parser

Topics

Resources

License

Stars

Watchers

Forks

Contributors