1.7.8 crashes with a tokenize ValueError on a multi-line f-string inside parentheses
メンテナーはふだん 1 日以内に返信
@adityaanikam がすでに取り組んでいます。
2026年8月10日 から。
評価
調査の方向性
format.py:499-535 の classify.is_f_string とその consumer である _get_unmatched_start_end_indices から始め、続いて提供された複数行 f-string の再現コードを Python 3.11 で実行します。3.12 より前の f-string チェックによって token の開始行がどのように変わるかを追跡します。この入力で再現コードが ValueError なしにフォーマットされ、複数ファイルでの実行が中断されなければ完了です。
索引モデルが issue の本文から書いたものです。
説明
docformatter 1.7.8 aborts with a ValueError out of tokenize.untokenize when a file contains a multi-line f-string inside a parenthesized expression. 1.7.7 formats the same file cleanly.
Reproducer
def build(x):
return (
f"""a
{x}""",
)
$ docformatter --in-place t.py
File ".../docformatter/format.py", line 876, in _do_format_code
_code = tokenize.untokenize(self.new_tokens)
File "/usr/lib/python3.11/tokenize.py", line 216, in untokenize
self.add_whitespace(start)
File "/usr/lib/python3.11/tokenize.py", line 177, in add_whitespace
raise ValueError("start ({},{}) precedes previous end ({},{})"
ValueError: start (2,8) precedes previous end (3,4)
$ echo $?
1
No config file and no flags are needed. Two things narrow it:
- Remove the
fprefix and the run exits 3 (would reformat) with no error, so the f-string prefix is the trigger. - Assign the same f-string to a name instead of putting it in a tuple and it does not crash, so it has to sit inside parentheses.
Affected versions
| docformatter | Python 3.11 | Python 3.12 | Python 3.13 |
|---|---|---|---|
| 1.7.7 | ok | ok | ok |
| 1.7.8 | ValueError | ok | ok |
| master | ValueError | ok | ok |
Root cause
Bisected to d9ace1f ("fix: resolve several empty line regressions", #330). Its parent 7798699 is clean.
That commit adds a Python < 3.12 workaround to classify.is_f_string:
elif any(
[
token.string.startswith('f"""'),
prev_token.string.startswith('f"""'),
token.string.startswith("f'''"),
prev_token.string.startswith("f'''"),
]
):
return True
The PY312 branch above it tests tokenize.FSTRING_MIDDLE, which only appears inside an f-string. The < 3.12 branch instead matches any token whose text begins with f""", including an ordinary triple-quoted f-string that is not a docstring — and, via prev_token, the token that follows one.
The single consumer is _get_unmatched_start_end_indices (format.py:499), where a true verdict sets _start_row = prev_token.end[0] (format.py:526-535). That rewrites the f-string's start row back to where the previous token ended, so untokenize sees a token starting above one it has already emitted and raises.
Impact
With several files on one command line, the run aborts at the first offending file and exits 1, leaving every file after it unprocessed. On a 1,255-file tree this stopped at file 318 with no indication that the remainder was skipped.
Environment
CPython 3.11.15, Linux x86-64, docformatter==1.7.8 from PyPI.
- 主要言語
- Python
- スター
- 598
- フォーク
- 97
- 平均マージ
- 1日 8時間
- マージ済み PR(30日)
- 11
環境構築
このプロジェクトには開発コンテナ、Dockerfile、コントリビューションガイドがありません。まず README を読み、一般的な手順ははじめてのコントリビューションガイドを参照してください。
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
PyCQA/docformatter のほかの issue
-
fresh
難易度 3/5 1〜2日 初心者へのやさしさ 58/100
PyCQA/docformatter#399 ·
メンテナーはふだん 1 日以内に返信
-
1.7.8 rewrites the contents of a non-docstring triple-quoted string, silently changing its valueオープンC: convention P: bug U: high
難易度 3/5 1〜2日 初心者へのやさしさ 72/100
PyCQA/docformatter#366 ·
メンテナーはふだん 1 日以内に返信
-
C: stakeholder P: enhancement U: low
難易度 3/5 1〜2日 初心者へのやさしさ 65/100
PyCQA/docformatter#346 ·
メンテナーはふだん 1 日以内に返信
-
C: convention P: bug U: high
難易度 3/5 1〜2日 初心者へのやさしさ 48/100
PyCQA/docformatter#345 · リアクション 1 件 ·
メンテナーはふだん 1 日以内に返信
-
Multiline summary is not split correctly再び着手できるかも このイシューのプルリクエストはマージされずにクローズされました。 オープンC: convention P: bug U: high
難易度 3/5 1〜2日 初心者へのやさしさ 55/100
PyCQA/docformatter#315 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
PyCQA/docformatter の issue をすべて見る
似ている issue
-
resource-submission validation-passed
難易度 1/5 1時間未満 初心者へのやさしさ 88/100
hesreallyhim/awesome-claude-code#3214 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
room-commons replace accepts an empty old string and silently prepends the new text to the pageオープン
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100
doobidoo/mcp-memory-service#1650 ·
メンテナーはふだん 1 日以内に返信
-
難易度 1/5 1〜3時間 初心者へのやさしさ 82/100
-
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
ml-explore/mlx-lm#2002 ·
メンテナーはふだん 2 日以内に返信