patchedast: locate multi-line f-strings, replacement fields and match-sequence delimiters by parser coordinates - #882
Open
marlon-costa-dc wants to merge 3 commits into
Conversation
A PEP 701 replacement field may span lines, which the textual string pattern cannot match, so JoinedStr raised MismatchedTokenError; its region now comes from the node's own coordinates, implicit concatenation included. A MatchSequence took every character before its first pattern as its opening delimiter, so a grouped first pattern (`((a | b), c)`) produced `((`; the delimiters are now the bracket pair that encloses the whole node, found by tokenizing its segment. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In an implicit concatenation mixing f-strings and plain strings, the
field's opening brace was searched for as text and matched a "{" inside
a plain part (`"({})," f"{a}"`), shifting the field's region and
duplicating source on write. Since Python 3.12 the parser locates each
FormattedValue at its own "{"; the walker now starts there when the
coordinate points at one. test_extract_method_f_string_false_format_value
_in_regular_string, marked as an expected failure for this bug, passes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
marlon-costa-dc
pushed a commit
to marlon-costa-dc/rope
that referenced
this pull request
Sep 25, 2026
Adds the parser-coordinate patchedast fixes (upstream PR python-rope#882): multi-line f-string replacement fields, replacement-field braces in plain parts of a concatenation, and grouped first patterns in match sequences. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Three
patchedastdefects found by walking a ~6,300-file Python 3.13 codebase withget_patched_ast(source, True)and requiringwrite_ast()to reproduce each file exactly:x = (f"{a}{\n b\n}{c}").upper()raisedMismatchedTokenError:_JoinedStrlocated the literal with the textual string pattern, whose short-string form excludes newlines. The literal's region now comes from the node's own coordinates (ast_adapter), implicit concatenation included."({})," f"{a}"the field's{was searched for as text and matched the{inside the plain part, so theFormattedValueregion started at(andwrite_astduplicated source. Since Python 3.12 the parser locates eachFormattedValueat its own{; the walker starts there when the coordinate points at one (older parsers give the enclosing literal's position, which is left untouched). This also fixes the case covered bytest_extract_method_f_string_false_format_value_in_regular_string, whose@expectedFailureis removed.match-casestatement syntaxes #623).case (("a" | None), None):warnedUnexpected character in MatchSequence's opening_paren <((>and duplicated the source on write, because every character before the first pattern was taken as the delimiter. The sequence's delimiters are now the bracket pair that encloses the whole node, found by tokenizing its segment;case (1), (2):, whose outer parentheses belong to its elements, is also handled.Checklist
ropetest/refactor/patchedasttest.pyfail on currentmasterand pass here; the previously expected-failure extract test passes.🤖 Generated with Claude Code