docformatter deadlock with ruff-format: blank lines around nested definitions
Chưa có ai nhận issue này.
Đánh giá
- Độ khó
- 3/5
- Thời gian dự kiến
- 1-2 ngày
- Mức phù hợp với người mới
- 68/100
Hướng nghiên cứu
Bắt đầu với các bản tái hiện tối thiểu trong example.py và example_class.py, sau đó chạy docformatter và ruff format với các cấu hình được nêu để tái hiện cả hai vòng lặp. Theo dõi cách docformatter phân loại các dòng trống sau các docstring một dòng lồng nhau, đồng thời bổ sung phạm vi kiểm thử hồi quy cho hai trường hợp này. Công việc được hoàn tất khi các lần chạy lặp lại hội tụ mà không công cụ nào trong hai công cụ hoàn tác thay đổi của công cụ kia.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
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.
- Ngôn ngữ chính
- Python
- Star
- 598
- Fork
- 93
- Merge trung bình
- 12 ngày 10 giờ
- Pull request đã merge (30 ngày)
- 1
Chuẩn bị môi trường
Chúng tôi chưa kiểm tra các tệp thiết lập môi trường của dự án này. Hãy bắt đầu từ README và xem hướng dẫn đóng góp lần đầu của chúng tôi để biết các bước chung.
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của PyCQA/docformatter
-
fresh
Độ khó 3/5 1-2 ngày Mức phù hợp với người mới 78/100
PyCQA/docformatter#385 ·
-
C: convention P: bug U: high
Độ khó 3/5 1-2 ngày Mức phù hợp với người mới 74/100
PyCQA/docformatter#367 · 1 bình luận ·
-
1.7.8 rewrites the contents of a non-docstring triple-quoted string, silently changing its valueĐang mởC: convention P: bug U: high
Độ khó 3/5 1-2 ngày Mức phù hợp với người mới 72/100
PyCQA/docformatter#366 ·
-
C: stakeholder P: enhancement U: low
Độ khó 3/5 1-2 ngày Mức phù hợp với người mới 65/100
PyCQA/docformatter#346 ·
-
C: convention P: bug U: high
Độ khó 3/5 1-2 ngày Mức phù hợp với người mới 48/100
PyCQA/docformatter#345 · 1 reaction ·
Tất cả issue của PyCQA/docformatter
Issue tương tự
-
bug server
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
sportsdataverse/sportsdataverse-py#641 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 82/100
googleapis/google-cloud-python#18532 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
Maintainer thường phản hồi trong vòng 1 ngày