docformatter deadlock with ruff-format: blank lines around nested definitions
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 3/5
- Tempo stimato
- 1-2 giorni
- Idoneità per principianti
- 68/100
Direzione di ricerca
Inizia con le riproduzioni minime in example.py e example_class.py, quindi esegui docformatter e ruff format con le configurazioni mostrate per riprodurre entrambi i cicli. Traccia il modo in cui docformatter classifica le righe vuote dopo docstring annidati su una sola riga e aggiungi una copertura di regressione per i due casi. Il lavoro è completo quando esecuzioni ripetute convergono senza che nessuno dei due strumenti annulli le modifiche dell’altro.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Summary
docformatter and ruff-format enter an infinite correction loop in pre-commit when
test functions contain nested def or class definitions whose only body is a one-line
docstring. The tools disagree on whether a blank line should follow the nested definition,
so each tool's output is the other tool's input and pre-commit never converges.
Two distinct sub-issues are present:
- Loop A — nested
defwith one-liner docstring: docformatter removes the blank line
after it; ruff-format adds it back. - Loop B — nested
classwith one-liner docstring (blank = truein config): docformatter
adds an extra blank line; ruff-format removes it.
Versions
| Tool | Version |
|---|---|
| docformatter (pip / pre-commit hook) | 1.7.8 |
ruff-format (pre-commit hook astral-sh/ruff-pre-commit) |
v0.15.9 |
| Python | 3.10.11 |
Configuration
pyproject.toml:
[tool.docformatter]
recursive = true
wrap-summaries = 120
wrap-descriptions = 120
blank = true
.pre-commit-config.yaml (relevant hooks):
- repo: https://github.com/PyCQA/docformatter
rev: v1.7.8
hooks:
- id: docformatter
language_version: python3.10
additional_dependencies: [tomli]
args: ["--in-place"]
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.15.9
hooks:
- id: ruff-check
args: ["--fix"]
- id: ruff-format
Minimal reproduction — Loop A
Create example.py:
def outer() -> None:
"""Outer function."""
def inner() -> None:
"""One-liner docstring."""
next_statement = 1
Run docformatter:
docformatter --in-place --blank example.py
Result — blank line removed:
def outer() -> None:
"""Outer function."""
def inner() -> None:
"""One-liner docstring."""
next_statement = 1 # ← blank line gone
Run ruff-format on that output:
ruff format example.py
Result — blank line restored (back to original):
def outer() -> None:
"""Outer function."""
def inner() -> None:
"""One-liner docstring."""
next_statement = 1 # ← blank line back
Running both again repeats the cycle indefinitely.
Minimal reproduction — Loop B (blank = true)
Create example_class.py:
def outer() -> None:
"""Outer function."""
class Inner:
"""One-liner class docstring."""
@some_decorator
class Another:
"""Another class."""
With blank = true, docformatter inserts an extra blank line after the one-liner
class docstring (before @some_decorator), producing two consecutive blank lines.
ruff-format then removes the extra one. Each tool undoes the other.
Root cause
Loop A
docformatter interprets the blank line between the closing """ of inner()'s docstring
and next_statement as being inside inner()'s function body and removes it as a
PEP 257 D202 violation ("no blank lines allowed after function docstring").
The blank line is not inside inner() — it is in the outer scope, separating two
statements. docformatter misattributes it because the last token of inner()'s body is
the closing """ of a one-liner docstring on the same line as the opening """, with no
other body statements.
ruff-format (Black-compatible) correctly requires the blank line between the nested
definition and the following statement per E301 / PEP 8.
Loop B
blank = true causes docformatter to insert a blank line at the end of one-liner class
docstrings when they are followed by another definition. Combined with the blank line
already present, this produces two blank lines (E303), which ruff-format then reduces
back to one.
Expected behaviour
docformatter should not remove the blank line that follows a nested function definition
whose only body is a one-liner docstring. That blank line belongs to the enclosing
scope, not to the nested function.
Workaround
Remove blank = true from [tool.docformatter] to mitigate Loop B.
Loop A has no configuration-level workaround; the only option is to exclude the affected
files from docformatter or avoid the nested-def-with-one-liner-docstring pattern in test
code.
- Lingua principale
- Python
- Stelle
- 598
- Fork
- 94
- Merge medio
- 12g 10h
- PR unite (30g)
- 1
Preparare l'ambiente
Questo progetto non fornisce container di sviluppo, Dockerfile né guida per i contributori, quindi l'ambiente è a tuo carico: parti dal suo README e consulta la nostra guida al primo contributo per i passaggi generali.
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di PyCQA/docformatter
-
fresh
Difficoltà 3/5 1-2 giorni Idoneità per principianti 78/100
PyCQA/docformatter#385 ·
-
C: convention P: bug U: high
Difficoltà 3/5 1-2 giorni Idoneità per principianti 74/100
PyCQA/docformatter#367 · 1 commento ·
-
1.7.8 rewrites the contents of a non-docstring triple-quoted string, silently changing its valueApertaC: convention P: bug U: high
Difficoltà 3/5 1-2 giorni Idoneità per principianti 72/100
PyCQA/docformatter#366 ·
-
C: stakeholder P: enhancement U: low
Difficoltà 3/5 1-2 giorni Idoneità per principianti 65/100
PyCQA/docformatter#346 ·
-
C: convention P: bug U: high
Difficoltà 3/5 1-2 giorni Idoneità per principianti 48/100
PyCQA/docformatter#345 · 1 reazione ·
Tutte le issue di PyCQA/docformatter
Issue simili
-
repo-audit
Difficoltà 2/5 1-3 ore Idoneità per principianti 75/100
scverse/repo-health#20 ·
I maintainer di solito rispondono entro 1 giorno
-
/context/prime scope override double-prefixes an entity-ref project and drops its scoped memoriesAperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 85/100
phasespace-labs/palinode#232 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
collective/icalendar#1858 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
I maintainer di solito rispondono entro 1 giorno
-
lfx-mcp cannot supply global variables: LangflowClient drops X-LANGFLOW-GLOBAL-VAR-* from envApertabug
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
langflow-ai/langflow#15496 ·
I maintainer di solito rispondono entro 1 giorno