diff --git a/src/black/linegen.py b/src/black/linegen.py index 4b29a049dba..980785e94fa 100644 --- a/src/black/linegen.py +++ b/src/black/linegen.py @@ -37,6 +37,7 @@ WHITESPACE, Visitor, ensure_visible, + fstring_to_string, get_annotation_type, is_arith_like, is_async_stmt_or_funcdef, @@ -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) @@ -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], @@ -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 diff --git a/src/black/nodes.py b/src/black/nodes.py index f75e0848663..9579b715ad2 100644 --- a/src/black/nodes.py +++ b/src/black/nodes.py @@ -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 diff --git a/tests/data/cases/pep_701.py b/tests/data/cases/pep_701.py index c5bc48e95f2..f4a69e47413 100644 --- a/tests/data/cases/pep_701.py +++ b/tests/data/cases/pep_701.py @@ -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" @@ -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, +)