Skip to content

Commit 23c8a17

Browse files
committed
Handle the edge case where an f-string looks like f"#{some_var}"
1 parent b78c976 commit 23c8a17

File tree

1 file changed

+39
-13
lines changed

1 file changed

+39
-13
lines changed

rope/refactor/similarfinder.py

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -269,10 +269,26 @@ def get_region(self):
269269

270270

271271
class CodeTemplate:
272+
_dollar_name_pattern = r"(?P<name>\$\{[^\s\$\}]*\})"
273+
272274
def __init__(self, template):
273275
self.template = template
274276
self._find_names()
275277

278+
@classmethod
279+
def _get_pattern(cls):
280+
if cls._match_pattern is None:
281+
pattern = "|".join(
282+
(
283+
codeanalyze.get_comment_pattern(),
284+
codeanalyze.get_string_pattern(),
285+
f"(?P<fstring>{codeanalyze.get_formatted_string_pattern()})",
286+
cls._dollar_name_pattern,
287+
)
288+
)
289+
cls._match_pattern = re.compile(pattern)
290+
return cls._match_pattern
291+
276292
def _find_names(self):
277293
self.names = {}
278294
for match in CodeTemplate._get_pattern().finditer(self.template):
@@ -283,6 +299,29 @@ def _find_names(self):
283299
self.names[name] = []
284300
self.names[name].append((start, end))
285301

302+
elif "fstring" in match.groupdict() and match.group("fstring") is not None:
303+
self._fstring_case(match)
304+
305+
def _fstring_case(self, fstring_match: re.Match):
306+
"""Needed because CodeTemplate._match_pattern short circuits
307+
as soon as it sees a '#', even if that '#' is inside a f-string
308+
that has a ${variable}."""
309+
310+
string_start, string_end = fstring_match.span("fstring")
311+
312+
for match in re.finditer(self._dollar_name_pattern, self.template):
313+
if match.start("name") < string_start:
314+
continue
315+
316+
if match.end("name") > string_end:
317+
break
318+
319+
start, end = match.span("name")
320+
name = self.template[start + 2 : end - 1]
321+
if name not in self.names:
322+
self.names[name] = []
323+
self.names[name].append((start, end))
324+
286325
def get_names(self):
287326
return self.names.keys()
288327

@@ -298,19 +337,6 @@ def substitute(self, mapping):
298337

299338
_match_pattern = None
300339

301-
@classmethod
302-
def _get_pattern(cls):
303-
if cls._match_pattern is None:
304-
pattern = (
305-
codeanalyze.get_comment_pattern()
306-
+ "|"
307-
+ codeanalyze.get_string_pattern()
308-
+ "|"
309-
+ r"(?P<name>\$\{[^\s\$\}]*\})"
310-
)
311-
cls._match_pattern = re.compile(pattern)
312-
return cls._match_pattern
313-
314340

315341
class _RopeVariable:
316342
"""Transform and identify rope inserted wildcards"""

0 commit comments

Comments
 (0)