Skip to content

Commit

Permalink
Merge d709a80 into a18ee40
Browse files Browse the repository at this point in the history
  • Loading branch information
hauntsaninja committed Nov 29, 2021
2 parents a18ee40 + d709a80 commit c955452
Showing 1 changed file with 46 additions and 17 deletions.
63 changes: 46 additions & 17 deletions src/black/trans.py
Original file line number Diff line number Diff line change
Expand Up @@ -942,6 +942,48 @@ def _get_max_string_length(self, line: Line, string_idx: int) -> int:
return max_string_length


def iter_fexpr_spans(s: str) -> Iterator[Tuple[int, int]]:
"""
Yields spans corresponding to expressions in a given f-string.
Assumes the input string is a valid f-string.
"""
stack = [] # our curly paren stack
i = 0
while i < len(s):
if s[i] == "{":
# if we're in a string part of the f-string, ignore escaped curly braces
if not stack and i + 1 < len(s) and s[i + 1] == "{":
i += 2
continue
stack.append(i)
i += 1
continue

if s[i] == "}":
if not stack:
i += 1
continue
j = stack.pop()
# we've made it back out of the expression! yield the span
if not stack:
yield (j, i + 1)
i += 1
continue

# if we're in an expression part of the f-string, fast forward through strings
if stack:
delim = None
if s[i : i + 3] in ("'''", '"""'):
delim = s[i : i + 3]
elif s[i] in ("'", '"'):
delim = s[i]
if delim:
i += len(delim)
while i < len(s) and s[i : i + len(delim)] != delim:
i += 1
i += 1


class StringSplitter(BaseStringSplitter, CustomSplitMapMixin):
"""
StringTransformer that splits "atom" strings (i.e. strings which exist on
Expand Down Expand Up @@ -981,17 +1023,6 @@ class StringSplitter(BaseStringSplitter, CustomSplitMapMixin):
"""

MIN_SUBSTR_SIZE: Final = 6
# Matches an "f-expression" (e.g. {var}) that might be found in an f-string.
RE_FEXPR: Final = r"""
(?<!\{) (?:\{\{)* \{ (?!\{)
(?:
[^\{\}]
| \{\{
| \}\}
| (?R)
)+
\}
"""

def do_splitter_match(self, line: Line) -> TMatchResult:
LL = line.leaves
Expand Down Expand Up @@ -1058,8 +1089,8 @@ def do_transform(self, line: Line, string_idx: int) -> Iterator[TResult[Line]]:
# contain any f-expressions, but ONLY if the original f-string
# contains at least one f-expression. Otherwise, we will alter the AST
# of the program.
drop_pointless_f_prefix = ("f" in prefix) and re.search(
self.RE_FEXPR, LL[string_idx].value, re.VERBOSE
drop_pointless_f_prefix = ("f" in prefix) and any(
True for _ in iter_fexpr_spans(LL[string_idx].value)
)

first_string_line = True
Expand Down Expand Up @@ -1299,9 +1330,7 @@ def _iter_fexpr_slices(self, string: str) -> Iterator[Tuple[Index, Index]]:
"""
if "f" not in get_string_prefix(string).lower():
return

for match in re.finditer(self.RE_FEXPR, string, re.VERBOSE):
yield match.span()
yield from iter_fexpr_spans(string)

def _get_illegal_split_indices(self, string: str) -> Set[Index]:
illegal_indices: Set[Index] = set()
Expand Down Expand Up @@ -1417,7 +1446,7 @@ def _normalize_f_string(self, string: str, prefix: str) -> str:
"""
assert_is_leaf_string(string)

if "f" in prefix and not re.search(self.RE_FEXPR, string, re.VERBOSE):
if "f" in prefix and not any(True for _ in iter_fexpr_spans(string)):
new_prefix = prefix.replace("f", "")

temp = string[len(prefix) :]
Expand Down

0 comments on commit c955452

Please sign in to comment.