Skip to content

ruff check --add-noqa / --fix / ruff format truncate source files to 0 bytes on SIGINT (non-atomic in-place write) #26480

Description

@natetarrh

Summary

ruff check --add-noqa (and presumably --fix / ruff format) rewrites
source files in place without an atomic-rename step. If the process is
interrupted (SIGINT / crash) while a write is in flight, the affected files
are left truncated to 0 bytes — neither the old content nor the new content
survives.

The cache path was hardened against this in #9981 ("Use atomic write when
persisting cache"). The same pattern hasn't been applied to source-file
writes.

Repro

  1. Run ruff check --add-noqa (or ruff format, or ruff check --fix)
    against a large tree where many files will be rewritten.
  2. SIGINT (Ctrl-C) it mid-run, while ruff is rewriting files in parallel.
  3. find <tree> -name '*.py' -empty — a handful of files that previously
    had content are now 0 bytes. Their mtimes cluster tightly around the
    SIGINT.

Expected

An interrupted in-place rewrite should leave the original file intact (or
the fully-written new file), never an empty / partially-written file.
Standard pattern: write to <file>.tmp, fsync, then rename over the
original — rename(2) is atomic on POSIX, so a crash anywhere before the
rename leaves the original untouched.

Prior art in-repo

#9981 introduced atomic-rename for cache writes for exactly this reason.
The same fix shape (write-tmp + rename) should apply to the source-write
path used by --add-noqa, --fix, and ruff format.

Version

ruff 0.14.10, Linux (Docker, ext4).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions