@@ -269,10 +269,26 @@ def get_region(self):
269269
270270
271271class 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
315341class _RopeVariable :
316342 """Transform and identify rope inserted wildcards"""
0 commit comments