Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# **Upcoming release**

- Fix `patchedast` aborting on a `'''` sequence inside a double-quoted
f-string (`end_quote_char` picked the longest quote in the body instead of
the matching delimiter), which raised `MismatchedTokenError` and broke
rename/inline for the whole module (@marlon-costa-dc)
- ...

# Release 1.14.0
Expand Down
18 changes: 10 additions & 8 deletions rope/refactor/patchedast.py
Original file line number Diff line number Diff line change
Expand Up @@ -383,14 +383,16 @@ def start_quote_char():
return self.source[start : quote_pos + len(quote_char)]

def end_quote_char():
possible_quotes = [
(self.source.source.rfind(q, start, end), q)
for q in reversed(QUOTE_CHARS)
]
_, quote_pos, quote_char = max(
(len(q), pos, q) for pos, q in possible_quotes if pos != -1
)
return self.source[end - len(quote_char) : end]
# The closing delimiter is the quote char the f-string literally
# ends with -- NOT the longest quote char anywhere in the body.
# A ''' appearing inside a "..." f-string body must not be picked
# as the delimiter (issue: end_quote_char used max(len(q)) and
# mis-matched, producing a token that consume_joined_string could
# not locate).
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]
Comment on lines +392 to +395

QUOTE_CHARS = ['"""', "'''", '"', "'"]
offset = self.source.offset
Expand Down
13 changes: 13 additions & 0 deletions ropetest/refactor/patchedasttest.py
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,19 @@ def test_handling_format_strings_basic(self):
checker.check_children("JoinedStr", ['f"', "abc", "FormattedValue", "", '"'])
checker.check_children("FormattedValue", ["{", "", "Name", "", "}"])

@testutils.only_for_versions_higher("3.6")
def test_handling_format_strings_with_triple_quote_in_body(self):
# A double-quoted f-string whose body contains a ''' sequence must not
# confuse the end-quote detection (end_quote_char picked the longest
# quote in the body instead of the matching delimiter).
source = 'f"abc = \'\'\'{a}"\n'
ast_frag = patchedast.get_patched_ast(source, True)
checker = _ResultChecker(self, ast_frag)
checker.check_children(
"JoinedStr", ['f"', "abc = '''", "FormattedValue", "", '"']
)
checker.check_children("FormattedValue", ["{", "", "Name", "", "}"])

@testutils.only_for_versions_higher("3.6")
def test_handling_format_strings_with_implicit_join(self):
source = '''"1" + rf'abc{a}' f"""xxx{b} """\n'''
Expand Down
Loading