Skip to content

fix: do not crash on a backslash line continuation after a reformatted docstring - #378

Open
ekanshul wants to merge 1 commit into
PyCQA:masterfrom
ekanshul:fix-backslash-continuation-indices
Open

fix: do not crash on a backslash line continuation after a reformatted docstring#378
ekanshul wants to merge 1 commit into
PyCQA:masterfrom
ekanshul:fix-backslash-continuation-indices

Conversation

@ekanshul

Copy link
Copy Markdown

Fixes #377.

When a docstring edit shifts the lines after it, _do_update_token_indices() decides whether a token stays on the previous token's row with

is_same_position = tokens[i].start[0] == tokens[i - 1].end[0]

where tokens[i - 1] has already been shifted and tokens[i] has not. A parenthesised continuation has an NL token between the physical lines, which routes the next token to the "new row" branch; a backslash continuation has no NL token, so for the first token on the continuation line the comparison is satisfied by coincidence whenever the shift is one row, the token is placed on the previous token's row, and tokenize.untokenize() raises ValueError: start (r,c) precedes previous end (r,c).

The fix treats a token as starting a new row when the previous token's physical line ends with a backslash, which is exactly what the tokenizer guarantees for a continuation. Two cases in do_format_code.toml (class docstring and module docstring, each followed by a backslash continuation) fail on master with the ValueError and pass with the change.

Running docformatter --check over the Python 3.14 standard library, this crash was hit in _sitebuiltins.py, pdb.py, tokenize.py, ctypes/util.py, email/feedparser.py, encodings/utf_16.py, encodings/utf_32.py, encodings/utf_8_sig.py and http/cookies.py; with the fix all nine format, the output still parses, and the diff against the original is limited to docstrings.

When a docstring edit shifts the lines that follow it,
_do_update_token_indices() decides whether a token stays on the previous
token's row by comparing its (not yet shifted) start row with the (already
shifted) end row of the previous token. A backslash continuation emits no
NL token between the two physical lines, so for the first token of the
continuation line that comparison is satisfied by coincidence whenever the
shift is one row, the token is put on the previous token's row, and
tokenize.untokenize() raises "start (r,c) precedes previous end (r,c)".

Treat a token whose previous token's physical line ends with a backslash
as starting a new row, which is what the tokenizer guarantees.

Fixes PyCQA#377
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ValueError: start precedes previous end, for a backslash line continuation after a reformatted docstring

1 participant