Skip to content

Commit

Permalink
Prevent wrapping of multiline fstrings in parens (#4325)
Browse files Browse the repository at this point in the history
  • Loading branch information
tusharsadhwani authored Apr 23, 2024
1 parent 551ede2 commit f4b644b
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 9 deletions.
11 changes: 3 additions & 8 deletions src/black/linegen.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
WHITESPACE,
Visitor,
ensure_visible,
fstring_to_string,
get_annotation_type,
is_arith_like,
is_async_stmt_or_funcdef,
Expand Down Expand Up @@ -504,7 +505,7 @@ def visit_NUMBER(self, leaf: Leaf) -> Iterator[Line]:

def visit_fstring(self, node: Node) -> Iterator[Line]:
# currently we don't want to format and split f-strings at all.
string_leaf = _fstring_to_string(node)
string_leaf = fstring_to_string(node)
node.replace(string_leaf)
yield from self.visit_STRING(string_leaf)

Expand Down Expand Up @@ -574,12 +575,6 @@ def __post_init__(self) -> None:
self.visit_guard = partial(v, keywords=Ø, parens={"if"})


def _fstring_to_string(node: Node) -> Leaf:
"""Converts an fstring node back to a string node."""
string_without_prefix = str(node)[len(node.prefix) :]
return Leaf(token.STRING, string_without_prefix, prefix=node.prefix)


def _hugging_power_ops_line_to_string(
line: Line,
features: Collection[Feature],
Expand Down Expand Up @@ -1421,7 +1416,7 @@ def normalize_invisible_parens( # noqa: C901
# of case will be not parsed as a Python keyword.
break

elif not (isinstance(child, Leaf) and is_multiline_string(child)):
elif not is_multiline_string(child):
wrap_in_parentheses(node, child, visible=False)

comma_check = child.type == token.COMMA
Expand Down
22 changes: 21 additions & 1 deletion src/black/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -762,8 +762,28 @@ def is_vararg(leaf: Leaf, within: Set[NodeType]) -> bool:
return p.type in within


def is_multiline_string(leaf: Leaf) -> bool:
def is_fstring(node: Node) -> bool:
"""Return True if the node is an f-string"""
return node.type == syms.fstring


def fstring_to_string(node: Node) -> Leaf:
"""Converts an fstring node back to a string node."""
string_without_prefix = str(node)[len(node.prefix) :]
string_leaf = Leaf(token.STRING, string_without_prefix, prefix=node.prefix)
string_leaf.lineno = node.get_lineno() or 0
return string_leaf


def is_multiline_string(node: LN) -> bool:
"""Return True if `leaf` is a multiline string that actually spans many lines."""
if isinstance(node, Node) and is_fstring(node):
leaf = fstring_to_string(node)
elif isinstance(node, Leaf):
leaf = node
else:
return False

return has_triple_quotes(leaf.value) and "\n" in leaf.value


Expand Down
18 changes: 18 additions & 0 deletions tests/data/cases/pep_701.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,15 @@
{1}_cte AS ()'''}
"""

value: str = f'''foo
'''

log(
f"Received operation {server_operation.name} from "
f"{self.writer._transport.get_extra_info('peername')}", # type: ignore[attr-defined]
level=0,
)

# output

x = f"foo"
Expand Down Expand Up @@ -222,3 +231,12 @@
WITH {f'''
{1}_cte AS ()'''}
"""

value: str = f"""foo
"""

log(
f"Received operation {server_operation.name} from "
f"{self.writer._transport.get_extra_info('peername')}", # type: ignore[attr-defined]
level=0,
)

0 comments on commit f4b644b

Please sign in to comment.