Skip to content

Fix patchedast aborting on ''' inside a double-quoted f-string#844

Open
marlon-costa-dc wants to merge 2 commits into
python-rope:masterfrom
marlon-costa-dc:fix/joinedstr-triple-quote-in-body
Open

Fix patchedast aborting on ''' inside a double-quoted f-string#844
marlon-costa-dc wants to merge 2 commits into
python-rope:masterfrom
marlon-costa-dc:fix/joinedstr-triple-quote-in-body

Conversation

@marlon-costa-dc

Copy link
Copy Markdown

Description

patchedast._JoinedStr.end_quote_char() selects the closing delimiter by max(len(quote)) over every quote character found anywhere in the f-string body. When a ''' sequence appears inside a double-quoted f-string, e.g.:

f"cfg = '''\n{value}"

the ''' in the body is picked as the delimiter instead of the actual closing ". That produces a reconstructed token that consume_joined_string() cannot locate, raising:

ValueError: substring not found

which surfaces as MismatchedTokenError and aborts rename / inline for the entire module — a single such f-string anywhere in a project breaks refactoring of that file.

Root cause

end_quote_char() chose the quote by length precedence rather than by which delimiter the f-string literally ends with. The opening quote (start_quote_char) was already correct; the closing detection was not symmetric.

Fix

Pick the delimiter the f-string literally ends with. QUOTE_CHARS is ordered longest-first (""", ''', ", '), so triple-quoted strings are still matched before single-character quotes, and the closing quote now always matches the opening one regardless of quote characters in the body.

Reproducer (before the fix)

import ast
from rope.refactor import patchedast
patchedast.patch_ast(ast.parse('f"a = \'\'\'{y}"\n'), 'f"a = \'\'\'{y}"\n')
# ValueError: substring not found

Testing

  • New regression test test_handling_format_strings_with_triple_quote_in_body in ropetest/refactor/patchedasttest.py (red before, green after).
  • Full suites green: ropetest/refactor/ = 944 passed, 7 skipped, 1 xfailed.
  • patchedasttest.py = 141 passed, 6 skipped.

Checklist

  • I have added tests that prove my fix is effective
  • I have updated CHANGELOG.md
  • I have made corresponding changes to user documentation for new features (n/a — bug fix)
  • I have made corresponding changes to library documentation for API changes (n/a — no API change)

Marlon Costa added 2 commits July 22, 2026 10:15
end_quote_char() picked the closing delimiter by max(len(quote)) over every
quote char found anywhere in the f-string body, so a ''' sequence inside a
double-quoted f-string (e.g. f"cfg = '''\n{value}") was selected as the
delimiter. That produced a reconstructed token consume_joined_string() could
not locate, raising `ValueError: substring not found` -> MismatchedTokenError,
which aborts rename/inline for the entire module.

Pick the delimiter the f-string literally ends with (QUOTE_CHARS is ordered
longest-first, so """/''' win over "/' correctly) so the closing quote matches
the opening one regardless of quote characters appearing in the body.

Adds a regression test; full patchedast, rename and inline suites stay green.
Copilot AI review requested due to automatic review settings July 22, 2026 13:18

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes patchedast f-string reconstruction when the f-string body contains quote sequences (notably ''') that previously confused _JoinedStr.end_quote_char(), causing consume_joined_string() failures and aborting refactorings for the whole module.

Changes:

  • Update _JoinedStr.end_quote_char() to select the closing delimiter based on what the source literally ends with (rather than the longest quote found in the body).
  • Add a regression test covering a double-quoted f-string containing ''' inside its body.
  • Document the fix in CHANGELOG.md.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
rope/refactor/patchedast.py Fixes f-string closing delimiter detection to avoid mismatches caused by quote sequences in the f-string body.
ropetest/refactor/patchedasttest.py Adds a regression test proving the bug and preventing future regressions.
CHANGELOG.md Notes the bug fix and its impact on refactorings.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +392 to +395
for quote_char in QUOTE_CHARS:
if self.source.source[end - len(quote_char) : end] == quote_char:
return self.source[end - len(quote_char) : end]
return self.source[end - 1 : end]
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.

2 participants